From e04890100fa8696ed4a19d1e0a7b71c6b1cf293f Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Fri, 18 Oct 2024 15:54:18 +0100 Subject: [PATCH 01/12] Add 8-bit SME LHS packing kernel with unit tests Resolves : [KLEIDIAI-171] Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 2 + .../pack/kai_lhs_pack_x8p2vlx1_x8_sme.c | 350 ++++++++++++++++++ .../pack/kai_lhs_pack_x8p2vlx1_x8_sme.h | 78 ++++ 3 files changed, 430 insertions(+) create mode 100644 kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c create mode 100644 kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b9720632..1481769f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,8 @@ set(KLEIDIAI_FILES_SME kai/ukernels/matmul/pack/kai_rhs_pack_kxn_x16p2vlx2b_x16_x16_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_nxk_x16p2vlx2b_x16_x16_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.c + kai/ukernels/matmul/pack/kai_rhs_pack_kxn_f32pb_f32_f32_16vlx1_sme.c + kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c ) set(KLEIDIAI_FILES_SME2 diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c new file mode 100644 index 00000000..2708bb41 --- /dev/null +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c @@ -0,0 +1,350 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#if !defined(__aarch64__) || !defined(__ARM_FEATURE_SVE2) +#error This file must be compiled for AArch64, FEAT_SVE2. +#else // Architectural features check. + +#include +#include +#include "kai/kai_common.h" + +static const size_t kai_mr = 2; +static const size_t kai_kr = 4; +static const size_t kai_sr = 1; + +size_t kai_get_m_step_lhs_pack_x8p2vlx1_x8_sme(size_t mr) { + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_UNUSED(mr); + + return (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; +} + +size_t kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t lhs_stride) { + KAI_ASSUME(m_idx % (kai_mr * kai_get_sme_vector_length_u8()) == 0); + + return m_idx * lhs_stride; +} + +size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr) { + const size_t scaled_mr = kai_mr * kai_get_sme_vector_length_u8(); + KAI_ASSUME(m_idx % scaled_mr == 0); + KAI_ASSUME(mr == scaled_mr); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == kai_sr); + + KAI_UNUSED(mr); + KAI_UNUSED(kr); + KAI_UNUSED(sr); + + return m_idx * kai_roundup(k, kai_kr) * sizeof(int8_t); +} + +size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx1_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr) { + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == kai_sr); + + KAI_UNUSED(mr); + KAI_UNUSED(kr); + KAI_UNUSED(sr); + + return (kai_roundup(m, kai_mr * kai_get_sme_vector_length_u8()/kai_kr) * kai_roundup(k,kai_kr) * sizeof(int8_t)); +} + +void kai_run_lhs_pack_x8p2vlx1_x8_sme( + size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, + const void* lhs, size_t lhs_stride, void* lhs_packed +) +{ + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == kai_sr); + KAI_ASSUME(lhs != NULL); + KAI_ASSUME(lhs_packed != NULL); + + KAI_ASSUME(m_idx_start == 0); + + const size_t block_height = (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; + const size_t width = k; + const size_t row_offset = 0; + + const void* in[block_height]; + + for (size_t block_y = 0; block_y < m; block_y += block_height) { + const size_t height = KAI_MIN(m - block_y, block_height); + void* out = lhs_packed + block_y * kai_roundup(k, kai_kr) * sizeof(int8_t); + + for (size_t y = 0; y < height; y++) { + in[y] = lhs + (block_y + y) * lhs_stride; + } + + __asm__ __volatile__( + ".inst 0xd503477f // SMSTART ZA\n" + "mov x23, %x[width]\n" + "mov x21, %x[width]\n" + "cntb x20\n" + "incb x23\n" + "sub x7, x20, #0x1\n" + "cntw x8\n" + "sub x23, x23, #0x1\n" + "ands x7, x21, x7\n" + "udiv x23, x23, x20\n" // n_passes = ceildiv(width, VL) + "csel x7, x7, x20, NE\n" + "lsl x22, %x[height], #0x1\n" // height * 2 + "lsl x21, x8, #0x1\n" + "sub x20, x23, #0x1\n" + "add x7, x7, #0x3\n" + "sub x17, x8, #0x2\n" + "whilelt p9.b, XZR, x22\n" + "whilelt p8.b, x21, x22\n" + "mov x16, #0x0\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + "cntw x9, ALL, MUL #2\n" + "cntw x28, ALL, MUL #3\n" + "ldr x27, [x11, #0x0]\n" + "lsr x20, x20, #0x1\n" // n_loops = (n_passes - 1) / 2 + "and x26, x23, #0x1\n" // odd_tail = bool(n_passes & 0x1) + "ldr x25, [x10, #0x0]\n" + "lsr x7, x7, #0x2\n" + "ptrue p11.s\n" + "ldr x24, [x11, #0x8]\n" + "zip1 p10.b, p9.b, p8.b\n" + "mov x23, %x[row_offset]\n" + "ldr x21, [x10, #0x8]\n" + "mov x22, %x[out]\n" + "whilelt p9.b, x16, %x[width]\n" + "whilelt p8.b, x16, %x[width]\n" + "add x11, x11, #0x10\n" + "add x10, x10, #0x10\n" + "mov x12, #0x0\n" + "cbz x17, 2f\n" + "1:" // K loop: Charge: Loop + ".inst 0x25246143 // psel p3.b, p8.b/Z, p10.b[w12]\n" + ".inst 0x252c6142 // psel p2.b, p8.b/Z, p10.b[w12, #1]\n" + ".inst 0x25646141 // psel p1.b, p8.b/Z, p10.b[w12, #4]\n" + ".inst 0x256c6140 // psel p0.b, p8.b/Z, p10.b[w12, #5]\n" + ".inst 0xe0170f60 // ld1b { za0h.b[x12] }, p3/Z, [x27, x23]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0170b21 // ld1b { za0h.b[x12, #1] }, p2/Z, [x25, x23]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0170704 // ld1b { za0h.b[x12, #4] }, p1/Z, [x24, x23]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01702a5 // ld1b { za0h.b[x12, #5] }, p0/Z, [x21, x23]\n" + "add x12, x12, #0x8\n" + "ldr x21, [x10, #0x8]\n" + "add x10, x10, #0x10\n" + "cmp x12, x17, LSL #2\n" + "blt 1b\n" + "2:" // K loop: Charge: End + ".inst 0x25246143 // psel p3.b, p8.b/Z, p10.b[w12]\n" + ".inst 0x252c6142 // psel p2.b, p8.b/Z, p10.b[w12, #1]\n" + ".inst 0x25646141 // psel p1.b, p8.b/Z, p10.b[w12, #4]\n" + ".inst 0x256c6140 // psel p0.b, p8.b/Z, p10.b[w12, #5]\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + ".inst 0xe0170f60 // ld1b { za0h.b[x12] }, p3/Z, [x27, x23]\n" + "ldr x27, [x11, #0x0]\n" + "incb x16\n" + ".inst 0xe0170b21 // ld1b { za0h.b[x12, #1] }, p2/Z, [x25, x23]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0170704 // ld1b { za0h.b[x12, #4] }, p1/Z, [x24, x23]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01702a5 // ld1b { za0h.b[x12, #5] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + "add x10, x10, #0x10\n" + "incb x23\n" + "cbz x20, 8f\n" + "mov x20, x20\n" + "3:" // K loop: Main loop + "whilelt p8.b, x16, %x[width]\n" + "mov x15, #0x0\n" + "mov x14, #0x0\n" + "cbz x17, 5f\n" + "4:" // K loop: Main loop: First: Loop + ".inst 0x25376143 // psel p3.b, p8.b/Z, p10.b[w15, #2]\n" + ".inst 0x253f6142 // psel p2.b, p8.b/Z, p10.b[w15, #3]\n" + ".inst 0x25776141 // psel p1.b, p8.b/Z, p10.b[w15, #6]\n" + ".inst 0x257f6140 // psel p0.b, p8.b/Z, p10.b[w15, #7]\n" + ".inst 0xe0176f62 // ld1b { za0h.b[x15, #2] }, p3/Z, [x27, x23]\n" + ".inst 0x25266d23 // psel p3.b, p11.b/Z, p9.b[w14]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0176b23 // ld1b { za0h.b[x15, #3] }, p2/Z, [x25, x23]\n" + ".inst 0x25266d22 // psel p2.b, p11.b/Z, p9.b[w14]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0176706 // ld1b { za0h.b[x15, #6] }, p1/Z, [x24, x23]\n" + ".inst 0x252e6d21 // psel p1.b, p11.b/Z, p9.b[w14, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01762a7 // ld1b { za0h.b[x15, #7] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252e6d20 // psel p0.b, p11.b/Z, p9.b[w14, #1]\n" + "add x10, x10, #0x10\n" + ".inst 0xe0bfcec0 // st1w { za0v.s[x14] }, p3/Z, [x22, XZR, LSL #2]\n" + "add x15, x15, #0x8\n" + ".inst 0xe0a8cac4 // st1w { za1v.s[x14] }, p2/Z, [x22, x8, LSL #2]\n" + ".inst 0xe0a9c6c1 // st1w { za0v.s[x14, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bcc2c5 // st1w { za1v.s[x14, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "add x14, x14, #0x2\n" + "addvl x22, x22, #4\n" + "cmp x14, x17\n" + "blt 4b\n" + "5:" // K loop: Main loop: First: Tail + ".inst 0x25376143 // psel p3.b, p8.b/Z, p10.b[w15, #2]\n" + ".inst 0x253f6142 // psel p2.b, p8.b/Z, p10.b[w15, #3]\n" + ".inst 0x25776141 // psel p1.b, p8.b/Z, p10.b[w15, #6]\n" + ".inst 0x257f6140 // psel p0.b, p8.b/Z, p10.b[w15, #7]\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + ".inst 0xe0176f62 // ld1b { za0h.b[x15, #2] }, p3/Z, [x27, x23]\n" + ".inst 0x25266d23 // psel p3.b, p11.b/Z, p9.b[w14]\n" + "ldr x27, [x11, #0x0]\n" + "mov x13, #0x0\n" + ".inst 0xe0176b23 // ld1b { za0h.b[x15, #3] }, p2/Z, [x25, x23]\n" + ".inst 0x25266d22 // psel p2.b, p11.b/Z, p9.b[w14]\n" + "ldr x25, [x10, #0x0]\n" + "mov x12, #0x0\n" + ".inst 0xe0176706 // ld1b { za0h.b[x15, #6] }, p1/Z, [x24, x23]\n" + ".inst 0x252e6d21 // psel p1.b, p11.b/Z, p9.b[w14, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01762a7 // ld1b { za0h.b[x15, #7] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252e6d20 // psel p0.b, p11.b/Z, p9.b[w14, #1]\n" + "whilelt p9.b, x16, %x[width]\n" + ".inst 0xe0bfcec0 // st1w { za0v.s[x14] }, p3/Z, [x22, XZR, LSL #2]\n" + "incb x16\n" + "add x10, x10, #0x10\n" + ".inst 0xe0a8cac4 // st1w { za1v.s[x14] }, p2/Z, [x22, x8, LSL #2]\n" + "incb x23\n" + "whilelt p8.b, x16, %x[width]\n" + ".inst 0xe0a9c6c1 // st1w { za0v.s[x14, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bcc2c5 // st1w { za1v.s[x14, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "addvl x22, x22, #4\n" + "cbz x17, 7f\n" + "6:" // K loop: Main loop: Second: Loop + ".inst 0x25256143 // psel p3.b, p8.b/Z, p10.b[w13]\n" + ".inst 0x252d6142 // psel p2.b, p8.b/Z, p10.b[w13, #1]\n" + ".inst 0x25656141 // psel p1.b, p8.b/Z, p10.b[w13, #4]\n" + ".inst 0x256d6140 // psel p0.b, p8.b/Z, p10.b[w13, #5]\n" + ".inst 0xe0172f60 // ld1b { za0h.b[x13] }, p3/Z, [x27, x23]\n" + ".inst 0x25246d23 // psel p3.b, p11.b/Z, p9.b[w12]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0172b21 // ld1b { za0h.b[x13, #1] }, p2/Z, [x25, x23]\n" + ".inst 0x25246d22 // psel p2.b, p11.b/Z, p9.b[w12]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0172704 // ld1b { za0h.b[x13, #4] }, p1/Z, [x24, x23]\n" + ".inst 0x252c6d21 // psel p1.b, p11.b/Z, p9.b[w12, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01722a5 // ld1b { za0h.b[x13, #5] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252c6d20 // psel p0.b, p11.b/Z, p9.b[w12, #1]\n" + "add x10, x10, #0x10\n" + ".inst 0xe0bf8ec8 // st1w { za2v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" + "add x13, x13, #0x8\n" + ".inst 0xe0a88acc // st1w { za3v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" + ".inst 0xe0a986c9 // st1w { za2v.s[x12, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bc82cd // st1w { za3v.s[x12, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "add x12, x12, #0x2\n" + "addvl x22, x22, #4\n" + "cmp x12, x17\n" + "blt 6b\n" + "7:" // K loop: Main loop: Second: Tail + ".inst 0x25256143 // psel p3.b, p8.b/Z, p10.b[w13]\n" + ".inst 0x252d6142 // psel p2.b, p8.b/Z, p10.b[w13, #1]\n" + ".inst 0x25656141 // psel p1.b, p8.b/Z, p10.b[w13, #4]\n" + ".inst 0x256d6140 // psel p0.b, p8.b/Z, p10.b[w13, #5]\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + ".inst 0xe0172f60 // ld1b { za0h.b[x13] }, p3/Z, [x27, x23]\n" + ".inst 0x25246d23 // psel p3.b, p11.b/Z, p9.b[w12]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0172b21 // ld1b { za0h.b[x13, #1] }, p2/Z, [x25, x23]\n" + ".inst 0x25246d22 // psel p2.b, p11.b/Z, p9.b[w12]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0172704 // ld1b { za0h.b[x13, #4] }, p1/Z, [x24, x23]\n" + ".inst 0x252c6d21 // psel p1.b, p11.b/Z, p9.b[w12, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01722a5 // ld1b { za0h.b[x13, #5] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252c6d20 // psel p0.b, p11.b/Z, p9.b[w12, #1]\n" + "whilelt p9.b, x16, %x[width]\n" + ".inst 0xe0bf8ec8 // st1w { za2v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" + "subs x20, x20, #0x1\n" + "add x10, x10, #0x10\n" + ".inst 0xe0a88acc // st1w { za3v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" + "incb x16\n" + "incb x23\n" + ".inst 0xe0a986c9 // st1w { za2v.s[x12, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bc82cd // st1w { za3v.s[x12, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "addvl x22, x22, #4\n" + "bgt 3b\n" + "8:" // K loop: Tails + "cbnz x26, 11f\n" + "mov x11, %x[in]\n" + "whilelt p8.b, x16, %x[width]\n" + "mov x13, #0x0\n" + "mov x12, #0x0\n" + "9:" // K loop: Tails: Even: First + ".inst 0x25306d23 // psel p3.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25306d22 // psel p2.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25356141 // psel p1.b, p8.b/Z, p10.b[w13, #2]\n" + ".inst 0x253d6140 // psel p0.b, p8.b/Z, p10.b[w13, #3]\n" + ".inst 0xe0bf8ec0 // st1w { za0v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" + ".inst 0xe0a88ac4 // st1w { za1v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" + "add x12, x12, #0x1\n" + "addvl x22, x22, #2\n" + "ldr x21, [x11, #0x0]\n" + "cmp x12, x8\n" + "ldr x20, [x11, x8, LSL #0x3]\n" + "add x11, x11, #0x8\n" + ".inst 0xe01726a2 // ld1b { za0h.b[x13, #2] }, p1/Z, [x21, x23]\n" + ".inst 0xe0172283 // ld1b { za0h.b[x13, #3] }, p0/Z, [x20, x23]\n" + "add x13, x13, #0x4\n" + "blt 9b\n" + "whilelt p9.b, x16, %x[width]\n" + "whilelt p8.b, x16, %x[width]\n" + "mov x20, #0x0\n" + "mov x12, #0x0\n" + "10:" // K loop: Tails: Even: Second + ".inst 0x25306d21 // psel p1.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25306d20 // psel p0.s, p11.s/Z, p9.s[w12]\n" + "add x20, x20, #0x4\n" + ".inst 0xe0bf86c8 // st1w { za2v.s[x12] }, p1/Z, [x22, XZR, LSL #2]\n" + ".inst 0xe0a882cc // st1w { za3v.s[x12] }, p0/Z, [x22, x8, LSL #2]\n" + "add x12, x12, #0x1\n" + "addvl x22, x22, #2\n" + "cmp x12, x7\n" + "blt 10b\n" + "whilelt p8.b, x16, %x[width]\n" + "b 13f\n" + "11:" // K loop: Tails: Odd + "mov x12, #0x0\n" + "12:" // K loop: Tails: Odd: Loop + ".inst 0x25306d21 // psel p1.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25306d20 // psel p0.s, p11.s/Z, p9.s[w12]\n" + ".inst 0xe0bf86c0 // st1w { za0v.s[x12] }, p1/Z, [x22, XZR, LSL #2]\n" + ".inst 0xe0a882c4 // st1w { za1v.s[x12] }, p0/Z, [x22, x8, LSL #2]\n" + "add x12, x12, #0x1\n" + "addvl x22, x22, #2\n" + "cmp x12, x7\n" + "blt 12b\n" + "13:" // K loop: End + "mov %x[out], x22\n" + ".inst 0xd503467f // SMSTOP\n" + : [out] "+&r" (out) + : [height] "r" (height), [in] "r" (in), [row_offset] "r" (row_offset), [width] "r" (width) + : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31" + ); + } +} + +#endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h new file mode 100644 index 00000000..d50607e5 --- /dev/null +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h @@ -0,0 +1,78 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/// Gets m step value. +/// +/// The starting row index must be divisible by `m_step`. +/// +/// @param[in] mr Number of rows to be interleaved. +/// +/// @return The m step value. +size_t kai_get_m_step_lhs_pack_x8p2vlx1_x8_sme(size_t mr); + +/// Gets the offset in bytes to the data element in the LHS buffer. +/// +/// @param[in] m_idx Row index. +/// @param[in] lhs_stride Row stride in bytes. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t lhs_stride); + +/// Gets the offset in bytes to the data element in the packed LHS buffer. +/// +/// @param[in] m_idx Row index in the unpacked LHS matrix. +/// @param[in] k Number of columns in the unpacked LHS matrix. +/// @param[in] mr Number of rows to be interleaved. +/// @param[in] kr Unused. Must be 1. +/// @param[in] sr Unused. Must be 1. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr); + +/// Gets the size in bytes of the packed LHS buffer. +/// +/// @param[in] m Number of rows in the unpacked LHS matrix. +/// @param[in] k Number of columns in the unpacked LHS matrix. +/// @param[in] mr Number of rows to be interleaved. +/// @param[in] kr Unused. Must be 1. +/// @param[in] sr Unused. Must be 1. +/// +/// @return The size in bytes of the packed LHS buffer. +size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx1_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr); + +/// Runs the LHS packing function for matrix multiplication. +/// +/// The pointer of each buffers (LHS and packed LHS) needs to be added with offset +/// calculated using the following functions: +/// +/// * LHS: @ref kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme. +/// * Packed LHS: @ref kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme. +/// +/// @param[in] m Number of rows of the unpacked LHS matrix. +/// @param[in] k Common dimension between the LHS and RHS matrix. +/// @param[in] mr Block size in M dimension. It must be 2 * kai_get_sme_vector_length_u8(). +/// @param[in] kr Block size in K dimension. It must be 4. +/// @param[in] sr Number of kr splits. It must be 1. +/// @param[in] m_idx_start Unused. Must be 0. +/// @param[in] lhs LHS matrix data buffer. +/// @param[in] lhs_stride Row stride in bytes of the LHS matrix. +/// @param[out] lhs_packed Packed RHS matrix. +void kai_run_lhs_pack_x8p2vlx1_x8_sme( + size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, + const void* lhs, size_t lhs_stride, void* lhs_packed +); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus -- GitLab From c7c18cee9b9b4e20c17a784548ab6468b77f1d9c Mon Sep 17 00:00:00 2001 From: Viet-Hoa Do Date: Wed, 23 Oct 2024 17:32:35 +0100 Subject: [PATCH 02/12] Add SME Int8 GEMM * Add the SME Int8 GEMM set of microkernels: - LHS packing kernel. - Non-transposed RHS packing kernel. - Main kernel. * Update the test framework to support static int8 GEMM. Signed-off-by: Viet-Hoa Do --- CMakeLists.txt | 8 +- kai/kai_common.h | 11 + ...mp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c | 401 ++++++++++++++++++ ...mp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h | 127 ++++++ .../pack/kai_lhs_pack_x8p2vlx1_x8_sme.c | 350 --------------- .../pack/kai_lhs_pack_x8p2vlx4_x8_sme.c | 352 +++++++++++++++ ...8_sme.h => kai_lhs_pack_x8p2vlx4_x8_sme.h} | 19 +- ..._pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c | 199 +++++++++ ..._pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h | 83 ++++ test/reference/binary_elementwise.cpp | 28 ++ test/reference/binary_elementwise.hpp | 38 ++ test/reference/clamp.cpp | 32 ++ test/reference/clamp.hpp | 18 + test/reference/fill.cpp | 61 ++- test/reference/fill.hpp | 35 ++ test/reference/matmul.cpp | 81 +++- test/reference/matmul.hpp | 11 + test/reference/matmul_pack.cpp | 54 +++ test/reference/matmul_pack.hpp | 44 ++ test/reference/pack.cpp | 50 +++ test/reference/pack.hpp | 64 +++ test/reference/quantize.cpp | 12 +- test/reference/quantize.hpp | 6 + test/reference/reduce.cpp | 51 +++ test/reference/reduce.hpp | 19 + test/reference/reorder.cpp | 50 +++ test/reference/reorder.hpp | 72 ++++ test/reference/transpose.cpp | 24 +- test/reference/transpose.hpp | 13 +- .../matmul_clamp_qai8_qai8p_qsi8cp_test.cpp | 251 +++++++++++ 30 files changed, 2190 insertions(+), 374 deletions(-) create mode 100644 kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c create mode 100644 kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h delete mode 100644 kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c create mode 100644 kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c rename kai/ukernels/matmul/pack/{kai_lhs_pack_x8p2vlx1_x8_sme.h => kai_lhs_pack_x8p2vlx4_x8_sme.h} (84%) create mode 100644 kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c create mode 100644 kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h create mode 100644 test/reference/clamp.cpp create mode 100644 test/reference/clamp.hpp create mode 100644 test/reference/matmul_pack.cpp create mode 100644 test/reference/matmul_pack.hpp create mode 100644 test/reference/reorder.cpp create mode 100644 test/reference/reorder.hpp create mode 100644 test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1481769f..3c0a3a0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,7 +147,8 @@ set(KLEIDIAI_FILES_SME kai/ukernels/matmul/pack/kai_rhs_pack_nxk_x16p2vlx2b_x16_x16_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_kxn_f32pb_f32_f32_16vlx1_sme.c - kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c + kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c + kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c ) set(KLEIDIAI_FILES_SME2 @@ -156,6 +157,7 @@ set(KLEIDIAI_FILES_SME2 kai/ukernels/matmul/matmul_clamp_f32_f32_f32p/kai_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla.c kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa.c kai/ukernels/matmul/matmul_clamp_f16_f16_f16p/kai_matmul_clamp_f16_f16_f16p2vlx2b_1x16vl_sme2_dot.c + kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c ) add_library(kleidiai) @@ -221,6 +223,7 @@ if(KLEIDIAI_BUILD_TESTS) test/reference/binary_elementwise.cpp test/reference/matmul.cpp + test/reference/matmul_pack.cpp test/reference/fill.cpp test/reference/pack.cpp test/reference/pad.cpp @@ -228,6 +231,8 @@ if(KLEIDIAI_BUILD_TESTS) test/reference/reduce.cpp test/reference/transpose.cpp test/reference/cast.cpp + test/reference/clamp.cpp + test/reference/reorder.cpp ) target_compile_options(kleidiai_test_framework @@ -250,6 +255,7 @@ if(KLEIDIAI_BUILD_TESTS) test/tests/matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp test/tests/matmul_clamp_f16_bf16p_bf16p_test.cpp test/tests/matmul_clamp_f32_bf16p_bf16p_test.cpp + test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp ) target_link_libraries(kleidiai_test diff --git a/kai/kai_common.h b/kai/kai_common.h index 33252850..f2c2aae0 100644 --- a/kai/kai_common.h +++ b/kai/kai_common.h @@ -175,6 +175,17 @@ struct kai_rhs_pack_qs4cxs1s0_param { uint8_t rhs_zero_point; /**< RHS Matrix quantization zero-point */ }; +struct kai_rhs_pack_qsi8_params { + int32_t input_zero_point; + float scale_multiplier; +}; + +struct kai_matmul_requantize32_params { + int32_t min_value; + int32_t max_value; + int32_t output_zero_point; +}; + #ifdef __cplusplus } #endif diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c new file mode 100644 index 00000000..e300d42c --- /dev/null +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c @@ -0,0 +1,401 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#if !defined(__aarch64__) || !defined(__ARM_FEATURE_SVE2) +#error This file must be compiled for AArch64, FEAT_SVE2. +#else // Architectural features check. + +#include +#include + +#include "kai/kai_common.h" + +static const size_t kai_mr = 2; +static const size_t kai_nr = 2; +static const size_t kai_kr = 1; +static const size_t kai_sr = 1; + +size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { + return kai_mr * kai_get_sme_vector_length_u8(); +} + +size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { + return kai_nr * kai_get_sme_vector_length_u8(); +} + +size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { + return kai_mr * kai_get_sme_vector_length_u8(); +} + +size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { + return kai_nr * kai_get_sme_vector_length_u8(); +} + +size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { + return kai_kr; +} + +size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { + return kai_sr; +} + +size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k) { + KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); + return m_idx * k * sizeof(int8_t); +} + +size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k) { + KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); + return n_idx * (k * sizeof(int8_t) + sizeof(int32_t)); +} + +size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( + size_t m_idx, size_t n_idx, size_t dst_stride) { + KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); + KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); + + return m_idx * dst_stride + n_idx * sizeof(int8_t); +} + +size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m, size_t n) { + return m * n * sizeof(int8_t); +} + +void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( + size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, + size_t dst_stride_col, + + const struct kai_matmul_requantize32_params* params + +) { + KAI_ASSUME(dst_stride_col == sizeof(int8_t)); + + typedef struct { + const void* A; + const void* B; + + void* C; + long ldcb; + long M, N, K; + int32_t min; + int32_t max; + int32_t result_zero_point; + + void* accumulator_buffer; + uint64_t flags; + } KernelArgs; + + KernelArgs args; + + args.A = lhs_packed; + args.B = rhs_packed; + + args.C = dst; + args.ldcb = dst_stride_row; + args.M = m; + args.N = n; + args.K = k; + args.min = params->min_value; + args.max = params->max_value; + args.result_zero_point = params->output_zero_point; + + args.accumulator_buffer = NULL; + args.flags = 0; + + __asm__ __volatile__( + ".inst 0xd503477f // SMSTART ZA\n" + "ldr w14, [%x[args], %[offsetof_M]]\n" + "mov x13, #0x0\n" + "mov x11, #0x0\n" + "ptrue p1.b\n" + ".inst 0x25207811 // ptrue pn9.b\n" + "ldr w10, [%x[args], %[offsetof_N]]\n" + "ldr x9, [%x[args], %[offsetof_A]]\n" + "1:" // M loop + "ldr x28, [%x[args], %[offsetof_B]]\n" + "2:" // N loop + ".inst 0x25aa4570 // whilelt pn8.s, x11, x10, VLx2\n" + ".inst 0xc00800ff // zero { zad0, zad1, zad2, zad3, zad4, zad5, zad6, zad7 }\n" + "mov x27, x9\n" + ".inst 0xa040438e // ld1w { z14.s-z15.s }, p8/Z, [x28]\n" // Load bias + "addvl x28, x28, #2\n" + ".inst 0xc09025c0 // addha za0.s, p1/M, p1/M, z14.s\n" + ".inst 0xc09025e1 // addha za1.s, p1/M, p1/M, z15.s\n" + ".inst 0xc09025c2 // addha za2.s, p1/M, p1/M, z14.s\n" + ".inst 0xc09025e3 // addha za3.s, p1/M, p1/M, z15.s\n" + "ldr x20, [%x[args], %[offsetof_K]]\n" + "add x20, x20, #0x3\n" + "lsr x20, x20, #0x2\n" + "lsr x21, x20, #0x2\n" + "and x20, x20, #0x3\n" + "cbz x21, 6f\n" + "subs x21, x21, #0x1\n" + ".inst 0xa0400762 // ld1b { z2.b-z3.b }, pn9.b/Z, [x27]\n" + ".inst 0xa1400780 // ld1b { z0.b, z8.b }, pn9.b/Z, [x28]\n" + ".inst 0xa0410772 // ld1b { z18.b-z19.b }, pn9.b/Z, [x27, #0x2, MUL VL]\n" + ".inst 0xa0410794 // ld1b { z20.b-z21.b }, pn9.b/Z, [x28, #0x2, MUL VL]\n" + ".inst 0xa042077a // ld1b { z26.b-z27.b }, pn9.b/Z, [x27, #0x4, MUL VL]\n" + ".inst 0xa0420796 // ld1b { z22.b-z23.b }, pn9.b/Z, [x28, #0x4, MUL VL]\n" + ".inst 0xa0430778 // ld1b { z24.b-z25.b }, pn9.b/Z, [x27, #0x6, MUL VL]\n" + "addvl x27, x27, #8\n" + ".inst 0xa0430784 // ld1b { z4.b-z5.b }, pn9.b/Z, [x28, #0x6, MUL VL]\n" + "addvl x28, x28, #8\n" + "ble 5f\n" + "4:" // K loop + ".inst 0xa0802440 // smopa za0.s, p1/M, p1/M, z2.b, z0.b\n" + "subs x21, x21, #0x1\n" + ".inst 0xa0882441 // smopa za1.s, p1/M, p1/M, z2.b, z8.b\n" + ".inst 0xa0802462 // smopa za2.s, p1/M, p1/M, z3.b, z0.b\n" + ".inst 0xa0882463 // smopa za3.s, p1/M, p1/M, z3.b, z8.b\n" + ".inst 0xa0400762 // ld1b { z2.b-z3.b }, pn9.b/Z, [x27]\n" + ".inst 0xa0942640 // smopa za0.s, p1/M, p1/M, z18.b, z20.b\n" + ".inst 0xa1400780 // ld1b { z0.b, z8.b }, pn9.b/Z, [x28]\n" + ".inst 0xa0952641 // smopa za1.s, p1/M, p1/M, z18.b, z21.b\n" + ".inst 0xa0942662 // smopa za2.s, p1/M, p1/M, z19.b, z20.b\n" + ".inst 0xa0952663 // smopa za3.s, p1/M, p1/M, z19.b, z21.b\n" + ".inst 0xa0410772 // ld1b { z18.b-z19.b }, pn9.b/Z, [x27, #0x2, MUL VL]\n" + ".inst 0xa0962740 // smopa za0.s, p1/M, p1/M, z26.b, z22.b\n" + ".inst 0xa0410794 // ld1b { z20.b-z21.b }, pn9.b/Z, [x28, #0x2, MUL VL]\n" + ".inst 0xa0972741 // smopa za1.s, p1/M, p1/M, z26.b, z23.b\n" + ".inst 0xa0962762 // smopa za2.s, p1/M, p1/M, z27.b, z22.b\n" + ".inst 0xa0972763 // smopa za3.s, p1/M, p1/M, z27.b, z23.b\n" + ".inst 0xa042077a // ld1b { z26.b-z27.b }, pn9.b/Z, [x27, #0x4, MUL VL]\n" + ".inst 0xa0420796 // ld1b { z22.b-z23.b }, pn9.b/Z, [x28, #0x4, MUL VL]\n" + ".inst 0xa0842700 // smopa za0.s, p1/M, p1/M, z24.b, z4.b\n" + ".inst 0xa0852701 // smopa za1.s, p1/M, p1/M, z24.b, z5.b\n" + ".inst 0xa0842722 // smopa za2.s, p1/M, p1/M, z25.b, z4.b\n" + ".inst 0xa0852723 // smopa za3.s, p1/M, p1/M, z25.b, z5.b\n" + ".inst 0xa0430778 // ld1b { z24.b-z25.b }, pn9.b/Z, [x27, #0x6, MUL VL]\n" + "addvl x27, x27, #8\n" + ".inst 0xa0430784 // ld1b { z4.b-z5.b }, pn9.b/Z, [x28, #0x6, MUL VL]\n" + "addvl x28, x28, #8\n" + "bgt 4b\n" + "5:" // K loop tail + ".inst 0xa0802440 // smopa za0.s, p1/M, p1/M, z2.b, z0.b\n" + ".inst 0xa0882441 // smopa za1.s, p1/M, p1/M, z2.b, z8.b\n" + ".inst 0xa0802462 // smopa za2.s, p1/M, p1/M, z3.b, z0.b\n" + ".inst 0xa0882463 // smopa za3.s, p1/M, p1/M, z3.b, z8.b\n" + ".inst 0xa0942640 // smopa za0.s, p1/M, p1/M, z18.b, z20.b\n" + ".inst 0xa0952641 // smopa za1.s, p1/M, p1/M, z18.b, z21.b\n" + ".inst 0xa0942662 // smopa za2.s, p1/M, p1/M, z19.b, z20.b\n" + ".inst 0xa0952663 // smopa za3.s, p1/M, p1/M, z19.b, z21.b\n" + ".inst 0xa0962740 // smopa za0.s, p1/M, p1/M, z26.b, z22.b\n" + ".inst 0xa0972741 // smopa za1.s, p1/M, p1/M, z26.b, z23.b\n" + ".inst 0xa0962762 // smopa za2.s, p1/M, p1/M, z27.b, z22.b\n" + ".inst 0xa0972763 // smopa za3.s, p1/M, p1/M, z27.b, z23.b\n" + ".inst 0xa0842700 // smopa za0.s, p1/M, p1/M, z24.b, z4.b\n" + ".inst 0xa0852701 // smopa za1.s, p1/M, p1/M, z24.b, z5.b\n" + ".inst 0xa0842722 // smopa za2.s, p1/M, p1/M, z25.b, z4.b\n" + ".inst 0xa0852723 // smopa za3.s, p1/M, p1/M, z25.b, z5.b\n" + "6:" // K oddments + "cbz x20, 8f\n" + "7:" // K oddments: Loop + ".inst 0xa0400770 // ld1b { z16.b-z17.b }, pn9.b/Z, [x27]\n" + "subs x20, x20, #0x1\n" + "addvl x27, x27, #2\n" + ".inst 0xa0400788 // ld1b { z8.b-z9.b }, pn9.b/Z, [x28]\n" + "addvl x28, x28, #2\n" + ".inst 0xa0882600 // smopa za0.s, p1/M, p1/M, z16.b, z8.b\n" + ".inst 0xa0892601 // smopa za1.s, p1/M, p1/M, z16.b, z9.b\n" + ".inst 0xa0882622 // smopa za2.s, p1/M, p1/M, z17.b, z8.b\n" + ".inst 0xa0892623 // smopa za3.s, p1/M, p1/M, z17.b, z9.b\n" + "bgt 7b\n" + "8:" // K oddments: End + "ldr x26, [%x[args], %[offsetof_C]]\n" + "sub x25, x14, x13\n" + "cntw x24\n" + "ld1rw { z27.s }, p1/Z, [%x[args], %[offsetof_KernelArgs_min]]\n" + "ldr x23, [%x[args], %[offsetof_ldcb]]\n" + "whilelt p0.h, x11, x10\n" + "cmp x25, x24\n" + "ld1rw { z1.s }, p1/Z, [%x[args], %[offsetof_KernelArgs_max]]\n" + "csel x22, x25, x24, LT\n" + "ld1rw { z0.s }, p1/Z, [%x[args], %[offsetof_KernelArgs_result_zero_point]]\n" + "mov x12, #0x0\n" + "add x26, x26, x11\n" // C += n + "lsr x21, x22, #0x2\n" + "ld1w { z22.s }, p1/Z, [x28]\n" + "madd x26, x13, x23, x26\n" // C += m * ldc + "ld1w { z26.s }, p1/Z, [x28, #1, MUL VL]\n" + "and x20, x22, #0x3\n" + "addvl x28, x28, #2\n" + "cbz x21, 11f\n" + "10:" // Store to output array: Accumulator row 0 loop + ".inst 0xc0860410 // mova { z16.s-z19.s }, za0h.s[x12]\n" + ".inst 0xc086043c // mova { z28.s-z31.s }, za1h.s[x12]\n" + ".inst 0xc132e210 // scvtf { z16.s-z19.s }, { z16.s-z19.s }\n" + ".inst 0xc132e39c // scvtf { z28.s-z31.s }, { z28.s-z31.s }\n" + "fmul z16.s, z16.s, z22.s\n" + "fmul z17.s, z17.s, z22.s\n" + "add x12, x12, #0x4\n" + "fmul z18.s, z18.s, z22.s\n" + "fmul z19.s, z19.s, z22.s\n" + "cmp x12, x21, LSL #2\n" + "fmul z28.s, z28.s, z26.s\n" + "fmul z29.s, z29.s, z26.s\n" + "fmul z30.s, z30.s, z26.s\n" + "fmul z31.s, z31.s, z26.s\n" + ".inst 0xc1b8e210 // frintn { z16.s-z19.s }, { z16.s-z19.s }\n" + ".inst 0xc131e210 // fcvtzs { z16.s-z19.s }, { z16.s-z19.s }\n" + ".inst 0xc1b8e39c // frintn { z28.s-z31.s }, { z28.s-z31.s }\n" + ".inst 0xc1a0ab10 // add { z16.s-z19.s }, { z16.s-z19.s }, z0.s\n" + ".inst 0xc131e39c // fcvtzs { z28.s-z31.s }, { z28.s-z31.s }\n" + ".inst 0xc1a0ab1c // add { z28.s-z31.s }, { z28.s-z31.s }, z0.s\n" + ".inst 0xc1a1cf70 // sclamp { z16.s-z19.s }, z27.s, z1.s\n" + ".inst 0xc1a1cf7c // sclamp { z28.s-z31.s }, z27.s, z1.s\n" + "uzp1 z5.h, z16.h, z28.h\n" + "uzp1 z20.h, z17.h, z29.h\n" + "uzp1 z17.h, z18.h, z30.h\n" + "uzp1 z16.h, z19.h, z31.h\n" + "st1b { z5.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "st1b { z20.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "st1b { z17.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "blt 10b\n" + "11:" // Store to output array: Accumulator row 0 oddments + "cbz x20, 12f\n" + ".inst 0xc0860404 // mova { z4.s-z7.s }, za0h.s[x12]\n" + ".inst 0xc086042c // mova { z12.s-z15.s }, za1h.s[x12]\n" + ".inst 0xc132e084 // scvtf { z4.s-z7.s }, { z4.s-z7.s }\n" + ".inst 0xc132e18c // scvtf { z12.s-z15.s }, { z12.s-z15.s }\n" + "fmul z4.s, z4.s, z22.s\n" + "fmul z5.s, z5.s, z22.s\n" + "subs x20, x20, #0x1\n" + "fmul z6.s, z6.s, z22.s\n" + "fmul z7.s, z7.s, z22.s\n" + "fmul z12.s, z12.s, z26.s\n" + "fmul z13.s, z13.s, z26.s\n" + "fmul z14.s, z14.s, z26.s\n" + "fmul z15.s, z15.s, z26.s\n" + ".inst 0xc1b8e084 // frintn { z4.s-z7.s }, { z4.s-z7.s }\n" + ".inst 0xc131e084 // fcvtzs { z4.s-z7.s }, { z4.s-z7.s }\n" + ".inst 0xc1b8e18c // frintn { z12.s-z15.s }, { z12.s-z15.s }\n" + ".inst 0xc1a0ab04 // add { z4.s-z7.s }, { z4.s-z7.s }, z0.s\n" + ".inst 0xc131e18c // fcvtzs { z12.s-z15.s }, { z12.s-z15.s }\n" + ".inst 0xc1a0ab0c // add { z12.s-z15.s }, { z12.s-z15.s }, z0.s\n" + ".inst 0xc1a1cf64 // sclamp { z4.s-z7.s }, z27.s, z1.s\n" + ".inst 0xc1a1cf6c // sclamp { z12.s-z15.s }, z27.s, z1.s\n" + "uzp1 z16.h, z4.h, z12.h\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "beq 12f\n" + "subs x20, x20, #0x1\n" + "uzp1 z16.h, z5.h, z13.h\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "beq 12f\n" + "uzp1 z16.h, z6.h, z14.h\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "12:" // Store to output array: Accumulator row 0 oddments: End + "subs x25, x25, x22\n" + "beq 16f\n" + "cmp x25, x24\n" + "mov x12, #0x0\n" + "csel x20, x25, x24, LT\n" + "lsr x21, x20, #0x2\n" + "and x20, x20, #0x3\n" + "cbz x21, 14f\n" + "13:" // Store to output array: Accumulator row 1 loop + ".inst 0xc0860448 // mova { z8.s-z11.s }, za2h.s[x12]\n" + ".inst 0xc0860470 // mova { z16.s-z19.s }, za3h.s[x12]\n" + ".inst 0xc132e108 // scvtf { z8.s-z11.s }, { z8.s-z11.s }\n" + ".inst 0xc132e210 // scvtf { z16.s-z19.s }, { z16.s-z19.s }\n" + "fmul z8.s, z8.s, z22.s\n" + "fmul z9.s, z9.s, z22.s\n" + "add x12, x12, #0x4\n" + "fmul z10.s, z10.s, z22.s\n" + "fmul z11.s, z11.s, z22.s\n" + "cmp x12, x21, LSL #2\n" + "fmul z16.s, z16.s, z26.s\n" + "fmul z17.s, z17.s, z26.s\n" + "fmul z18.s, z18.s, z26.s\n" + "fmul z19.s, z19.s, z26.s\n" + ".inst 0xc1b8e108 // frintn { z8.s-z11.s }, { z8.s-z11.s }\n" + ".inst 0xc131e108 // fcvtzs { z8.s-z11.s }, { z8.s-z11.s }\n" + ".inst 0xc1b8e210 // frintn { z16.s-z19.s }, { z16.s-z19.s }\n" + ".inst 0xc1a0ab08 // add { z8.s-z11.s }, { z8.s-z11.s }, z0.s\n" + ".inst 0xc131e210 // fcvtzs { z16.s-z19.s }, { z16.s-z19.s }\n" + ".inst 0xc1a0ab10 // add { z16.s-z19.s }, { z16.s-z19.s }, z0.s\n" + ".inst 0xc1a1cf68 // sclamp { z8.s-z11.s }, z27.s, z1.s\n" + ".inst 0xc1a1cf70 // sclamp { z16.s-z19.s }, z27.s, z1.s\n" + "uzp1 z21.h, z8.h, z16.h\n" + "uzp1 z20.h, z9.h, z17.h\n" + "uzp1 z17.h, z10.h, z18.h\n" + "uzp1 z16.h, z11.h, z19.h\n" + "st1b { z21.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "st1b { z20.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "st1b { z17.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "blt 13b\n" + "14:" // Store to output array: Accumulator row 1 oddments + "cbz x20, 15f\n" + ".inst 0xc086044c // mova { z12.s-z15.s }, za2h.s[x12]\n" + ".inst 0xc0860464 // mova { z4.s-z7.s }, za3h.s[x12]\n" + ".inst 0xc132e18c // scvtf { z12.s-z15.s }, { z12.s-z15.s }\n" + ".inst 0xc132e084 // scvtf { z4.s-z7.s }, { z4.s-z7.s }\n" + "fmul z12.s, z12.s, z22.s\n" + "fmul z13.s, z13.s, z22.s\n" + "subs x20, x20, #0x1\n" + "fmul z14.s, z14.s, z22.s\n" + "fmul z15.s, z15.s, z22.s\n" + "fmul z4.s, z4.s, z26.s\n" + "fmul z5.s, z5.s, z26.s\n" + "fmul z6.s, z6.s, z26.s\n" + "fmul z7.s, z7.s, z26.s\n" + ".inst 0xc1b8e18c // frintn { z12.s-z15.s }, { z12.s-z15.s }\n" + ".inst 0xc131e18c // fcvtzs { z12.s-z15.s }, { z12.s-z15.s }\n" + ".inst 0xc1b8e084 // frintn { z4.s-z7.s }, { z4.s-z7.s }\n" + ".inst 0xc1a0ab0c // add { z12.s-z15.s }, { z12.s-z15.s }, z0.s\n" + ".inst 0xc131e084 // fcvtzs { z4.s-z7.s }, { z4.s-z7.s }\n" + ".inst 0xc1a0ab04 // add { z4.s-z7.s }, { z4.s-z7.s }, z0.s\n" + ".inst 0xc1a1cf6c // sclamp { z12.s-z15.s }, z27.s, z1.s\n" + ".inst 0xc1a1cf64 // sclamp { z4.s-z7.s }, z27.s, z1.s\n" + "uzp1 z16.h, z12.h, z4.h\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "beq 15f\n" + "subs x20, x20, #0x1\n" + "uzp1 z16.h, z13.h, z5.h\n" + "st1b { z16.h }, p0, [x26]\n" + "add x26, x26, x23\n" + "beq 15f\n" + "uzp1 z16.h, z14.h, z6.h\n" + "st1b { z16.h }, p0, [x26]\n" + "15:" // Store to output array: Accumulator row 1 oddments: End + "16:" // Store to output array: End + "incw x11, ALL, MUL #2\n" + "cmp x11, x10\n" + "blt 2b\n" + "incw x13, ALL, MUL #2\n" + "mov x11, #0x0\n" + "cmp x13, x14\n" + "mov x9, x27\n" + "blt 1b\n" + ".inst 0xd503467f // SMSTOP\n" + : + : [args] "r"(&args), [offsetof_A] "I"(offsetof(KernelArgs, A)), [offsetof_B] "I"(offsetof(KernelArgs, B)), + [offsetof_C] "I"(offsetof(KernelArgs, C)), [offsetof_K] "I"(offsetof(KernelArgs, K)), + [offsetof_KernelArgs_max] "I"(offsetof(KernelArgs, max)), + [offsetof_KernelArgs_min] "I"(offsetof(KernelArgs, min)), + [offsetof_KernelArgs_result_zero_point] "I"(offsetof(KernelArgs, result_zero_point)), + [offsetof_M] "I"(offsetof(KernelArgs, M)), [offsetof_N] "I"(offsetof(KernelArgs, N)), + [offsetof_ldcb] "I"(offsetof(KernelArgs, ldcb)) + : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", + "p15", "x9", "x10", "x11", "x12", "x13", "x14", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", + "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", + "z17", "z18", "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"); +} + +#endif // Architectural features check. diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h new file mode 100644 index 00000000..650ab660 --- /dev/null +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h @@ -0,0 +1,127 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "kai/kai_common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/// Micro-kernel dependencies +/// +/// -# kai_lhs_pack_f8p2vlx1_f8_sme to pack the LHS matrix. +/// -# kai_rhs_pack_kxn_f8p2vlx1biasf32_f8_f32_sme to pack the RHS matrix. + +/// Gets m step value. +/// +/// The starting row index must be divisible by `m_step`. +/// +/// @return The m step value. +size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); + +/// Gets n step value. +/// +/// The starting column index must be divisible by `n_step`. +/// +/// @return The n step value. +size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); + +/// Gets mr value. +/// +/// This is the packing parameter which must be used to pack the LHS matrix. +/// +/// @return The mr value. +size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); + +/// Gets nr value. +/// +/// This is the packing parameter which must be used to pack the RHS matrix. +/// +/// @return The nr value. +size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); + +/// Gets kr value. +/// +/// This is the packing parameter which must be used to pack the LHS and RHS matrix. +/// +/// @return The kr value. +size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); + +/// Gets sr value. +/// +/// This is the packing parameter which must be used to pack the LHS and RHS matrix. +/// +/// @return The sr value. +size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); + +/// Gets the offset in bytes to the data element in the packed LHS matrix buffer. +/// +/// @param[in] m_idx Row index in the unpacked LHS matrix. +/// @param[in] k Number of columns in the unpacked LHS matrix. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k); + +/// Gets the offset in bytes to the data element in the packed RHS matrix buffer. +/// +/// @param[in] n_idx Column index in the unpacked RHS matrix. +/// @param[in] k Number of rows in the unpacked RHS matrix. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k); + +/// Gets the offset in bytes to the data element in the destination matrix buffer. +/// +/// @param[in] m_idx Row index. +/// @param[in] n_idx Column index. +/// @param[in] stride Row stride in bytes. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( + size_t m_idx, size_t n_idx, size_t dst_stride); + +/// Gets the size in bytes of the destination matrix buffer. +/// +/// @param[in] m Number of rows. +/// @param[in] n Number of columns. +/// +/// @return The size in bytes of the destination matrix buffer. +size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m, size_t n); + +/// Runs the matrix multiplication microkernel followed by a clamp operation. +/// +/// The pointer of each buffers (packed LHS, packed RHS and output) needs to be added with offset +/// calculated using the following functions: +/// +/// * Packed LHS: @ref kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa. +/// * Packed RHS: @ref kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa. +/// * Output: @ref kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa. +/// +/// @param[in] m Number of output rows to be computed. +/// @param[in] n Number of output columns to be computed. +/// @param[in] k Common dimension of the LHS and RHS operands. +/// @param[in] packed_lhs Packed LHS matrix buffer. +/// @param[in] packed_rhs Packed RHS matrix buffer. +/// @param[out] dst Output matrix buffer. +/// @param[in] dst_stride_row Row stride in bytes of the output matrix. +/// @param[in] dst_stride_col Column stride in bytes of the output matrix. +/// @param[in] clamp_min Minimum value to clamp the final result. +/// @param[in] clamp_max Maximum value to clamp the final result. +void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( + size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, + size_t dst_stride_col, + + const struct kai_matmul_requantize32_params* params + +); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c deleted file mode 100644 index 2708bb41..00000000 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.c +++ /dev/null @@ -1,350 +0,0 @@ -// -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates -// -// SPDX-License-Identifier: Apache-2.0 -// - -#if !defined(__aarch64__) || !defined(__ARM_FEATURE_SVE2) -#error This file must be compiled for AArch64, FEAT_SVE2. -#else // Architectural features check. - -#include -#include -#include "kai/kai_common.h" - -static const size_t kai_mr = 2; -static const size_t kai_kr = 4; -static const size_t kai_sr = 1; - -size_t kai_get_m_step_lhs_pack_x8p2vlx1_x8_sme(size_t mr) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); - KAI_UNUSED(mr); - - return (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; -} - -size_t kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t lhs_stride) { - KAI_ASSUME(m_idx % (kai_mr * kai_get_sme_vector_length_u8()) == 0); - - return m_idx * lhs_stride; -} - -size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr) { - const size_t scaled_mr = kai_mr * kai_get_sme_vector_length_u8(); - KAI_ASSUME(m_idx % scaled_mr == 0); - KAI_ASSUME(mr == scaled_mr); - KAI_ASSUME(kr == kai_kr); - KAI_ASSUME(sr == kai_sr); - - KAI_UNUSED(mr); - KAI_UNUSED(kr); - KAI_UNUSED(sr); - - return m_idx * kai_roundup(k, kai_kr) * sizeof(int8_t); -} - -size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx1_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); - KAI_ASSUME(kr == kai_kr); - KAI_ASSUME(sr == kai_sr); - - KAI_UNUSED(mr); - KAI_UNUSED(kr); - KAI_UNUSED(sr); - - return (kai_roundup(m, kai_mr * kai_get_sme_vector_length_u8()/kai_kr) * kai_roundup(k,kai_kr) * sizeof(int8_t)); -} - -void kai_run_lhs_pack_x8p2vlx1_x8_sme( - size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, - const void* lhs, size_t lhs_stride, void* lhs_packed -) -{ - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); - KAI_ASSUME(kr == kai_kr); - KAI_ASSUME(sr == kai_sr); - KAI_ASSUME(lhs != NULL); - KAI_ASSUME(lhs_packed != NULL); - - KAI_ASSUME(m_idx_start == 0); - - const size_t block_height = (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; - const size_t width = k; - const size_t row_offset = 0; - - const void* in[block_height]; - - for (size_t block_y = 0; block_y < m; block_y += block_height) { - const size_t height = KAI_MIN(m - block_y, block_height); - void* out = lhs_packed + block_y * kai_roundup(k, kai_kr) * sizeof(int8_t); - - for (size_t y = 0; y < height; y++) { - in[y] = lhs + (block_y + y) * lhs_stride; - } - - __asm__ __volatile__( - ".inst 0xd503477f // SMSTART ZA\n" - "mov x23, %x[width]\n" - "mov x21, %x[width]\n" - "cntb x20\n" - "incb x23\n" - "sub x7, x20, #0x1\n" - "cntw x8\n" - "sub x23, x23, #0x1\n" - "ands x7, x21, x7\n" - "udiv x23, x23, x20\n" // n_passes = ceildiv(width, VL) - "csel x7, x7, x20, NE\n" - "lsl x22, %x[height], #0x1\n" // height * 2 - "lsl x21, x8, #0x1\n" - "sub x20, x23, #0x1\n" - "add x7, x7, #0x3\n" - "sub x17, x8, #0x2\n" - "whilelt p9.b, XZR, x22\n" - "whilelt p8.b, x21, x22\n" - "mov x16, #0x0\n" - "mov x11, %x[in]\n" - "add x10, %x[in], x8, LSL #3\n" - "cntw x9, ALL, MUL #2\n" - "cntw x28, ALL, MUL #3\n" - "ldr x27, [x11, #0x0]\n" - "lsr x20, x20, #0x1\n" // n_loops = (n_passes - 1) / 2 - "and x26, x23, #0x1\n" // odd_tail = bool(n_passes & 0x1) - "ldr x25, [x10, #0x0]\n" - "lsr x7, x7, #0x2\n" - "ptrue p11.s\n" - "ldr x24, [x11, #0x8]\n" - "zip1 p10.b, p9.b, p8.b\n" - "mov x23, %x[row_offset]\n" - "ldr x21, [x10, #0x8]\n" - "mov x22, %x[out]\n" - "whilelt p9.b, x16, %x[width]\n" - "whilelt p8.b, x16, %x[width]\n" - "add x11, x11, #0x10\n" - "add x10, x10, #0x10\n" - "mov x12, #0x0\n" - "cbz x17, 2f\n" - "1:" // K loop: Charge: Loop - ".inst 0x25246143 // psel p3.b, p8.b/Z, p10.b[w12]\n" - ".inst 0x252c6142 // psel p2.b, p8.b/Z, p10.b[w12, #1]\n" - ".inst 0x25646141 // psel p1.b, p8.b/Z, p10.b[w12, #4]\n" - ".inst 0x256c6140 // psel p0.b, p8.b/Z, p10.b[w12, #5]\n" - ".inst 0xe0170f60 // ld1b { za0h.b[x12] }, p3/Z, [x27, x23]\n" - "ldr x27, [x11, #0x0]\n" - ".inst 0xe0170b21 // ld1b { za0h.b[x12, #1] }, p2/Z, [x25, x23]\n" - "ldr x25, [x10, #0x0]\n" - ".inst 0xe0170704 // ld1b { za0h.b[x12, #4] }, p1/Z, [x24, x23]\n" - "ldr x24, [x11, #0x8]\n" - "add x11, x11, #0x10\n" - ".inst 0xe01702a5 // ld1b { za0h.b[x12, #5] }, p0/Z, [x21, x23]\n" - "add x12, x12, #0x8\n" - "ldr x21, [x10, #0x8]\n" - "add x10, x10, #0x10\n" - "cmp x12, x17, LSL #2\n" - "blt 1b\n" - "2:" // K loop: Charge: End - ".inst 0x25246143 // psel p3.b, p8.b/Z, p10.b[w12]\n" - ".inst 0x252c6142 // psel p2.b, p8.b/Z, p10.b[w12, #1]\n" - ".inst 0x25646141 // psel p1.b, p8.b/Z, p10.b[w12, #4]\n" - ".inst 0x256c6140 // psel p0.b, p8.b/Z, p10.b[w12, #5]\n" - "mov x11, %x[in]\n" - "add x10, %x[in], x8, LSL #3\n" - ".inst 0xe0170f60 // ld1b { za0h.b[x12] }, p3/Z, [x27, x23]\n" - "ldr x27, [x11, #0x0]\n" - "incb x16\n" - ".inst 0xe0170b21 // ld1b { za0h.b[x12, #1] }, p2/Z, [x25, x23]\n" - "ldr x25, [x10, #0x0]\n" - ".inst 0xe0170704 // ld1b { za0h.b[x12, #4] }, p1/Z, [x24, x23]\n" - "ldr x24, [x11, #0x8]\n" - "add x11, x11, #0x10\n" - ".inst 0xe01702a5 // ld1b { za0h.b[x12, #5] }, p0/Z, [x21, x23]\n" - "ldr x21, [x10, #0x8]\n" - "add x10, x10, #0x10\n" - "incb x23\n" - "cbz x20, 8f\n" - "mov x20, x20\n" - "3:" // K loop: Main loop - "whilelt p8.b, x16, %x[width]\n" - "mov x15, #0x0\n" - "mov x14, #0x0\n" - "cbz x17, 5f\n" - "4:" // K loop: Main loop: First: Loop - ".inst 0x25376143 // psel p3.b, p8.b/Z, p10.b[w15, #2]\n" - ".inst 0x253f6142 // psel p2.b, p8.b/Z, p10.b[w15, #3]\n" - ".inst 0x25776141 // psel p1.b, p8.b/Z, p10.b[w15, #6]\n" - ".inst 0x257f6140 // psel p0.b, p8.b/Z, p10.b[w15, #7]\n" - ".inst 0xe0176f62 // ld1b { za0h.b[x15, #2] }, p3/Z, [x27, x23]\n" - ".inst 0x25266d23 // psel p3.b, p11.b/Z, p9.b[w14]\n" - "ldr x27, [x11, #0x0]\n" - ".inst 0xe0176b23 // ld1b { za0h.b[x15, #3] }, p2/Z, [x25, x23]\n" - ".inst 0x25266d22 // psel p2.b, p11.b/Z, p9.b[w14]\n" - "ldr x25, [x10, #0x0]\n" - ".inst 0xe0176706 // ld1b { za0h.b[x15, #6] }, p1/Z, [x24, x23]\n" - ".inst 0x252e6d21 // psel p1.b, p11.b/Z, p9.b[w14, #1]\n" - "ldr x24, [x11, #0x8]\n" - "add x11, x11, #0x10\n" - ".inst 0xe01762a7 // ld1b { za0h.b[x15, #7] }, p0/Z, [x21, x23]\n" - "ldr x21, [x10, #0x8]\n" - ".inst 0x252e6d20 // psel p0.b, p11.b/Z, p9.b[w14, #1]\n" - "add x10, x10, #0x10\n" - ".inst 0xe0bfcec0 // st1w { za0v.s[x14] }, p3/Z, [x22, XZR, LSL #2]\n" - "add x15, x15, #0x8\n" - ".inst 0xe0a8cac4 // st1w { za1v.s[x14] }, p2/Z, [x22, x8, LSL #2]\n" - ".inst 0xe0a9c6c1 // st1w { za0v.s[x14, #1] }, p1/Z, [x22, x9, LSL #2]\n" - ".inst 0xe0bcc2c5 // st1w { za1v.s[x14, #1] }, p0/Z, [x22, x28, LSL #2]\n" - "add x14, x14, #0x2\n" - "addvl x22, x22, #4\n" - "cmp x14, x17\n" - "blt 4b\n" - "5:" // K loop: Main loop: First: Tail - ".inst 0x25376143 // psel p3.b, p8.b/Z, p10.b[w15, #2]\n" - ".inst 0x253f6142 // psel p2.b, p8.b/Z, p10.b[w15, #3]\n" - ".inst 0x25776141 // psel p1.b, p8.b/Z, p10.b[w15, #6]\n" - ".inst 0x257f6140 // psel p0.b, p8.b/Z, p10.b[w15, #7]\n" - "mov x11, %x[in]\n" - "add x10, %x[in], x8, LSL #3\n" - ".inst 0xe0176f62 // ld1b { za0h.b[x15, #2] }, p3/Z, [x27, x23]\n" - ".inst 0x25266d23 // psel p3.b, p11.b/Z, p9.b[w14]\n" - "ldr x27, [x11, #0x0]\n" - "mov x13, #0x0\n" - ".inst 0xe0176b23 // ld1b { za0h.b[x15, #3] }, p2/Z, [x25, x23]\n" - ".inst 0x25266d22 // psel p2.b, p11.b/Z, p9.b[w14]\n" - "ldr x25, [x10, #0x0]\n" - "mov x12, #0x0\n" - ".inst 0xe0176706 // ld1b { za0h.b[x15, #6] }, p1/Z, [x24, x23]\n" - ".inst 0x252e6d21 // psel p1.b, p11.b/Z, p9.b[w14, #1]\n" - "ldr x24, [x11, #0x8]\n" - "add x11, x11, #0x10\n" - ".inst 0xe01762a7 // ld1b { za0h.b[x15, #7] }, p0/Z, [x21, x23]\n" - "ldr x21, [x10, #0x8]\n" - ".inst 0x252e6d20 // psel p0.b, p11.b/Z, p9.b[w14, #1]\n" - "whilelt p9.b, x16, %x[width]\n" - ".inst 0xe0bfcec0 // st1w { za0v.s[x14] }, p3/Z, [x22, XZR, LSL #2]\n" - "incb x16\n" - "add x10, x10, #0x10\n" - ".inst 0xe0a8cac4 // st1w { za1v.s[x14] }, p2/Z, [x22, x8, LSL #2]\n" - "incb x23\n" - "whilelt p8.b, x16, %x[width]\n" - ".inst 0xe0a9c6c1 // st1w { za0v.s[x14, #1] }, p1/Z, [x22, x9, LSL #2]\n" - ".inst 0xe0bcc2c5 // st1w { za1v.s[x14, #1] }, p0/Z, [x22, x28, LSL #2]\n" - "addvl x22, x22, #4\n" - "cbz x17, 7f\n" - "6:" // K loop: Main loop: Second: Loop - ".inst 0x25256143 // psel p3.b, p8.b/Z, p10.b[w13]\n" - ".inst 0x252d6142 // psel p2.b, p8.b/Z, p10.b[w13, #1]\n" - ".inst 0x25656141 // psel p1.b, p8.b/Z, p10.b[w13, #4]\n" - ".inst 0x256d6140 // psel p0.b, p8.b/Z, p10.b[w13, #5]\n" - ".inst 0xe0172f60 // ld1b { za0h.b[x13] }, p3/Z, [x27, x23]\n" - ".inst 0x25246d23 // psel p3.b, p11.b/Z, p9.b[w12]\n" - "ldr x27, [x11, #0x0]\n" - ".inst 0xe0172b21 // ld1b { za0h.b[x13, #1] }, p2/Z, [x25, x23]\n" - ".inst 0x25246d22 // psel p2.b, p11.b/Z, p9.b[w12]\n" - "ldr x25, [x10, #0x0]\n" - ".inst 0xe0172704 // ld1b { za0h.b[x13, #4] }, p1/Z, [x24, x23]\n" - ".inst 0x252c6d21 // psel p1.b, p11.b/Z, p9.b[w12, #1]\n" - "ldr x24, [x11, #0x8]\n" - "add x11, x11, #0x10\n" - ".inst 0xe01722a5 // ld1b { za0h.b[x13, #5] }, p0/Z, [x21, x23]\n" - "ldr x21, [x10, #0x8]\n" - ".inst 0x252c6d20 // psel p0.b, p11.b/Z, p9.b[w12, #1]\n" - "add x10, x10, #0x10\n" - ".inst 0xe0bf8ec8 // st1w { za2v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" - "add x13, x13, #0x8\n" - ".inst 0xe0a88acc // st1w { za3v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" - ".inst 0xe0a986c9 // st1w { za2v.s[x12, #1] }, p1/Z, [x22, x9, LSL #2]\n" - ".inst 0xe0bc82cd // st1w { za3v.s[x12, #1] }, p0/Z, [x22, x28, LSL #2]\n" - "add x12, x12, #0x2\n" - "addvl x22, x22, #4\n" - "cmp x12, x17\n" - "blt 6b\n" - "7:" // K loop: Main loop: Second: Tail - ".inst 0x25256143 // psel p3.b, p8.b/Z, p10.b[w13]\n" - ".inst 0x252d6142 // psel p2.b, p8.b/Z, p10.b[w13, #1]\n" - ".inst 0x25656141 // psel p1.b, p8.b/Z, p10.b[w13, #4]\n" - ".inst 0x256d6140 // psel p0.b, p8.b/Z, p10.b[w13, #5]\n" - "mov x11, %x[in]\n" - "add x10, %x[in], x8, LSL #3\n" - ".inst 0xe0172f60 // ld1b { za0h.b[x13] }, p3/Z, [x27, x23]\n" - ".inst 0x25246d23 // psel p3.b, p11.b/Z, p9.b[w12]\n" - "ldr x27, [x11, #0x0]\n" - ".inst 0xe0172b21 // ld1b { za0h.b[x13, #1] }, p2/Z, [x25, x23]\n" - ".inst 0x25246d22 // psel p2.b, p11.b/Z, p9.b[w12]\n" - "ldr x25, [x10, #0x0]\n" - ".inst 0xe0172704 // ld1b { za0h.b[x13, #4] }, p1/Z, [x24, x23]\n" - ".inst 0x252c6d21 // psel p1.b, p11.b/Z, p9.b[w12, #1]\n" - "ldr x24, [x11, #0x8]\n" - "add x11, x11, #0x10\n" - ".inst 0xe01722a5 // ld1b { za0h.b[x13, #5] }, p0/Z, [x21, x23]\n" - "ldr x21, [x10, #0x8]\n" - ".inst 0x252c6d20 // psel p0.b, p11.b/Z, p9.b[w12, #1]\n" - "whilelt p9.b, x16, %x[width]\n" - ".inst 0xe0bf8ec8 // st1w { za2v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" - "subs x20, x20, #0x1\n" - "add x10, x10, #0x10\n" - ".inst 0xe0a88acc // st1w { za3v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" - "incb x16\n" - "incb x23\n" - ".inst 0xe0a986c9 // st1w { za2v.s[x12, #1] }, p1/Z, [x22, x9, LSL #2]\n" - ".inst 0xe0bc82cd // st1w { za3v.s[x12, #1] }, p0/Z, [x22, x28, LSL #2]\n" - "addvl x22, x22, #4\n" - "bgt 3b\n" - "8:" // K loop: Tails - "cbnz x26, 11f\n" - "mov x11, %x[in]\n" - "whilelt p8.b, x16, %x[width]\n" - "mov x13, #0x0\n" - "mov x12, #0x0\n" - "9:" // K loop: Tails: Even: First - ".inst 0x25306d23 // psel p3.s, p11.s/Z, p9.s[w12]\n" - ".inst 0x25306d22 // psel p2.s, p11.s/Z, p9.s[w12]\n" - ".inst 0x25356141 // psel p1.b, p8.b/Z, p10.b[w13, #2]\n" - ".inst 0x253d6140 // psel p0.b, p8.b/Z, p10.b[w13, #3]\n" - ".inst 0xe0bf8ec0 // st1w { za0v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" - ".inst 0xe0a88ac4 // st1w { za1v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" - "add x12, x12, #0x1\n" - "addvl x22, x22, #2\n" - "ldr x21, [x11, #0x0]\n" - "cmp x12, x8\n" - "ldr x20, [x11, x8, LSL #0x3]\n" - "add x11, x11, #0x8\n" - ".inst 0xe01726a2 // ld1b { za0h.b[x13, #2] }, p1/Z, [x21, x23]\n" - ".inst 0xe0172283 // ld1b { za0h.b[x13, #3] }, p0/Z, [x20, x23]\n" - "add x13, x13, #0x4\n" - "blt 9b\n" - "whilelt p9.b, x16, %x[width]\n" - "whilelt p8.b, x16, %x[width]\n" - "mov x20, #0x0\n" - "mov x12, #0x0\n" - "10:" // K loop: Tails: Even: Second - ".inst 0x25306d21 // psel p1.s, p11.s/Z, p9.s[w12]\n" - ".inst 0x25306d20 // psel p0.s, p11.s/Z, p9.s[w12]\n" - "add x20, x20, #0x4\n" - ".inst 0xe0bf86c8 // st1w { za2v.s[x12] }, p1/Z, [x22, XZR, LSL #2]\n" - ".inst 0xe0a882cc // st1w { za3v.s[x12] }, p0/Z, [x22, x8, LSL #2]\n" - "add x12, x12, #0x1\n" - "addvl x22, x22, #2\n" - "cmp x12, x7\n" - "blt 10b\n" - "whilelt p8.b, x16, %x[width]\n" - "b 13f\n" - "11:" // K loop: Tails: Odd - "mov x12, #0x0\n" - "12:" // K loop: Tails: Odd: Loop - ".inst 0x25306d21 // psel p1.s, p11.s/Z, p9.s[w12]\n" - ".inst 0x25306d20 // psel p0.s, p11.s/Z, p9.s[w12]\n" - ".inst 0xe0bf86c0 // st1w { za0v.s[x12] }, p1/Z, [x22, XZR, LSL #2]\n" - ".inst 0xe0a882c4 // st1w { za1v.s[x12] }, p0/Z, [x22, x8, LSL #2]\n" - "add x12, x12, #0x1\n" - "addvl x22, x22, #2\n" - "cmp x12, x7\n" - "blt 12b\n" - "13:" // K loop: End - "mov %x[out], x22\n" - ".inst 0xd503467f // SMSTOP\n" - : [out] "+&r" (out) - : [height] "r" (height), [in] "r" (in), [row_offset] "r" (row_offset), [width] "r" (width) - : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31" - ); - } -} - -#endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c new file mode 100644 index 00000000..c4a8d64c --- /dev/null +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c @@ -0,0 +1,352 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#if !defined(__aarch64__) || !defined(__ARM_FEATURE_SVE2) +#error This file must be compiled for AArch64, FEAT_SVE2. +#else // Architectural features check. + +#include +#include + +#include "kai/kai_common.h" + +static const size_t kai_mr = 2; +static const size_t kai_kr = 4; +static const size_t kai_sr = 1; + +size_t kai_get_m_step_lhs_pack_x8p2vlx4_x8_sme(size_t mr) { + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_UNUSED(mr); + + return (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; +} + +size_t kai_get_lhs_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t lhs_stride) { + KAI_ASSUME(m_idx % (kai_mr * kai_get_sme_vector_length_u8()) == 0); + + return m_idx * lhs_stride; +} + +size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr) { + const size_t scaled_mr = kai_mr * kai_get_sme_vector_length_u8(); + KAI_ASSUME(m_idx % scaled_mr == 0); + KAI_ASSUME(mr == scaled_mr); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == kai_sr); + + KAI_UNUSED(mr); + KAI_UNUSED(kr); + KAI_UNUSED(sr); + + return m_idx * kai_roundup(k, kai_kr) * sizeof(int8_t); +} + +size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr) { + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == kai_sr); + + KAI_UNUSED(mr); + KAI_UNUSED(kr); + KAI_UNUSED(sr); + + return (kai_roundup(m, kai_mr * kai_get_sme_vector_length_u8() / kai_kr) * kai_roundup(k, kai_kr) * sizeof(int8_t)); +} + +void kai_run_lhs_pack_x8p2vlx4_x8_sme( + size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, const void* lhs, size_t lhs_stride, + void* lhs_packed) { + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == kai_sr); + KAI_ASSUME(lhs != NULL); + KAI_ASSUME(lhs_packed != NULL); + + KAI_ASSUME(m_idx_start == 0); + + const size_t block_height = (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; + const size_t width = k; + const size_t row_offset = 0; + + const void* in[block_height]; + + for (size_t block_y = 0; block_y < m; block_y += block_height) { + const size_t height = KAI_MIN(m - block_y, block_height); + void* out = lhs_packed + block_y * kai_roundup(k, kai_kr) * sizeof(int8_t); + + for (size_t y = 0; y < height; y++) { + in[y] = lhs + (block_y + y) * lhs_stride; + } + + __asm__ __volatile__( + ".inst 0xd503477f // SMSTART ZA\n" + "mov x23, %x[width]\n" + "mov x21, %x[width]\n" + "cntb x20\n" + "incb x23\n" + "sub x7, x20, #0x1\n" + "cntw x8\n" + "sub x23, x23, #0x1\n" + "ands x7, x21, x7\n" + "udiv x23, x23, x20\n" // n_passes = ceildiv(width, VL) + "csel x7, x7, x20, NE\n" + "lsl x22, %x[height], #0x1\n" // height * 2 + "lsl x21, x8, #0x1\n" + "sub x20, x23, #0x1\n" + "add x7, x7, #0x3\n" + "sub x17, x8, #0x2\n" + "whilelt p9.b, XZR, x22\n" + "whilelt p8.b, x21, x22\n" + "mov x16, #0x0\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + "cntw x9, ALL, MUL #2\n" + "cntw x28, ALL, MUL #3\n" + "ldr x27, [x11, #0x0]\n" + "lsr x20, x20, #0x1\n" // n_loops = (n_passes - 1) / 2 + "and x26, x23, #0x1\n" // odd_tail = bool(n_passes & 0x1) + "ldr x25, [x10, #0x0]\n" + "lsr x7, x7, #0x2\n" + "ptrue p11.s\n" + "ldr x24, [x11, #0x8]\n" + "zip1 p10.b, p9.b, p8.b\n" + "mov x23, %x[row_offset]\n" + "ldr x21, [x10, #0x8]\n" + "mov x22, %x[out]\n" + "whilelt p9.b, x16, %x[width]\n" + "whilelt p8.b, x16, %x[width]\n" + "add x11, x11, #0x10\n" + "add x10, x10, #0x10\n" + "mov x12, #0x0\n" + "cbz x17, 2f\n" + "1:" // K loop: Charge: Loop + ".inst 0x25246143 // psel p3.b, p8.b/Z, p10.b[w12]\n" + ".inst 0x252c6142 // psel p2.b, p8.b/Z, p10.b[w12, #1]\n" + ".inst 0x25646141 // psel p1.b, p8.b/Z, p10.b[w12, #4]\n" + ".inst 0x256c6140 // psel p0.b, p8.b/Z, p10.b[w12, #5]\n" + ".inst 0xe0170f60 // ld1b { za0h.b[x12] }, p3/Z, [x27, x23]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0170b21 // ld1b { za0h.b[x12, #1] }, p2/Z, [x25, x23]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0170704 // ld1b { za0h.b[x12, #4] }, p1/Z, [x24, x23]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01702a5 // ld1b { za0h.b[x12, #5] }, p0/Z, [x21, x23]\n" + "add x12, x12, #0x8\n" + "ldr x21, [x10, #0x8]\n" + "add x10, x10, #0x10\n" + "cmp x12, x17, LSL #2\n" + "blt 1b\n" + "2:" // K loop: Charge: End + ".inst 0x25246143 // psel p3.b, p8.b/Z, p10.b[w12]\n" + ".inst 0x252c6142 // psel p2.b, p8.b/Z, p10.b[w12, #1]\n" + ".inst 0x25646141 // psel p1.b, p8.b/Z, p10.b[w12, #4]\n" + ".inst 0x256c6140 // psel p0.b, p8.b/Z, p10.b[w12, #5]\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + ".inst 0xe0170f60 // ld1b { za0h.b[x12] }, p3/Z, [x27, x23]\n" + "ldr x27, [x11, #0x0]\n" + "incb x16\n" + ".inst 0xe0170b21 // ld1b { za0h.b[x12, #1] }, p2/Z, [x25, x23]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0170704 // ld1b { za0h.b[x12, #4] }, p1/Z, [x24, x23]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01702a5 // ld1b { za0h.b[x12, #5] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + "add x10, x10, #0x10\n" + "incb x23\n" + "cbz x20, 8f\n" + "mov x20, x20\n" + "3:" // K loop: Main loop + "whilelt p8.b, x16, %x[width]\n" + "mov x15, #0x0\n" + "mov x14, #0x0\n" + "cbz x17, 5f\n" + "4:" // K loop: Main loop: First: Loop + ".inst 0x25376143 // psel p3.b, p8.b/Z, p10.b[w15, #2]\n" + ".inst 0x253f6142 // psel p2.b, p8.b/Z, p10.b[w15, #3]\n" + ".inst 0x25776141 // psel p1.b, p8.b/Z, p10.b[w15, #6]\n" + ".inst 0x257f6140 // psel p0.b, p8.b/Z, p10.b[w15, #7]\n" + ".inst 0xe0176f62 // ld1b { za0h.b[x15, #2] }, p3/Z, [x27, x23]\n" + ".inst 0x25266d23 // psel p3.b, p11.b/Z, p9.b[w14]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0176b23 // ld1b { za0h.b[x15, #3] }, p2/Z, [x25, x23]\n" + ".inst 0x25266d22 // psel p2.b, p11.b/Z, p9.b[w14]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0176706 // ld1b { za0h.b[x15, #6] }, p1/Z, [x24, x23]\n" + ".inst 0x252e6d21 // psel p1.b, p11.b/Z, p9.b[w14, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01762a7 // ld1b { za0h.b[x15, #7] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252e6d20 // psel p0.b, p11.b/Z, p9.b[w14, #1]\n" + "add x10, x10, #0x10\n" + ".inst 0xe0bfcec0 // st1w { za0v.s[x14] }, p3/Z, [x22, XZR, LSL #2]\n" + "add x15, x15, #0x8\n" + ".inst 0xe0a8cac4 // st1w { za1v.s[x14] }, p2/Z, [x22, x8, LSL #2]\n" + ".inst 0xe0a9c6c1 // st1w { za0v.s[x14, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bcc2c5 // st1w { za1v.s[x14, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "add x14, x14, #0x2\n" + "addvl x22, x22, #4\n" + "cmp x14, x17\n" + "blt 4b\n" + "5:" // K loop: Main loop: First: Tail + ".inst 0x25376143 // psel p3.b, p8.b/Z, p10.b[w15, #2]\n" + ".inst 0x253f6142 // psel p2.b, p8.b/Z, p10.b[w15, #3]\n" + ".inst 0x25776141 // psel p1.b, p8.b/Z, p10.b[w15, #6]\n" + ".inst 0x257f6140 // psel p0.b, p8.b/Z, p10.b[w15, #7]\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + ".inst 0xe0176f62 // ld1b { za0h.b[x15, #2] }, p3/Z, [x27, x23]\n" + ".inst 0x25266d23 // psel p3.b, p11.b/Z, p9.b[w14]\n" + "ldr x27, [x11, #0x0]\n" + "mov x13, #0x0\n" + ".inst 0xe0176b23 // ld1b { za0h.b[x15, #3] }, p2/Z, [x25, x23]\n" + ".inst 0x25266d22 // psel p2.b, p11.b/Z, p9.b[w14]\n" + "ldr x25, [x10, #0x0]\n" + "mov x12, #0x0\n" + ".inst 0xe0176706 // ld1b { za0h.b[x15, #6] }, p1/Z, [x24, x23]\n" + ".inst 0x252e6d21 // psel p1.b, p11.b/Z, p9.b[w14, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01762a7 // ld1b { za0h.b[x15, #7] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252e6d20 // psel p0.b, p11.b/Z, p9.b[w14, #1]\n" + "whilelt p9.b, x16, %x[width]\n" + ".inst 0xe0bfcec0 // st1w { za0v.s[x14] }, p3/Z, [x22, XZR, LSL #2]\n" + "incb x16\n" + "add x10, x10, #0x10\n" + ".inst 0xe0a8cac4 // st1w { za1v.s[x14] }, p2/Z, [x22, x8, LSL #2]\n" + "incb x23\n" + "whilelt p8.b, x16, %x[width]\n" + ".inst 0xe0a9c6c1 // st1w { za0v.s[x14, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bcc2c5 // st1w { za1v.s[x14, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "addvl x22, x22, #4\n" + "cbz x17, 7f\n" + "6:" // K loop: Main loop: Second: Loop + ".inst 0x25256143 // psel p3.b, p8.b/Z, p10.b[w13]\n" + ".inst 0x252d6142 // psel p2.b, p8.b/Z, p10.b[w13, #1]\n" + ".inst 0x25656141 // psel p1.b, p8.b/Z, p10.b[w13, #4]\n" + ".inst 0x256d6140 // psel p0.b, p8.b/Z, p10.b[w13, #5]\n" + ".inst 0xe0172f60 // ld1b { za0h.b[x13] }, p3/Z, [x27, x23]\n" + ".inst 0x25246d23 // psel p3.b, p11.b/Z, p9.b[w12]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0172b21 // ld1b { za0h.b[x13, #1] }, p2/Z, [x25, x23]\n" + ".inst 0x25246d22 // psel p2.b, p11.b/Z, p9.b[w12]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0172704 // ld1b { za0h.b[x13, #4] }, p1/Z, [x24, x23]\n" + ".inst 0x252c6d21 // psel p1.b, p11.b/Z, p9.b[w12, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01722a5 // ld1b { za0h.b[x13, #5] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252c6d20 // psel p0.b, p11.b/Z, p9.b[w12, #1]\n" + "add x10, x10, #0x10\n" + ".inst 0xe0bf8ec8 // st1w { za2v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" + "add x13, x13, #0x8\n" + ".inst 0xe0a88acc // st1w { za3v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" + ".inst 0xe0a986c9 // st1w { za2v.s[x12, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bc82cd // st1w { za3v.s[x12, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "add x12, x12, #0x2\n" + "addvl x22, x22, #4\n" + "cmp x12, x17\n" + "blt 6b\n" + "7:" // K loop: Main loop: Second: Tail + ".inst 0x25256143 // psel p3.b, p8.b/Z, p10.b[w13]\n" + ".inst 0x252d6142 // psel p2.b, p8.b/Z, p10.b[w13, #1]\n" + ".inst 0x25656141 // psel p1.b, p8.b/Z, p10.b[w13, #4]\n" + ".inst 0x256d6140 // psel p0.b, p8.b/Z, p10.b[w13, #5]\n" + "mov x11, %x[in]\n" + "add x10, %x[in], x8, LSL #3\n" + ".inst 0xe0172f60 // ld1b { za0h.b[x13] }, p3/Z, [x27, x23]\n" + ".inst 0x25246d23 // psel p3.b, p11.b/Z, p9.b[w12]\n" + "ldr x27, [x11, #0x0]\n" + ".inst 0xe0172b21 // ld1b { za0h.b[x13, #1] }, p2/Z, [x25, x23]\n" + ".inst 0x25246d22 // psel p2.b, p11.b/Z, p9.b[w12]\n" + "ldr x25, [x10, #0x0]\n" + ".inst 0xe0172704 // ld1b { za0h.b[x13, #4] }, p1/Z, [x24, x23]\n" + ".inst 0x252c6d21 // psel p1.b, p11.b/Z, p9.b[w12, #1]\n" + "ldr x24, [x11, #0x8]\n" + "add x11, x11, #0x10\n" + ".inst 0xe01722a5 // ld1b { za0h.b[x13, #5] }, p0/Z, [x21, x23]\n" + "ldr x21, [x10, #0x8]\n" + ".inst 0x252c6d20 // psel p0.b, p11.b/Z, p9.b[w12, #1]\n" + "whilelt p9.b, x16, %x[width]\n" + ".inst 0xe0bf8ec8 // st1w { za2v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" + "subs x20, x20, #0x1\n" + "add x10, x10, #0x10\n" + ".inst 0xe0a88acc // st1w { za3v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" + "incb x16\n" + "incb x23\n" + ".inst 0xe0a986c9 // st1w { za2v.s[x12, #1] }, p1/Z, [x22, x9, LSL #2]\n" + ".inst 0xe0bc82cd // st1w { za3v.s[x12, #1] }, p0/Z, [x22, x28, LSL #2]\n" + "addvl x22, x22, #4\n" + "bgt 3b\n" + "8:" // K loop: Tails + "cbnz x26, 11f\n" + "mov x11, %x[in]\n" + "whilelt p8.b, x16, %x[width]\n" + "mov x13, #0x0\n" + "mov x12, #0x0\n" + "9:" // K loop: Tails: Even: First + ".inst 0x25306d23 // psel p3.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25306d22 // psel p2.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25356141 // psel p1.b, p8.b/Z, p10.b[w13, #2]\n" + ".inst 0x253d6140 // psel p0.b, p8.b/Z, p10.b[w13, #3]\n" + ".inst 0xe0bf8ec0 // st1w { za0v.s[x12] }, p3/Z, [x22, XZR, LSL #2]\n" + ".inst 0xe0a88ac4 // st1w { za1v.s[x12] }, p2/Z, [x22, x8, LSL #2]\n" + "add x12, x12, #0x1\n" + "addvl x22, x22, #2\n" + "ldr x21, [x11, #0x0]\n" + "cmp x12, x8\n" + "ldr x20, [x11, x8, LSL #0x3]\n" + "add x11, x11, #0x8\n" + ".inst 0xe01726a2 // ld1b { za0h.b[x13, #2] }, p1/Z, [x21, x23]\n" + ".inst 0xe0172283 // ld1b { za0h.b[x13, #3] }, p0/Z, [x20, x23]\n" + "add x13, x13, #0x4\n" + "blt 9b\n" + "whilelt p9.b, x16, %x[width]\n" + "whilelt p8.b, x16, %x[width]\n" + "mov x20, #0x0\n" + "mov x12, #0x0\n" + "10:" // K loop: Tails: Even: Second + ".inst 0x25306d21 // psel p1.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25306d20 // psel p0.s, p11.s/Z, p9.s[w12]\n" + "add x20, x20, #0x4\n" + ".inst 0xe0bf86c8 // st1w { za2v.s[x12] }, p1/Z, [x22, XZR, LSL #2]\n" + ".inst 0xe0a882cc // st1w { za3v.s[x12] }, p0/Z, [x22, x8, LSL #2]\n" + "add x12, x12, #0x1\n" + "addvl x22, x22, #2\n" + "cmp x12, x7\n" + "blt 10b\n" + "whilelt p8.b, x16, %x[width]\n" + "b 13f\n" + "11:" // K loop: Tails: Odd + "mov x12, #0x0\n" + "12:" // K loop: Tails: Odd: Loop + ".inst 0x25306d21 // psel p1.s, p11.s/Z, p9.s[w12]\n" + ".inst 0x25306d20 // psel p0.s, p11.s/Z, p9.s[w12]\n" + ".inst 0xe0bf86c0 // st1w { za0v.s[x12] }, p1/Z, [x22, XZR, LSL #2]\n" + ".inst 0xe0a882c4 // st1w { za1v.s[x12] }, p0/Z, [x22, x8, LSL #2]\n" + "add x12, x12, #0x1\n" + "addvl x22, x22, #2\n" + "cmp x12, x7\n" + "blt 12b\n" + "13:" // K loop: End + "mov %x[out], x22\n" + ".inst 0xd503467f // SMSTOP\n" + : [out] "+&r"(out) + : [height] "r"(height), [in] "r"(in), [row_offset] "r"(row_offset), [width] "r"(width) + : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", + "p14", "p15", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x20", "x21", + "x22", "x23", "x24", "x25", "x26", "x27", "x28", "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8", + "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", + "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"); + } +} + +#endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h similarity index 84% rename from kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h rename to kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h index d50607e5..3b95a0a9 100644 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx1_x8_sme.h +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h @@ -19,7 +19,7 @@ extern "C" { /// @param[in] mr Number of rows to be interleaved. /// /// @return The m step value. -size_t kai_get_m_step_lhs_pack_x8p2vlx1_x8_sme(size_t mr); +size_t kai_get_m_step_lhs_pack_x8p2vlx4_x8_sme(size_t mr); /// Gets the offset in bytes to the data element in the LHS buffer. /// @@ -27,7 +27,7 @@ size_t kai_get_m_step_lhs_pack_x8p2vlx1_x8_sme(size_t mr); /// @param[in] lhs_stride Row stride in bytes. /// /// @return The offset in bytes to the data element. -size_t kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t lhs_stride); +size_t kai_get_lhs_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t lhs_stride); /// Gets the offset in bytes to the data element in the packed LHS buffer. /// @@ -38,7 +38,7 @@ size_t kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t lhs_stri /// @param[in] sr Unused. Must be 1. /// /// @return The offset in bytes to the data element. -size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr); +size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr); /// Gets the size in bytes of the packed LHS buffer. /// @@ -49,15 +49,15 @@ size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme(size_t m_idx, size_t k /// @param[in] sr Unused. Must be 1. /// /// @return The size in bytes of the packed LHS buffer. -size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx1_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr); +size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr); /// Runs the LHS packing function for matrix multiplication. /// /// The pointer of each buffers (LHS and packed LHS) needs to be added with offset /// calculated using the following functions: /// -/// * LHS: @ref kai_get_lhs_offset_lhs_pack_x8p2vlx1_x8_sme. -/// * Packed LHS: @ref kai_get_lhs_packed_offset_lhs_pack_x8p2vlx1_x8_sme. +/// * LHS: @ref kai_get_lhs_offset_lhs_pack_x8p2vlx4_x8_sme. +/// * Packed LHS: @ref kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme. /// /// @param[in] m Number of rows of the unpacked LHS matrix. /// @param[in] k Common dimension between the LHS and RHS matrix. @@ -68,10 +68,9 @@ size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx1_x8_sme(size_t m, size_t k, size /// @param[in] lhs LHS matrix data buffer. /// @param[in] lhs_stride Row stride in bytes of the LHS matrix. /// @param[out] lhs_packed Packed RHS matrix. -void kai_run_lhs_pack_x8p2vlx1_x8_sme( - size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, - const void* lhs, size_t lhs_stride, void* lhs_packed -); +void kai_run_lhs_pack_x8p2vlx4_x8_sme( + size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, const void* lhs, size_t lhs_stride, + void* lhs_packed); #ifdef __cplusplus } // extern "C" diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c new file mode 100644 index 00000000..ac18e9cd --- /dev/null +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c @@ -0,0 +1,199 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#if !defined(__aarch64__) || !defined(__ARM_FEATURE_SVE2) +#error This file must be compiled for AArch64, FEAT_SVE2. +#else // Architectural features check. + +#include "kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" + +#include +#include +#include +#include + +#include "kai/kai_common.h" + +static const size_t kai_nr = 2; +static const size_t kai_kr = 4; +static const size_t kai_num_bytes_input = 1; +static const size_t kai_num_bytes_output = 1; +static const size_t kai_num_bytes_bias = 4; +static const size_t kai_num_bytes_scale = 4; + +size_t kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(void) { + return kai_nr * kai_get_sme_vector_length_u8(); +} + +size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { + KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8()) == 0); + + return n_idx * kai_num_bytes_input; +} + +size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { + return n_idx * kai_num_bytes_bias; +} + +size_t kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t k) { + return kai_nr * kai_get_sme_vector_length_u8() / kai_kr * + (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); +} + +size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k) { + KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8() / kai_kr) == 0); + + return n_idx * (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); +} + +size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k) { + return kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( + kai_roundup(n, kai_nr * kai_get_sme_vector_length_u8() / kai_kr), k); +} + +void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( + size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, + const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, + const struct kai_rhs_pack_qsi8_params* params) { + KAI_ASSUME(num_groups == 1); + KAI_ASSUME(nr == kai_nr * kai_get_sme_vector_length_u8() / kai_kr); + KAI_ASSUME(kr == kai_kr); + KAI_ASSUME(sr == 1); + KAI_ASSUME(rhs != NULL); + KAI_ASSUME(bias != NULL); + KAI_ASSUME(rhs_packed != NULL); + KAI_ASSUME(extra_bytes == 0); + KAI_ASSUME(params != NULL); + + size_t height = k; + const size_t width = n; + const void* in = rhs; + void* out = rhs_packed; + const size_t in_stride = rhs_stride; + uint8_t* pad_row = (uint8_t*)alloca(width * sizeof(uint8_t)); + + if (height % 4) { + memset(pad_row, 0, width * sizeof(uint8_t)); + } + + size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(height); + const int32_t input_zero_point = params->input_zero_point; + const float scale_multiplier = params->scale_multiplier; + + __asm__ __volatile__( + ".inst 0xd503477f // SMSTART ZA\n" + "mov x27, %x[out]\n" + "mov x26, %x[height]\n" + "ptrue p2.b\n" + "incb %x[out], ALL, MUL #2\n" + "1:" // Main row loop: Head + "mov x25, %x[in]\n" + "cmp %x[height], #0x3\n" + "add x24, x25, %x[in_stride]\n" + "mov x23, %x[out]\n" + "add x22, x24, %x[in_stride]\n" + "mov x21, %x[width]\n" + "add x20, x22, %x[in_stride]\n" + "csel x22, x22, %x[pad_row], GE\n" + "add %x[in], x20, %x[in_stride]\n" + "csel x20, x20, %x[pad_row], GT\n" + "cmp %x[height], #0x1\n" + "sub %x[height], %x[height], #0x4\n" + "csel x24, x24, %x[pad_row], GT\n" + "2:" // Main row loop: Column loop + "whilelt p0.b, XZR, x21\n" + "decw x21, ALL, MUL #2\n" + "ld1b { z18.b }, p0/Z, [x25]\n" + "cmp x21, #0x0\n" + "incd x25, ALL, MUL #4\n" + "ld1b { z19.b }, p0/Z, [x24]\n" + "incd x24, ALL, MUL #4\n" + "ld1b { z17.b }, p0/Z, [x22]\n" + "incd x22, ALL, MUL #4\n" + "ld1b { z16.b }, p0/Z, [x20]\n" + "incd x20, ALL, MUL #4\n" + "zip1 z18.b, z18.b, z17.b\n" + "zip1 z16.b, z19.b, z16.b\n" + "zip1 z17.b, z18.b, z16.b\n" + "zip2 z16.b, z18.b, z16.b\n" + "st1b { z17.b }, p2, [x23]\n" + "st1b { z16.b }, p2, [x23, #1, MUL VL]\n" + "add x23, x23, %x[out_stride]\n" + "bgt 2b\n" + "cmp %x[height], #0x1\n" + "addvl %x[out], %x[out], #2\n" + "bge 1b\n" + "mov x22, %x[out]\n" + "mov x21, %x[width]\n" + "dup z18.s, %w[scale_multiplier]\n" + "cbz %x[scale], 5f\n" + "4:" // Scale: Full loop + "mov x20, x21\n" + "decw x21, ALL, MUL #2\n" + "whilelt p1.s, XZR, x20\n" + "decw x20\n" + "whilelt p0.s, XZR, x20\n" + "ld1w { z17.s }, p1/Z, [%x[scale]]\n" + "cmp x21, #0x0\n" + "ld1w { z16.s }, p0/Z, [%x[scale], #1, MUL VL]\n" + "incb %x[scale], ALL, MUL #2\n" + "fmul z17.s, z17.s, z18.s\n" + "fmul z16.s, z16.s, z18.s\n" + "st1w { z17.s }, p2, [x22]\n" + "st1w { z16.s }, p2, [x22, #1, MUL VL]\n" + "add x22, x22, %x[out_stride]\n" + "bgt 4b\n" + "5:" // Scale: Done + "cbz %x[width], 8f\n" + "cbz x26, 8f\n" + "dup z21.s, %w[input_zero_point]\n" + "add x25, x26, #0x3\n" + "cntw x24, ALL, MUL #2\n" + "mov z20.b, #0x1\n" + "lsr x25, x25, #0x2\n" + "mov x23, %x[width]\n" + "addvl x22, x27, #2\n" + "neg z21.s, p2/M, z21.s\n" + "6:" // Bias: N loop + "mov x21, x22\n" + "mov x20, x25\n" + "mov z19.s, #0x0\n" + "mov z18.s, #0x0\n" + "7:" // Bias: K loop + "ld1b { z17.b }, p2/Z, [x21]\n" + "subs x20, x20, #0x1\n" + "ld1b { z16.b }, p2/Z, [x21, #1, MUL VL]\n" + "addvl x21, x21, #2\n" + "sdot z19.s, z17.b, z20.b\n" + "sdot z18.s, z16.b, z20.b\n" + "bgt 7b\n" + "mov x20, x23\n" + "add x22, x22, %x[out_stride]\n" + "whilelt p1.s, XZR, x20\n" + "decw x20\n" + "whilelt p0.s, XZR, x20\n" + "ld1w { z17.s }, p1/Z, [%x[bias]]\n" + "subs x23, x23, x24\n" + "ld1w { z16.s }, p0/Z, [%x[bias], #1, MUL VL]\n" + "addvl %x[bias], %x[bias], #2\n" + "mla z17.s, p2/M, z19.s, z21.s\n" + "mla z16.s, p2/M, z18.s, z21.s\n" + "st1w { z17.s }, p2, [x27]\n" + "st1w { z16.s }, p2, [x27, #1, MUL VL]\n" + "add x27, x27, %x[out_stride]\n" + "bgt 6b\n" + "8:" // Bias: Done + ".inst 0xd503467f // SMSTOP\n" + : [bias] "+&r"(bias), [height] "+&r"(height), [in] "+&r"(in), [out] "+&r"(out), [scale] "+&r"(scale) + : [in_stride] "r"(in_stride), [input_zero_point] "r"(input_zero_point), [out_stride] "r"(out_stride), + [pad_row] "r"(pad_row), [scale_multiplier] "r"(scale_multiplier), [width] "r"(width) + : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", + "p15", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", + "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", + "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"); +} + +#endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h new file mode 100644 index 00000000..ac24eb12 --- /dev/null +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h @@ -0,0 +1,83 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "kai/kai_common.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/// Gets n step value. +/// +/// The starting row index must be divisible by `n_step`. +/// +/// @return The n step value. +size_t kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(void); + +/// Gets the offset in bytes to the data element in the RHS matrix buffer. +/// +/// @param[in] n_idx Column index. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); + +/// Gets the offset in bytes to the data element in the bias buffer. +/// +/// @param[in] n_idx Column index. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); + +/// Gets the offset in bytes to the data element in the packed RHS buffer. +/// +/// @param[in] n_idx Row index. +/// @param[in] k Number of columns. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k); + +/// Gets the size in bytes of the packed RHS buffer. +/// +/// @param[in] n Number of rows. +/// @param[in] k Number of columns. +/// +/// @return The size in bytes of the packed RHS buffer. +size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k); + +/// Runs the RHS packing function for matrix multiplication. +/// +/// The pointer of each buffers (RHS, bias and packed RHS) needs to be added with offset +/// calculated using the following functions: +/// +/// * RHS: @ref kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. +/// * Bias: @ref kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. +/// * Output: @ref kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. +/// +/// @param[in] num_groups Number of groups. It must be 1. +/// @param[in] n Number of columns of the output matrix. +/// @param[in] k Common dimension between the LHS and RHS matrix. +/// @param[in] nr Block size in N dimension. It must be 2 * kai_get_sme_vector_length_u8(). +/// @param[in] kr Block size in K dimension. It must be 4. +/// @param[in] sr Number of kr splits. It must be 1. +/// @param[in] rhs_stride Row stride in bytes of the RHS matrix. +/// @param[in] rhs RHS matrix data buffer. +/// @param[in] bias Bias matrix data buffer. +/// @param[in] scale Scale data buffer. It must be NULL. +/// @param[out] rhs_packed Packed RHS matrix. +/// @param[in] extra_bytes Extra bytes to append to the end of each row of the packed RHS matrix. It must be 0. +/// @param[in] params Extra packing parameters. It must be NULL. +void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( + size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, + const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, + const struct kai_rhs_pack_qsi8_params* params); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/test/reference/binary_elementwise.cpp b/test/reference/binary_elementwise.cpp index d5abd26c..f6513657 100644 --- a/test/reference/binary_elementwise.cpp +++ b/test/reference/binary_elementwise.cpp @@ -136,6 +136,18 @@ std::vector sub( lhs, lhs_dt, lhs_height, lhs_width, rhs, rhs_dt, rhs_height, rhs_width); } +template +std::vector sub( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width) { + return binary_elementwise_any_op_type( + lhs, rhs, lhs_height, lhs_width, rhs_height, rhs_width); +} + +template std::vector sub( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width); + std::vector mul( const void* lhs, DataType lhs_dt, size_t lhs_height, size_t lhs_width, // const void* rhs, DataType rhs_dt, size_t rhs_height, size_t rhs_width) { @@ -143,6 +155,22 @@ std::vector mul( lhs, lhs_dt, lhs_height, lhs_width, rhs, rhs_dt, rhs_height, rhs_width); } +template +std::vector mul( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width) { + return binary_elementwise_any_op_type( + lhs, rhs, lhs_height, lhs_width, rhs_height, rhs_width); +} + +template std::vector mul( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width); + +template std::vector mul( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width); + std::vector div( const void* lhs, DataType lhs_dt, size_t lhs_height, size_t lhs_width, // const void* rhs, DataType rhs_dt, size_t rhs_height, size_t rhs_width) { diff --git a/test/reference/binary_elementwise.hpp b/test/reference/binary_elementwise.hpp index e3d3a9e1..f2f5c0ab 100644 --- a/test/reference/binary_elementwise.hpp +++ b/test/reference/binary_elementwise.hpp @@ -50,6 +50,25 @@ std::vector sub( const void* lhs, DataType lhs_dt, size_t lhs_height, size_t lhs_width, // const void* rhs, DataType rhs_dt, size_t rhs_height, size_t rhs_width); +/// Elementwise subtraction. +/// +/// Broadcasting is supported for any dimension and both LHS and RHS operands. +/// +/// @tparam T The data type. +/// +/// @param[in] lhs The LHS data buffer. +/// @param[in] lhs_height The number of rows of the LHS matrix. +/// @param[in] lhs_width The number of columns of the LHS matrix. +/// @param[in] rhs The RHS data buffer. +/// @param[in] rhs_height The number of rows of the RHS matrix. +/// @param[in] rhs_width The number of columns of the LHS matrix. +/// +/// @return The result matrix. +template +std::vector sub( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width); + /// Elementwise multiplication. /// /// Broadcasting is supported for any dimension and both LHS and RHS operands. @@ -68,6 +87,25 @@ std::vector mul( const void* lhs, DataType lhs_dt, size_t lhs_height, size_t lhs_width, // const void* rhs, DataType rhs_dt, size_t rhs_height, size_t rhs_width); +/// Elementwise multiplication. +/// +/// Broadcasting is supported for any dimension and both LHS and RHS operands. +/// +/// @tparam T The data type. +/// +/// @param[in] lhs The LHS data buffer. +/// @param[in] lhs_height The number of rows of the LHS matrix. +/// @param[in] lhs_width The number of columns of the LHS matrix. +/// @param[in] rhs The RHS data buffer. +/// @param[in] rhs_height The number of rows of the RHS matrix. +/// @param[in] rhs_width The number of columns of the LHS matrix. +/// +/// @return The result matrix. +template +std::vector mul( + const void* lhs, size_t lhs_height, size_t lhs_width, // + const void* rhs, size_t rhs_height, size_t rhs_width); + /// Elementwise division. /// /// Broadcasting is supported for any dimension and both LHS and RHS operands. diff --git a/test/reference/clamp.cpp b/test/reference/clamp.cpp new file mode 100644 index 00000000..a4ba773c --- /dev/null +++ b/test/reference/clamp.cpp @@ -0,0 +1,32 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "test/reference/clamp.hpp" + +#include +#include +#include +#include + +#include "test/common/memory.hpp" +#include "test/common/round.hpp" + +namespace kai::test { + +template +std::vector clamp(const void* src, size_t len, T min_value, T max_value) { + std::vector dst(round_up_division(len * size_in_bits, 8)); + + for (size_t i = 0; i < len; ++i) { + write_array(dst.data(), i, std::clamp(read_array(src, i), min_value, max_value)); + } + + return dst; +} + +template std::vector clamp(const void* src, size_t len, float min_value, float max_value); + +} // namespace kai::test diff --git a/test/reference/clamp.hpp b/test/reference/clamp.hpp new file mode 100644 index 00000000..d28484f3 --- /dev/null +++ b/test/reference/clamp.hpp @@ -0,0 +1,18 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +namespace kai::test { + +template +std::vector clamp(const void* src, size_t len, T min_value, T max_value); + +} // namespace kai::test diff --git a/test/reference/fill.cpp b/test/reference/fill.cpp index 49b16987..faad8547 100644 --- a/test/reference/fill.cpp +++ b/test/reference/fill.cpp @@ -20,6 +20,9 @@ #include "test/common/float16.hpp" #include "test/common/int4.hpp" #include "test/common/memory.hpp" +#include "test/common/numeric_limits.hpp" +#include "test/common/round.hpp" +#include "test/common/type_traits.hpp" namespace kai::test { @@ -118,11 +121,67 @@ std::vector fill_matrix_random(size_t height, size_t width, const DataF } } +template +Value get_random(uint64_t seed, Value min_value, Value max_value) { + static_assert(is_floating_point || is_integral); + static_assert(size_in_bits <= 32); + + using Distribution = std::conditional_t< + is_floating_point, std::uniform_real_distribution, + std::conditional_t< + is_signed, std::uniform_int_distribution, std::uniform_int_distribution>>; + + std::mt19937 rnd(seed); + Distribution dist(min_value, max_value); + + return static_cast(dist(rnd)); +} + +template +Value get_random(uint64_t seed) { + if constexpr (is_floating_point) { + return get_random(seed, static_cast(0.0F), static_cast(1.0F)); + } else { + return get_random(seed, numeric_lowest, numeric_highest); + } +} + +template float get_random(uint64_t seed); +template int32_t get_random(uint64_t seed); + +template +std::vector fill_random(size_t length, uint64_t seed, Value min_value, Value max_value) { + static_assert(is_floating_point || is_integral); + static_assert(size_in_bits <= 32); + + using Distribution = std::conditional_t< + is_floating_point, std::uniform_real_distribution, + std::conditional_t< + is_signed, std::uniform_int_distribution, std::uniform_int_distribution>>; + + std::mt19937 rnd(seed); + Distribution dist(min_value, max_value); + + std::vector data(round_up_division(length * size_in_bits, 8)); + + for (size_t i = 0; i < length; ++i) { + write_array(data.data(), i, static_cast(dist(rnd))); + } + + return data; +} + template std::vector fill_random(size_t length, uint64_t seed) { - return fill_matrix_random_raw(1, length, seed); + if constexpr (is_floating_point) { + return fill_random(length, seed, static_cast(0.0F), static_cast(1.0F)); + } else { + return fill_random(length, seed, numeric_lowest, numeric_highest); + } } template std::vector fill_random(size_t length, uint64_t seed); +template std::vector fill_random(size_t length, uint64_t seed); +template std::vector fill_random(size_t length, uint64_t seed); } // namespace kai::test diff --git a/test/reference/fill.hpp b/test/reference/fill.hpp index 80093952..df5f03d3 100644 --- a/test/reference/fill.hpp +++ b/test/reference/fill.hpp @@ -24,6 +24,28 @@ class DataFormat; /// @return The data buffer for the matrix. std::vector fill_matrix_random(size_t height, size_t width, const DataFormat& format, uint64_t seed); +/// Gets a random value. +/// +/// @tparam Value The data type. +/// +/// @param[in] seed The random seed. +/// +/// @return The random value. +template +Value get_random(uint64_t seed); + +/// Gets a random value. +/// +/// @tparam Value The data type. +/// +/// @param[in] seed The random seed. +/// @param[in] min_value The minimum value. +/// @param[in] max_value The maximum value. +/// +/// @return The random value. +template +Value get_random(uint64_t seed, Value min_value, Value max_value); + /// Creates a new data buffer filled with random data. /// /// @tparam Value The data type. @@ -35,4 +57,17 @@ std::vector fill_matrix_random(size_t height, size_t width, const DataF template std::vector fill_random(size_t length, uint64_t seed); +/// Creates a new data buffer filled with random data. +/// +/// @tparam Value The data type. +/// +/// @param[in] length The number of elements. +/// @param[in] seed The random seed. +/// @param[in] min_value The minimum value. +/// @param[in] max_value The maximum value. +/// +/// @return The data buffer. +template +std::vector fill_random(size_t length, uint64_t seed, Value min_value, Value max_value); + } // namespace kai::test diff --git a/test/reference/matmul.cpp b/test/reference/matmul.cpp index 6ac8fe71..a1be2556 100644 --- a/test/reference/matmul.cpp +++ b/test/reference/matmul.cpp @@ -185,6 +185,79 @@ std::vector matmul( return tmp_dst; } +template < + typename LhsData, typename LhsScale, typename LhsZeroPoint, typename RhsData, typename RhsScale, + typename RhsZeroPoint, typename BiasData, typename BiasScale, typename BiasZeroPoint, typename DstData> +std::vector matmul_nt_t_quantized( + size_t m, size_t n, size_t k, // + const void* lhs_data, const void* lhs_scales, const void* lhs_zero_points, size_t lhs_quant_height, + size_t lhs_quant_width, // + const void* rhs_data, const void* rhs_scales, const void* rhs_zero_points, size_t rhs_quant_height, + size_t rhs_quant_width, // + const void* bias_data, const void* bias_scales, const void* bias_zero_points, size_t bias_quant_width) { + const auto lhs_num_quant_per_row = round_up_division(k, lhs_quant_width); + const auto rhs_num_quant_per_row = round_up_division(k, rhs_quant_width); + + std::vector dst(m * n * sizeof(DstData)); + + for (size_t y = 0; y < m; ++y) { + for (size_t x = 0; x < n; ++x) { + DstData acc = 0; + + for (size_t i = 0; i < k; ++i) { + const auto lhs_data_index = y * k + i; + const auto lhs_quant_index = y / lhs_quant_height * lhs_num_quant_per_row + i / lhs_quant_width; + const auto lhs_value = read_array(lhs_data, lhs_data_index); + const auto lhs_scale = lhs_scales != nullptr ? read_array(lhs_scales, lhs_quant_index) + : static_cast(1); + const auto lhs_zero_point = lhs_zero_points != nullptr + ? read_array(lhs_zero_points, lhs_quant_index) + : static_cast(0); + + const auto rhs_data_index = x * k + i; + const auto rhs_quant_index = x / rhs_quant_height * rhs_num_quant_per_row + i / rhs_quant_width; + const auto rhs_value = read_array(rhs_data, rhs_data_index); + const auto rhs_scale = rhs_scales != nullptr ? read_array(rhs_scales, rhs_quant_index) + : static_cast(1); + const auto rhs_zero_point = rhs_zero_points != nullptr + ? read_array(rhs_zero_points, rhs_quant_index) + : static_cast(0); + + acc += (static_cast(lhs_value) - static_cast(lhs_zero_point)) * + static_cast(lhs_scale) * + (static_cast(rhs_value) - static_cast(rhs_zero_point)) * + static_cast(rhs_scale); + } + + if (bias_data != nullptr) { + const auto bias_value = read_array(bias_data, x); + const auto bias_scale = bias_scales != nullptr + ? read_array(bias_scales, x / bias_quant_width) + : static_cast(1); + const auto bias_zero_point = bias_zero_points != nullptr + ? read_array(bias_zero_points, x / bias_quant_width) + : static_cast(0); + + acc += (static_cast(bias_value) - static_cast(bias_zero_point)) * + static_cast(bias_scale); + } + + write_array(dst.data(), y * n + x, acc); + } + } + + return dst; +} + +template std::vector +matmul_nt_t_quantized( + size_t m, size_t n, size_t k, // + const void* lhs_data, const void* lhs_scales, const void* lhs_zero_points, size_t lhs_quant_height, + size_t lhs_quant_width, // + const void* rhs_data, const void* rhs_scales, const void* rhs_zero_points, size_t rhs_quant_height, + size_t rhs_quant_width, // + const void* bias_data, const void* bias_scales, const void* bias_zero_points, size_t bias_quant_width); + template < typename LhsData, typename LhsScale, typename LhsZeroPoint, typename RhsData, typename RhsScale, typename RhsZeroPoint, typename Bias, typename IntAcc, typename DstData> @@ -224,8 +297,8 @@ std::vector matmul_clamp_nt_t( : 0; acc += static_cast( - (static_cast(lhs_value) + static_cast(lhs_zero_point)) * - (static_cast(rhs_value) + static_cast(rhs_zero_point))) * + (static_cast(lhs_value) - static_cast(lhs_zero_point)) * + (static_cast(rhs_value) - static_cast(rhs_zero_point))) * static_cast(lhs_scale) * static_cast(rhs_scale); } @@ -309,8 +382,8 @@ std::vector matmul_clamp_nt_nt( : 0; acc += static_cast( - (static_cast(lhs_value) + static_cast(lhs_zero_point)) * - (static_cast(rhs_value) + static_cast(rhs_zero_point))) * + (static_cast(lhs_value) - static_cast(lhs_zero_point)) * + (static_cast(rhs_value) - static_cast(rhs_zero_point))) * static_cast(lhs_scale) * static_cast(rhs_scale); } diff --git a/test/reference/matmul.hpp b/test/reference/matmul.hpp index 88a0729f..9a8ce9f8 100644 --- a/test/reference/matmul.hpp +++ b/test/reference/matmul.hpp @@ -145,4 +145,15 @@ std::vector matmul_clamp_nt_nt( const void* biases, // DstData min_value, DstData max_value); +template < + typename LhsData, typename LhsScale, typename LhsZeroPoint, typename RhsData, typename RhsScale, + typename RhsZeroPoint, typename BiasData, typename BiasScale, typename BiasZeroPoint, typename DstData> +std::vector matmul_nt_t_quantized( + size_t m, size_t n, size_t k, // + const void* lhs_data, const void* lhs_scales, const void* lhs_zero_points, size_t lhs_quant_height, + size_t lhs_quant_width, // + const void* rhs_data, const void* rhs_scales, const void* rhs_zero_points, size_t rhs_quant_height, + size_t rhs_quant_width, // + const void* bias_data, const void* bias_scales, const void* bias_zero_points, size_t bias_quant_width); + } // namespace kai::test diff --git a/test/reference/matmul_pack.cpp b/test/reference/matmul_pack.cpp new file mode 100644 index 00000000..f04de8dc --- /dev/null +++ b/test/reference/matmul_pack.cpp @@ -0,0 +1,54 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "test/reference/matmul_pack.hpp" + +#include +#include +#include +#include + +#include "test/common/round.hpp" +#include "test/reference/binary_elementwise.hpp" +#include "test/reference/pack.hpp" +#include "test/reference/reduce.hpp" +#include "test/reference/reorder.hpp" + +namespace kai::test { + +template +std::vector matmul_pack_rhs_nxk_static_quantized( + const void* data, const void* scales, Scale lhs_scale, Scale dst_scale, const void* biases, + ZeroPoint lhs_zero_point, size_t n, size_t k, size_t block_height, size_t block_width) { + // The RHS data matrix is reordered according to the blocking parameters. + const auto reordered_data = reorder_block(data, n, k, block_height, block_width); + + // The effective per-channel scale: + // final_scales[n_index] = lhs_scale * rhs_scales[n_index] / dst_scale. + const auto scale_multiplier = lhs_scale / dst_scale; + auto combined_scales = mul(scales, 1, n, &scale_multiplier, 1, 1); + combined_scales.resize(round_up_multiple(n, block_height) * sizeof(Scale)); // Pads with 0s. + + // The effective per-channel biases: + // final_biases[n_index] = biases[n_index] - lhs_zero_point * sum(data[n_index, :]). + const auto row_sum = reduce_add_x(data, n, k); + const auto row_sum_times_lhs_zp = mul(row_sum.data(), n, k, &lhs_zero_point, 1, 1); + auto combined_biases = sub(biases, 1, n, row_sum_times_lhs_zp.data(), 1, n); + combined_biases.resize(round_up_multiple(n, block_height) * sizeof(ZeroPoint)); // Pads with 0s. + + // Packs the effective biases followed by the data block followed by the effective scales for the block. + auto packed_rhs = pack_zero_points_data_scales_per_block( + combined_biases.data(), reordered_data.data(), combined_scales.data(), round_up_division(n, block_height), + block_height, block_height * round_up_multiple(k, block_width), block_height); + + return packed_rhs; +} + +template std::vector matmul_pack_rhs_nxk_static_quantized( + const void* data, const void* scales, float lhs_scale, float dst_scale, const void* biases, int32_t lhs_zero_point, + size_t n, size_t k, size_t block_height, size_t block_width); + +} // namespace kai::test diff --git a/test/reference/matmul_pack.hpp b/test/reference/matmul_pack.hpp new file mode 100644 index 00000000..30646c98 --- /dev/null +++ b/test/reference/matmul_pack.hpp @@ -0,0 +1,44 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +namespace kai::test { + +/// Packs the RHS buffer for static quantized GeMM. +/// +/// The RHS matrix must be transposed. +/// +/// This function can be used when the following conditions are met: +/// * LHS, RHS and DST data types have the same size and are quantized. +/// * LHS is asymmetric per-tensor, RHS is symmetric per-channel and DST is asymmetric per-tensor. +/// +/// @tparam Data The data type of the RHS matrix. +/// @tparam Scale The data type of the quantization scales. +/// @tparam ZeroPoint The data type of the quantization zero points and the operator biases. +/// +/// @param[in] data The data buffer of the RHS matrix. +/// @param[in] scales The quantization scales of the RHS matrix. +/// @param[in] lhs_scale The quantization scale of the LHS matrix. +/// @param[in] dst_scale The quantization scale of the DST matrix. +/// @param[in] biases The biases of the operator. +/// @param[in] lhs_zero_point The quantization zero point of the LHS matrix. +/// @param[in] n The number of columns of the non-transposed RHS matrix. +/// @param[in] k The number of rows of the non-transposed RHS matrix. +/// @param[in] block_height The number of rows of a data block (N dimension). +/// @param[in] block_width The number of columns of a data block (K dimension). +/// +/// @return The packed RHS. +template +std::vector matmul_pack_rhs_nxk_static_quantized( + const void* data, const void* scales, Scale lhs_scale, Scale dst_scale, const void* biases, + ZeroPoint lhs_zero_point, size_t n, size_t k, size_t block_height, size_t block_width); + +} // namespace kai::test diff --git a/test/reference/pack.cpp b/test/reference/pack.cpp index 25c77a63..ed8dd09d 100644 --- a/test/reference/pack.cpp +++ b/test/reference/pack.cpp @@ -255,6 +255,56 @@ std::vector pack_data_scales( return dst; } +template +std::vector pack_zero_points_data_scales_per_block( + const void* zero_points, const void* data, const void* scales, size_t num_blocks, size_t block_num_zero_points, + size_t block_num_data, size_t block_num_scales) { + // Only data is allowed to be sub-byte. + KAI_ASSUME(size_in_bits % 8 == 0); + KAI_ASSUME(size_in_bits % 8 == 0); + + // Checks for memory alignment. + KAI_ASSUME(size_in_bits % size_in_bits == 0); + KAI_ASSUME( + (block_num_zero_points * size_in_bits + block_num_data * size_in_bits) % size_in_bits == + 0); + KAI_ASSUME( + (block_num_data * size_in_bits + block_num_scales * size_in_bits) % size_in_bits == 0); + + std::vector dst(round_up_division( + num_blocks * + (block_num_zero_points * size_in_bits + block_num_data * size_in_bits + + block_num_scales * size_in_bits), + 8)); + auto* dst_ptr = dst.data(); + + for (size_t block_no = 0; block_no < num_blocks; ++block_no) { + for (size_t i = 0; i < block_num_zero_points; ++i) { + write_array( + dst_ptr, i, read_array(zero_points, block_no * block_num_zero_points + i)); + } + dst_ptr += block_num_zero_points * sizeof(ZeroPoint); + + for (size_t i = 0; i < block_num_data; ++i) { + write_array(dst_ptr, i, read_array(data, block_no * block_num_data + i)); + } + dst_ptr += round_up_division(block_num_data * size_in_bits, 8); + + for (size_t i = 0; i < block_num_scales; ++i) { + write_array(dst_ptr, i, read_array(scales, block_no * block_num_scales + i)); + } + dst_ptr += block_num_scales * sizeof(Scale); + } + + KAI_ASSERT(dst_ptr == &*dst.end()); + + return dst; +} + +template std::vector pack_zero_points_data_scales_per_block( + const void* zero_points, const void* data, const void* scales, size_t num_blocks, size_t block_num_zero_points, + size_t block_num_data, size_t block_num_scales); + template std::vector pack_data_scales_interleave_block( const void* data, const void* scales, size_t height, size_t width, size_t quant_width) { diff --git a/test/reference/pack.hpp b/test/reference/pack.hpp index 128ad040..63c94d58 100644 --- a/test/reference/pack.hpp +++ b/test/reference/pack.hpp @@ -79,6 +79,70 @@ template std::vector pack_data_scales( const void* data, const void* scales, size_t height, size_t width, size_t quant_width); +/// Packs the zero point, data and scale into a single buffer. +/// +/// ``` +/// Data matrix: +/// +/// +-----------------+ +/// | q00 q01 q02 q03 | +/// | q10 q11 q12 q13 | +/// | q20 q21 q22 q23 | +/// | q30 q31 q32 q33 | +/// | ............... | +/// : ............... : +/// +/// Scales for each row: +/// +/// +----+ +/// | s0 | +/// | s1 | +/// | s2 | +/// | s3 | +/// | .. | +/// : .. : +/// +/// Zero points for each row: +/// +/// +----+ +/// | z0 | +/// | z1 | +/// | z2 | +/// | z3 | +/// | .. | +/// : .. : +/// ``` +/// +/// The packed data has each zero point followed by the data row followed by the scale. +/// +/// ``` +/// Packed data: +/// +/// +----+-----------------+----+ +/// | z0 | q00 q01 q02 q03 | s0 | +/// | z1 | q10 q11 q12 q13 | s1 | +/// | z2 | q20 q21 q22 q23 | s2 | +/// | z3 | q30 q31 q32 q33 | s3 | +/// | .. | ............... | .. | +/// : .. : ............... : .. : +/// ``` +/// +/// @tparam Data The data type of the data. +/// @tparam Scale The data type of the scale. +/// @tparam ZeroPoint The data type of the zero point. +/// +/// @param[in] data The data buffer. +/// @param[in] scales The scales buffer. +/// @param[in] zero_points The zero points buffer. +/// @param[in] height The number of rows. +/// @param[in] width The number of columns. +/// +/// @return The packed data buffer. +template +std::vector pack_zero_points_data_scales_per_block( + const void* zero_points, const void* data, const void* scales, size_t num_blocks, size_t block_num_zero_points, + size_t block_num_data, size_t block_num_scales); + /// Packs the quantized data and the quantization scale into a single buffer. /// /// ``` diff --git a/test/reference/quantize.cpp b/test/reference/quantize.cpp index 1a2acddf..67c13e99 100644 --- a/test/reference/quantize.cpp +++ b/test/reference/quantize.cpp @@ -48,11 +48,13 @@ std::tuple get_scale_zero_point_from_range(FloatData min_v const FloatData scaled_max = max_value / scale; const FloatData zero_point_f = -(scaled_min + q_min) < scaled_max + q_max ? scaled_min - q_min : scaled_max - q_max; - const ZeroPoint zero_point = round_to_nearest_even(zero_point_f); + const ZeroPoint zero_point = -round_to_nearest_even(zero_point_f); return {scale, zero_point}; } +} // namespace + template IntType quantize_symmetric(float value, float scale) { const auto inv_scale = scale != 0 ? 1.0F / scale : 0.0F; @@ -68,12 +70,12 @@ IntType quantize_symmetric(float value, float scale) { template IntType quantize_asymmetric(FloatType value, FloatType scale, ZeroPointType zero_point) { const auto inv_scale = scale != 0 ? 1.0F / scale : 0.0F; - auto quantized_value = round_to_nearest_even(value * inv_scale) - zero_point; + auto quantized_value = round_to_nearest_even(value * inv_scale) + zero_point; return static_cast( std::clamp(quantized_value, numeric_lowest, numeric_highest)); } -} // namespace +template int8_t quantize_asymmetric(float value, float scale, int32_t zero_point); template std::vector compute_symmetric_per_block_quantization_info( @@ -102,7 +104,7 @@ std::vector compute_symmetric_per_block_quantization_info( } } - const auto scale = max_abs / ((1 << (size_in_bits - 1)) - 1); + const auto scale = max_abs / ((static_cast(1) << (size_in_bits - 1)) - 1); // Stores the scales. write_array(scales.data(), y * num_quant_packets_x + x_quant / quant_width, scale); @@ -172,6 +174,8 @@ template std::tuple, std::vector> quantize_symmetr float, int8_t, Float16>(const void* src, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> quantize_symmetric_per_block_dynamic< float, int8_t, float>(const void* src, size_t height, size_t width, size_t quant_width); +template std::tuple, std::vector> quantize_symmetric_per_block_dynamic< + float, int32_t, float>(const void* src, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> compute_asymmetric_per_block_quantization_info( diff --git a/test/reference/quantize.hpp b/test/reference/quantize.hpp index 35532acc..42645796 100644 --- a/test/reference/quantize.hpp +++ b/test/reference/quantize.hpp @@ -19,6 +19,12 @@ enum class QuantizationMethod : uint32_t { PER_ROW, ///< Per-row, i.e. one quantization scale and zero point for each row. }; +template +IntType quantize_symmetric(float value, float scale); + +template +IntType quantize_asymmetric(FloatType value, FloatType scale, ZeroPointType zero_point); + /// Computes the quantization information using symmetric per-block quantization method. /// /// The input matrix is divided into quantization blocks of the same size. diff --git a/test/reference/reduce.cpp b/test/reference/reduce.cpp index 0e83b9bf..d4935c3f 100644 --- a/test/reference/reduce.cpp +++ b/test/reference/reduce.cpp @@ -6,6 +6,7 @@ #include "test/reference/reduce.hpp" +#include #include #include #include @@ -15,6 +16,7 @@ #include "test/common/data_type.hpp" #include "test/common/int4.hpp" #include "test/common/memory.hpp" +#include "test/common/round.hpp" namespace kai::test { @@ -110,4 +112,53 @@ std::vector reduce_add( return reduce_any_op(src, src_format, height, width, dst_format, dimension); } +template +std::vector reduce_add_x(const void* src, size_t height, size_t width) { + std::vector dst(round_up_division(height * size_in_bits, 8)); + + for (size_t y = 0; y < height; ++y) { + Accumulator acc = 0; + + for (size_t x = 0; x < width; ++x) { + acc += static_cast(read_array(src, y * width + x)); + } + + write_array(dst.data(), y, acc); + } + + return dst; +} + +template std::vector reduce_add_x(const void* src, size_t height, size_t width); + +template +T reduce_min(const void* src, size_t len) { + KAI_ASSUME(len > 0); + + T min = read_array(src, 0); + + for (size_t i = 1; i < len; ++i) { + min = std::min(min, read_array(src, i)); + } + + return min; +} + +template float reduce_min(const void* src, size_t len); + +template +T reduce_max(const void* src, size_t len) { + KAI_ASSUME(len > 0); + + T max = read_array(src, 0); + + for (size_t i = 1; i < len; ++i) { + max = std::max(max, read_array(src, i)); + } + + return max; +} + +template float reduce_max(const void* src, size_t len); + } // namespace kai::test diff --git a/test/reference/reduce.hpp b/test/reference/reduce.hpp index f6ba197a..f5455acb 100644 --- a/test/reference/reduce.hpp +++ b/test/reference/reduce.hpp @@ -33,4 +33,23 @@ std::vector reduce_add( const void* src, const DataFormat& src_format, size_t height, size_t width, const DataFormat& dst_format, size_t dimension); +/// Accumulates the matrix along the first dimension. +/// +/// @tparam Value The data type of the matrix value. +/// @tparam Accumulator The data type of the accumulator. +/// +/// @param[in] src The input data. +/// @param[in] height The number of rows of the input matrix. +/// @param[in] width The number of columns of the input matrix. +/// +/// @return The vector containing the sum of each input matrix row. +template +std::vector reduce_add_x(const void* src, size_t height, size_t width); + +template +T reduce_min(const void* src, size_t len); + +template +T reduce_max(const void* src, size_t len); + } // namespace kai::test diff --git a/test/reference/reorder.cpp b/test/reference/reorder.cpp new file mode 100644 index 00000000..564f96f6 --- /dev/null +++ b/test/reference/reorder.cpp @@ -0,0 +1,50 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "test/reference/reorder.hpp" + +#include +#include +#include + +#include "test/common/memory.hpp" +#include "test/common/round.hpp" + +namespace kai::test { + +template +std::vector reorder_block( + const void* src, size_t height, size_t width, size_t block_height, size_t block_width) { + const auto num_dst_elements = round_up_multiple(height, block_height) * round_up_multiple(width, block_width); + const auto dst_size = round_up_division(num_dst_elements * size_in_bits, 8); + + std::vector dst(dst_size); + size_t dst_index = 0; + + for (size_t y_block = 0; y_block < height; y_block += block_height) { + for (size_t x_block = 0; x_block < width; x_block += block_width) { + for (size_t y_element = 0; y_element < block_height; ++y_element) { + for (size_t x_element = 0; x_element < block_width; ++x_element) { + const auto y = y_block + y_element; + const auto x = x_block + x_element; + + if (y < height && x < width) { + write_array(dst.data(), dst_index, read_array(src, y * width + x)); + } + + ++dst_index; + } + } + } + } + + return dst; +} + +template std::vector reorder_block( + const void* src, size_t height, size_t width, size_t block_height, size_t block_width); + +} // namespace kai::test diff --git a/test/reference/reorder.hpp b/test/reference/reorder.hpp new file mode 100644 index 00000000..48449e37 --- /dev/null +++ b/test/reference/reorder.hpp @@ -0,0 +1,72 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +namespace kai::test { + +/// Reorders the input matrix block by block. +/// +/// Example: +/// +/// The input matrix: 5x7. +/// +/// ``` +/// +-----------------------------+ +/// | a00 a01 a02 a03 a04 a05 a06 | +/// | a10 a11 a12 a13 a14 a15 a16 | +/// | a20 a21 a22 a23 a24 a25 a26 | +/// | a30 a31 a32 a33 a34 a35 a36 | +/// | a40 a41 a42 a43 a44 a45 a46 | +/// +-----------------------------+ +/// ``` +/// +/// The matrix is divided into blocks of 2x3. +/// At the right and bottom edges, the partial blocks are padded with 0s. +/// +/// ``` +// +-------------+-------------+-------------+ +/// | a00 a01 a02 | a03 a04 a05 | a06 0 0 | +/// | a10 a11 a12 | a13 a14 a15 | a16 0 0 | +/// +-------------+-------------+-------------+ +/// | a20 a21 a22 | a23 a24 a25 | a26 0 0 | +/// | a30 a31 a32 | a33 a34 a35 | a36 0 0 | +/// +-------------+-------------+-------------+ +/// | a40 a41 a42 | a43 a44 a45 | a46 0 0 | +/// | 0 0 0 | 0 0 0 | 0 0 0 | +/// +-------------+-------------+-------------+ +/// ``` +/// +/// Each block is then flatten to get the final reordered matrix: +/// +/// ``` +/// +-------------------------+-------------------------+-------------------------+ +/// | a00 a01 a02 a10 a11 a12 | a03 a04 a05 a13 a14 a15 | a06 0 0 a16 0 0 | +/// +-------------------------+-------------------------+-------------------------+ +/// | a20 a21 a22 a30 a31 a32 | a23 a24 a25 a33 a34 a35 | a26 0 0 a36 0 0 | +/// +-------------------------+-------------------------+-------------------------+ +/// | a40 a41 a42 0 0 0 | a43 a44 a45 0 0 0 | a46 0 0 0 0 0 | +/// +-------------------------+-------------------------+-------------------------+ +/// +/// @tparam T The data type. +/// +/// @param[in] src The input data. +/// @param[in] height The number of rows of the input matrix. +/// @param[in] width The number of columns of the input matrix. +/// @param[in] block_height The number of rows of a block. +/// @param[in] block_width The number of columns of a block. +/// +/// @param[in] The reordered matrix. +/// ``` +template +std::vector reorder_block( + const void* src, size_t height, size_t width, size_t block_height, size_t block_width); + +} // namespace kai::test diff --git a/test/reference/transpose.cpp b/test/reference/transpose.cpp index d0ca5590..84958422 100644 --- a/test/reference/transpose.cpp +++ b/test/reference/transpose.cpp @@ -14,6 +14,7 @@ #include "kai/kai_common.h" #include "test/common/data_type.hpp" #include "test/common/memory.hpp" +#include "test/common/round.hpp" namespace kai::test { @@ -38,7 +39,7 @@ std::vector transpose(const void* data, DataType data_type, size_t heig } template -std::vector transpose( +std::vector transpose_with_padding( const void* data, const size_t height, const size_t width, const size_t src_stride, const size_t dst_stride, const size_t dst_size) { std::vector output(dst_size); @@ -53,11 +54,28 @@ std::vector transpose( return output; } -template std::vector transpose( +template std::vector transpose_with_padding( const void* data, const size_t height, const size_t width, const size_t src_stride, const size_t dst_stride, const size_t dst_size); -template std::vector transpose( +template std::vector transpose_with_padding( const void* data, const size_t height, const size_t width, const size_t src_stride, const size_t dst_stride, const size_t dst_size); + +template +std::vector transpose(const void* src, size_t height, size_t width) { + std::vector dst(round_up_division(height * width * size_in_bits, 8)); + + for (size_t y = 0; y < width; ++y) { + for (size_t x = 0; x < height; ++x) { + write_array(dst.data(), y * height + x, read_array(src, x * width + y)); + } + } + + return dst; +} + +template std::vector transpose(const void* src, size_t height, size_t width); +template std::vector transpose(const void* src, size_t height, size_t width); + } // namespace kai::test diff --git a/test/reference/transpose.hpp b/test/reference/transpose.hpp index 63d94b5b..306bc89d 100644 --- a/test/reference/transpose.hpp +++ b/test/reference/transpose.hpp @@ -37,7 +37,18 @@ std::vector transpose(const void* data, DataType data_type, size_t heig /// @return The transposed matrix. /// template -std::vector transpose( +std::vector transpose_with_padding( const void* data, size_t height, size_t width, size_t src_stride, size_t dst_stride, size_t dst_size); +/// +/// @tparam T The data type. +/// +/// @param[in] src The data buffer of the source matrix. +/// @param[in] height The number of rows of the source matrix. +/// @param[in] width The number of columns of the source matrix. +/// +/// @return The transposed matrix. +template +std::vector transpose(const void* src, size_t height, size_t width); + } // namespace kai::test diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp new file mode 100644 index 00000000..e4177609 --- /dev/null +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp @@ -0,0 +1,251 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include + +#include "kai/kai_common.h" +#include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h" +#include "kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h" +#include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" +#include "test/common/memory.hpp" +#include "test/common/sme.hpp" +#include "test/reference/binary_elementwise.hpp" +#include "test/reference/clamp.hpp" +#include "test/reference/fill.hpp" +#include "test/reference/matmul.hpp" +#include "test/reference/matmul_pack.hpp" +#include "test/reference/quantize.hpp" +#include "test/reference/reduce.hpp" +#include "test/reference/reorder.hpp" +#include "test/reference/transpose.hpp" + +namespace kai::test { + +namespace { + +struct GemmVariant { + size_t acc_height; + size_t acc_width; + size_t acc_fanin; + + size_t (*fn_pack_lhs_get_packed_lhs_size)(size_t m, size_t k, size_t mr, size_t kr, size_t sr); + void (*fn_pack_lhs_run)( + size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, const void* lhs, size_t lhs_stride, + void* lhs_packed); + + size_t (*fn_pack_rhs_get_packed_rhs_size)(size_t n, size_t k); + void (*fn_pack_rhs_run)( + size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, + const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, + const struct kai_rhs_pack_qsi8_params* params); + + size_t (*fn_main_get_dst_size)(size_t m, size_t n); + void (*fn_main_run)( + size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, + size_t dst_stride_col, const kai_matmul_requantize32_params* params); +}; + +struct GemmShape { + size_t m; + size_t n; + size_t k; +}; + +const std::array gemm_variants = { + GemmVariant{ + .acc_height = 2 * get_sme_vector_length(), + .acc_width = 2 * get_sme_vector_length(), + .acc_fanin = sizeof(int32_t) / sizeof(int8_t), + + .fn_pack_lhs_get_packed_lhs_size = kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme, + .fn_pack_lhs_run = kai_run_lhs_pack_x8p2vlx4_x8_sme, + + .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + + .fn_main_get_dst_size = kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_run = kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + }, +}; + +constexpr float output_clamp_rate = 0.1F; // Clamping 10% the range of the output. + +const std::array gemm_shapes = { + GemmShape{1, 1, 1}, // + GemmShape{ + 2 * get_sme_vector_length(), 2 * get_sme_vector_length(), + sizeof(int32_t) / sizeof(int8_t)}, // + GemmShape{20, 30, 40}, // + GemmShape{1, 49, 21}, // + GemmShape{23, 1, 43}, // + GemmShape{32, 14, 1}, // + GemmShape{123, 85, 45}, // +}; + +void run_test(const GemmShape& shape, const GemmVariant& variant) { + const uint64_t seed = 0; + + // ============================================================ + // Generates input and reference output data + // ============================================================ + + // Generates the input data in floating-point. + const auto lhs_f32 = fill_random(shape.m * shape.k, seed + 0); + const auto rhs_f32 = fill_random(shape.k * shape.n, seed + 1); + const auto bias_f32 = fill_random(shape.n, seed + 2); + + // Quantizes the input data. + // * LHS: 8-bit asymmetric per-matrix quantization. + // * RHS: 8-bit symmetric per-channel quantization. + // * Bias: 32-bit symmetric per-channel quantization. + const auto [lhs_qai8, lhs_qai8_scales, lhs_qai8_zero_points] = + quantize_asymmetric_per_block_dynamic( + lhs_f32.data(), 1, shape.m * shape.k, shape.m * shape.k); + const auto lhs_scale = read_array(lhs_qai8_scales.data(), 0); + const auto lhs_zero_point = read_array(lhs_qai8_zero_points.data(), 0); + + const auto rhs_f32_t = transpose(rhs_f32.data(), shape.k, shape.n); + const auto [rhs_qsi8_t, rhs_scales] = + quantize_symmetric_per_block_dynamic(rhs_f32_t.data(), shape.n, shape.k, shape.k); + const auto rhs_qsi8 = transpose(rhs_qsi8_t.data(), shape.n, shape.k); + + const auto bias_scale = mul(&lhs_scale, 1, 1, rhs_scales.data(), 1, shape.n); + const auto bias_qsi32 = + quantize_symmetric_per_block(bias_f32.data(), bias_scale.data(), shape.n, 1, 1); + + // Runs the reference implementation of matmul to produce floating-point result. + const auto ref_dst_f32 = + matmul_nt_t_quantized( + shape.m, shape.n, shape.k, lhs_qai8.data(), &lhs_scale, &lhs_zero_point, shape.m, shape.k, + rhs_qsi8_t.data(), rhs_scales.data(), nullptr, 1, shape.k, bias_qsi32.data(), bias_scale.data(), nullptr, + 1); + + // Computes the output quantization information and clamping limits. + // + // To get a realistic value for the output quantization information and clamping limits + // and avoid uncontrolled saturation problem, these information will be calculated + // based on the reference floating-point output. + // + // The clamping limits will be slightly narrower than the actual range of the output + // so that a portion of the output will be clampped. + const auto [dst_scales, dst_zero_points] = + compute_asymmetric_per_block_quantization_info( + ref_dst_f32.data(), 1, shape.m * shape.n, shape.m * shape.n); + const auto dst_scale = read_array(dst_scales.data(), 0); + const auto dst_zero_point = read_array(dst_zero_points.data(), 0); + + const auto ref_dst_f32_min = reduce_min(ref_dst_f32.data(), shape.m * shape.n); + const auto ref_dst_f32_max = reduce_max(ref_dst_f32.data(), shape.m * shape.n); + const auto ref_dst_f32_range = ref_dst_f32_max - ref_dst_f32_min; + + const auto ref_dst_f32_clamp_min = ref_dst_f32_min + ref_dst_f32_range * output_clamp_rate / 2; + const auto ref_dst_f32_clamp_max = ref_dst_f32_max - ref_dst_f32_range * output_clamp_rate / 2; + const auto dst_qai8_clamp_min = + quantize_asymmetric(ref_dst_f32_clamp_min, dst_scale, dst_zero_point); + const auto dst_qai8_clamp_max = + quantize_asymmetric(ref_dst_f32_clamp_max, dst_scale, dst_zero_point); + + // Clamps and quantizes the reference output matrix. + const auto ref_dst_f32_clamped = + clamp(ref_dst_f32.data(), shape.m * shape.n, ref_dst_f32_clamp_min, ref_dst_f32_clamp_max); + const auto ref_dst_qsi8_clamped = quantize_asymmetric_per_block( + ref_dst_f32_clamped.data(), &dst_scale, &dst_zero_point, 1, shape.m * shape.n, shape.m * shape.n); + + // Runs the reference implementation of the packing functions. + // + // The reference packing functions cannot be executed earlier + // because we need the reference floating-point output first to have + // the quantization information. + const auto ref_packed_lhs = + reorder_block(lhs_qai8.data(), shape.m, shape.k, variant.acc_height, variant.acc_fanin); + + const auto ref_packed_rhs = matmul_pack_rhs_nxk_static_quantized( + rhs_qsi8_t.data(), rhs_scales.data(), lhs_scale, dst_scale, bias_qsi32.data(), lhs_zero_point, shape.n, shape.k, + variant.acc_width, variant.acc_fanin); + + // ============================================================ + // Runs the optimized implementation and checks for correctness + // ============================================================ + + // Runs the optimized implementation of LHS packing. + const auto imp_packed_lhs_size = + variant.fn_pack_lhs_get_packed_lhs_size(shape.m, shape.k, variant.acc_height, variant.acc_fanin, 1); + ASSERT_EQ(imp_packed_lhs_size, ref_packed_lhs.size()); + std::vector imp_packed_lhs(imp_packed_lhs_size); + + variant.fn_pack_lhs_run( + shape.m, shape.k, variant.acc_height, variant.acc_fanin, 1, 0, lhs_qai8.data(), shape.k * sizeof(int8_t), + imp_packed_lhs.data()); + + for (size_t i = 0; i < ref_packed_lhs.size(); ++i) { + ASSERT_EQ(imp_packed_lhs[i], ref_packed_lhs[i]); + } + + // Runs the optimized implementation of RHS packing. + const auto imp_packed_rhs_size = variant.fn_pack_rhs_get_packed_rhs_size(shape.n, shape.k); + ASSERT_EQ(imp_packed_rhs_size, ref_packed_rhs.size()); + std::vector imp_packed_rhs(imp_packed_rhs_size); + + const kai_rhs_pack_qsi8_params imp_pack_rhs_params{ + .input_zero_point = lhs_zero_point, + .scale_multiplier = lhs_scale / dst_scale, + }; + + variant.fn_pack_rhs_run( + 1, shape.n, shape.k, variant.acc_width, variant.acc_fanin, 1, shape.n * sizeof(int8_t), rhs_qsi8.data(), + bias_qsi32.data(), rhs_scales.data(), imp_packed_rhs.data(), 0, &imp_pack_rhs_params); + + for (size_t i = 0; i < ref_packed_rhs.size(); ++i) { + ASSERT_EQ(imp_packed_rhs[i], ref_packed_rhs[i]); + } + + // Runs the optimized implementation of GEMM kernel. + const auto imp_dst_size = variant.fn_main_get_dst_size(shape.m, shape.n); + ASSERT_EQ(imp_dst_size, ref_dst_qsi8_clamped.size()); + + std::vector imp_dst(imp_dst_size); + + const kai_matmul_requantize32_params imp_main_params{ + .min_value = dst_qai8_clamp_min, + .max_value = dst_qai8_clamp_max, + .output_zero_point = dst_zero_point, + }; + + variant.fn_main_run( + shape.m, shape.n, shape.k, imp_packed_lhs.data(), imp_packed_rhs.data(), imp_dst.data(), + shape.n * sizeof(int8_t), sizeof(int8_t), &imp_main_params); + + for (size_t i = 0; i < ref_dst_qsi8_clamped.size(); ++i) { + const int32_t imp_value = read_array(imp_dst.data(), i); + const int32_t ref_value = read_array(ref_dst_qsi8_clamped.data(), i); + const auto error = std::abs(imp_value - ref_value); + + if (error > 1) { + ASSERT_EQ(imp_dst[i], ref_dst_qsi8_clamped[i]); + } + } +} + +using ThisTest = testing::TestWithParam>; + +TEST_P(ThisTest, EndToEnd) { + const auto& [variant, shape] = GetParam(); + + run_test(shape, variant); +} + +} // namespace + +INSTANTIATE_TEST_SUITE_P( + matmul_clamp_qai8_qai8p_qsi8cp, ThisTest, + testing::Combine(testing::ValuesIn(gemm_variants), testing::ValuesIn(gemm_shapes))); + +} // namespace kai::test -- GitLab From 04e98cc2aaca3da288d5a174316da32339bb61bd Mon Sep 17 00:00:00 2001 From: Viet-Hoa Do Date: Mon, 28 Oct 2024 17:18:44 +0000 Subject: [PATCH 03/12] Improve the SME GEMM Int8 work * Remove unconstrained stack allocation. * Test all utility functions. * Test arbitrary portion of the output. * Add more documentation. Signed-off-by: Viet-Hoa Do --- kai/kai_common.h | 12 +- kai/ukernels/matmul/BUILD.bazel | 33 +++ ...mp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c | 24 +-- ...mp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h | 15 +- .../pack/kai_lhs_pack_x8p2vlx4_x8_sme.c | 10 +- ..._pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c | 162 +++++++++----- ..._pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h | 8 + test/reference/quantize.cpp | 8 +- .../matmul_clamp_qai8_qai8p_qsi8cp_test.cpp | 203 ++++++++++++++---- 9 files changed, 351 insertions(+), 124 deletions(-) diff --git a/kai/kai_common.h b/kai/kai_common.h index f2c2aae0..dac87b87 100644 --- a/kai/kai_common.h +++ b/kai/kai_common.h @@ -175,15 +175,17 @@ struct kai_rhs_pack_qs4cxs1s0_param { uint8_t rhs_zero_point; /**< RHS Matrix quantization zero-point */ }; +/// RHS packing parameter for static 8-bit quantization. struct kai_rhs_pack_qsi8_params { - int32_t input_zero_point; - float scale_multiplier; + int32_t input_zero_point; ///< Input quantization zero point. + float scale_multiplier; ///< Product of input and output quantization scales. }; +/// Requantization and clamp parameters for GEMM output stage. struct kai_matmul_requantize32_params { - int32_t min_value; - int32_t max_value; - int32_t output_zero_point; + int32_t min_value; ///< Minimum output value. + int32_t max_value; ///< Maximum output value. + int32_t output_zero_point; ///< Output quantization zero point. }; #ifdef __cplusplus diff --git a/kai/ukernels/matmul/BUILD.bazel b/kai/ukernels/matmul/BUILD.bazel index 48193686..493f0541 100644 --- a/kai/ukernels/matmul/BUILD.bazel +++ b/kai/ukernels/matmul/BUILD.bazel @@ -152,6 +152,13 @@ kai_c_library( ], ) +kai_c_library( + name = "clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa", + srcs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c"], + hdrs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h"], + cpu_uarch = kai_cpu_sme(), +) + cc_library( name = "clamp_f32_qai8dxp_qsi4cxp_interface", hdrs = ["matmul_clamp_f32_qai8dxp_qsi4cxp/kai_matmul_clamp_f32_qai8dxp_qsi4cxp_interface.h"], @@ -310,6 +317,20 @@ kai_c_library( srcs = ["pack/kai_lhs_quant_pack_bf16p8x4_f32_neon.c"], hdrs = ["pack/kai_lhs_quant_pack_bf16p8x4_f32_neon.h"], cpu_uarch = kai_cpu_bf16(), +} + +kai_c_library( + name = "lhs_pack_x8p2vlx4_x8_sme", + srcs = ["pack/kai_lhs_pack_x8p2vlx4_x8_sme.c"], + hdrs = ["pack/kai_lhs_pack_x8p2vlx4_x8_sme.h"], + cpu_uarch = kai_cpu_sme(), +) + +kai_c_library( + name = "lhs_quant_pack_bf16p_f32_neon", + srcs = ["pack/kai_lhs_quant_pack_bf16p_f32_neon.c"], + hdrs = ["pack/kai_lhs_quant_pack_bf16p_f32_neon.h"], + cpu_uarch = kai_cpu_bf16(), ) kai_c_library( @@ -368,6 +389,13 @@ kai_c_library( cpu_uarch = kai_cpu_sme(), ) +kai_c_library( + name = "rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme", + srcs = ["pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c"], + hdrs = ["pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h"], + cpu_uarch = kai_cpu_sme(), +) + kai_c_library( name = "rhs_pack_nxk_qsi4cxp_qs4cxs1s0", srcs = ["pack/kai_rhs_pack_nxk_qsi4cxp_qs4cxs1s0.c"], @@ -546,6 +574,10 @@ kai_c_library( ":lhs_pack_f32p2vlx1_f32_sme", ":lhs_quant_pack_bf16p1x4_f32_neon", ":lhs_quant_pack_bf16p8x4_f32_neon", + ":clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa", + ":lhs_pack_f32p2vlx1_f32_sme", + ":lhs_pack_x8p2vlx4_x8_sme", + ":lhs_quant_pack_bf16p_f32_neon", ":lhs_quant_pack_qai8dxp_f32", ":lhs_quant_pack_qsi8d32p_f32", ":rhs_pack_kxn_bf16p12x4biasf16_f16_neon", @@ -558,6 +590,7 @@ kai_c_library( ":rhs_pack_kxn_qsi4cxp_qs4cxs1s0", ":rhs_pack_kxn_qsi8cxp_qsi8cx_neon", ":rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme", + ":rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme", ":rhs_pack_nxk_qsi4c32p_qsu4c32s1s0", ":rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0", ":rhs_pack_nxk_qsi4cxp_qs4cxs1s0", diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c index e300d42c..e464a9f2 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c @@ -15,23 +15,23 @@ static const size_t kai_mr = 2; static const size_t kai_nr = 2; -static const size_t kai_kr = 1; +static const size_t kai_kr = 4; static const size_t kai_sr = 1; size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { - return kai_mr * kai_get_sme_vector_length_u8(); + return kai_mr * kai_get_sme_vector_length_u32(); } size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { - return kai_nr * kai_get_sme_vector_length_u8(); + return kai_nr * kai_get_sme_vector_length_u32(); } size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { - return kai_mr * kai_get_sme_vector_length_u8(); + return kai_mr * kai_get_sme_vector_length_u32(); } size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { - return kai_nr * kai_get_sme_vector_length_u8(); + return kai_nr * kai_get_sme_vector_length_u32(); } size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { @@ -44,12 +44,12 @@ size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k) { KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); - return m_idx * k * sizeof(int8_t); + return m_idx * kai_roundup(k, kai_kr) * sizeof(int8_t); } size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k) { KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); - return n_idx * (k * sizeof(int8_t) + sizeof(int32_t)); + return n_idx * (sizeof(int32_t) + kai_roundup(k, kai_kr) * sizeof(int8_t) + sizeof(float)); } size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( @@ -66,11 +66,7 @@ size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_ void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, - size_t dst_stride_col, - - const struct kai_matmul_requantize32_params* params - -) { + size_t dst_stride_col, const struct kai_matmul_requantize32_params* params) { KAI_ASSUME(dst_stride_col == sizeof(int8_t)); typedef struct { @@ -78,8 +74,8 @@ void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( const void* B; void* C; - long ldcb; - long M, N, K; + uint64_t ldcb; + uint64_t M, N, K; int32_t min; int32_t max; int32_t result_zero_point; diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h index 650ab660..138ba4e3 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h @@ -16,8 +16,8 @@ extern "C" { /// Micro-kernel dependencies /// -/// -# kai_lhs_pack_f8p2vlx1_f8_sme to pack the LHS matrix. -/// -# kai_rhs_pack_kxn_f8p2vlx1biasf32_f8_f32_sme to pack the RHS matrix. +/// -# kai_lhs_pack_x8p2vlx4_x8_sme to pack the LHS matrix. +/// -# kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme to pack the RHS matrix. /// Gets m step value. /// @@ -81,7 +81,7 @@ size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_m /// /// @param[in] m_idx Row index. /// @param[in] n_idx Column index. -/// @param[in] stride Row stride in bytes. +/// @param[in] dst_stride Row stride in bytes. /// /// @return The offset in bytes to the data element. size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( @@ -112,15 +112,10 @@ size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_ /// @param[out] dst Output matrix buffer. /// @param[in] dst_stride_row Row stride in bytes of the output matrix. /// @param[in] dst_stride_col Column stride in bytes of the output matrix. -/// @param[in] clamp_min Minimum value to clamp the final result. -/// @param[in] clamp_max Maximum value to clamp the final result. +/// @param[in] params Requantization and clamp parmaters. void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, - size_t dst_stride_col, - - const struct kai_matmul_requantize32_params* params - -); + size_t dst_stride_col, const struct kai_matmul_requantize32_params* params); #ifdef __cplusplus } // extern "C" diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c index c4a8d64c..76464f12 100644 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c @@ -18,20 +18,20 @@ static const size_t kai_kr = 4; static const size_t kai_sr = 1; size_t kai_get_m_step_lhs_pack_x8p2vlx4_x8_sme(size_t mr) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8() / kai_kr); KAI_UNUSED(mr); return (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; } size_t kai_get_lhs_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t lhs_stride) { - KAI_ASSUME(m_idx % (kai_mr * kai_get_sme_vector_length_u8()) == 0); + KAI_ASSUME(m_idx % (kai_mr * kai_get_sme_vector_length_u8() / kai_kr) == 0); return m_idx * lhs_stride; } size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr) { - const size_t scaled_mr = kai_mr * kai_get_sme_vector_length_u8(); + const size_t scaled_mr = kai_mr * kai_get_sme_vector_length_u8() / kai_kr; KAI_ASSUME(m_idx % scaled_mr == 0); KAI_ASSUME(mr == scaled_mr); KAI_ASSUME(kr == kai_kr); @@ -45,7 +45,7 @@ size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t k } size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8() / kai_kr); KAI_ASSUME(kr == kai_kr); KAI_ASSUME(sr == kai_sr); @@ -59,7 +59,7 @@ size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme(size_t m, size_t k, size void kai_run_lhs_pack_x8p2vlx4_x8_sme( size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, const void* lhs, size_t lhs_stride, void* lhs_packed) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8()); + KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8() / kai_kr); KAI_ASSUME(kr == kai_kr); KAI_ASSUME(sr == kai_sr); KAI_ASSUME(lhs != NULL); diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c index ac18e9cd..c5ee1a13 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c @@ -10,7 +10,6 @@ #include "kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" -#include #include #include #include @@ -25,11 +24,11 @@ static const size_t kai_num_bytes_bias = 4; static const size_t kai_num_bytes_scale = 4; size_t kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(void) { - return kai_nr * kai_get_sme_vector_length_u8(); + return kai_nr * kai_get_sme_vector_length_u8() / kai_kr; } size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { - KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8()) == 0); + KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8() / kai_kr) == 0); return n_idx * kai_num_bytes_input; } @@ -38,6 +37,10 @@ size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_ return n_idx * kai_num_bytes_bias; } +size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { + return n_idx * kai_num_bytes_scale; +} + size_t kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t k) { return kai_nr * kai_get_sme_vector_length_u8() / kai_kr * (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); @@ -73,10 +76,10 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( const void* in = rhs; void* out = rhs_packed; const size_t in_stride = rhs_stride; - uint8_t* pad_row = (uint8_t*)alloca(width * sizeof(uint8_t)); + uint8_t pad_row[nr]; if (height % 4) { - memset(pad_row, 0, width * sizeof(uint8_t)); + memset(pad_row, 0, nr * sizeof(uint8_t)); } size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(height); @@ -85,52 +88,111 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( __asm__ __volatile__( ".inst 0xd503477f // SMSTART ZA\n" - "mov x27, %x[out]\n" - "mov x26, %x[height]\n" + "cmp %x[height], #0x8\n" + "mov x11, %x[out]\n" "ptrue p2.b\n" + "mov x10, %x[height]\n" "incb %x[out], ALL, MUL #2\n" + "blt 4f\n" "1:" // Main row loop: Head - "mov x25, %x[in]\n" - "cmp %x[height], #0x3\n" - "add x24, x25, %x[in_stride]\n" - "mov x23, %x[out]\n" - "add x22, x24, %x[in_stride]\n" - "mov x21, %x[width]\n" - "add x20, x22, %x[in_stride]\n" - "csel x22, x22, %x[pad_row], GE\n" + "mov x9, %x[in]\n" + "mov x28, %x[out]\n" + "add x27, x9, %x[in_stride]\n" + "sub %x[height], %x[height], #0x8\n" + "add x26, x27, %x[in_stride]\n" + "mov x24, %x[width]\n" + "add x25, x26, %x[in_stride]\n" + "add x23, x25, %x[in_stride]\n" + "add x22, x23, %x[in_stride]\n" + "add x21, x22, %x[in_stride]\n" + "add x20, x21, %x[in_stride]\n" "add %x[in], x20, %x[in_stride]\n" - "csel x20, x20, %x[pad_row], GT\n" - "cmp %x[height], #0x1\n" - "sub %x[height], %x[height], #0x4\n" - "csel x24, x24, %x[pad_row], GT\n" "2:" // Main row loop: Column loop - "whilelt p0.b, XZR, x21\n" - "decw x21, ALL, MUL #2\n" - "ld1b { z18.b }, p0/Z, [x25]\n" - "cmp x21, #0x0\n" + "whilelt p0.b, XZR, x24\n" + "decw x24, ALL, MUL #2\n" + "ld1b { z18.b }, p0/Z, [x9]\n" + "cmp x24, #0x0\n" + "incd x9, ALL, MUL #4\n" + "ld1b { z22.b }, p0/Z, [x27]\n" + "incd x27, ALL, MUL #4\n" + "ld1b { z17.b }, p0/Z, [x26]\n" + "incd x26, ALL, MUL #4\n" + "ld1b { z16.b }, p0/Z, [x25]\n" "incd x25, ALL, MUL #4\n" - "ld1b { z19.b }, p0/Z, [x24]\n" - "incd x24, ALL, MUL #4\n" - "ld1b { z17.b }, p0/Z, [x22]\n" + "ld1b { z20.b }, p0/Z, [x23]\n" + "incd x23, ALL, MUL #4\n" + "ld1b { z19.b }, p0/Z, [x22]\n" + "zip1 z21.b, z18.b, z17.b\n" "incd x22, ALL, MUL #4\n" + "ld1b { z18.b }, p0/Z, [x21]\n" + "zip1 z17.b, z22.b, z16.b\n" + "incd x21, ALL, MUL #4\n" "ld1b { z16.b }, p0/Z, [x20]\n" "incd x20, ALL, MUL #4\n" + "zip1 z20.b, z20.b, z18.b\n" + "zip1 z16.b, z19.b, z16.b\n" + "zip1 z19.b, z21.b, z17.b\n" + "zip2 z18.b, z21.b, z17.b\n" + "zip1 z17.b, z20.b, z16.b\n" + "zip2 z16.b, z20.b, z16.b\n" + "st1b { z19.b }, p2, [x28]\n" + "st1b { z18.b }, p2, [x28, #1, MUL VL]\n" + "st1b { z17.b }, p2, [x28, #2, MUL VL]\n" + "st1b { z16.b }, p2, [x28, #3, MUL VL]\n" + "add x28, x28, %x[out_stride]\n" + "bgt 2b\n" + "cmp %x[height], #0x8\n" + "addvl %x[out], %x[out], #4\n" + "bge 1b\n" + "cbz %x[height], 8f\n" + "4:" // Main loop skip + "5:" // Tail row loop: Head + "mov x9, %x[in]\n" + "cntw x24, ALL, MUL #2\n" + "add x27, x9, %x[in_stride]\n" + "cmp %x[height], #0x3\n" + "add x26, x27, %x[in_stride]\n" + "csel x23, x24, XZR, GT\n" + "add x25, x26, %x[in_stride]\n" + "csel x26, x26, %x[pad_row], GE\n" + "add %x[in], x25, %x[in_stride]\n" + "csel x25, x25, %x[pad_row], GT\n" + "csel x22, x24, XZR, GE\n" + "cmp %x[height], #0x1\n" + "mov x28, %x[out]\n" + "csel x27, x27, %x[pad_row], GT\n" + "csel x21, x24, XZR, GT\n" + "sub %x[height], %x[height], #0x4\n" + "mov x20, %x[width]\n" + "6:" // Tail row loop: Column loop + "whilelt p0.b, XZR, x20\n" + "decw x20, ALL, MUL #2\n" + "ld1b { z18.b }, p0/Z, [x9]\n" + "cmp x20, #0x0\n" + "add x9, x9, x24\n" + "ld1b { z19.b }, p0/Z, [x27]\n" + "add x27, x27, x21\n" + "ld1b { z17.b }, p0/Z, [x26]\n" + "add x26, x26, x22\n" + "ld1b { z16.b }, p0/Z, [x25]\n" + "add x25, x25, x23\n" "zip1 z18.b, z18.b, z17.b\n" "zip1 z16.b, z19.b, z16.b\n" "zip1 z17.b, z18.b, z16.b\n" "zip2 z16.b, z18.b, z16.b\n" - "st1b { z17.b }, p2, [x23]\n" - "st1b { z16.b }, p2, [x23, #1, MUL VL]\n" - "add x23, x23, %x[out_stride]\n" - "bgt 2b\n" + "st1b { z17.b }, p2, [x28]\n" + "st1b { z16.b }, p2, [x28, #1, MUL VL]\n" + "add x28, x28, %x[out_stride]\n" + "bgt 6b\n" "cmp %x[height], #0x1\n" "addvl %x[out], %x[out], #2\n" - "bge 1b\n" + "bge 5b\n" + "8:" // Done "mov x22, %x[out]\n" "mov x21, %x[width]\n" "dup z18.s, %w[scale_multiplier]\n" - "cbz %x[scale], 5f\n" - "4:" // Scale: Full loop + "cbz %x[scale], 10f\n" + "9:" // Scale: Full loop "mov x20, x21\n" "decw x21, ALL, MUL #2\n" "whilelt p1.s, XZR, x20\n" @@ -145,31 +207,31 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( "st1w { z17.s }, p2, [x22]\n" "st1w { z16.s }, p2, [x22, #1, MUL VL]\n" "add x22, x22, %x[out_stride]\n" - "bgt 4b\n" - "5:" // Scale: Done - "cbz %x[width], 8f\n" - "cbz x26, 8f\n" + "bgt 9b\n" + "10:" // Scale: Done + "cbz %x[width], 13f\n" + "cbz x10, 13f\n" "dup z21.s, %w[input_zero_point]\n" - "add x25, x26, #0x3\n" + "add x25, x10, #0x3\n" "cntw x24, ALL, MUL #2\n" "mov z20.b, #0x1\n" "lsr x25, x25, #0x2\n" "mov x23, %x[width]\n" - "addvl x22, x27, #2\n" + "addvl x22, x11, #2\n" "neg z21.s, p2/M, z21.s\n" - "6:" // Bias: N loop + "11:" // Bias: N loop "mov x21, x22\n" "mov x20, x25\n" "mov z19.s, #0x0\n" "mov z18.s, #0x0\n" - "7:" // Bias: K loop + "12:" // Bias: K loop "ld1b { z17.b }, p2/Z, [x21]\n" "subs x20, x20, #0x1\n" "ld1b { z16.b }, p2/Z, [x21, #1, MUL VL]\n" "addvl x21, x21, #2\n" "sdot z19.s, z17.b, z20.b\n" "sdot z18.s, z16.b, z20.b\n" - "bgt 7b\n" + "bgt 12b\n" "mov x20, x23\n" "add x22, x22, %x[out_stride]\n" "whilelt p1.s, XZR, x20\n" @@ -181,19 +243,19 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( "addvl %x[bias], %x[bias], #2\n" "mla z17.s, p2/M, z19.s, z21.s\n" "mla z16.s, p2/M, z18.s, z21.s\n" - "st1w { z17.s }, p2, [x27]\n" - "st1w { z16.s }, p2, [x27, #1, MUL VL]\n" - "add x27, x27, %x[out_stride]\n" - "bgt 6b\n" - "8:" // Bias: Done + "st1w { z17.s }, p2, [x11]\n" + "st1w { z16.s }, p2, [x11, #1, MUL VL]\n" + "add x11, x11, %x[out_stride]\n" + "bgt 11b\n" + "13:" // Bias: Done ".inst 0xd503467f // SMSTOP\n" : [bias] "+&r"(bias), [height] "+&r"(height), [in] "+&r"(in), [out] "+&r"(out), [scale] "+&r"(scale) : [in_stride] "r"(in_stride), [input_zero_point] "r"(input_zero_point), [out_stride] "r"(out_stride), [pad_row] "r"(pad_row), [scale_multiplier] "r"(scale_multiplier), [width] "r"(width) : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", - "p15", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", - "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", - "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"); + "p15", "x9", "x10", "x11", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "z0", "z1", "z2", + "z3", "z4", "z5", "z6", "z7", "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", + "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"); } #endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h index ac24eb12..7af07d6d 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h @@ -35,6 +35,13 @@ size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_i /// @return The offset in bytes to the data element. size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +/// Gets the offset in bytes to the data element in the scale buffer. +/// +/// @param[in] n_idx Column index. +/// +/// @return The offset in bytes to the data element. +size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); + /// Gets the offset in bytes to the data element in the packed RHS buffer. /// /// @param[in] n_idx Row index. @@ -58,6 +65,7 @@ size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_ /// /// * RHS: @ref kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. /// * Bias: @ref kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. +/// * Scale: @ref kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. /// * Output: @ref kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. /// /// @param[in] num_groups Number of groups. It must be 1. diff --git a/test/reference/quantize.cpp b/test/reference/quantize.cpp index 67c13e99..ba5b6a28 100644 --- a/test/reference/quantize.cpp +++ b/test/reference/quantize.cpp @@ -104,7 +104,8 @@ std::vector compute_symmetric_per_block_quantization_info( } } - const auto scale = max_abs / ((static_cast(1) << (size_in_bits - 1)) - 1); + const auto scale = + max_abs / static_cast((static_cast(1) << (size_in_bits - 1)) - 1); // Stores the scales. write_array(scales.data(), y * num_quant_packets_x + x_quant / quant_width, scale); @@ -146,6 +147,9 @@ std::vector quantize_symmetric_per_block( return data; } +template std::vector quantize_symmetric_per_block( + const void* src, const void* scales, size_t height, size_t width, size_t quant_width, bool is_transposed); + template std::tuple, std::vector> quantize_symmetric_per_block_dynamic( const void* src, size_t height, size_t width, size_t quant_width) { @@ -176,6 +180,8 @@ template std::tuple, std::vector> quantize_symmetr float, int8_t, float>(const void* src, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> quantize_symmetric_per_block_dynamic< float, int32_t, float>(const void* src, size_t height, size_t width, size_t quant_width); +template std::tuple, std::vector> quantize_symmetric_per_block_dynamic< + float, int8_t, float>(const void* src, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> compute_asymmetric_per_block_quantization_info( diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp index e4177609..cd366d61 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp @@ -15,7 +15,10 @@ #include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h" #include "kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h" #include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" +#include "test/common/cpu_info.hpp" +#include "test/common/matrix_portion.hpp" #include "test/common/memory.hpp" +#include "test/common/rect.hpp" #include "test/common/sme.hpp" #include "test/reference/binary_elementwise.hpp" #include "test/reference/clamp.hpp" @@ -36,17 +39,36 @@ struct GemmVariant { size_t acc_width; size_t acc_fanin; + bool (*fn_is_supported)(); + + size_t (*fn_pack_lhs_get_m_step)(size_t mr); + size_t (*fn_pack_lhs_get_lhs_offset)(size_t m_idx, size_t lhs_stride); + size_t (*fn_pack_lhs_get_packed_lhs_offset)(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr); size_t (*fn_pack_lhs_get_packed_lhs_size)(size_t m, size_t k, size_t mr, size_t kr, size_t sr); void (*fn_pack_lhs_run)( size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, const void* lhs, size_t lhs_stride, void* lhs_packed); + size_t (*fn_pack_rhs_get_n_step)(); + size_t (*fn_pack_rhs_get_rhs_offset)(size_t n_idx); + size_t (*fn_pack_rhs_get_bias_offset)(size_t n_idx); + size_t (*fn_pack_rhs_get_scale_offset)(size_t n_idx); + size_t (*fn_pack_rhs_get_packed_rhs_offset)(size_t n_idx, size_t k); size_t (*fn_pack_rhs_get_packed_rhs_size)(size_t n, size_t k); void (*fn_pack_rhs_run)( size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, const struct kai_rhs_pack_qsi8_params* params); + size_t (*fn_main_get_m_step)(); + size_t (*fn_main_get_n_step)(); + size_t (*fn_main_get_mr)(); + size_t (*fn_main_get_nr)(); + size_t (*fn_main_get_kr)(); + size_t (*fn_main_get_sr)(); + size_t (*fn_main_get_packed_lhs_offset)(size_t m_idx, size_t k); + size_t (*fn_main_get_packed_rhs_offset)(size_t n_idx, size_t k); + size_t (*fn_main_get_dst_offset)(size_t m_idx, size_t n_idx, size_t dst_stride); size_t (*fn_main_get_dst_size)(size_t m, size_t n); void (*fn_main_run)( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, @@ -65,12 +87,31 @@ const std::array gemm_variants = { .acc_width = 2 * get_sme_vector_length(), .acc_fanin = sizeof(int32_t) / sizeof(int8_t), + .fn_is_supported = cpu_has_sme2, + + .fn_pack_lhs_get_m_step = kai_get_m_step_lhs_pack_x8p2vlx4_x8_sme, + .fn_pack_lhs_get_lhs_offset = kai_get_lhs_offset_lhs_pack_x8p2vlx4_x8_sme, + .fn_pack_lhs_get_packed_lhs_offset = kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme, .fn_pack_lhs_get_packed_lhs_size = kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme, .fn_pack_lhs_run = kai_run_lhs_pack_x8p2vlx4_x8_sme, + .fn_pack_rhs_get_n_step = kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_rhs_offset = kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_bias_offset = kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_scale_offset = kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_packed_rhs_offset = kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_mr = kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_nr = kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_kr = kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_sr = kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_packed_lhs_offset = kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_packed_rhs_offset = kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_dst_offset = kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, .fn_main_get_dst_size = kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, .fn_main_run = kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, }, @@ -90,9 +131,45 @@ const std::array gemm_shapes = { GemmShape{123, 85, 45}, // }; -void run_test(const GemmShape& shape, const GemmVariant& variant) { +const std::array output_portions = { + MatrixPortion(0, 0, 1, 1), // Full matrix. + MatrixPortion(0, 0, 0.25, 0.25), // Top-left corner. + MatrixPortion(0.75, 0.75, 1, 1), // Bottom-right corner. +}; + +void run_test(const GemmShape& shape, const GemmVariant& variant, const MatrixPortion& output_portion) { const uint64_t seed = 0; + if (!variant.fn_is_supported()) { + GTEST_SKIP(); + } + + // ============================================================ + // Test the packing and scheduling parameters + // ============================================================ + + const auto imp_mr = variant.fn_main_get_mr(); + const auto imp_nr = variant.fn_main_get_nr(); + const auto imp_kr = variant.fn_main_get_kr(); + const auto imp_sr = variant.fn_main_get_sr(); + + ASSERT_EQ(imp_mr, variant.acc_height); + ASSERT_EQ(imp_nr, variant.acc_width); + ASSERT_EQ(imp_kr, variant.acc_fanin); + ASSERT_EQ(imp_sr, 1); + + const auto imp_m_step = variant.fn_main_get_m_step(); + const auto imp_n_step = variant.fn_main_get_n_step(); + + ASSERT_EQ(imp_m_step, variant.acc_height); + ASSERT_EQ(imp_n_step, variant.acc_width); + + // ============================================================ + // Calculates the output area under test + // ============================================================ + + const auto output_area = output_portion.compute_portion(shape.m, shape.n, variant.acc_height, variant.acc_width); + // ============================================================ // Generates input and reference output data // ============================================================ @@ -181,12 +258,27 @@ void run_test(const GemmShape& shape, const GemmVariant& variant) { ASSERT_EQ(imp_packed_lhs_size, ref_packed_lhs.size()); std::vector imp_packed_lhs(imp_packed_lhs_size); - variant.fn_pack_lhs_run( - shape.m, shape.k, variant.acc_height, variant.acc_fanin, 1, 0, lhs_qai8.data(), shape.k * sizeof(int8_t), - imp_packed_lhs.data()); - - for (size_t i = 0; i < ref_packed_lhs.size(); ++i) { - ASSERT_EQ(imp_packed_lhs[i], ref_packed_lhs[i]); + { + const auto imp_lhs_offset = + variant.fn_pack_lhs_get_lhs_offset(output_area.start_row(), shape.k * sizeof(int8_t)); + const auto imp_packed_lhs_offset = + variant.fn_pack_lhs_get_packed_lhs_offset(output_area.start_row(), shape.k, imp_mr, imp_kr, imp_sr); + + variant.fn_pack_lhs_run( + output_area.height(), shape.k, imp_mr, imp_kr, imp_sr, 0, lhs_qai8.data() + imp_lhs_offset, + shape.k * sizeof(int8_t), imp_packed_lhs.data() + imp_packed_lhs_offset); + + const auto imp_packed_lhs_end_offset = output_area.end_row() < shape.m + ? variant.fn_pack_lhs_get_packed_lhs_offset(output_area.end_row(), shape.k, imp_mr, imp_kr, imp_sr) + : imp_packed_lhs_size; + + for (size_t i = 0; i < ref_packed_lhs.size(); ++i) { + if (i >= imp_packed_lhs_offset && i < imp_packed_lhs_end_offset) { + ASSERT_EQ(imp_packed_lhs[i], ref_packed_lhs[i]); + } else { + ASSERT_EQ(imp_packed_lhs[i], 0); + } + } } // Runs the optimized implementation of RHS packing. @@ -194,17 +286,33 @@ void run_test(const GemmShape& shape, const GemmVariant& variant) { ASSERT_EQ(imp_packed_rhs_size, ref_packed_rhs.size()); std::vector imp_packed_rhs(imp_packed_rhs_size); - const kai_rhs_pack_qsi8_params imp_pack_rhs_params{ - .input_zero_point = lhs_zero_point, - .scale_multiplier = lhs_scale / dst_scale, - }; - - variant.fn_pack_rhs_run( - 1, shape.n, shape.k, variant.acc_width, variant.acc_fanin, 1, shape.n * sizeof(int8_t), rhs_qsi8.data(), - bias_qsi32.data(), rhs_scales.data(), imp_packed_rhs.data(), 0, &imp_pack_rhs_params); - - for (size_t i = 0; i < ref_packed_rhs.size(); ++i) { - ASSERT_EQ(imp_packed_rhs[i], ref_packed_rhs[i]); + { + const auto imp_rhs_offset = variant.fn_pack_rhs_get_rhs_offset(output_area.start_col()); + const auto imp_bias_offset = variant.fn_pack_rhs_get_bias_offset(output_area.start_col()); + const auto imp_scale_offset = variant.fn_pack_rhs_get_scale_offset(output_area.start_col()); + const auto imp_packed_rhs_offset = variant.fn_pack_rhs_get_packed_rhs_offset(output_area.start_col(), shape.k); + + const kai_rhs_pack_qsi8_params imp_pack_rhs_params{ + .input_zero_point = lhs_zero_point, + .scale_multiplier = lhs_scale / dst_scale, + }; + + variant.fn_pack_rhs_run( + 1, output_area.width(), shape.k, imp_nr, imp_kr, imp_sr, shape.n * sizeof(int8_t), + rhs_qsi8.data() + imp_rhs_offset, bias_qsi32.data() + imp_bias_offset, rhs_scales.data() + imp_scale_offset, + imp_packed_rhs.data() + imp_packed_rhs_offset, 0, &imp_pack_rhs_params); + + const auto imp_packed_rhs_end_offset = output_area.end_col() < shape.n + ? variant.fn_pack_rhs_get_packed_rhs_offset(output_area.end_col(), shape.k) + : imp_packed_rhs_size; + + for (size_t i = 0; i < ref_packed_rhs.size(); ++i) { + if (i >= imp_packed_rhs_offset && i < imp_packed_rhs_end_offset) { + ASSERT_EQ(imp_packed_rhs[i], ref_packed_rhs[i]); + } else { + ASSERT_EQ(imp_packed_rhs[i], 0); + } + } } // Runs the optimized implementation of GEMM kernel. @@ -213,39 +321,56 @@ void run_test(const GemmShape& shape, const GemmVariant& variant) { std::vector imp_dst(imp_dst_size); - const kai_matmul_requantize32_params imp_main_params{ - .min_value = dst_qai8_clamp_min, - .max_value = dst_qai8_clamp_max, - .output_zero_point = dst_zero_point, - }; - - variant.fn_main_run( - shape.m, shape.n, shape.k, imp_packed_lhs.data(), imp_packed_rhs.data(), imp_dst.data(), - shape.n * sizeof(int8_t), sizeof(int8_t), &imp_main_params); - - for (size_t i = 0; i < ref_dst_qsi8_clamped.size(); ++i) { - const int32_t imp_value = read_array(imp_dst.data(), i); - const int32_t ref_value = read_array(ref_dst_qsi8_clamped.data(), i); - const auto error = std::abs(imp_value - ref_value); - - if (error > 1) { - ASSERT_EQ(imp_dst[i], ref_dst_qsi8_clamped[i]); + { + const auto imp_packed_lhs_offset = variant.fn_main_get_packed_lhs_offset(output_area.start_row(), shape.k); + const auto imp_packed_rhs_offset = variant.fn_main_get_packed_rhs_offset(output_area.start_col(), shape.k); + const auto imp_dst_offset = + variant.fn_main_get_dst_offset(output_area.start_row(), output_area.start_col(), shape.n * sizeof(int8_t)); + ASSERT_EQ(imp_dst_offset, output_area.start_row() * shape.n + output_area.start_col()); + + const kai_matmul_requantize32_params imp_main_params{ + .min_value = dst_qai8_clamp_min, + .max_value = dst_qai8_clamp_max, + .output_zero_point = dst_zero_point, + }; + + variant.fn_main_run( + output_area.height(), output_area.width(), shape.k, imp_packed_lhs.data() + imp_packed_lhs_offset, + imp_packed_rhs.data() + imp_packed_rhs_offset, imp_dst.data() + imp_dst_offset, shape.n * sizeof(int8_t), + sizeof(int8_t), &imp_main_params); + + for (size_t y = 0; y < shape.m; ++y) { + for (size_t x = 0; x < shape.n; ++x) { + const auto i = y * shape.n + x; + const auto in_area = y >= output_area.start_row() && y < output_area.end_row() && + x >= output_area.start_col() && x < output_area.end_col(); + + const int32_t imp_value = read_array(imp_dst.data(), i); + const int32_t ref_value = in_area ? read_array(ref_dst_qsi8_clamped.data(), i) : 0; + const auto error = std::abs(imp_value - ref_value); + const auto threshold = in_area ? 1 : 0; + + if (error > threshold) { + ASSERT_EQ(imp_value, ref_value); + } + } } } } -using ThisTest = testing::TestWithParam>; +using ThisTest = testing::TestWithParam>; TEST_P(ThisTest, EndToEnd) { - const auto& [variant, shape] = GetParam(); + const auto& [variant, shape, output_portion] = GetParam(); - run_test(shape, variant); + run_test(shape, variant, output_portion); } } // namespace INSTANTIATE_TEST_SUITE_P( matmul_clamp_qai8_qai8p_qsi8cp, ThisTest, - testing::Combine(testing::ValuesIn(gemm_variants), testing::ValuesIn(gemm_shapes))); + testing::Combine( + testing::ValuesIn(gemm_variants), testing::ValuesIn(gemm_shapes), testing::ValuesIn(output_portions))); } // namespace kai::test -- GitLab From 7ca435c146f59b3c2a01fbbb4bdb71bd871f1838 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Fri, 1 Nov 2024 13:38:58 +0000 Subject: [PATCH 04/12] Small filename and variable changes Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 3 +- kai/kai_common.h | 4 +-- kai/ukernels/matmul/BUILD.bazel | 8 ++--- ..._qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c} | 30 +++++++++---------- ..._qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h} | 28 ++++++++--------- .../pack/kai_lhs_pack_x8p2vlx4_x8_sme.c | 2 +- ..._pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c | 6 ++-- ... matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp} | 26 ++++++++-------- 8 files changed, 54 insertions(+), 53 deletions(-) rename kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/{kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c => kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c} (94%) rename kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/{kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h => kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h} (77%) rename test/tests/{matmul_clamp_qai8_qai8p_qsi8cp_test.cpp => matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c0a3a0e..aa96c7c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ set(KLEIDIAI_FILES_SME2 kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa.c kai/ukernels/matmul/matmul_clamp_f16_f16_f16p/kai_matmul_clamp_f16_f16_f16p2vlx2b_1x16vl_sme2_dot.c kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c + kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c ) add_library(kleidiai) @@ -255,7 +256,7 @@ if(KLEIDIAI_BUILD_TESTS) test/tests/matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp test/tests/matmul_clamp_f16_bf16p_bf16p_test.cpp test/tests/matmul_clamp_f32_bf16p_bf16p_test.cpp - test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp + test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp ) target_link_libraries(kleidiai_test diff --git a/kai/kai_common.h b/kai/kai_common.h index dac87b87..92c9cc1d 100644 --- a/kai/kai_common.h +++ b/kai/kai_common.h @@ -177,8 +177,8 @@ struct kai_rhs_pack_qs4cxs1s0_param { /// RHS packing parameter for static 8-bit quantization. struct kai_rhs_pack_qsi8_params { - int32_t input_zero_point; ///< Input quantization zero point. - float scale_multiplier; ///< Product of input and output quantization scales. + int32_t lhs_zero_point; ///< LHS quantization zero point. + float scale_multiplier; ///< Product of input and output quantization scales. }; /// Requantization and clamp parameters for GEMM output stage. diff --git a/kai/ukernels/matmul/BUILD.bazel b/kai/ukernels/matmul/BUILD.bazel index 493f0541..85635fd6 100644 --- a/kai/ukernels/matmul/BUILD.bazel +++ b/kai/ukernels/matmul/BUILD.bazel @@ -153,9 +153,9 @@ kai_c_library( ) kai_c_library( - name = "clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa", - srcs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c"], - hdrs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h"], + name = "clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa", + srcs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c"], + hdrs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h"], cpu_uarch = kai_cpu_sme(), ) @@ -574,7 +574,7 @@ kai_c_library( ":lhs_pack_f32p2vlx1_f32_sme", ":lhs_quant_pack_bf16p1x4_f32_neon", ":lhs_quant_pack_bf16p8x4_f32_neon", - ":clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa", + ":clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa", ":lhs_pack_f32p2vlx1_f32_sme", ":lhs_pack_x8p2vlx4_x8_sme", ":lhs_quant_pack_bf16p_f32_neon", diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c similarity index 94% rename from kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c rename to kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c index e464a9f2..ee074ac3 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c @@ -18,53 +18,53 @@ static const size_t kai_nr = 2; static const size_t kai_kr = 4; static const size_t kai_sr = 1; -size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { return kai_mr * kai_get_sme_vector_length_u32(); } -size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { return kai_nr * kai_get_sme_vector_length_u32(); } -size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { return kai_mr * kai_get_sme_vector_length_u32(); } -size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { return kai_nr * kai_get_sme_vector_length_u32(); } -size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { return kai_kr; } -size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { return kai_sr; } -size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k) { - KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); +size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k) { + KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); return m_idx * kai_roundup(k, kai_kr) * sizeof(int8_t); } -size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k) { - KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); +size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k) { + KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); return n_idx * (sizeof(int32_t) + kai_roundup(k, kai_kr) * sizeof(int8_t) + sizeof(float)); } -size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( +size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( size_t m_idx, size_t n_idx, size_t dst_stride) { - KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); - KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa() == 0); + KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); + KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); return m_idx * dst_stride + n_idx * sizeof(int8_t); } -size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m, size_t n) { +size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m, size_t n) { return m * n * sizeof(int8_t); } -void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( +void kai_run_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, size_t dst_stride_col, const struct kai_matmul_requantize32_params* params) { KAI_ASSUME(dst_stride_col == sizeof(int8_t)); diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h similarity index 77% rename from kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h rename to kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h index 138ba4e3..b5c3132d 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h @@ -24,42 +24,42 @@ extern "C" { /// The starting row index must be divisible by `m_step`. /// /// @return The m step value. -size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// Gets n step value. /// /// The starting column index must be divisible by `n_step`. /// /// @return The n step value. -size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// Gets mr value. /// /// This is the packing parameter which must be used to pack the LHS matrix. /// /// @return The mr value. -size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// Gets nr value. /// /// This is the packing parameter which must be used to pack the RHS matrix. /// /// @return The nr value. -size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// Gets kr value. /// /// This is the packing parameter which must be used to pack the LHS and RHS matrix. /// /// @return The kr value. -size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// Gets sr value. /// /// This is the packing parameter which must be used to pack the LHS and RHS matrix. /// /// @return The sr value. -size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// Gets the offset in bytes to the data element in the packed LHS matrix buffer. /// @@ -67,7 +67,7 @@ size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(void); /// @param[in] k Number of columns in the unpacked LHS matrix. /// /// @return The offset in bytes to the data element. -size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k); +size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k); /// Gets the offset in bytes to the data element in the packed RHS matrix buffer. /// @@ -75,7 +75,7 @@ size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_m /// @param[in] k Number of rows in the unpacked RHS matrix. /// /// @return The offset in bytes to the data element. -size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k); +size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k); /// Gets the offset in bytes to the data element in the destination matrix buffer. /// @@ -84,7 +84,7 @@ size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_m /// @param[in] dst_stride Row stride in bytes. /// /// @return The offset in bytes to the data element. -size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( +size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( size_t m_idx, size_t n_idx, size_t dst_stride); /// Gets the size in bytes of the destination matrix buffer. @@ -93,16 +93,16 @@ size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( /// @param[in] n Number of columns. /// /// @return The size in bytes of the destination matrix buffer. -size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_t m, size_t n); +size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m, size_t n); /// Runs the matrix multiplication microkernel followed by a clamp operation. /// /// The pointer of each buffers (packed LHS, packed RHS and output) needs to be added with offset /// calculated using the following functions: /// -/// * Packed LHS: @ref kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa. -/// * Packed RHS: @ref kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa. -/// * Output: @ref kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa. +/// * Packed LHS: @ref kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa. +/// * Packed RHS: @ref kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa. +/// * Output: @ref kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa. /// /// @param[in] m Number of output rows to be computed. /// @param[in] n Number of output columns to be computed. @@ -113,7 +113,7 @@ size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa(size_ /// @param[in] dst_stride_row Row stride in bytes of the output matrix. /// @param[in] dst_stride_col Column stride in bytes of the output matrix. /// @param[in] params Requantization and clamp parmaters. -void kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa( +void kai_run_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, size_t dst_stride_col, const struct kai_matmul_requantize32_params* params); diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c index 76464f12..b16ef82a 100644 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c @@ -67,7 +67,7 @@ void kai_run_lhs_pack_x8p2vlx4_x8_sme( KAI_ASSUME(m_idx_start == 0); - const size_t block_height = (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; + const size_t block_height = kai_mr * kai_get_sme_vector_length_u8() / kai_kr; const size_t width = k; const size_t row_offset = 0; diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c index c5ee1a13..449b1141 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c @@ -83,7 +83,7 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( } size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(height); - const int32_t input_zero_point = params->input_zero_point; + const int32_t lhs_zero_point = params->lhs_zero_point; const float scale_multiplier = params->scale_multiplier; __asm__ __volatile__( @@ -211,7 +211,7 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( "10:" // Scale: Done "cbz %x[width], 13f\n" "cbz x10, 13f\n" - "dup z21.s, %w[input_zero_point]\n" + "dup z21.s, %w[lhs_zero_point]\n" "add x25, x10, #0x3\n" "cntw x24, ALL, MUL #2\n" "mov z20.b, #0x1\n" @@ -250,7 +250,7 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( "13:" // Bias: Done ".inst 0xd503467f // SMSTOP\n" : [bias] "+&r"(bias), [height] "+&r"(height), [in] "+&r"(in), [out] "+&r"(out), [scale] "+&r"(scale) - : [in_stride] "r"(in_stride), [input_zero_point] "r"(input_zero_point), [out_stride] "r"(out_stride), + : [in_stride] "r"(in_stride), [lhs_zero_point] "r"(lhs_zero_point), [out_stride] "r"(out_stride), [pad_row] "r"(pad_row), [scale_multiplier] "r"(scale_multiplier), [width] "r"(width) : "cc", "memory", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "x9", "x10", "x11", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "z0", "z1", "z2", diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp similarity index 96% rename from test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp rename to test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp index cd366d61..4adbc03e 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp @@ -12,7 +12,7 @@ #include #include "kai/kai_common.h" -#include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.h" +#include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h" #include "kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h" #include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" #include "test/common/cpu_info.hpp" @@ -103,17 +103,17 @@ const std::array gemm_variants = { .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_mr = kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_nr = kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_kr = kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_sr = kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_packed_lhs_offset = kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_packed_rhs_offset = kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_dst_offset = kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_get_dst_size = kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, - .fn_main_run = kai_run_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa, + .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_mr = kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_nr = kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_kr = kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_sr = kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_packed_lhs_offset = kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_packed_rhs_offset = kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_dst_offset = kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_dst_size = kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_run = kai_run_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, }, }; @@ -293,7 +293,7 @@ void run_test(const GemmShape& shape, const GemmVariant& variant, const MatrixPo const auto imp_packed_rhs_offset = variant.fn_pack_rhs_get_packed_rhs_offset(output_area.start_col(), shape.k); const kai_rhs_pack_qsi8_params imp_pack_rhs_params{ - .input_zero_point = lhs_zero_point, + .lhs_zero_point = lhs_zero_point, .scale_multiplier = lhs_scale / dst_scale, }; -- GitLab From f80391e0d08437edd8e4bd46a9c30a509d26aaa9 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Wed, 13 Nov 2024 20:48:15 +0000 Subject: [PATCH 05/12] Add documentation for int8 RHS packing and rename kernel Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 2 +- docs/imgs/kai_rhs_packing_pattern_2.png | Bin 0 -> 137081 bytes .../kai_rhs_packing_pattern_2.png.license | 3 +++ kai/ukernels/matmul/BUILD.bazel | 8 +++---- ...p_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h | 2 +- kai/ukernels/matmul/pack/README.md | 14 +++++++++++ ...ack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c} | 22 +++++++++--------- ...ack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h} | 22 +++++++++--------- .../matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp | 16 ++++++------- 9 files changed, 53 insertions(+), 36 deletions(-) create mode 100644 docs/imgs/kai_rhs_packing_pattern_2.png create mode 100644 docs/imgs/kai_rhs_packing_pattern_2.png.license rename kai/ukernels/matmul/pack/{kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c => kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c} (91%) rename kai/ukernels/matmul/pack/{kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h => kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h} (76%) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa96c7c8..3a4f91e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ set(KLEIDIAI_FILES_SME kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_kxn_f32pb_f32_f32_16vlx1_sme.c kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c - kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c + kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c ) set(KLEIDIAI_FILES_SME2 diff --git a/docs/imgs/kai_rhs_packing_pattern_2.png b/docs/imgs/kai_rhs_packing_pattern_2.png new file mode 100644 index 0000000000000000000000000000000000000000..700da43e57406dbb4a1f39343b1655cedf0b9c4d GIT binary patch literal 137081 zcmbrm1zePA_dY%#2#5#>f`Wt~qNJ24IVy;NbT`u7-6JR=DWxY*Ey)9cT;$b%{8(J@@ndpv3sW6^BW(!esduOxn!MCkf*2*q8~G4q$w#Iy+(ICd zkMxi}5Po{{6xrjlCb?H%2R~{r1Hs4mbP69598L{g+(l;zzK05gm^M=)7iLSdolGlD zmk)PhSxz2UE)R0rLd1FNuDrGs$AVb!@4e{@gna3n#6O=(90+4-N4}}vG&SX|(9wYl z3H!9YGBqUP*3fTwi8<4C`=mGFE8*lC1e$O|bYf=B;sy^2MD;74@H!HCquoa8p2oFT zSM-F{-EbH~#x9~}g^aPH4pSF-2WUSYya-VUZ3@+b#PyuFe}cq#&LCy$;o{A4OnyiT z3;A|SOLVj6{VN*8dX%r^liHpgJfxm5jnID_p=|O6N4A+?wE^~-%lwjU|H(W56BP@~ z5AHk}>M|`M9$y-#FK*ayI$9K8On)>;EA-m;g*@gs_M^%{QjL&^J6Iy(eeYkDbKPsc z*8Z~3yAA82XCKbEx`e2f#~U$W!t<8ouV_)|@uw}vZ}hm^#gjAM7S+#FV16JwhnXoH z{!u~b7+H>DhjwF2JR-VUa=xk+-z;SRt)hgE)8$%eFZv4}-a$C&4;SBtlMrU*UsDPB z>VG{y=&7dmniBLnEzV1n+nYHrtOU(6IxAV(P0cF%rk~xQmh{G+)cxW~v+mBeQM|r8 zru^|9%q#S63S|Q^G1Gm*O7go;nQSwZ$c4P5=ACe6&}wPEj7yt~=68~^<4t-r*s*x2 zY+M)*(Oj!_Dki_OZ&!MsmqQhoS~a8&>$PmHJL5TAH!NhNb5Ea(oC|(PfK#~Lv-6pg zU+nHEGDZUgc{EPzyui(`j(n>`Xg<}kI2h33_F{;AHUuAfynvHl%;9EbZwhnbyJvU7 zoB;p_6+M<+34T2(A`4Sct+&BY14+K;1R^1Ax; zBGq|3WAk%^ggaj-N4v>n38TCnFSSmTagW`Jk6wLi=HC9Wm_<;YZtqSbQ{1_EG+N7` zZA}I<+LXChXrnyhBs3GH#Ykq=D)D3l%V^wX55qEjmBOxk5$ku2{Ddv`SYNuhv^g=l8zz9(XQpJSRTRqbkJV^op(*)g;W(l;>7g zr_MQ_fy&8H^EiFM+sPWs;dN+zFqXB)$twGSlag^|%)asR`vES_iMkH@0p)sWc@or5`D=cZG~vTBxg!&e}38-h>=* zk1n`Qa_ZK2D0yCh5c;ovc!P}U*~Ead8!GN;8o@JI)eOUm3cxf&8?Gipf!)PH9e>FD z7X5vj$AU6)(u-^A47ZWA)vw88^|*_u)AnI(yPb<8y?!qF(*v_>`7q*9Qe|$HQPkOU zE7iIv51*oA#Z&HkVQ6^wQ8J+8t?|DIM88QO_(U@3BA>_8plc0WA1*4uG6V|AyBl04 z(JhEdyz(h+0|lO9uM_QgXr$1j`4Pp-&OQzC&TThiC5?8|iIZxlu;M92QF$#JU)I5~ z$?9;e0%jA(*zR&so#rirj?V-2XTE;?&+o`{-1?;RJW>9E?8|Nqztsn7H5?yR7b!(+ z=kA}gb)&tHFIH{8i|<5Gc~vc7rFLOe=aA?aeWj)fR}ba!3+_*^t8e&HY2hSY7DAu$ zSn{ax$XivJrzoeq>|M%dn1JR)80@W3J6Bs*i&a}uD^+WwL7fxJM==+?D9H2S_T}&p zF`65hluNgwZ>msP2k`{TG~Ci6w7C1=B^?D}aMRba2I>a6uWb#ncZl$XeF&+1J|{nW zPnT%)o>ekSy!3W#YJ{|58a7)@1b?)!ig0FPOyY2IrG$h8lhm5Dw*+olvxGu=uGrLz zM#|8DG{rQVG^6gdG|6tNG)`%;)L{wd$L~8b^p5lK6?u#?G9zqWZ3k{Y^vjw~`64lq z-XfbWo-S3Ewk;W*V3OJ@8JnW|K{d-o{K&^4`#gV*kbCB*?zf2&@#cIJVNa@VsdOo@ z2(idn=1eoRyp9-Byh%@+Nlstc=F+NJ&9a2U-YBs3(*rCB&Bb&LKy_u1#DB7k%l5mV_#jv(!|2MsKlCK)7Slc zW$V?#-U7y^(AM_+Q2W7$ws!2!wjQ-Cwd|8>2>VACFQ&H!A3H`f9*9d!( zU3KZjVasRnWs5Q??*3T!aipQ|cI{K6{MK1tiR_%LoGSCMHJS;$FWk4R@YL|)ztRK; z-)g(Yagl)1GbgO0d6tEZ6^ku2!i33~?Z`;SI80sEsIYet_MkgTKgOi8vHWX!+vi#h z-@1#hiC&k5;R`2eCruGGZG^34W@Ns}Adv4?vQ}D9u*`U!VVJQp&~9{Sl+-8vUAc&= zpV>Iin7yy?d%TT_%{!YbHgby+j<$~RN9PZ=aXoNjDETPUo(x)uTdP07zyIw%m&G@0 zUo$$J3|prMof=w=4O5hD0f*C*(;*Z2Epst>VU}+#b1dI)7!q<4#t6S`+LFkZ`1D>U zz~JguWB0JkXlch*A@Pv0?*MYqt)lE@(&fuLiaVs(m;urOLIFz8jCEKonKoWfe`2Y& zESzC#3s7BF-BU#?KVG+4ZQUN88shGE$XDMi?1<*k;ZcsI=OL-QI3hjLQc2>H>jh0@)<`H}S_+f&-xH}ss_Yt-{b zeMcikD{+bf=WZlF!iXP>Z+dKM?t7n0lr5RDnbJr{-m=)hPI-5KwI0_7_l?&VrrEb2 znP}?=1M+?G`FjLlw?)4-rl587bVwxxe8JI7(4^6b`NC1(>cvea5DBB zb;P)b(dm)Iw^h65-R->+j2tX`)7qEhfz5PZ)7SG~KTp2GmlV=y+zvT5e>fo*`necOAiV;R-R|X|*tWG1atR>KHdV#FB(oP&$%R-_bQ? zc)-rKs9Fr8Oj%u94_nn9xuo8apbZKcR2;2P?|S4E zZ}xm4LE?1}{l07?V}^CEXO$o5Xb;hRN$Zp`l@n8R&KWFl>LQvanv3u+z%?Z9QOHm#TyENL-eRXgrrDMy zl4VG9s)ALG%|AKuD~R;H7izqpuTZ?V9TrG$Eh8&4rR1%IS;#XKR=jj*+B}k0L~O)3 zaBS_VCs5zo7v0iL+TC4t(Uv~*q2UKM?y~w7lL5Tq#wKW7N*ZB?03u5zMW ze{#^`5m{vFSJiDE=~{Lm+8Qlqi@EGl$v(p~<}!EA4{Zt?i%j7JJ#DrB3Gz>f+?3z2X@a##%<6ov_2yJ>4nc$Z*f& z^^^2OQzNxr+OO%b-q$0uRo~Zowv44*txjreOICQQ7?0vkp3ewO*KfqW;o&~u+QOV` z>d!7zBd&VsTE-j5d6#@zsCZos>7pzvH;G#>CnS#Q9Mp4$W&VEMo|r05-8&ioYS+&` zED7bs=fp8jMl>D9Mq210+s5woj49bXq;7gdkhK^H37bL6rI_x?86kxmpDnAW8?yCF z^hZbV>)vg67Q33jo^@Y296ZLHGI6q)arIsT5k;7`yr7PV2!sY)qeD=UE^x(k*#=Fdn%*^!Q7xb3)CRWew=uIps|MQU3=kRG;YFg-< zS?Qaaki(z*T*K7bikpH0KG4~p|IAa{PXF(bOf3Ie7FZx7{2N9lhP#Zvy3I;o=Qp>( zzxmH?Ki2i1;ke)r;}F-k(>9Xl(>DfN4W`D!a+m2Y*N<`j_0``q{m-p3mf9ANO^v~w zRy=#v)C{_>wU{uoo??=jifS$`h#r*D3`6TSuxNo`A0BWw5`%9!X|@i22S{_5j@ z-lg#OZ9Gh@V5}cE{ru@a?~woH9Y25i&pX5{^uaznhfj@%>8}xf{_L;cb1}kY|C28K zN6~(K3N(%90vF?%*796P+wKR-4uuHvJ(RXXS{%X%QI>gke{~?67#%(3F3Kx@a-6VP z6ov7)s}G?h9v;qk3CI-W4dl;pck@uL^1UR#eM23+q4GT)McgQJ!>xzz&=)u%9`tn2 zv6LjY+T?Ug#jJbNMYqD16I^zpc#ejhZ5^xTYcN)vw}z6ab-ASI@Gt1kDyCDvgdicK zpyQB3-Tvo`i&n6V#6I$RH;fsUA$~g~n&VH#RU&W7orYCBN=JBDBxG%5 z=Fj2J5nnD0#`uw((&^r+oc>D$wE_rf^aLeWBM0LCKWoydV9~$%LBo8k^JFop5Zz)V zN9bBFUxch~ZI~ksQjf0m?*iA9tzpHVjw(O zab9EvVN1)Sj2=fSTjh_ED~*q?CJ-B*h^P|7xB1(* z)$|9Y0-(N9g1%t~M)Wpc%79hOP4xmr*?X%~XV-c3k z{8Ms6ef_NWQ;IYQA9hyYAm1`lI=w%^Ep|oJNq!54%4Q^YE7Br9Y&o>&+bYT*h&2W! z_g4Js)~3Az!lM$Y0v*196#zll#-Pj#z+LOULS`tUf^~z|U^bwSthyp2yjXEHWK8DI zx?2Sr>4@8jR|E|{b&npOE4D{esyHc(FuE%XhM#1G5#A_2`oP4bFXzp65I;O>$O`e~ zR8G#$S_qE|`$Ev5s2D-4R6T@;sPloUU;qazM;2a(xMXGN2=4rk{^F;dgq7j~6F#q# zNr1SsL+O0syZR?E;s_2Scw<8m*WYSnfJg%`533;~e9FHCKzlj|m3h+(BoS6ZJv%UL z2yW%r$pr|adknYzkDj}b(DzCr?yZ0!PD;w?z#8O5e7!-?Fl~`P_Ujj3YP=^#+zR8Q zP=fP{`nk7bMmrVJa|gDWj*YDx34*YI;G=qbWWNt+LVT0SgK>t*kPzSBU}#u`$LHQI zG@VR@7Yag9@!PDWumkEp!E3<0Ea;VlA%0^07y_?MH=&Qrx}qTN+P7{7URlsD=s|o~ zEXF`KHm*MrD>W=b@U|1Tvns-)!d?*qT>Geo5`w4^hQRi58*(#BTx1Os5fvbbIaP4Z>@z^zPQ;^v$hEy4ViJQmU=t=6uG?4q*p0Z$(ZtkBP0c|X z`ulX+^d>1s% z1O=GzQiXKHV`c~}pPZ5l6;VY3Y!NSfm2JA(i0ki1D1fCOJa)nnwM+1@Ik140y|>LE zGX!BJ{DFqGJxq720Q-($cm-^KhWJ}Gfu;g!gttl$7~<4BhF0fx#C`%r|C>5n-wf1N zkosf4{wTihPXjDre%Put2s#jsu)sD8i(r}lNzCz-T$o~PVRj-H@l9^H>9Hg52l{j~ zF!4up1^a{O3eFH*_+y3um=_MkEQARHH|Tpzt(SYi0qm0@?%JW;7+Q4>ikSt78{!&F zEfFlSKM|`SoE=sZiw#G3)E}f%eETXo!l5KI&IKix&M+&sTM=>UlC|8;cNOb|8u7($ zQNg)kSvA^1Ma1od9xOwij+NCd5^=z`f4M<2r;@;^08zt6-rff|90MbWE#k&$iz@KS z1?{Pmq-4p#F(t4+Y|}-ZEF9``?V{PkRCBBKcL>}a2hV4-WfZhLNtzag96H*zNKX=Pm%Ty7#M(t`LB%;A7?`lUM`^Y zXz{0e|CFjVG-xCmsy;%T3?aeLQs|NXW4|tb``#Z0SbBp0aIkTY)b}^%=0fFEQ&U!i z>fQ)5ybDt@Dak)vZ%R%KS1qMg8R#dt@X~6;d8Z({{-H0M)9$K`wV#=!@~Y{g<4Q%* zP-fQ0&md#j9=|jn^;^lS;MuKyLhC{!b80UT)y~YL6DHeh5D;478y{+jd+6Sn)#I|; zZ~siRqDnvaZ(q@7=g9|!v;WLmkWGawnRpz{v@)6s?B zSPc0PNLQ3?3i*tX2jht481o@dB;x^1F(x1K#+6TeH^ zZ{56p{Jd0dN3yKCN69pr!clmk4TCyL+)67+JIv!M5@yZms0X3 zSp=J54@70IT18f>tJSeh_>w}pOyoY1h_TdIuoD;q*Y3Dx$5VMQoIJLYX zI)w_Q41rjSu0A$LuQt!Z-C1r}pXz2_?2%FF{(QQlsW@eKG}K6!h!Gk8H~w$+Ie{06 zohKG?7^&;w8uo_qy}=6SalF{uRIoIg382Te4hxw@X)4S4Jj3zV9T#&-jPLd9y|-#+ zs-IMq68uSn6o^Jg8W*DnSkHtC6+_wX(+sexC$ZhsLU&$}9|fA6Ink}eak|a_uuCZ_ zqb=+s&yMTX)UtdMWU98+_<($c4u|Y~9XpR{eJE56ri45QVt{a;s!cexD zo8PDLTBc(8%#T3ZP!$0U&?dli_EEJP-u=z?$DArshn%bMbCX@UZ!q_K`7^T%nN|Bk zeQ?rK6B84=%vBD}Fv?dyW6qUtv>uoZg_Pa+C5VM#hgJvPc-(0-VB9;$U2$@JFjvs4 zDr`C3EErVB`64zw^n0FeviZc9M8V)&GKLA2mr%!@1#Y_dFmhVa!H=adY#pwv^};f3 zZ5#_3_+_*+p_Z)=s57gM_wB>K;hsu#Gokv${iNy5LpWQ z!Xx`VMV&`GBe4{LJlThbGrVo-vO72Myo~M>s#r+MK3Y5}Q|JPY!2jWlwghPAU>a`JFM#*Bfc| z=f9=Sk)7-cMBFo0bXd%(?*v1tj<9QQovr)7FXEoS(Dhu_>If~*#l_lsp~9TE%iKDQvTPOH8M0Md&@UUAiE%X9uchYc$ZEwnJvh)2 zjC|Iu|C+88443lBWR}zR;`h1CqTw`s;f6sZ=}Y%v7q<_}(X5}|9x$&bk=m3w zdmuqHR9LEz9N*K(QYHOMM64+Zs;H+scedug zF50M0`g^s9%kAf2XrxbV2h9Z{m^7a_jJ+h3EXTAv+Uo5B4pzRAffQ+A zZFqWu?8GjcPWSk5Zw?$gv8ufm(@rTd?ogf9sCQwC2C1EWk3BEZSKujFwy+5+bW992 zC)~E2X>Ezt$*Cg{Uh5i|!c^OR#4(@pzB9mO^Sw_zrPLu){qnJ~_Yx$aAGEPkx%47np^QSY@dH8n7DY%@2eYkGbx zJbTk<|N*QoqGxpk<1wD=F%b}$^Bd)yBj>{!; zwRmFB;J!Xtv+%suO4&-~T=9fIEoz!=e|%$|7jEg~8{y6l0kUWf!qIYzgQcP(838j} z6b~||)lYYFQ+8w?9Pdtar6$Kja2^8K5V1Y%xH4$Soyj%#Ay`Q8%{ueR$)YZ=Fy~6S zd<08`(!_x)nRk?<@rzHj5II4QC@Y)uGkhbtva&VOMFQx5VnU-6W~kM z{bF?Ee97nwOd*A_i5uo>PN|zoktS(GC{@7V%4(5)RnnB3S7a%-N(?AGI}QJRFX|zY_m&1I)yEhqpK!?slPzClm31F#= zd1v2iEJ^u}W~_GWU#Yv|JxL=5?hUj10b{unA@sKyywb2wVV7(gZe;cMX9OwR)wb?f z_hz?JV}We*fx}2#zeuSPJqgal)7XQ-8A-0%%tXHS!|egn*m0P-t*}w3eZvh;t0G5} zDq_>}ocyfv*Ez!r*;SRB;`de@Y!|-xlSFFJwzay9BNaV5MGf z<-Na9a;%F(os-0xJJ}&Qf6{Ix1F=c*^ZnS9z1Tth@d_!BST~Z(xQ;fv;28L;%J-tGqou+9W!W$8T@ACxK;@-LC<=#s zpKW4deu)7{&&PKTocc@-S!FM@bZ`b71CUpgsPC_oZ*&3^-S5!4^N=%fgeYEBe}pybpDGxk=Q1R~B(s9CaC-sljI>Yc2`>2XB^#n`*FmmHSf* z^N!uoutjTLVF|C5 zOZoYqFVIe^E{IM65h*o}t7fiXyYAL(HnX&SAXIZ=;@+QqpE$DSKbi2FL1^umBKuj? zeI^wUPuYj}&yPDdEq>1%Xl=sQVDINWUJFRvxv`c-IUK;XT77Pgcx3m9eV$Eya+HNo z*+y1OE!WA>$Vqf7zbR5AkDVfb^vDEtUzx?LE~kk4iX zw97YHmtdwPHuhCkfx}3f&iS+bRwe8+%&w5sI+~R?0a1;=%u=f+aec3vD|ZFJYeaYdX@8ru1-;}L2SQLh0|rzkcdUzqt66^)s?NPgQ1nQ^F1T> zGtn~H1()!@_f3o$t9oa0Ybtg4PnFEX2&ZS{3f0ed+8gO#d$_*#ktLf8*y_0^wPTB~ zX%k!U*jFVf(9W(B;BsrDu!PEzk`k4psc<#btxr2|nKU~mzXqpgr4B)z?Q;7K(q#sB zfX971&LVy)q5rm$Zq*6+UW)@c6Grym_1^n+3Q|W)tIC*PZ<|$f-s{UMKR&331=ac?3qyckz(@+8iu;8;;Q4V>@G#a zOd0dcg7IkEVZo83Wm`-4Va--TRl6JX1>YnyTLxU}%M#FS+GvAsi3kA~r8b->IRpX} z6!kAL1z63 zUA#M>w8F1}*87^Vvbc+UH%qeqQvO7JK-&_*iaH18ivZCeuiL|#e4k_c9u$?=h9grL z9)3b2Q&v^s-~lFuWYgoENEVp#cTtp2z0Xo_{02hKUV(jbM+-;p*~m@b z(Rz`dUCF_I;H2f%HXy@pnw6B4BuzgirfRlF8Nl_1LdvoJGDhH~2a|#R-6b>i*Das< z_->F0JFUghQoR_ZY~V7DW@@DhSMFSSxSHet^esNpp8d}CUZo~D6uLKPR?{O@b;d62 zOTY{7mh0z4L{24kw;fgEpbbC}hL)%YdA(r{fa;m_4-L<}Su}PcQ<$Is!PI#{sYGNM zfNh&C+zcMa?L`}F^UDus3^Q_`@H-dyG@x|laDiZ%B%Omh0Ij<_H=?#?2EEVJWP4>( z6j$YUK(K-iX9ZVQ$+*C3cg!=n#Byf4e>RAlD|<1ojmN%QWa8HW7=?v*ARSR3+_D znNC1BbM?xW6Wy1>`l6ebuEwyOW+wBq8lfgSxI~6O5 zATUo(THGu-m-4&Cqd3r+^(g2jf(Yz`LY~f+T?RTz6<{rBzj*Hu<64b$zac~?(;Nh* zY3bGc+q)@SFKEO)|iCnt&P0H3Hc8cDeFkIm=4l@SrR9n3eydqv|zyoUzf& zB4uQpcILMJc~O7~r`=M+RCIZP!{WHl+^n2nui6vw*&(x^0(u}k0-RJA+&q;x zvGu~eZ;5eP1XiVWU}j-|+SbRRm1`_I`O$KYgm^0ya8kC=rrDo19>?X=4HV0X)U6D&8K<*L98`NSk%0kOe)CL8IMAN8oW1fp;xTa5chkm<$p~f^#~`=>>2)v(Y()%c$?8qGyU8;Wa6a^PlS# zo0=v9cvW2#Ifjk$S~>-I)`NWZE7MYd9oabA%61iL<2`Zmsdo8o7fXtYI^c|l92hTO zYtm#Nd>_EWA@YtnQRDM~Ng2J0&3Ek~qB84`$uSN;cEY+>rD(`Lle+uO6*#&xeq0V5 ze#weP8fr(8^!mpZ%Ift*dPR)5W{O)lov>lO_+xJjP;4#PqLEfN+MCRwH15 zTrf;^0wBUNa_i=8X8>?BujWlW9`?UB{=R1PlRNGHmTsLN`|#T=K+hd3zbHAgz^5wn zpUr~SVDQPVKSf~$xd0!>0JoWP6JQdsVul9ZOY6}%vJ0OF;KGaVV(xZU$GIxQDTFmjD8mrrS1Ml+?0m6HdWjhz8;8+=|9~ zzk7{JEjEx%IO3GS1?*Q;T^TV$QO` zTJ8lk_PAUk{cFd!r)FtoF!oOvjl5InaociVt)2m1f|k2eQP}VlztT(-O&dfhP+=ZWQC*`Zjn6c%M<} zrAiCVO-XI18_#2%x4)7YH_;b83(Bl4fO`l(V)m@9{Gk=V#;^b^ZT~a(LPrEI)v40c zVa7b$36%@L)10Kp+lmz~9?lBzDl(}`Te0KtFdI*5iz>JtZ4I<+tehOLEVWKd0^F09 zq9o6+rC0(`P5;p${EN?}p;;jDHl*)KVxwnx@RHH##uA9#jAy3M)<(Ag6U&fo@$8=> zKgEul@zt1&<$;|0>rITYN@~K|1#z3eX?J&4B(!YKbavp7{l^7aK}1u3Zm*Zi&hi|5baMT!!v z;o_Sjk?HfqM(s)4qQkdKk~;UBCr~ajsMI z?tLBHSEOk=_pNE*WQx)C4sYfjJQ3MqtaMDO1?bsE=bhD2ckJoyr3{PD9vJ_@Fq#3} zg?S8#hOztKk|nh~7#ZEqV+jEUlP=qUvl8SIIz`xrzS#`-qUlDNKPd&AuSK#qiF2>K zrk{_YFkKGcejTHWgv5v9TcBB4LF->fkxM65Hk=L+-PvEmJ)|^ zdQk`tK04uXZU+@|E9E&yLwy4SA+_W6a3!$;my_e2DzUC45kt=GDe~bFD>}8qaY9bn zZYNUbsx?2;$wJ_fj=BKO2y@J2r%eV)T>l5tZwGHwt%qiR08tR*j-vDviX=FqE$N+j zeV%u}Bj9IVb`~_viO=@)rXA>9dvaDRa8(Z(P*kQ;1<+l1GJ{)j8P4brxORRJEZOR& zQL~>4wc2Q5ms9=$R;YQ_yluAZKkanHDdISkwVv2A1GYY7rI zZPxv4F-V%=@u|N|;sz!|F?kVBHZyZl+gqmhiaR5kMZLZc}+x85xoq0!9{00K2ihM#( z7W`A0LMt$vV|jU_oz}mm_CIchTfD;R#jnn?j@4Jci|MpP$ZWLdw3e@DnmVT^a1_#$ zfb+zE^et7Y3xF;;iCDR{iWO(Z9i$C-_P4iuy0qa9oKc%83_u&p^_=YdZM48m;C1sp zoVoUaOqW}HlMpCqDvasXd2Zd!>|5{X4<|654+;0e6~gK>2K8hi35bFCkCw|;R3!+n z1cl53Ti#qDt9lJ-!X9Zm$hlH6>|p&knj{;f=;t!>S{sd-&BtpQTpyC##sQdyaqLYU z(@;V5H5eYA!INRa^aavzZrNU@Yy!lAWz%g1DgxAJ5>z2*kepG|SX_7GIFm9cRW7&R z5W?mHmak_MSnvCFVIkBpb{gH0UiWZ|hk|nf`b;kO%nhp30wITJ0M~lMBhM<}7A^Bi z>U&~Ao>ti81n`qwMJ|By6ajcnW*OuRK#;_E1X53W+{Ona*LYuDyw+(mJmil4jTfYj zEz9cNmjuY1S^uvSX#)cuCplz~dzi(Sn4N*Ya0f(jOtx+fI4+wV0yC-$LQ^Rc?XWA^qrd3 zk2RN?jL8K3IUIY#1_b{`07WPSe0RM)R>Go0|JrOA5*?0`z?VY5f(>^gOr$X zQ1>J}P1DT@7}23laAsAv9OM|Jn-nh_26%d|LH0rHk#=?coo{r{k;O{evf}r|$v~|(r^=4TRB$m(H*d!LIa3xLGwIQ`^kap2_t)?bNTDN z!5N-#IIv+g?$4LLjW4wE1ab&M)My@%EDz7JR(|HhM9x@TsadAjN;%i*fq`cc*$QmL zX6vmT4R%_%9UHtU!lXPXr+48caQ->Tk;w6zU8j8nGa5p5gjhM<4p4L1uP5 zr;8SKSL8Mu8A>uSeT)vfnU@795&(EfDbNpag_lCyse}Z|wep@E;+I{sEvTIdEs8W- zxUY85-z#~a_RK)J`T4RAzcZAz8jo`yFy38&S`H4A*YH-Yhy?(Roy28#EZYBp zVNZr6L5N*3(WlXFA+4Y)P@N+DX}P-cK>c;G=?}e`vbxV_e5iNi- z!KKIh3+d-S2xNm(Oh;l~iyL~*D#d%}QumwH{Cj@tOLk;k3c%rN3USWb^dxciQW!cH z3|UNYcfw1vqMr|Jl`Q5q331{Ur5{!Wlh_V2)n$DeqF<|n*#(5jea;VSyPciQaprFN zQF42h{0t1UuAH7HC?$r|oUob@U^Uc)-!*`|!kTK;RzthX^gCU6jF%~(={iNLCI~Xz zO^@6Ml&HNkR@QJz`rW&GY`R@sHzG%#u@9O(zN6_>aF07RJ8TW)L42v|G;*kG7Jl0& zIf>2(-Pma3Rb%`3AXzv(Ee!Ann~@`jx9S+mx$gw=a}QJ1I)*6({n%LXBH*9A?6h)?s?HtrqeQeZ?o z0oWDqT2%;}DkNsr6E|_T*Ve*)2Mlz-#%ubUI#byI$C1-?b%f{!XFP%t2J z1<`1lUJ*zhNq@7>@NxmAXqtB>9Odw~`R?sKRkzxoOO|sc>@ctF*?79X0r)Ih5W$#a z%3vOmaCTS1tBiIh0>tR)>176H6gvTA{{SbCK!&4= zmOId|nKp+?m+P(ysG$?$#r;V3aE{nGUm}gbO>H71Q3oU^MejU5j6L2=-UQ@r(gVxs z59p%-Xg#2!K?LAa8%5n{>rN1F^oBk!IJ2Yw*gh0==_`QW!|ntqE!YL9d@?*Mk+?&V zr_(}H5a7XudmC;8l0g=fqi(qv)Gx|*=agEE0rbq!ve*6?JCu1;U~&iE;vPEZ2vlEOqDn}`fX9wRKNz&KO3vnb*J<06h2#9!W7a{BKJm)V?5Vl= z>B}<{{p*W%ETn5=Ww4~PSp1*f=bv`0eLlx1)mL zO>!&`!nZ=>)Zjg9F7J9j{K36S@b?7RrpKP5 z+W%cK;Nxm}tPt7+|2AL$yZJzE!5b?c3hgeB&k6ju)c=n&d-~ODRp6dXK&fm*(Eoe_ z&W?hW6ey0@_E<@ZHu>G;e*4gNZ19?ayH#XKY-eG?Z-4tQJ)eyMZ>$SX?1{-K)sZW5CAPfZoAlX zx{HPAIJ3?$P&RkJ&Xrfd_o~D`VBY4B1bv_WdjWBuP-PXgLqX*-Vs824KV z2rNR7$4x3CvnoT7dvkm3`AG5P!XS1P#fag2L&sRZ?IE;q^J8)r^vHKS< zK$w>$e~3Ub*nHlDHyPz~59a3iMPYLU#LdM?rQ_ zuP6cJKZnmv+E9p|D(6qfXtNT^#WxIi;(jbH?M8g0wIx2#9MkTF3<#sA{h+4U(zeO# z#AX=)Wxs~;m|x&II!RbCU>xY<#h%fTy9BN8=;fk%zm0HYt%fcgg4PGROi)BO2Pl|y zbhiMuY>=_L!Z>n%zCxbx-);%!Kw7D#Oz zt%p}NS7b_wKHz7LRlfGCJd1-|92c$AUPC6*8pyOc_;?I5R_by@#_CjXImnd$N2MO_fI65iP_fp; z$hj;4uW-zBiK^U5;sK>FU7)2w2x%1_k0qajmt96zS}o+~z5YpjXUb|NOioI{n8EjI zx5sT4-VB6kxD0RWn13`1CwKverF7@qA*iel1blh&0uQK19WbsN)YtrBG5&chXLF!8 zlR>KpHwOtdb8P?_FAx7KJEXN}N&4K^3OVxfTESb}eDI2$h`Y}pDT27A^M>d3Hv|Nb z5soUO)y&I(CwtE(h$C->4AfP$AFvRH5qFWtZ9#T4|Ld*D1{}5Ew@*;eFY>{9AiHAD z^HOv0wiJEuwa=M7C$c@Pap23pA9HHGzuhz&G_Owt(0(z#K>Fr>=E7>V@a_S(#qz`D-O#Akp zi))5iB}s0(5#0_&&%+3g>s}5==b*iJ25n|@hZ}8T@GuUI#7Z3AVG}N}0V!$H`kUtc zd)**GYUfoBDu=D?%h%m@LCQF>>Bt)FMs)(tTOMbJe#$%o*u%l zP6<`N3Z$SX=ux2MK}Bd5FFlIW*5uA(CzM(;z`?*h!BO7s`6xD8{PQkCyXbCJ@QP}r zRZtC}?f{sP(k%&lw?X=Q(Le9nUv6bH4SF*@Mcc5WT#%C7$I>A}_qVqm(yYQN846hXe zwIU8}0Og0~i*IxALk>YPd?%>g488>FQ#V0GpDxh#rLc*Ve8)>%_ZOl8OuuIXC@_@@ z_@a;HMaHDz;kHXEIRDHIv_?@t;C1CRsMT=ta_ADURea2`=t;k{3$to4&`e~_884Qh z3_VOZIE>Iaw3Iam-3stGqB52$7{1kCpZq!(A|)yaCut1zDpqebF;+QoOa?wC7#7F; z#oSQ#L759GHwgC~jAQ)n_1^{sSq%j6KB2g5R8Yt2`XN}1=K;Lnq~cyTXa;)w3Y%=$ zaN?TRzr8X9wv4F^IvwWL>d97)A==z%SVST4^gp~!>;t?wPQ|=W3YL@{6QdBt>w4^l z;Sqb^T!RA?QaeT+RV3ftKP%SwTLH<%U6FBd$lfUeS5UUERDK1I{O%|!#W^RGYBj*! zoO8(}__upsJ^5aSefN$}K5Sy8mEpn6Md2qFmlDu#Kg7Z3U%P1gg_C=$^?K{(wN{TY zF3Zuo(LO1<&}ug?nD6ZeGuNqVJ}@Y>AF#y6l5e694O@9|7o~f46-y+paHI} zQ5_R}?-16tR~yZ3V!%`Jej~VQ@7M$L^=&Ti#&OSMt33hug${7bLDaEMZ!p%F z?j~ON<;pKh#DRR?I>^a40HI9+>?F#EynGD_W&L3s$e*l(Uc#@SE{hJ7&9{2eic|ed zTt7s3^F{T?=QUa#3H;0sn|i5o*{T$S7L~)Aoe>4ZEM5Mjp-FzklM62sQV&7R&^j2T z5qPv)oVLrp)89U7H-$4Y_DhI7`h{G;9^)|vhK1ddN*47~cx<)T4pNwpfp34(QB8ak ztcaW!Ri`b6{$$7Xgc=aX{LKtqSt^{V(y5Zo^Y1Bsxn!J%it6>LChdtYekIw}*3)iC z=O#hZ#~TKf(qjLQk%X`hGKTlGNI7jX#6>)cOd29^cr7NMelIkR42@oB^Twn0aS3|* zOVuSe(?Eq6La9$~mlq}&bf<6ttK%wA2OUJ8&{s(~tQw@aqoOLHosHcKEL~k)m|J{y3j`S$Yhqk$9!4XT zLyi9P!J?BX#kYNbrM%E))Z_i$szzbPijZ^%ps9C2!E>Y@36*eIvkXa;2e@Z4+)dQ0 z3baeW8&FD0`3|<1w3i1|q#a zPkkr2nqz43(}ATDEy_qaK6&ktVVet6c~`I7heQ@W$)?hzX-`ctL%BO20XP zMf@@`pMR~&IK;56h2ADmCr@gwEhs3+?6hm9P;3?t+KJFQvsB8w+7i``P>|nTbHy9W zRO-c8E4`Te#4AAO5j;6*Ec1!_*K@=VgtC$}fG1-=@mDJ_2vr6(O_B+G-pB@2;SYfU z(4bZiz#*ISumkN^E{D5)Y%d~yBWsEuVQqrmDhvvJ;(PVRYeL2_(0?h^6h=>(7W*iN z5dEp-A}#$a(feqe?C6G4);k#gw)=3gUl4`9|CF4Ztn2vc+c#-@kjm7aY7DgiMaEBa zH0pBc+aAL03@OSY-Z-dwYQ7%%3>tgg`}Qwg5cC-;>)SSvRnN_2dGJ61;n&-|!ghvQHE=oI&MR7{XMXqIIPP#Ymy!&{0-+}Xq%A1Bk9H4{-$>&pe!B_)r%w-u0dAIK0 zs07EuUjRy->+VkSOY^tR59JIjaPNyz%-6dC?^e9)&bN?d7OVa8ITw^iK|hWepF5*$##?33RhVlrCA8P(dQ6E$e2;GRp82N+lsB>& zisA2qf^LWqvwJQ2-e<609erpIieI!R8-j9yM;(oJ0@*KSV59C=(nk3IV)+0{5x~)i zJ4s!CV;KGaBkjGzn%uJX;UiW=P((zKCLob6MOvhIXwn6wmngkRhfov5j&u-^5+Yr? z^j-ud^iTw(2Bd@*LJ0|wK)yI<=Dp^3bgp?{zCVp*Kl@qxUhQ6Mub96*^Rbo^`}15n zcV*+%uqhGKsK9{1HM) zWbacv&<^_9Hx`4?>^p0c_PIH1+Hb-(DGeBJLMS!j1xhE)1*qPNa<%QA^6wloXEl}4 z+44IlpnIEh_OacW4H|Y&c0badICa@j5%6VAQ;A{&uTUNEtkygws{UqyTjxRrN^1Z7 zIgf*4tbblFqa35*rKCj`(aYy-vmL&CEZED>^;sG6q6|ssoTzeHq}(x!a~jV5sypw* z_bX^9O5n+}Nx3)MMJY>jQZBW}Q%oydebW=S?#NGdE=~GpRCzY54VflmKbqN9$R1y92D!Yxh8l7LBU(d@lV&Ui){i^t;vm_St=j z&_BX20VcT7(BD4%Z-4&FKSTGfp5qZ^ly8P}7OVYc^S^xI7d^eK#TgSD8~doH7sK^i z6ZShq{y(36srczpqtN(a@Nd=R_xAeVHXC}1fs-rvnVg3k(=XfmuV1Eoi!Z zGXDp0yzvWFR6TQJZvODb5cF8*z!`grijS`=%%^4QKLmb%H_X4hL>kQ{dij11&c0(} zQTyjHBs&s%5085cWs019d{O4Q!^cH}POI_@-P)j~SI1o7hso17o1UaiN7-WmA?6KL zXvBp-+Wp`LjV6UCjSGmFu>2unP<`-rHYDi@6U6zL`Nt7J^}h~OHiQD+1Yu&v0*9dSg-I8A;iSR zvHt&qW9NMUSMU!x@W!=c!RZYsRMDPP4Lha~&hdPpn6?w{DaX{vOFy4~**~_`3oz$1^LJ?kk?)Y6HwFp^v8yq5fBI)6bpU znu1RJHq!oYNIb_G^N1?6w>RQcgWWNM6-CbU)08Y3*_Vg;HzoNWv-iLFER6S3Otx}^ z-Nx~vrGIRInfb+wX!P0plgCo1P-TX5*%Ti$A?Mxlzu55a{=`#IQF%`q>My}|EM>Z{ zOi?C#d#)L&V_GH7OLM<91V?e<&V&!?(`0oVtUq4gK z{OP67QNur8g#R|9`ZrJZA419>dYmzFX{^zh+`j+%xZkS=H9y5P^H73!`5?FJ$2!S3 znv}RzHPAvbP4#%*;T%uy&zLXfyMZ zeylt>*n(>4pTM}R^603SIW9JKqi*_)cB=YUenYu^+rO;$rQ+5N7|?cl+b(^WD%93# zxGI&xnfwK)HtKh`3^mx<@9PMs6nw<}?l3GVcE#+=F5FE(=3m1q7-ATJoC*W$t#EAKsT zXX8~kANno=#{i_Znk*pSICNfre=9{>@1MZFJUq5DZEW}l+!ic7bW2DAeJfG#Hp3(ti3 z+&>AQTkojio2}+2OD0?k?!TO{3yzKr`^UQ~N>HUTVG>cJ*qyY?_jO`*ApN@w@uC0t zzt)oCHQPnfisDac1GGLkRdnl@LiZWK5aE9X>4?)yhvnMfb?N1~TSl@fDQRYt@w#XJ z0$Lrt329fB9_|F*O{tDgp-n;OYV`kP+b`YvLA8^o9n*fc(PmbKR851!>!0^l-urPg zZt>`K;MH5_n0|7%KS`O8Q1=$o`JfMLFZ;5mKRiU)*#4EWUy5u+NB1VeA-ru&0($x- zkUflJL8jo(2P-~1r$uzWz%a+KVCtQE?ut>U_(2|Jf+qZ-Nvc}IUrf*aYoS0l%GE2b z;N*gwg=dKO-Z8g9r`)bk9@^;;`nuUtgp!e^7y5AQot4@7k$U%E%I7h*U;O?Y90_{< zRDC~~y5M*O1clcJQ{p=<_^z3?vBI5W3164w@c8y1%Z!f?l>44L`q%oz zt$HfHUx2J&Se(Z@?Pa^6o>~L943u)pk{@WO4ETl}oB*Y^{ zPP4#>|3*(seQRveBrIbrm~R+5&Ms@&BDI!Fx>1Sj2%{gs_STI-*bbUUO02qrjrE^L znpXahTG@Y%%yKqqSW3wDo^&3X%jOpgR^;Z>nJc_flVtJZ|F9{) z0sR!!n8IFs1ar#-42EgO2Z5iM8uqUw@uPQj`>F-cRxp_~+n16K;RbAnhk+ftNtN$j zAI0SSTnOsESRvW-CY2W$B^PW1sN49F7**}l$&P8aK!-!sV8MKcE@Y3suOk;5~xw7(F+pYd*`Nbof37}akx@^97csJpe|cA zAudtzkmzj38@r;9lXJNY~W%bL=d zo=Ql@A)o2lB%KBoNUw>2x;u*d-|)SRdCL2H!==bd?WmaS7_TbCL6{D#TixJT*lx(} zbSDmF7C+loeKfu*H#8D)a{T#>$?e#P$ByGrxmA?%MU&DxN&8b4p-=Z|1_asKm>c~*1 z$v=&RRzhnsXS^N%1rnhzn9eQOQ`xl@GM@MYb|qv`2$j4{;}YIFePvy!2<4?XbL3-} zE+QNJh+CHpu4^VRPD{rs+M@x*7ZXmu^0tKm+qK3Tt1rPnG%gyvRKc_! z!_E}UyC#WMm?LPM{N>R8>B|@gmIocIWbQ?=heQ8_WYRR>wf9V?*1_m&`g}Fap(qd>~(oE)mRLw>c_>i1_X@PEp8=mhamg9G#S&v ze~DOB9#W`b72IdlK=T?oQ6>C_0*jMpH*;u?K*)~o@>G4ng>}O*svH>tN$mb*dghQ4i^)e^22`LNob8i-`X6xk76+w{J`cA9R zL^u3i>q2XI=3CuQ7X9_m+D`$_I*0J<2Ia(CGXGr7fO*4_SRXb_y593}E~_ z=pNXf($BPfThcidK;K9odHL|5y@}N?SJT36($^%h=tx^WrPj=-$XnPnUz0}XHY|?& zOh8sVklz}>^T~HCW1zaqlf28e(4kOO4dy;50u>f5F9bw(xay+%zYAh?3#^gnT|hE~ zfTd`>5;SvQTjvlkaS>3INU;wa(&&WOIth@o6blN9sUZ9fAx589?ovKOsXU4 z?y!B0&d%xS_6=Gb%K<`+Z6NsKmtKcAi&@d1;+tCQ`_8hSt^@^iC80G};%3faqn?$( zDu|LXHAgjVvA``Nt=IDX`gi#z~;IqD$u#G$mXdbQnq)O z#g&NlgCBHE5%R!alBNfhr}Zg_y(z?<<%qI-Hf33*aZ}rNbM^5?btq~7*;TRGx76uf zn>2~oMEb}5L+e67x~}q(Alv+8trq_gcYD5GYp=MlYeS8XRh%Y1J$om#h>;%A8JG@= zl6Dqy?KTuNo{Ft$_i@Z&^M#U~9}3;<3N2c^?5qCO6$pz*Y9{wjXe`2fW%&E;(f7?% zKN(nJ?ZWoQ>*qg6Xcuz=l8H`k8-;F>VsY<$19Y`t({b!9RJrMQ#WSgQoOY1JX}y5o zwriRSP9|p`c*c(f6tFqVW=^a3NxqNTFuacMF4zsSYYD?!4%dzrnVHE}7V$H7GkU`> zZ#AQKZW&kCVlU?tFS5lq<<3v0SF)X$+^OOit~V}4F_B7y#9|4WNm`O3rynMa_VEWa z1MxfSlPk>0Vt#`<;sL?%Is)Xg`qZ6*ckj$bdhV~~CiD`jpEdbs%os(7G?T)!B9qdG zzYzTT^*YzT<(u-RDqK_+R;Wp?WZo{Dmxw(7>^;@czh(inIH2?pgsg&n?;MNdBCKYu zgM(oOR?kkA1wPWAvbYiVo-jScwN*EqA7W@lvLE(kHn|DLZ?%!cgnHyZ*ybSLS44E4Nw&GtbMo`-d!{Tu$eXB&XAJP-XBBAoB=ORt zp$S@O`ba~1qg;6nw@4gJL<{%E1xwbOf>qOuw7Gn!9ULiOP+xl>AG>q^mr%0%EP+wG<$N!nw1q4BgjDZw%Ep2+b!=N*>Y7`1nKpBI!UF1VKyW5|A!8+1woI`vmNPSn}LaE9^`-PZisWXIw0Ng zh=JVEX^K)!98It&me_i|>opGRFQaSO3KQ5!;fi;7Ql>)#)d>jtROzZW9-B0*W7T2D zn=%qY_UQ3zP8ukS)UcePV{dT0Zxb-r91rQK6A#bf>GEJ0K*%u%#F;jYx^t1F45aD{ zW4wt{J_}z9M8@^I*?S*K+?mcZaEZ>;l@0_fWhrz=`2KNxDKH|wcRNsQ~QwM&67M`~bo%C0F0yhRRR zzR3SQaogghBz54Da;&M-ah}z zP?vz8>BO z6O(urMYHY-t*|o=A#^(L$39{=9$tZG5ZT9n&jN&Q zQq^mG1Qjfs-H^NrIXVmsd>%zA_kTUV-BRBnfSbjEYGk?{IckC~H{z?R#ROog@!PV( z-e0N?+-hBj#evsLik9Hsfd%MKsMV?oG;u|0Mjj&`G(rR8C~RJM@N)lC?Bn!2;9;Xj z9!_jtbwho8!^@_lln$RjJpExvv}~BXN0}5lTO|+kAg`N;FX)S>`+kpbWn8NCML!-C zWeQpiakeD7rB^eRY@W%}C^isYm9@D8HM#D@iC{0))^F}mp>yb(7O=kZ!@ zf5HeRLGD|=H@t_=7to~ zn0u`@zC`f0%aLRNVJBfj>2s%idM~S3CHIjS1Ll5{yLs7Yx+Ny#bcBIuvm6=ArMkgl@YJjYdO|GoDXfka#6YVq9agFJdEh=9S2Ob`9wVIm?o z=-Kd#LZzJ+c#{fykX;E0MGGqzSdy;pz8i12)`(J5%U}hYNfz@|6(;p(eA{XV?MImJ zZd~4XZUpvS>%gO0I7UzJszla>gS&O@?+zal^X&rtA{(KF{U(uc#xLTP6TQD0Y-Qn? z%)#tw`V?*jSlml(ML@%3yfA&O>)2z{ipTIqpxa9=(hM;9pz%cHdR}Lz-VQ5qGzx68 zDwgi#w8Fc+6R7)o+L*Tue=*mlKV=#C(N7Mc>|d+~s557nLyPQS2hx{Pib{Pa3@E;d z6;_D1flg9#x1^A!x&iNx1z|5j+{h2}A2z=bG_O#T(5MGa6W0Zq$rG)Q@WsZ}cj$XA z_V_T2TLF$JyYd^)9pAdC<1+H5==p$bhzI;2J;zD`UTJg$(T#qfRtK)Ehg(vJl|~gq61RUd+M1DT$bzIKc8py zreh;6xhIf)yGkfD37koW0`6oeASkQP9DM*e*u}ckId<6jMEsb$9<7_wj0W{X{a~F? z1U#BRmeeht9eeSKl3#_)nMTUR$N4gv6P5SWhou5m2Sl$4{B*blr61(sK@J%X5r38z zqk00Xy7!`*97FcHHjbujH2WSuSf(%BWXlTdk2ra!1azbJ$2M`^xI0AN;;L-P zbsIoQlrt9z2p39nnAwbaEyLgU*kOS(qqB!o$m^CbX%n!-KAo?<(`o$YfFU$Xoj|H; z${rQE-pBX39NGdJjDjGK>PffcA>q9J`jmXhM{mHMqULnN_qPpAG9dW@J58@WOXz}44 z>(0Bwu`?NqTaGx{{TY}r%aC$h6!PRnaArLqphg@P32*+UuqRmLeUUwVJ^4XPYh%p7 zm~yiW{YqU8Qy|xu3$n!}jr;!C0UpA2VOKle4rz7(4E=Q ze8Y&lh0u(nQ5*5TDjh-&{~_)(b^F)!^dBDX5tp5Al*T;(jY|wm&|5=z$iCRuyO5m; zWXa%om4Ycid@}39oi6$|x@#sTEpE$jj5yiLj- zB=Pe7l{mirp@(e9#>H((y|H43?>3@PqlXmV{o%L1d+Wu_trwBm>igWA*oKB46{S>Z zesbWdDx!Jg!rB<81of1xN$$h9cMqdC(y8ZUnM0nNUao`|mk-rx-zoH8_RcM?K$br5 z<*#iO%oxv7@~t3cNKJIF$Z@8M-d-Bm*0Oyr0)jz#c;8;bsAuh!9ab zOAoH#<%Qty*W^FN+Hf^m{g`A<{gYu1puHoXbfanQwOJfkqWx~xs|?7rbPcG$<}!&w z#PgZn6>yF++b>HYdGPLzuaB#__7Tz6U9F78Py>jk8SY=goR#a#r=*WLjL6S-?b8BAl=t`Q z#98Tsx$?^&!@Q!f>8kd!Sq_$n{Fau|xlW>MsrUrs-re@;pkdqT+`}kvtsj4)fH3W< zGKXG=&+-=QewQ?+L5`~AZI*?&4FBQygKlQ4$FcA>0OawvID8!l2W&D z;A&2ae{TQP)Gu(Q>85@Q*i@z|h|Ou}L-6g*SSZRj!7!*G@R<>=s^+u1IlPIe68kN9 z-8_U;H7~+5wNLCm19jc*38y5x_vG4D1fcO*2r-h|&39vsdMc=`<_9Mv|LzCw-wwU9 zm74MX(K+g%2HrIP@~|m4-Ja2nGVGa}Qt8q;jm5F8%97` z%;2zZCMi@2!85JZZS}a@bmi?_A8YI~T6l&&?Odsz;Z(lM-fK%7z zKS=lP8px$&b>YT=+QGX0O7^z{@!Z2~cN_ubjMHs10R=BB#mij7yEC>~U`vZ`DnzYgT#;7Mf9+!S`8~+gcHi%QkG`o@7;<0Yo#tzozAVLLN4QeZT z(qM3q8n)SzLcL`)#Q1~=(^r!}a(2C~nFV%HAboB}C>z&R0f^4Yc#_9t8EX8kh zfl5&I89Cm5kq%7!0c#-G-Stukd5DvFlvYj_=O2#UFqjn z@94q641^lq3VgvR^M(DM6_R_cKwgBi1KboRqW3%RQ{^XC6|ee0vY;9*0vZ)FW?iPJeCNSks^@mG>z?*Xle3J%);O zq^QriA0o$1OM9L}af>Wqw;oS=dwtne{Cim_t5qH1;zELH# zP3U$&$JM&c4t24W;?Ef$hU|`a z$nF`mCS72$d#>C0BW15?$l0hf;Z;xc)y*UXIjYIQhz;)TmeR2^ArkMy&-et+y2Elb zQww4(cpol}3QTrag-G*N!c!x??9xQwFA8nKDK0j}J}yvV1>yJl{*G$DXhUVzN;$Y4 zYFg`6Gqw9o5NR@|B+{3laKY)aZ);ZLUolumbonn<+rxsB$ul-XpomJlcl#~8MXYtO zk=x$sPng!>@RX3)kg|q{h{0YrQInNn&bU6&;#)Vr;W>_%6Yka}a$K6F!d0x8K^P^G zjd^E_elaWAC!n5GJ+d&llKOlxWKS;TZIHTtWZPx!iTa(GJpZchoVx)=uT;;Eg$;oL z(=#rz0zFAxW^Kd#96iW6-jufxuEx<~Iv_X*JGU;MANW01UUCps+DJUBX7zYxym91u zvBCtN9cWMKd(e&Bp)@vJ-f3%#&ssv8yp%@*tl$o31dV!LjGaxn7u5+!GVWjUyMdrl zGi`NG?mMq1n3wv53+MQb-EU|aaNw(Lz1~GM$E} zS!G`OMi=$_UN^-f>P8RH#g$2JmrQ^A+|HRpr*h~pG+?VqB{Lp{Q8!M8IT@v`KjyM? zdFu-gX+32oQ(4K)vB=|GC~4;c31}unZCFtKXzk63zQH z(1JI`Ma{qn;V~sb8*0x~TqWQ0W^Yum zJOdg=SYJ>=YEvd?Zg@t#TX;8H&|hEf-LPbsy|xdR70wc&6*O;BGrJQ>A_!iccR0O! zflaE1?(cNjIc1(0uG6(zR?0$|4@ypVzkf3#`*{S$@{RAh1y`w0*p<57u;i$A*)20* zN^$AE`Gdv2FKd`6F_yt>Ek5^bAE8RuB&i8?3csZ(xmtB6vr#T{zi59_`=MV&?g}=a z60Ykvnkd$%=(=zOHN@900rOv!RDQW~~^xy0m763onqR%bb6^JU#e&%$IgiqKwf zV#shBB~ta)BSsO(=38Equ+f~`lt$s8NC;)#no{9MY;K}TA9i_o;+kt+?i^;9cfrrf>3j+7P!sUET z7?=6a!nD7z95$||bKH$JFY+37pctw(v*6!f$x-}F^H3_(c?yzE$&KQc>~D_6Yi~w@ zl@#ze?tZKHcj)C64oJk5a~t@u%>uExAoQ1wD51hPXK-bCGBkdjORoJ}uWmrrUU!(U z+f__P?bd36$GUq&En&W2KB#WDg)qG?7%YQR_#F(hu}lrf`C~*CeNVG(VSgj^Km}Jb zwsKUmS~fAFsh+bvck=!?vecTUWVlUP9=?sLu1B}Q+wbRcC`!d?MuYRvamJ3 zs&rc-ZPfLc5`Wf#GFAnFHxBQw1O954`nNhZ=iq7mZLxgu!L1*dBccxhu3W-qu-L;M4K9h*uz$bI zkFV_3x>f?}3E?7bR|T!BJfEC!a?yPrH0JFSaU|_f_)ZAMXxXGjoR77Tg)Lb8*vcVP z$Qn>cLQag7%y%e-ma}2FiGp1%#Z`~f$oZ8QtkYxoeP)z`?l@QO!Tm3t%OHcY{GKHI^pn zCAM>f=&ObrTa-t%NDf#+z7SbXEaU_0yXI@a2{JU`&Tn<*iw(@FY_UP>36SkR*?^5^ z8~xEnOCLS}ogWGx5GVCb1wIS9X$IyeSa;sZqI^UZJ{r{HoL>r}titFc?VDfUP8+LHCW zZ3LiEtCB^Eg-sUrh)s~|!Ch%D>rr1iRwLhbIXN-b&| zjMm0`5uW1(qK94mTf%bq?UQ+LP<|ahgGxy9Y~~H^XA0(E);KtH#u-}OnWXR}MtidH zaMui(hboEm=OPW0lL5%avfERO+}#`jK}>b4q_nJoF$s}Vweg33BM?C;qS}yHFyd-l z(Y?}h{E@qRrcj0Utx~!eZl{&;8B^~a{nNi*R4+fJPpRPE?DOG?dE2b0&0zRdgiWG{ z$&6txRdN%Y-Y5wU!)+M#co%?&Akvg?F9p zvZ^KdBN`4{!wH0Us=H|-RDve#Q0w}`sr70B($Ub1re14!&fSp7q^m5KJ|$JTuW6pa zGl}@rA)5}YRg;UDZdNve!TqJGo5P&Eps8#2>}#5KN0JpJRA|s@SdY*?C97|3El+w4GWP=r@b}?zj1|if?}q z4a%{mUin^L&i!W%-q1WhjJnpK%wlWSjWP*a>2PNBg@0ih<(C!~U=Q6DHfmrT^J37k zFcqsjBg0^BpUJgRd22uAEtB1wlcn*~;IG>Vcv4d@r8J$BY#Pv-ZlC(`Xgo)Cw~=8k z@2GslkBda9uPYO$G5Jb`1)rZP)Glh&gUg)a^dfPNg+*rk9Vim?k``?$t%zq95;SHxe*A^Q^Y+T~w8BOoR& zwx4Hjx${R&WyAbneSEK$i)MBRvVJ={&%wd&eesroWM@dPSR}ryI0(;{u*SZrx9577g*KVdZ>4K=!y9J3900Jl<8t2R$11#lPq3-fZ|B!$I5xv zuSg&RWgbl}_-+pc&FEBRG&M0Gvs~4(7ti7g9Ny~Cm|JA^?Wr`D`F$`0U?m2%*BR!j z7)F~K3lpp4pljcrCZdaZcmSC%7378Ii{3oDMdx2cJ*IfSt)du#e+vlCG z^r{9Ph^p%%<>OLEE=M{V7rpl@f90EooT6Fwrp&<$e==DuYN8I4rOokLoYEHVlnB`r zG!-+JbhEgbDp@QFFI?=5ZP%}HeLF|1s?wDCMJF`@N6z zXuRgG1urF6PJi2OyWB&&ft&cAGn5y^#-~TxYw-nWPxEktkj>b7rvi#1+LZRj&saL2 zJ-KBvHXs@%E_|KiLbuK03c8Z;S|hxJ(RQ)EDV=(QyMO8y0`VdWo-uzjr1`_kyfmE@ zk)nK+_U&@p-I)hFQvy6M=w6J!cjo%hfT$VLBBR8H(c*rCbc2SF>HO+2Wvo^5gk?uF zwgeQtXiqBoD6iDOIC&Zn1MS6CrEDS7^jmRyOgJ*q+nE7T!L+%?bTLR(@^A}^W(7JQ zTyq7bt{{Pxk=K1TIbQ)xO5f>~_Av(oa{gX8`qwPEN5%C3PJg$@x+~ zKz*0W_5qQAjV(5pdIL@5H#AC-bmbl_dSR5 zc@KXzRPSrd2b#HlchpMl@dP-ULs=GlX)S1z0v6AvwDkBW(6aO^xvySY4-o!7MF;kJ?NSHXJ+pZ7*a-IfgiVt&q$yI#S! z#M>f)ri|ZC^bncB^hl#;p5CsCoHm>eyS8DfO#Ptkzb5_dwM4c6GwAaD(Yh$i``-4 zbzYT+qx{(|(d|&bs_k;3R`D8UVIYC^XNv+(l414EL7#xlRbmONXRl;(`A7No^>yjQ z5UO|u%bOxoQrwv|@A%`nXj2T|$=WNf;k;At@8D%)OlyZ}mE_~KUGOhi#+yKvb(odm zKZ!4W=c5#~Vi;-4jc)1@{c@w<6x7MC>>pIfN3XIr=?IB`ZY1`m7{%GQV+YDPx~ zJWdbZEAak1O9Q<$4c zQ#?{LdoO;r5C!byc8MdWcz4haWcNrqHl0|PQi!4~c%4zb3Q*Zg8D6rG;&LD};W1_@ z(iwn0q_o1IfVv8h!U*P_XY*nQZEbmQS+cO_mD|9HI-ui&0!Ku%9AzSt3uWSr9lprI zHJWAjW8-%B$tZ6`#J&oPl-pvb&A&PytpPFCR+(S`@!Bi)o@jZp{IqjV79~(`_VH-( zUMrH%qAN*o#Q-iy#IJbAZ`)Y#N@3pM(kiqzKX_0aII_vu82es>!iICZ+PseUx!Wbv zp92|Vdkqn^oHU9*-2?79&rrmfmn~3Z1-a)PfH~=4d9pO+7NzSbE*>~~$?~NM;?9&h zAk2LJqHOb`)#-gC;pkR~l)wa7tdKWJ0;0~+y0l10I}uvhPa*c}3WX(zmfDg+N$y}8B0pwQ@n`6S7bhf;_ix%K%{F!z86J#d)8dMi;zzVDFYzWw_5aQ z7MsvZ;}r{SYZKl3W0jr@D^WT_L{Ab+fXxC@2C6_zn||<$To?IG!dufi7B@kz-vOA4 zNx9@ChJ=6b`V#N_w3}_Xx#-xzW_`ywFANi?9KW zqiuALK6ary1mE`pI>_;Z;cR36q^1)~0cf|G@GLGkOjLx#&(YY}>bX#*-k$BXI~3K} zR=qQgE?hu!NWMBL zlwS$d@zHwz4tRq0%QlKEFF={s`HA)JGKt((gK~=uEUzEU*sUX09-0x7TBby}RV^G! z^t!Mf8QsI87l_i)(6cRMd6$iQ_awmRej%|(PYGmFv>ZM?&9h_kN$PF4sY%LjGZ|Bw zaz5pjHgChPFW-9K-XF{TI@>`PO*3_P027IpKX9XS@VXY733xQa&3iNMsgBP`)UBYQ zobzkryjdtpj~r6jU#L_wbRy|w)mX-)!)^&+F0+LItc$`fEE_zDeOMVKyu@(`WtcmQ zE4XfmNF24hiTT)wDUeL>WGB+G$5YyroEUcwNiA!@{Ux+RuBzBYDSGI~hL~TqW(bKT@ZKW5L)dm)|~fLAS)v*nAmGMy^yO0gTZ9C z4o5`$qwI+J=rldhnN)0QZn=Fq2}S;{pAP96zgOhil`m(UO!q`!yRiw{f)V4rJjhgv zOS*-+?(xu2Q0tq8S_aAw=T}~_4;j$$5ztO%hL!qSWy5XYa+VtVS~C~?ptL)2FI7RF zEgwDFl}s5n+F&jquBs2|k87)AK$WL51M1WOmCku7M_LCc{*}R&bc@HlRackjGNval z%CLVQTr6-HdTVHRa2{7Cm(SoxyN+HE5uS}fGJbvxH8RWa*I)BreU{TMg$E9RZTw{Qf{&G&c=BK=$OH*Q6fKx- z6W*-h1Fc7ro|K9=V4CF`^YylscOQ!uGB^$x$)A_F=(bQ;7{f#-;6A^Ml!Dl0>ElFQE=eWTC<2#JB;yeg%kslDc z=Gg6dn+XVtG~IZA3PmRI>gPRv`6woEW50&CWhdkO1x)g=wwISB^uJc7C5LV{h>Fwhl${P?SPYKD-jio1$0iBJYRo znxlKUy^FjY3ANFk=u)s-T6$naDf$n5{jSIv@l@x^$QuR(K&g9~B1OtNM|Cp2;!)l_ zyY)1>ls=aT`=Jn88<16Gs8R*5O$~d7(UBExOiuokF!_$cJ=M8t>Y!H&T3G}UA~0{& z0ucDN2qoivEb<8WpfAGv5WAlSiuz{gRuyu$Av@GU^{~R&V><-ASY*Qzqft8DU%gsb z5GdnVobG>Fg%IVNwwkU0=rSiZDj zg4n~Izreb8R(B~|og>xIurWl$^z`sh)cX&{9Oexf$h6GP*XPSAH?iqyPMvQz7pJdD ze0C%B3cPdZ>sy(u=vU__CF`qpzyr>J?P|;ZH~hsZM(?_?X*ZM7r@w=%$xE7Kzw*W> zyq_C@4eL3nsdSGpB=0<(;?-R8|iWf?Q8n-THa8(gwZ;2()^v z^E{ZVsW+vjZ+PK;)`U4=^+QCn^zB|{wBE2CC_2WVPkcwSOD4Exm$0xKlFouBXtcyr zn#2T|F1r56KkCvm6`8v-;;>xjBzC^(Dfol7zq@IMJAk&gsdX*7_q?i97ld{Yw%?0U zcqNx%+Q2`w_03GbAcFj~Q26xzmP;LWefZ=2{EX>p?GYt$yfQ&p`(}A}j7aD`SZsOO z@9AD-v7Gki1H!nT7vN|WKbipHMy!eIADv$=Z!fO5J>*aXo!|GtWs;h1RUsZe8Y%d? zeMCeeD*Kf`KH2&Km#fKVDAtv+i;-{|>hKn{c%_P_j3Vx7XG>(4JJ4(Z176D={Rk&? zQ!=tPc66&}=#Q_O@9k7XHa+%KG|HF`=3aZ^r)R4{4wZSA{yDXQlQP!(E2KlvYgN;I zy0<0Te?3Wn%{9sn-eOo*@?=eCVyt+qJg?t1ZJs4?Jx!cs>|F&<8U_m@?4}^7*a(ir zZ}P;g4BpGNKqro7od~)lN+7T_?oNQCJ5N`n1j`W-!|^vJYhw zd4KA8?O3=@fL?bXem-vhN0x<8vaGU`cG)BIyWP)0o$WBK;)_+e@FG4$xFusPMUAJ{ zPn46jPTYH9)1|~R`J6}wnQs}~dF?)dO$~^lTB3ZcI(L>^J)Ookpg9|nFm)|%ueb5%5?8q z%N^EPOf4%?E?n2}_WOHHE2nL18p5-M)a5oMdFAQ61__p)(LD2f)rP7(pRMK6CTp9k zZ31Ku=^R+~N*AibE9dMlYguV(3Su(T>wJQp*;oeG_%e#mv1St7&1cBIdmC6~$Zjis ziS$U@gkr{z0phoRN6xjT-^Ff*$Mr=0$fWT1wl}C+drcsW8n*S_-`WN*Q&749WNDC>w8}2Qs&UDR#6Y=3+IyNX)C| zj5z;u)NQnN{JG#OXghZ!DLbo{_r&RGJ<`wCP;_wt+(W+)i=ZcOeG6B26L&c$^%50n z7DZPen$yCB>Gk7%F6?;r7sw!nnBs(sBzAA#t8j8()3Y#h?(o5Bt2^R*S)9mE8|okn8cQirgmDl{qZ(%hnW*N&B-Z0 z@1=#EZ7?}fHQct5rNI2wDsH4a7^}-L&Y*6fL&<1i8`>sMG`2pzx>qaVv;!C>z+^Ra z&dTz!M0>DfTlu~%aO2w1$o$BlSShZ5e4%T#h-g;> z#>wZ`eP1-9ohQ?H@R5dTa3_n@IJ+cn6rSKeD%p5;OvZTWa!xLgm1rH@>yqHHG8vEo zSWmPHp(}N$|1oxoPgJOVpOqaud=;wrW~NEX4E^kfA9!Or9d~fpmy%+2qW3xkJicfL z`^#N^HIIxV8s4+sGefh!FYI&VfIdnAn^Gfp-y8nH4N7?K8%HzEw2lGZ5O;!J-*F5S z_B@}cgw|7HO$NtGj$TLPwBX?H563_=t@Mo)S^yN@rNch6g$KR4S|_}{;j92IkO`ai zZ;h(YV?AmTygN|@i-+C7WGHU$yf~m~Z*WYp(}C{{p3dcufK)slfB2#8-k+f~daVxv z_Zgf=1nHaH#Qg63Ni<4v$_3%C&zB1o8TH@ip^g`;8X6(H`}Y+Kq;Kn8S4ee!E9JS~ zj52RXM^&#iSZmPOF5f8g(H%_Ye4c$RF?ctUTjbP4{y(uwgzP^Dlk*_?V}CSiG?a1T z06j-22V9f<6EUaR6fi0rp`aYrwav!2->4A2*Rn6_I{mOpEjUHeY08rF?D^xN;!-4J z=B!O&7KFS=Lt$W!#1uR7%f(19!@OZ@^x9KY7UsTVai_p?(zkzfLK(}XhOztQVL;0p zF<8-IASL!E9xGmdabrkMZWcqr=1&CkQHd z?A$_LzhIm`W~%fjJYqjiE~(eSev(9q`K)0LC>gAT?0l?}EUosJvEm$VbisGG8+N(r z8jJDGEN3qCFN3)0)di8dbH(f=DuHIXY*M;h&|wYkUnRjiy*EJMn{1_COAp|w&1(CA zZ3%tSf}4D)WJWmJpv)=#SYqiNcQ}(yx{P|wR4}csu%H^LrCw7XCPE6A@nk&};Ywlp zopB=cO61N@#d0LGa{olbju+se(U7bd;r3A8h@CB(L^z(=^UH?!IMk{?w5T}vi5oKg4P8JA2s|HeXjTAsK101e z9xzCemoafu;_CYiCj=oq?FB{S|qd+q93ggl*o`p## zi$A7o4v}FB&+<-#z`_72r&=d0GaEx%)K5D3BVg4xBl(sc2_~g4(ALn^S3=P@X#!bHWt=i|727)zbIrRAbvMRP1h)l~jilX%jNgG=V6@L+8;bo>3 z;LHD!->LCUwq;o9I{A`uK-jZC@Tm>El9rZ|eg&p*UFXt!oP=2(I!E_+z-XRpj*+mt z61&x3r4K(siG=#tq3M(x#A|mjqjv)t2qvpZi1RIuN1~AcN6dQ7@}EHFbq~YS&<978 z2KO@G(p?y+<{e^W11*$c#=vVH%lZ5Dwg!28R=r$D!Vgy{&UPRD&tt?4Ou~o)hDsp8r|#&zg*(fL?k#L!$ej+91w|fNXiqJb4b647}w}0kT zs?-fQ1-ZPFgtbg&CvBWKAF$3whcD8`f6E!JW9u4BWU-rwx+Km(Ol@MdGF zX?hA)t^$-a6#3j{!?9AlDcS+!qO_XTlI8_s4y?U9uY#U8fHptAtdpNU4wF#OK_HHltD`m;LkE^lQ3;9KN&N#~co-PvY<>~d zQdMkRLjzr>ucJdTB(4kipIWjibz4LSB2wH=l_m7t&8JvS#b+|tFD5N)FlMxe3|4xG9$mm=4 zq)r=v!zjnE&Cmo&^B(Abz_zt0@a3k!3%z-N@&V*`M11vZ9^eMc%TkNG=_W7XrCM|&k-m25vp^}a6c$IX7UYbr5M_PRk;5E z?-K9jtpyLL4#fG^o%!oSzu}J!oXj>iDw%P~YwnBm$s9!zHj>GhrXOrVOl`#elgu^E z2>x?FwfH|B$Vwxc30kTU=GjDP=I{WPO+u>@k^w^JKOtKJg$3Y-H2<>;;37YHyewL} zNEmJ^`R^a<~2k#lkQ?cyOAH1!~YQk^5We+-YbTyofh*h~B6m zP6ChX1KFP019E?KGx&Y%7yXP^kk}^-`;i( zc#E6vSn?!YvSB?$LeA4IAUceSPStA=M|Kdyx;^ku>*ynte){0HVdwVPm6Q=jG z(wrvzu#J3H_y;w@jq;-Ah}Jn4#Dq1^TxZkWji!XQ@kw#h=DCX{Cz&&mZZzz%eIb zEpjMi9ii$)a23dHA$`9DwsxZG?LG6$b~CEHp3-HQ>vI%&P~q$~dlFW#K$>9;J^e z01eDjjQ`pGW`~+>$>nsLGdf6}fZ~gdu5YS%O^cX6512`r7yi}GR4>JL>TPE@DstQc zS0gUk09{|^LAdiVuK`na8;Ng`B=3^sN9>ZKJBd(oF_!R|S7PB+Nd3{0u1bYc>`mu8 zD|Az9v;ow{5K{>y(jvPS62;Geum0Q(~-Z?PV5Gq z7&RaV*U}?4h&v-C^pLvC5u<@%C>)CWn3}{Nk)VZ}tNeZsqY~V;4?W}JYX@fgdIC6XpNbt+fcvj^D<8i6 zShc^cujK9CGsHVuIFqx54w4xOh00D63Cd=ztv2C4g&h0}jq7iN)=?)|hKrDzhY9f` z6IX_v?LkH7K`{YE+GI7+{b$u~ z*n)4Y*18NTH$84_!;|7TaCxt^FZ4k&<)gmHCxHW71? zkqCg1OF2w{enenu@PoHl?O0>f3bsp$sLk}EPD!- znqw9*EX>@eEx|lGVaH(`rkPoe52wJDA=(J)BBdl- zL)8reeG?@v6wxk)v>6AKYdOsXp}X3@%xpse_&KGFz28PtCJPqzPblP&tM3kSCX<)5 zeHydj#&W(`L1WXaFBCNSJVP>$rcg6<30#ty+AMo8p{C`!DK}VR;9Zkc=CeOWghj}Z zklDifv$sTLC|KKuRfFl(XImP;$@OD3Tb0z?{lT0ZgZQ^wKTG@L(n^!x#3g%>_W%Vc z;a3!?aSGC7^@DI=dHyF3uM!dbMZ&bPX***%-WqM1^HqF9%W=qd`e`qUtl4R zXT%>%kc|I+y4`b!#qzxW@kbyBVuls0J#`&gVYksK=CuDJnkSCxTq|v;pYX+c;a97h z-)zx+&zJSaH6a|y&$a4nLMgUYbK2dI*xWDza>s?^71>_U>KG5=&3xMei(uo22IvH!I6^O-K)l^wk<2^#|KJ@04_i0Ty z%t1L#!*>qcGI@^!`NwyXgl{+~Yq|aI$eOaEe(mZdNgPdOAk>41udMs?d0)HdB6 zpy1x&2iS&KOV37Hz4>ihWnR^ogf5wfEbEJ0D8gh6=1b+MRO!oc+!5L;qoX<1;B71;wwTK4=6dfr2Yi8%=-Cl(2S*tR8Rrr1uJF&h52xFD$mAo!FfA=~Rg zpg}IH)p~o4wf{P2KUxHoqokJq9l-+)y*%gJdp7KVLpz}J>>6n8zyy#-jc!mrbkzcnS2E?O??SW_ZX|r!ft%t9)00O6hFkx@)bwNU171s>`%XGv z2oU4NU;erSl9ed;sG=eyH?I*ts@+tuO*0j9hOni}Wr$2p+$N#E4{Db8CsMN42);Y+ zegBTz;&CkvA3Vy(q{mwo5H*cU+!%5}irbOZJ6~je z?czf7)BDjh7snZ524_k=Tbg@Z>UumU5oXXo$j0k-esX;9v%xxLDlaBEW_Aee;t^9p z)uDF&s^*2;cG0XF#B*LStv3KqJ!sbItRIh$+XB+RDV=c)b%DR3xwcr&75aF6fa z+0j&oLm=g3;k5)+tIoV;;cn`iLQ8FZ;30yjhQq)wmJt#^*&>VVk`tC9>jWyNpe`E$ zOGPexw)_kY41Oltx0ewVgbn{_!njNH5pU3)^E(uBKIw+<&O!me1 z#k|Ho>-h*!fCUY$M=vb77hUs#ZcUc2&ZZ2vx|IeUBbl|2|Eblv1gG|I!S>HdQQ5vUMGm!oEAAlli?mp4ss%&2h67E;0&Dr zsbfOEE@@7FF+}kokBifQJ+T+kAQkUj$vKa+mP%C#$im|ZN4J@(@N2u*mGMI~S7Ifk zq+$6#17_mK9SlhfDPa}oY<6XuI)eAcgQ9}Z672LaB;>@GObnSgpE)0O#T%j86C6gO zU0QeeDp{#;++9%CbAIL6s%Q9AN`M9E3!rYzNV^{;J<6p==sf1Ap1hRm7t9IHJ7HzJ zoyw)QD9H;*2 zvSh^5&1^$GR`v^FAvs_M@Qb=2_@~-jVGb9VdN4J9(&&6>A1Xf_i&BLunT%fsZ7c8H zkHz&=FrX^$kV}e$!TAG9ur-2ud&MwlBY^@eEWK#d_$bmLafEb7?#r zdrth^;i6iu8Cw18E0%vl+y{}wwYDZp0)+32Kemt>NnNN@120ye>Flff(!9C;b#US_ z@2^$g+L@rWU?IMExK*s`p#RE}f2Hp~;jaS-7BpzrO3=uH?CoisZb43-l3)w3fV6B= zP^{CuhD<>Kln`{c)i#1{j_1Rs#i?K9SM7ij^z+Vs^2<<9C~DV>-W-(G(!l`^b`TJgT>{zBGV8hC*Hd&HK%4>^6_%|+QuvFK0M zRR^9iNJ`eZ!!-1U5ODgoYTZ%@Q;Nsgm?8q7BM#87&`prnJH?|3_Qn#juaLEXFW3^8 zi<>3wF1Jcm1RM{|^8QU8TQRg(JKptMYIOT zm$El%wu%7w6vOLi+Vw5v>Yr`Kz0Rig5GiSncr$d4opZg{Hx1KWR~K4& zy~v-xA$VvPhk#0mhMbh;g@mqn>ICyZCeJl5NPA$!ki2>WnxEfcy9h?y6$9S^qUMjn z=n9K6gSb3KyYUTSQb%*|@`k}bl5ro%FgL_|Uy<4`Br^fuC+06 zvV2K+2PWximWcnH1_|b#AY;X$bH#Ydc14Lxh>72xdWlo-xe#O zN_Pxa(aNJ^}Oo(^}rYRj#hgGNdp zsF!Q>--0e4wve?;B`eWg&miuTWqK&(;z8hKePsd1Fqylx+*~4Ju{a$q%41!-#n`BBIO?)={de@h#Ds^ zQYqm#*165jI^n5uBNxbL%uvU+vsI`M=wjn7z28;%S@Nuq{#gezmYEQmQ{*FtU~+j0 z^YVIb2IBaaXi~@a()IH`c`O7{b{%FIr90DnmE*-KP_(7}pd z2=@>6tkRee5}rgU7DexOs{8q+rlNlZN)or&*9};YT+K|_D9Ks^8T9C&5O=61e!1ui zEluA!74__gTqLI6Ubn;*nY(9Ah6dSG0M6xqpKgD^b1sA@MpFSZkW!gRsi=~)8`9H4 zJZuQKel*vhZ|-jojAAaWr!c+BnYaSo-CmyxR9hTeKDN zW-Lh#MiGctXbbEe4)u!?fbGiE^)$7|zdK)!x^#vfT;&oHG<8r$%Xidp zzW0$Wi^v4h{@D*f{bLHO3{D&(H*?SB?yvC(+UWpyjoxIDGleoz@OX;EzA}JCF(3d! zJ5XuNI`?@@A%^UK+=I?s0*2{}il|S@F|3%67xoy}LBQE(YG`%5*BU#&+1NmsD}2Al zSdsnflY72~hTj2o@)01#sBm9PQG{=|u6c37n?S(I>HzzsSoEh?tbW^nFZUYJc47DGCsJaoQVNGc^Ou7+1=*Nna_dHd$1 zg=539e)$k#mR0k!*BCM3qBF2YAFIFJ6SZ>UTm+}pJ^InvZ$)C5>@!klLbkUSH7O$F zC%&rUHY1SH_c?X*N8D#y9xI05zIK1ma4a#B0qt!zAi}f$TtA$ww~I#;;)gL2QZgjr z^PZl8Oe+6&xmv4P#}X}AFfN0p>J^X7-;i(!@wwL8A!0F+Ny@TP22#|iKOEcsT+@|P zgV?Q6qTliBgDOh0i+6MPyH1I+0G&Z6&Oe6M2#en@#jDk?nqUyT+#u4%d?8b%-QfQL zvB6EA$K{yD_xmEN+c^{iyS?34*WC`UCce{KfO|w>3p6xxxSalIa}!m3MhNPP^3jZ% zR{L&PoaS*+j-U}MPn!Sm$**(gg{t@JZ;2GgO!_bE9k&ZyOo22Q3&mi>$_@zTRcQoq zRJTv^0Nxitm|*9#W$e&bpRL}K_E8>UA>UyST&LYB#3_maiqePCg+hy+hDC9nNowOl z?n%g1OD&@oL$7=si3F*sI}AuDV13Ed4bkEu%CF{}BO zo$@H{iJfxjQ>L|H$|eKeY-jvC_mCm?)_TrrB%5itz+!-a#o=D#weEE;z@81BLWbxo z?CC}yo=F>>S?OM3iuhc-f8T+;!Jm@6+lF?o8rsiUlqt({=rhkgW(VDn8t(+$+7jj* zhu-wraI+w1k8SqXC0+T@;_?x0ZCn^|GTfFb@S;5eJnbP<$WS_sRlB z_jj2~w9KeLX|BcE`di*#kuHBem|+Cv>JAj zp}^p?sW&-erL#7y#+{^2mIIQ(_=hN1ZX;cY)(JyvhV@4Qwh0}QtT|D0aYGj!c@A}4 zndAjBUJFqpjK1F0szU;qXXuU0bWuNT)jVDXt`d24Y_wN@_kw67ggrg; zMoHqA&6CyDVSB%QmdHk#Y3Qw7;*QT6H==xGm}&x(U-Ete46U`ICJuVP-jbF?BPp}` z-b|ywAO7WAsrN``bshG1<`zg{&XI9BuFsGBRL3n(ISoXi#!uCD+(U8}?k$CmpG1{l zC!C5U;xi6FYeyd{RU-&jtJ*6rUJIT}D@FNqCD34b8C=%lb>Bn?=RVJaV*ehD=%=En zXp}rtTa_0R1x1F@YS=?oDYzIchy~aUxjyT-_w<@yZ?ePP>I1H-m45ksca}rWt=*GI z`8SpI+d!#G$o0bz$-C=0B0d@UhT+5}QarKn3WRDaTy42l5e+_H1+TV{+@3uLSdXbF zbbOUd$BM<%+zb;xo=5q#3wB&2EvRk1YQ%mol+@e-t}tPL6H6vKp=+hSFIlqLZ)zxKK$`CV8T~_0dMnsP zFCJ_&sN&9mkp%j0-XS`A)&bAUPykqq$+Np@J(CP6G_a7wbr;EkfX=7vr+r$b^0nXwS*}&@7N0R{}`<5cs?By+H4N5Pj z`|HxXq>^L}+gw`)eE^TgZakI~;I|+pc~0*>>Ia0b7+^Orc*EqPC8g@qVfu!F!j|8{Ua{7eNa|z8ifU#d9g^O+P zmk!V=gbrY+EKTh!7ds-kBLy&r3V7*Uw1XA*o?vy)+=pTaxtTjGNKM0e@CdjZXluW! ze`B!rYbGH1e7x*9qXjUWrBgHBS?tU%kcep_?BZCwPnjgq`dq4Rn;&r8WM^;^PL%51 zqY3|V8)oNE0q^U=s_*~ybi0FFcB!EoGQTmBgJaVlptg-b`m==Douuy4)O>>%Gf>?9SqkGwo4 zSathP(mI)gG$w}Jd5sgG04K&asFT_dBp1%PY8RzX99~V>xqnCOU?OXVjUx1?+V84P z?cwelTHS9D#ylT*3}FeCZ;#Pio#X2^(RIj2Yg=1jO;y559DpJV5IIo;!w>}1I3kPO zZiP^1-HfqxYNd(JS!di0|H{C>u(#78O+1?!914e!X~HiI7%i_~#A=0Jypxv41NBM` z>x&oZ`XSVd)sV6B>+-x#BBhu1$TZx~GL0Xt{ucx(ByoekXYM<+nK1Z|4ef!m?MTsU z154}H9LNFTR1iLFwmMs;-YGPBOdcZYI)FO33NUO}g!T9M02A|88in++d*mdU(6ywm z$0L!av|F9HW@@Znik8Z%82|V z<|#+_&+k7W_+_#FJ=HAYGcxy>9i99W!-} zi%Ezss&%jv{Ue!$yzX>}&*h+Wqf{<;l4%c9yT!ohLGGep8U2yjHNa*vTV?|zpgtD| zfjJGFT?&vDFzm*SSGl-5(-Ak134oT6A$8Y9AnXUwmYrV8q`Vyiw*ElFVN7dH$$VNbDc0|Bwr-3=J9BzUNmp^7_kdcymrNuvL z<6rXr_YgK_@E7QoJ82HE`;|5t-GI})!NB4%r=G+7N!ZF1cTc%inHUP16Sp5<6oY>` zN#H5^crSwM0fRiX!SU7m-&?B`%8J;wB`x@0knUYjb$K@hEif$9RdwF{K;4%auR^^| zLHC%Y6JVOPmgK-iAf^tf&^HbK$}GsgvIkzkNsL4dTXgozD|LO3cSIy@K4LGY(rc?7 z8^$_6S~Ua?Nl?aI-mT(J_e4|iAy^U3mA-{Xp6XLbu?JFaxp|eldGtPURn6Vb zUw(i6EWRFu3do)NL7WTRszTU+&;6mhcfufEiX?p3a&2Ln=6qe3YHWU>eW4q)rk}`g z+9r#g&8T4`rcMfG$T4@!(aE#$3&HxYBvAS6d#`)h)gre?zcK2p-t9e+ zrUC7gam}4dm}))^s2?*#3GV+zGXItA_Pi=jwtvY#lQG$rFSd(ZxFDOsVz62 zQE`0UIYnkhDL@xGrEdr#4YAlx;@vJLtt-dZd42{9=?c5E?39rOK35rZQ4O&CUdfk& zhv4Yra8@&iHF~i`Yp9ng6+n9pSOSf1K+F>9q&lFM8L%rxt1P-6OL+%^sB6rEnh_12hgK*TcGPr(V;?aA5U@?f)nMqD& zQ-8&dq}=GgFiMBN_M37`OXTT>;F4+Q3dwvZ+oep$ls|tXf;T2oB_Z&z)HZv_INBD6B5(;(DX=79AoT@na4zT<_CWtNNKBachBf zdh;i8S|7VQu2|k#ZAevuZi%pk-BM3%bT{5+hvqOPFN&Q52782#wfP&om5j%+a`npQ zThBy+rnWZAnmZ;30B&s-#l}GL?z4MF?;T|wta85ONcZ%W_^`>)YDVINzaw3gZ3YL*dvW0<4k1c) zVZ)}-ZjlnGBqN=PkJ~hBk40S1GeLO@iLtRu8?;IEi`3rDhU;NrjPktmc@lYe&!fD_ zfvWJUPCf=PN=hHnkH<3@8k&2rpbDIiW@Lojbq@ldy^0k--`mmKvVDUlJRDCKC@i2` zt2IliYcW}CrCg@rmJmCd+VbcPMOGgFu~Z$In@667?thaC*8k`SN-u)JsY0DR(XktV z%Zv2^_&-P|9rK=kO=I(TtDv1Q8eM?;zsG$Dto~ayXFz|bOjCfGd)(XO0m*(>*YMIJ zx{~Y->>!=r`Y#KA?|}?LzWgCHy2V+pM$(hL=Y))zii{tj7Of~H?Q*s-JzPhM!N-DF z-w6+xNgN@_m91|a!Y{Ypp_NQCi`Pe;rmo0)%KHzO$d~JbF1~iGD3P}unBUW#ODs22 zP!VBxM)yzi=KVyi9T*j?AJ0PV>AJTyqn2?l^K%=5GR2b0zrsN`pu5DLLnK%AXCT*j z)y9jdc%CUS2t*Jg|ML~I^C(zlweuX$&M~^6bT@h13W7jLQC}+lV(mXYZbIS+{7Q06 zm`qCQUC{bup0H`m`#8h+O?I%aw!qw*I(4`0KFuJ_r`iFYlWBnSRJAn4fQ*hUiam}u zZ8%5P#CBz^PT1y!qo5w(_w=GtB)^04CoIhv=-uhK7mt~T;d2{C9|p1j`b)g@CnDC{ zfbeJ1JJcYHK`vb@gYQ0@oZlSj5Rv^Ei0~^+qCb#%^R&C}ywj#m=WpSLt{4i8L?>~z zquAb&bs))f>J6TEzAhxth!ymq5)6L2e;-pDv`d6Q!rZ6(p=f6&H9>2Ta_7j{xr@JP zvNaqBoo;@<3ad$I5wh^ZlL_-Li+QIuM`#0_741El`-V_+Kck%6Uk=|-ug#Gz2BiuiEo4_r{e){A6m>&7q z`HR(;D!R_h8u%kX^&$7auKxdr*tGA``901~S#n!f5Gi~VHHtnv1A4aH1H*;*|SgB`w+65O7C#D z&W!_dtTOtjPl8MMPR= zH#mlF-Ma<{>5_bEQ24 zW0N!!pH#Wa_C@a-ac*FD+3gaC6DgxOu?VO?lDX($u@$21J8gk_EFePw48rl@+BH%9 zws`cIGb!m5))UU!*GD@k4V)|XU`@mEbg{WA1M8tU1Vw%dUeB0DVLM?HOm1(mzMCQX z*bd$E-9m*Q-vLHeALKa6gAkw1{uheEp0fEYss-XvRNoU8TaofZ9p7H|4QZafehuFu zMHKEg>{gHtxURRD?3p-Ns`-YZ*)eA%9+0Q%+*oFv%4L_^w;de?!^-+(hjy#Mfj32n5QNOf6myDr{Pf?t`WFLG2^#19FANoX1oUgZ!apol zuIXmr6L3^J7!5{NeLRz0q*4fkgIs-qh%80!ANk83F6J7C+ovFU^T3_8|9?;QAN8p< z&@|8|KJF-NSHQdBpDKt4v)5{UcTJpxVe#6TzqhuhG@iPPV3_I-oA&JPG5qY3)=txP z>9cWwoi-K(_pH)uDF6XYX@BSlJ_7Dxv{F_95D$g6w;MXQaNU+6{f z?tO=gPvqF z^@lchuQ%(PW(#<0>Mug31qSBx$-BXI7JdrX2MR4k!r&P)uZHg4rv;_GCqD~n~en%Cl+=%yi#QeIYj}-V`l3iny!iSmV{m(s9u?br%iR1twzqJ!?==s?+<(^XBcHdDzGbgL1y^nACu(GOSvcr1gwwnjM z&xg+u0qN6#iG6}avud{naabC)?_8wMH>_(d(khvAUtRwCQj(13MV}K@5}HR~*n40cDQKZPlX<#p7{RymXdWXE9mPa2Wz2 z-Z1fo;`ZZ_|4K5Zv6|pmI6+uPM#>Po@~y$BlXz(KQyNJL8fo_jab6ecH(&T-;?1WA z1kF~pk#vsG<=s+?(L#9LttDCck%S`uK3Q~EPlWT)`v9(q9#{H+&e%+`bx7Ku-#PNR*BZ4exuA7Dwp^+uy|pRflm#zGg% ze-j@4ywVH}!{xl8w#x<;CmUTbRXhkzfWG5CyAqdxBlgW@uDAt@1cyT58susX{J%!P zgAM@1&2H-WOrVnQ?a#&{Tm=+|~aLL5-sSw7e=YYR2? z#FoO$OX`%y;Y;mus->=cpMw5qI!0-wuezCXPthU}o9#Xmqu>SpGhgD(c)#!D&-uX` zxHXkY(ucU|rX;Lfmr;|FVu-TrCewYc8wm-0cS9MbaIozhXxK#*b$$KIa(a|GociK<@N@3$R=Uzc zW=<~cqt=JrXNGkrrN!ig38-Nj9`iBdBbdc111CV|a4V1NDa$yR+w3n5A_CYh$8WwA zwcz$QR1Tm2MlKe;`o!lOtq#vWvyqV%7uY*ANRrmuv^*o6_3d&NTm6Gvp@6TXJqdZ|g&MW2c zrM|+TD-pPCQWyJsqChFHvHg&jNtZhc4$Zq)t!okat^QVqx#IxvJ|K&pT}hIGwC+(v z50U1k)LcMz_Cy=U6-t#lKM>9wba*N$Wf;y>~lKp=f^I~VRO5w zQGe!y>!&QO!+q*fh>i`x?o3d|iu&1W>guwjmK5%?I^lC;HeM1`Jss^ff-5ynSc+Z^ zh-Hpv)ZmpH@BiMPB)`^Iewg)?SOghAv+9{c(B|ZlcD%X1jgT%961`#uX8Acb{X#(~2~%l}pv^|-!d^KF!f;g-2ai$9 zN5B~w0`VCD6&hv|7UdiQKxRYKA+=_81-zO9o1!>o&v!1)+x;JEMx>PRH^583f0He9 z4YABI(ThJf;7UfCI6jt*zGelmA?k3!^n42))fRrCHP5llgrLX-%2 zd87G}T*`W3`YpFYuu=wFOGawc3kWpYVb0KKn2S9dOhet)RtyAp>yK7p7`?4Dn7pD@bX!j0G@N|=h~f%VqD)wb z5$Q0}lcY69zD;sv71UIKL9TRK{udSsdVll?OgDTjI}o8b++kW&fN`L964xRj=!LR;Ng48lYhsf8*R;(am6o?Xi(B_vpHnV zF1~emxhbluHs_=DVsb#!aH4iIM=P8xtU(51OVg`-HafHZO;UX&6io^?^*dW@`-x-F z-5#c-R<T>baNXdVJ(v8I&o7JqG^Oqd$L}oTWLhc0!t_Y_|9JF;e(Ab&e3C zzK4hqq-`al2`>-=hdAcTL1Qhrqz(8S$Ea2|%Ic7g=hVT|ZyEpoun)bcE)tghFqA~l zG|m6OgP$3!S#Jp!aWYo~p`do;Aiy`NRySL(%zicYf3HnVuy5(~Mde==641no^8l7bn~Vd-GS9LDXu4wsvCu>kPB&cA?`#r8l+DWEmDdA*KvXekq}%BbP(%)~#om*G zC@t$>^R|scZruHRp4J z-KO8HyTMAgWS2?SsVMP-nNMkc{xa4<*)ok}dFx+|G;%>EajZ&0yiBcHp3D$?V!_y6 z_E_S3tPC<)Stjpw!=A^ipqjEiDH~(`gC~UAuy_s$Pt^RFFR6Wv(O)W428E>m+!X8E$ilZ~M|3Jx5 z9=NkP_+gARwn9fE;TWq-tt^dutcVS7x5-9}@`#uhOX~3|Eo~+**HxyqIQ2A_cAySe z^6mNFPnDp-a#|_*Sk0o~ARGdmR0hmFn!|FRs)6b(%9eNM+`#Ln)yrX75I&KD#h1LJ zj82mBag4nRbLqH+K{wSNgMS|Zm9c=QbSTDPiy!%gP|8IVN#g@T%ym0tuH?*lY4{Ih z*97Jq72FN6m|TY7vEw#5L6DWoPtGYBNLv*~$I5CBhtGlcIa&m?(HNrA3L%m*(Um`t z!!kiBm#H&OK@oBb)zWQdRqDTrsQ!-z`2X;MFUSn`r&F*axRI35fcN8JUbNx#gj&+O zO*trJ^J(uW)@j^g_eX#go^W)8Uv&OT)2dCxg${XL0oRdWHQVdU8QS$>9mDGYzP-`dXxP}cwd1C-ZQHi3#e2Glz{6BSq0HJGB}t-bIYPi z2}pIdnpC~0%(PuS*oeY*+0$ZwM4AeGWTq4^47?cX$1pFyu&yi$jo*rO&q_gyA=%Rw zqP)R~m}^V;M(N?mR#$v_z(+Rct-72QwvrM6Jph97k=4D8W`D{rVvvq>mLXdP3AkLp zkJni7&)r|RsS?IKL{I@qG%6Y_Gy8w^#9ao(cE@dPBL&Df7)a@9P%xM*{eWz?MQCY` zXR@yMjSletL8ns>28}%X+hF1}Xu4a(*QWcN@RQrwyf#Gvy$4>f!TGHvlZjEOgl@j* zPvKMvB!SfK9mxB8AGA`rVzDRi4^5;<30Qq4@GAdo52u?SGR-SjF3;O2x!bsm_F(MY zVH`OcSSAca28xiG+}}oe-v*43l}dffD~391Ol5l^%%;b_Vha(^pEnIDBn+%_nnAV* zL5B7kEf>638_jT2ivFy(3uqE`nqsU0xwX?rnz+72{KMo3x)8qHIp1j7ypLC%4o-Q3WDePQaXty*U*5cFhkF3=kWXf%$kzufqQfWsa63U z|1mcbY0;v=ymdzb>Vo9AB-1j&4MHP_MC7fIQUjD0Fn-w7su8{S@p-4P!9Kbq)|)Z)G8?(5xsjP8e_WigGA$2O!m`2%w!Ci))C&zAcz8|Hx#VPb|S>X zgQ%tE_WA(BR50!7gzWl8Cc!qw1ln4p%VDplk$b*-eQ0cvPD5REd_Ym@*uw#@M0umv z9qg;gJJJcdi@IRBYU4LbkcAd43(B>A&6N1vyMRh^KIJ-Ya^+>1pceShbC8@(v}3Jy z0On|>+SWHxmfw|h1nLJZHdc7{Jc6ec@3-UX%@LrsQsOosE0q`kd^&FFl2;jf7^{XFP_W*Q3UtO~R(&()?A35d_T8>y6c zVVgDABbAHrq^gSR2S|wH%%*Mi=!gi4hs~gaAI^D(QwoKL+=9r6G zue2e!_b>uu8F$L-2LC7aITr~oKNwghXYIWwkKgj^`wJ_ZyFKR#uYJbTIrp;1RlNpS zIubBRdP*^Zf6ssG=fTI@0R1;zws3l_|D{a$SDDuLFvHhtg3@xZqVOiZ!O#2!<>ICNY0nQSy2 z(rD(Vt}^C;?X@lp8e@ut`2ZZE>vQ^ta&>){$~O^eC*Op0XPqdCP_XxxiZm9l3ZBoh z?`+R&C|cn$yi!6xojLSx9;K1kKDoTG6_o`(>Lv}yfc#BjoD{+FUfy8j)p4|&Ezj%9dSXCIZdrCMI{0)qvN{n$67QiS7IWg@I#>l^a z~5`ZNy5p;YiW2bmvHOCVuD+hTX0Rjd8(Nfha^6OvDt8Oq&_KzQ@| z;Af%aNRkT%31{V_)l#(xvJsAffcuzL1V8KnvLoY=pr-dBawibd~VuKb z@P^N!E~3qBsRpqiQWeO1`n?=z{Q~U<29YRtxWXU(%YvZ9W#4?t^?eo5gx-}`t%Bs$ zAo@vznbxVdPqCKneer6ubTll}0lV$n{`559Lcr$`^2pSS_9ar^r18aJune;FT4=QH z$MmsQGf=anNmNc>mkF9wI<Lf+z|3nPr0s7*0(~i52EN0Czvzy#HeXe4Z zASjnNQ0vOqWnk&c0UB!b;U#ukI!ARbl~z&uQ+rQ(v)M?^nUEbEtYt>h59=?^BQM2H zzwhMntsGw+vr*yhkXNG`nhJ6tq-iLq&Fypi9$zJg(e2lYksep1x(iD#NWt#O=C6~ zDwteL=Vh7T(utKN{@_hjWC6TXY-VR~E#X>-SLpKsJ?laI&D*!XlEVmnGt_j%xNucm zU6}%;Gw$1^!oCBE?t!QpATNa6K4u9zL8RTrn!Yq-)NJ{S+DP={3rAr@In+rCqt0L= z?dI1!0!JkA4`&b_!S@IUWl2f}nqnz670qx_p!7uGi&ypaa#ym^OfHYH8*8+(hP2f# z5}g{hilCzNPF4*%Z(^;m@Chfov`w|HvP|}~QmuBT+3NObG!RmWEsyo^doHCfznmBv z7jD&g7ItXtz{lxoGY7afr-RSyqYXWCpS_M=tox!?^XaZimqa3xrl0hLQ1!(``kv<6 z(^gMuGkFoQ8iUUdVh+b}7SoAu^k)Ifu?+&&znX^HzV9Bb*@=li!S}_|sNl#qntHYb z0b#Dn@dlUw8o>jMTR?mtBbX7A=+hNgwjjKIlQs5Y94hVI!29w}H#~lpS~z7Su;GwCC6bu-|}t8RkU4_1E?(72Due83> zj|;Ced@vMW2LJ7m`a7@jq%oJo(=>|gm?aeL00|dOGu^nOed@dsv{3$@VAqDKq6#9> zN1#DkR#dUMR@eq#x#slRQw6l#ny2Vz!APmQUp}g%p1i?^-IR+1os!Jc z$un&XMb3y)yEi>wDClfXte%eS+R`u=nQ(Yx*TO@rSKtJ6ro=Tuxa0k{2X$zGb#tcL zpDobu3<40)s zI;aQAO1V9}6o*Wn-y^s&X;o!$W1fYsHtCN?k)jcJgQRcMsU_So?GJOwWd>GoMDKnHmQKA-DLDEXx{OK1I$nEgJ56=}nB zltI{cDQi1tr!ac9>k;Z=DMe2gZBAUcIB5I>^pcH6kjiBy_-$FZok;*JSWhknQ-$u~ zurLC4nQBixkTU%G(;2)hVkL^Ze4UF0Mmyt)D>X;c93r<#X7HXk)1Z}R>@0&Sn@5{>LeL|9+8eC3nCiYkrS z)|-=iwB^uf%2ui6PvN)T3#2Ge4M5+8;8m^SP4H!NBoKfS10!b2Z;0RC@9kW%rb(bG z0K9skq5My!MbIVJIWlG_>TOCT-u{>$ooc6I#R_LLTpFD(d66!+O6@ov7UQQ_oBMd6 zBG6tCvx$d>Rj`u)jKVESF>xMX(2O`R*s3^Tf*8LKO9z-Y2#%&Nw}U{W|7yEh4?~}Z zNj-UY*9x%MnEe3(eMg=0YQN{WvO-S1H8cjKc4u4^84B@?HbAXnjX`crsSi{fauH8Bt8iC~{d! z2nLCya^K9cLw#c?lFA+XW1F{%@T&hKHkS8B5i-S9$q%Xr`!@ss99iaNL3%vIh#k~p z4TtfSZ(jk=-c&%ilXXxp;rR!xD7T|o-d$8H(-MfMXGZOzg>5_nz8HgVdUVZySP|O%@_T(ii)MtKfnn%0VgL{1y%0?O6n~e z=W0r(_a%Y#aPMar_oj*h5FxB)8_@_syRIMcoC6xS$Tprf&tyQHQt7pV7<75XfCg+< zuns-n7t&v_IvG?b7$Eg3g8U`mSf6up(BEduz}}H5;)9?`*^hu|VH%oEOj4G;a8!fA zfF8oVP7)5A`=EhEI{O)*evR4)9KuaKu=s0R$n6@NZXd{*$mqWhACfHFtheo~;+cQr zB*exD7PZdkWskI=iUqPWK(A_+eGlm7=UMHFA_pjcr^}fcOz+pA_&})5KjeZ<7PLdP zdR6{RDSJ~MgcN==9Vp5D`zKK>+iWsP1BlBt{Xs+&-=N-vFY~^e)E|k*#lkuV*Rz)Qzpfm1(6SQ%7IC(bpk;SxQWP^=CI}AnLgtYd5soywjO!a!= zP=d>sFz4?0T9v|@i^%rX%@0#}A0}B-q_qbxud#7|A>cW!;k zG<-J@HLG53bA7-TWmys3o*gFg+1xTCmm^?o60S|RieE%9kqxg#{hXvK!f zf(Pl-cZF_`p$hV%%a3#cqfTPB0^)6wQ38Uk4gmah@~OUYDov-lw%OB(O_2d5z9ANBdU(i0SpQ z3vu>DzqIu?gJT0(7TCz5_Y1A{#ovLvwboCX4qCb6ieq zddT`M(9v+bVUE7-vYLmvB~`^tGpUAd?Ia2t$&~aRyvb~=8v52M1JeEg80@P+p^~&z zj&z$gN;>y#+Vp?90f1I^J;0Xsl7lbMo@~JO8+3u^dQ2TO(idr%jDSW77-~w2_SZ?~>|&^SfZ@{6sSn z_-N`$)WOPR0Y=amFeh3JKg(qEYzCY=RFhgQW+6qrL}J`US4?L7Iop*aCLUscBNh!i zuUVb|Rek$gxV3va%goPAfB+1ZqWUEU$sC1*Kh|*G^KiW0KIee?at-@#V5Ni!Zmlh- z3cJo|T+i=p&iHXN;_R!rRIgfXTGK$U4qt{Tj=Njw^x=x<$Ee-opas^qw^{?& z5&I>xWVvKXA2*M={M{#jThPRGkx~;Q|MY+_=y+EXEluv~I|^vSdj_Lk#8xaf{}jQM z0KBqk47hbxS~FG3h2v+#4+z&}R~g|;0VA>-j37uYm-ER_2hJU|RPEpP8=w`!$QI%< zQyg4qpy3>_S+4kcR92_A%ZmpKk@t>(msmRgr&3n2r@0R!w?4N^tsKyKWUHx-HasiF zn79=yz$0BH=uSNo)4-lntvC?xW%qx1XS~UYA8+|~?m#=Mz|uSj z1zg>0lH!wtpE6^T(@51Zxb7?25EL_uJ%JLQY($ibIaJ9K^a6?$BK~|zOd|iAT(3&F za(!pJE3m_0);7z+KyT07m_E8{t3rSG=W&eBDFI7(KL4! zC7{m%OKXln!fh>MFF2Od;!~Y@B%l10=q&j6N`Wgc=lAftm4CWxSymO<>;&wz+kI+5 zpx-7RG#fNjRRe9l{U2`A{HHvXPU8=#wGonvWK!9_C0HEhlwl%);3I6ZSuTDV=nnJu zG{E%@nf>+f5X7EE72}0{WY5j;5}Wt)T?UUeFddPmTF2&jJE)v&bLn(}&{=(Fq269l zF8j5ZXT5yj(7ws3kG*pHOd^rr2pfcdkA@)jJ42RHCb_rKi6rptC8M3M03MeF{NgrO zw79HX^mArD&$R3&?U7Vcb#JEBKips2K^N3AI3I6pW>cNR%q7-fr#Mqt@z`qfzDoF+ za?(|FJ3E03tUts^2--5Y`u|?N$#NlW zu?6KS2yf0qtX2b+@n!mev5o7cm{<>UNW!i%3>w(S4(eO#gxGeCV$+yRKCRk~4ocHd zjeH4^P!Ba^pgUg38bVTjmOm}#j`Q>^@~Q{N!Aj1nuUTW<4J@+mwDrjXvPoY#6#P-ptyOZvs%REb+rp5OK;Gs|Vig^?Vhd%Q?gp0^iSWA3}%h5v84VZXcy+N}Z;ENh%pkv&|v!rIMBoFUN0B zU+YKe5p4B}LWi1ig3myg7tLz@f@1M~k5qkqZ&SNq_V)WrBT&)f+3nqj^k)GWtwqi^ z=#fI7N!mQ5auZg4)1ASgNpuFnzgeVGS=#Bc*8R2)Mor+}`&sXiD**m@TMdb79FT^V zdQ!UeD47e>mw$d%1Tru8<|B}8;;)cMq%<(^@kVtz^HZtSrV+Evj{bT#vAEdkiqo4t z+Zc(2_OrRy$Y+dOHl@uJ60L)#5_42`fPcV}2VuJ+r#wbEiBQ_&lBq58m9H7}`Ar|- z-S19fNG8T%bg8cP|kVwZf@19y>tA~C#d^Uvmyh0Ykl{bcZRF3E(p6X?t>~}e@IER^{mU*+)|1OAx|xcJL) zz~N0fR%QA0C?D}5Of#Ov%W2wA6b2|VbZ}j+yKmtDs zt|~#q`7GyoS&Ewm@$5e{W|IpT*_t4bV*J?$A$XzHu@Nv~lh0Sc&2RUAO-N|YA5jD2 zHKl`~{f-)ag)#bq6XfNe09W2pAl;QLKrjYr?)R!~MbHkA=KP}IT5b2ofOukKV78G3 zl1iT114X_}{lJhCuJTDONdHs2ZVi!imXmbq&R?t#eLI^C2WTY>p4WXy!>(-IA1!WX z)Xvvc5z`#2+s`^3uCS5(@ao|?e!lWquQD0DIHfTH)`(EJ5O}+!Fxo!P2Jbmh&l7eo z|6lm{G6fQoPJnEPiQ>J1Qb>ozchln>%OeC`d+X~ZodyYl6FdGDk>2NDGxo?na?CbwtJZ!QzpzKO)ERI|Il+TwZ%yK)YXMEdR&{s^JMd6S}!OK&_+cD!j0`__zl_0W1QZ z!q)!Jy=R4|09>ryj>XgHYpGaN?$B& zIe`BoOlsB=&Z;|8F86Nq^R#_e3;K_mb^4Q(LsOK-Z3i!%g)cGP)fEWGo+XK|r%5KWRWnTjXTz49<7Y-L^WsxGZ$`Ta_S6AM!zAx$ z(=wMc4$Pi`MG$-HNK+Iic`y(r7rR7Yd+r!kE^!MIGdbM6aiyO)R8HHjY_N}G--DqNU)soR%ExIjqLDNmbdyk$o}E>tPC4o#hfk|dJu8+h zX}%N?4`1M;VFehNH>}php???%{U7&B>MbCrEkA5x{*Q!+PA<#N#+JxkyW)Q3# z6^VG^9)SYcdc(uK;j11w)CIh4p5E3%F-Cn0)QuikvSL6o4tvJTw7?Y;Si?`BL^LdF zCY4b1h4N@_;Ok(NvdMWYg1x{gIfcNZ$SP|O8kt}^ZEVELIjLBZd~MU2ahEqDZ>Df$ zjm_=S_jI{Fb>_5vx!p+sSed9i3`+$yXRdnmu#ddk+|VehO1AZ1p1`p-Kslg~*U8Lt znT-HO*4PCuq#MT%XZ2r{Wlxx6B`slum;G4I1!X^@D4Sjep&-Dz-)yndXLBMwZ>Te| zlJJ9MdT!C;}Wel11P>Ha$;P z4bQ^do`MeZ8QvGv7pJc3Wg!-p!jz|5jyZH0q@Z?Hk;S^i4pCv9CwM>ooh(v zQ=OFGUAiG^XfDzw;VWt0V00_bXpR64AQbh=Ib=S;$3 z8=*jtz^-|TmtCYw!2W6B34PWR7PD{_av*7V+qbQ<4Z1-Kv@eNF*+;1l6Isd?6~QHn_gxM9@m5Wn zLWRahDfSN`{!)g!KzQW#5oFoM_kQB_%xQt#i$UxL6IQ>}0XGD{o)8- z8S0XU^oK@+F{LrCe6h#f$O45L{xoU>EUBsi>flFRyTM?1geIr@-apf3Q>L|%XN+J| za9RRo9>7qK)$H<`+Nuf&ET5zFyf@61x)bf{xU2dQlg&n(;9k`0DFf*Q0tK07P?F ziJXinzC?Rh|6Vo*?&qf>$hghB{$kvk=3)as^HjvUPv|TuZqN`F+*pP2BTPwB0bKR$ z97`o-%cx4l;tje;UUy;!b~xIk5LG!ZGTAP_C@`#R(9iG164mc)4|Ac0i9m+Vy}7p^ zBkcIEub(7Ohp(NS2ey)%qIeh1Ypiuv>BBXSa?MY1#jM+$^t;E4HT3oibbQk)cA#-E z{~YuLc1nWl6qO{EQMYv9ZE7yx=jeDe;4Uc<^paUkXgHw|%omNt&wE(g;L8oq^eYU{ z@Qiz^!EPU6LQp7Qqg-&ODv3A|f#ja!Dq`R}bwa%!zL68Km^GlN!AaTOeW<T0XUJ!h)h(7uVG}BSzmTA$*&YFaT$8^f-xA%PwN(Z#t_&l7N@Yoaex0e8kRBE5s=ooMY zGO`JS87A)A1gl@bD`hdMSi$k}U+{CMIN2QDQ?9MM19$_gs1M_*^EZk*pw_znY+a^p z3J4jh(@ec1^lIk->6Z(XRm4kftkeWbA3~MeR=1Ax z;RGzyEOetM^+tmLkM)(u9hAaK13T_jXx>`VYEY0ch!&(_VzY z)Q8)P5>%(@^Du-%+1MG5$34vGc?5Oz^bVtFWZuj8o6Az2d)R$6g9($NeCh-EGwYNx z9B!WI%L<_D@{7!YgN_@We9s-;Y6AdsJ-X!k@>lx6YX+wa<)S&bu7;#vEl-t9;xEuz zy;l`zN{h&^Tc~HOrwd*gF^vgOkadvZdF|hto9-_!?XcHR`w;^ZcH@R5*K4|6-a#n4 zb!p=2u7?9tX>`6y-IzcF!Vc*M5O7>MP8wj+sj_CEL^1@UxO3X772}oS4Rruo`+iSw zgd9v{{%wC6?ayo#G>z>(eqOd-J+5YZpmi&OiR+3sDH48K<@EO9;%RQAZ9>0Y*X#x7u!+Udh3YjrsNvJ2la`yI$wuuRDd- zXd5#lmqc9^WYL8NNVji}didh^BvM4L?`NRue-NrV&lr+3gmkE5X}Z8|bl=3b1S0ut z79Pso&#l`0_y^JE`UzkD?ZQhHTHgk`ik#2Yo1CH}xW&!k5k%fe((rGnN4=MCLviF> zx9^CdLfU$hX}@Pjw+=-nyBU&DFtu=f-&F%X;!<>cEPj;5#VMY~V(N^R7V#QE|1T1k zd+GC|BTF4>mmL`AZf|HBp1*DtxIB}?rJ^cHJ)qYgoR~oBLXXyCc4iN#4jS7GI zSE!Rd)TJ!+sf z-x-;hl)cH#TAY(q_51ht45B{%em8&mY-~zgAp-|LOJaLB z*_-1MyUY>*l1$a=gnP@BO@f$~8hUQ%n}#Rk$SiR>BJ#SZ~2l zCi(MFqG~6>5mnlzenhz@RtJIjO?dX6ksr@nbsrENJ>RKWjp*r>=qNEvth+6|^(%+G zBf7e^&tnm@Qll(@@4B(Fj0+z|TV#1fdz~(ST|@Fr*3=Tc8Ih6snS$*#F-~&|meb5jFh-S}+&#_xVZFTzc)6{2wy(y8R*L$4vvvWWk|L!R{0sD)zgINQ=ln&d=YtHN z$29^+ZQ5*|2_%na^Tqx!O<>|vpH`QOHkm#p2oBopW`*MX%XD?RH)f4Q7H~wT#0F4d zz}dCQ7i9pm{WO?F zJx49}`Ce?ad_FYy>pNq>?!<*;nC;EzqfDhA!2Y9i9?Wq zp@K&AmIn0Nlzj1xW_2KCbyglj{dni2hF`O`mXa$Y0q>gkU3>(aL;2wcH=C@idqX87 zc_=b+pD`2rTk-x?Mr=6nQfJ}i^bAnB)#Y*pS-K@Pcuzra^>8^uO=!lmhwGN3aQ0g$ zX=32Hb-j?XHhtf4$d zya7*0kMzsv1|u+rp6sEd{C4YLCyd63kGZ9nZkVLNDo=7bKM4KRJQ&)exH~n{?U_>{xfcfc!&e+v=O2s zjkZ`C-){;9nC1Cs*Db0K%+WMV!I$YNnG3s|`J`WJv$kfV(in(!3KR>mp=ES@h!f9P zzuDSB_(d8QkP9H{b_{-(^l>G5-2jo86=;;TZ>;FcdVdHO(uPbAcYqNw%kE*-7d7iZ zIEUKY>>i7!jAgCdTQh%~Yizw^AEM@B-wUS^QU=%C(mx2VU?ycnBEjDdr*UwLwaNH3 zjsRrDg1V?Fma_^@3H~@_{pig${0xOXQY8pbwQA8VZuw|&j0v^8TcFKJJHmzh|ciH;J4uAZLC3WQW&ObdZA5cXW9R^E>Zx)s%b*r<};Ub; zzL^`Zg#4G$5AWc#CRaDY2I971|3u>xSM{OL6){jleSOmT9%0%wSTvl6rNK-H(=oz) zY>Tq&tM(1?2{Kd26oHo7cqIVMcR>-m?a1Y|f9~FBgje+{+oa)En1l%B`52{akvGjL zxW_#bdlz~A(-{2&u%uhUDt77c^g_2bzuN6b{L=t_=(Jb~+1#Czv{GwxmvFIdK!zs) z);dY+)9m?*|G0+7iv!Q0J9dAjpce8{%grQ%966#jDplgs4nAu}dAKHg?*tEY@YjlJP@ zqm%u+<7+#|1IyeJeWTJ^cP6)vO?5*$9nxwVNLGTFEI>p+hIepMEZ4u=ewp(H;}*du z9ovFS&8&R%EV83@Oq5;Htf)5Za=U<{axdA~x^K^q&T@`=A^M?u%VFW$1}aD7zXV*W|aYB>nx4t_mQ~&7ZAF%%Ty(*Bk31GzyC9rD7Gh0?! z<20V4=V`!hmNf=8bOF;W(zjpibE<4Uf2kk&FAh_h-GmS`n(w1$QhpuTT{0G9?9y?K zalV)a39}8k^rF^P-e1IxY_r9B4&8;VVJ*0hFX}bDI7w7fPXFsuBfmhhN*;pw$IWu1 zU1P3me#6JET=i;-db{8uw@4_skcnmcVN+BdVf*PqA2?o!Lb~uPI1GpScW@4H!URr~ z3I|=6sVgu;bBF$q9l(qmpiaiOW3$;;e8!-#!EhElRb}258}gl2+b^QOApZ&mDWf>^ zlT;bc{7e`-XeLmepmU~P)$)wntJ);OwFQ}q+v}?{v!YeY1_S=y=Wy1DT!gH@@svEv zkF3+Uui|dr!QUlJdp>J2i7|v<&u|Ww;Gvfy<^?g^QC@+El#vD^W@j;Lo7oq7*l{M1 zn@y{uRiT+5QKJnq1)TCyF}YS%1HB?8@aen#`cJCOF0o6^$eb>BtM(R`8_jQUW12ox z+H3dBJo)*bY7@QJk@bfn%1zOnmReX&_ma-d2uD6*SBmBX#aT-AE!th7Lez46>Egp0Nf_w*Tt$xbLjC+l2YIO0ML>~gXk5Q%uhtn{gPZwHr{Cj1$Rm^juE~X#uQ~J@x(rjaA$i@~vjQxfcG}UhOM9&Jb=5sKt7+?)5 zqWeEi^Zz;A{9q6`EJxZ_ve)Va_p&qakr1yZV|q0WClIl&Yb(R2tN2T(;XxCa@t3RV z%oDR$mLtAXSYu~yx&z#OR^twcM>DI^v9{B&1TJQbd{X2%j7EROhkl#PGiyz0dy8Xe zebG7xj;IqE{n7J6v03)e;c`~4k-MjJHbZ*<_kHd#v&JZN_-fHP`Bg_OTl_)S?lg9d z2p{cCJ_?ChiCWYqSw6vlBlw#oG8t*Hvz#GJZd23{pXnsu4BtB8)EF4Vcf6aeH@`WZaMgnfF{totU zh#@m02{$Nu#*?X%ZNK|2IOX>OozMZIcLjImQ_lmTp6yY^`O2H`tqbzhp~k(r7E|t< zt`8n>=Skw<_Y+kZcGK`W|2>m-h{a$u~EDbxGi&?yR%grU}@f zN~jK2hMNxvZ#|)$=7UiO6w=v_$egZuOt#h8$2C=X4 zPqdVw$2h#eeje|)4|Dq*KUiST*;Ip09mA|0)4W*hf(G8PLr9a}jijhfi4*dAG&$SMj5LtQr2IWhkx`an zkcdnZiYYcSR02w6(Y#w>7vPE@R%_JHiMi#%)v%VpK$id(9sg)N^ED_y7}*dG=c&=M zQ@}4(LJo!+Acr2HY15#v8oe_%1FmH^J5Xm&2t&@WT}m(r=Ha zA!zMtck{;kj@L~y25u{-L=YGa9G|Y3JZNX-yh?^8$99ugm>R_ozBI0Yu($BBo3EE;an`KlVi_5RWDoot~y|HT4)s%XdyjGyZzPN91nLk z=oDAFNYcS!XPu_msv~`@)D9u19`M_=lQm`?T0;!DQ^Gf4$Ycm|SMLu=UA|t<0*?7C z5>3BT<^Wu9RYjK9?n>=OGw{K2p*8PEX|2Dbu|8Gx^vAzEn>`doB9f=eNq43?P61oP zza9>p6yjh83#N-`J4rc-s1l)F%<-zITE0QaGU`vvG$0Wrm2UpQy7$yWVP zRe)V9y*j$WIC{{Cmo+m42suG}Y4TMe=JDgGOKmZWhPm9Ybl4!Bu22AqWw5tqCiM^3 z5nSQS$fH*bW9zv4!yaE*0PsJt^&Qd{Rq=REh&If2^b0i|jb$makPmMLj(wcZblU=m zc&d?~YJH2P=i4#}BXaDh4r@0&OL{e^+4vqTQWJ5MZ`gaaO6)a@5e{_ej8hADGdBkm zUEX7VCSCtF@}si;Grpfa>UdERRnHm=ho6!^bGgOMxj=JdgoQKKGi1eJ{}M!TJ!NW1 zY(E6J%ikx-q)l@WQ7mucB=)Wp50yjQ3ck+ohAi;-S!!2h?)%CcY{8&LmVNy(yNGxr zC%J$X#LN+V44FxZgx6C!tt!k^=vO_V=OHxOSx`+{`=8wm9>C?Wkw(%?)w--zqWsR9N)%FHc877^+H6_cL26pOjc&fbj7;=Tw>W+L3ah#Sh_--(Qnn zgA+U2M=0Wh5qqK@jTEtUhyxq+>)j?PzMNolsuc{HP725J2wFl*hs0wFRAz8LTJ5*- z1>VYtw-&Hxlzu={qfS`d6NhUy zcLE1#Vtu60A6=`l2Hz-IG?x%b@JO&9-ca%rFvntUhx!kBT!$56WI#W99Uf!z$;DOJ z+^kXi`wMDzgslQ){NR*MZc`Q zC(c*OAdw6FuBtT-m}fgmWkFx#n=rTE8AF4!X_~j6p4RacIZ>iizNEZ7Y4PDanKn6U zV^^h*56DW)w7aEk$hKWVpcBktB&1n)#nE+ z%7lu3M1an%{F`W>BnOtvu(W{i*V|oicZSs54@fv;s@NsjT4@4UBU&TBxl>RKi@Kyt z=&X<0Enis+#lh0DXH5H`HE|K(as6dM^HV1yM1Bj&KsK3CudC2Ad&zOjr}-nV#-mJU z;QFY@xzP_Hla)cwPm+6uwetHqAw2-OyJy)wF@tYU@jpjSu!Js$&DfOt_p~pR(E7TL zb^0&SxB=|XGnD*j`~z$o4c~worj~&YYp1OZ+-WLXZ?{|h9xW1YbMeijq-y<)ScdtM zRz1|y8!6^-{68vW)d!mYd)OiaBm53VTcW&;ht`AJyp|WlJ9&P)%Rn}UIn1@_oFKMDi(zMO;)`xNOEXE@j*#aE53I|EwB zlD(u6X(CO8xI}!!`C#V3M_^U_5ZtS8+xCU-5p5a4*A(dxeE5cpCfs(dN>o4T_}c%b z;O2lztuD1c7Lwv{6<>AkFDvpqIn9y6T#Wn@<_Z#P{;^2DK5VRWoT3&G#_tUpl_IpCu_kDK$CZHqGq!Mx9^lyaD&!H~F(pB=7;!pmM|c3HxLIWEu2^J21#)Bk4x zIS%So^MO}XI7F|JjAVmd7?1tbuCs<3Qya@fvh9!!YtbtOaOVEKu#!7DxZ~XK>&~q+ zf?(3isDf0CCw$YnG(vSRV>?d^TCN?B-0m**{kl8$N#T|FpGDjr2Gq^Xjni`|$wje> z6eOhh6e3oW{~XzeNw0QgILys!*cV0RU?r;cwDnU1$2LM@aZ)^ZkT#f+o;rNQo+FQqtacO zPlZaVqjuUUhcStoYaheg-U8v`mxSh2goU0r7Rn=tSU7BKEiUxUhmM+~(`hXaeo4HY zzE9>lDcwjkz(hHM^egcGI~#F_oThkB##x$p)So=|*ThE&Dnv7DTZOkWX-wbVQS4Yw zO_Iqdzb`+T<6#h&tI37L5t00_pGYHw_#Luz~(&_MW91;ve%>FpW!Y3On)?2 zy1yul2B$}}-gVySXJRa_cqqJ$7TEO*<7k-ToQ?nfyw6WSNfrWrnhr0e{CaHeq+Oc( zy)xbjiAyBJ>9>?SdQ-Bbm}^X~{ScAuL@}Znq)gzeNpWUl5X#ja*CXB11FxOwb8tx1 z|2-r9H|zntO$)9;31y-2s%lkgQv{K|qh;Hk9KIpWX*#byZb;gknwA8%D^X9)0Kv~Z z4N^(-N_iT_LWamneMPWf&RjPQE21dDC!<&N0>__S$?-AS-F8@iu|I0^6Q8hW(7yOe z*vL(!_cT}B?rRKe?WrKO+9XMWU+b`YnZ016fTB7w0kz|jGqu|V*RzB(*pDd1%Q*Xy zav;KNzPZejRJ_YO;GT-b4d*;0hFwEsH#-)lu2)LY2L(_{JOzakdv4^>%5>c{)!xgo zbZI+AD^l$~nQS)FCmoX0J2FDJvqyyg4g@ab((%za5W6M_G$b7Y#rPLBc>Qp2MhYHJ zFKl03O-tPHIaUdKd23Kn%AL+;=`#CJd^aAMys9gEAW?MeoWF)E&iXjTfy=L39%zva zgYSu}TFdCDp0%w|YvJM-l>O3YRF6pNG3##LA5nP`v#!rV4D90~UJyW(zY#78iF6|B zG0~+D0>y{It=@TTC-Ab+CN;M{j^{;S=Z(O|E;=jizo7_2BCyM=O-V|3ttUj{ZIPH@ z5A4j2O%wv4|L>V41uEVffO<~kl{6DLlvZf7qfL!*=)z4ok=! zby^^#1_qUR{(MkkH!Y+zVFg;JYZhiR3ohPHb2Q+ufsg)+{EAPL)Aa5LAhJ-}-m6*0 zaFAWS{XzCRtt~}DOGg~y1=t(dmvBMV{!`LI8}9^ zyUSnQ=wu}Ko^*Roz%D*!y~;M!rx+nJtK_K+mD(MSuuUlCq_jN#t@{6(F#mhtymlaw zuMtoC(dO#nIg64MAfrJmB#m6P^sB}&7>A2!Mg!wdc7o)-oYc?TqC*&!rjNGW*N4on zvV&G!#F#y-%B9qma#*Kc<)1xJ`+&kNmAAQEzn7YCW~>V14`gDac9U6YDlPWSIi9vk z0^vOOTQ$IeU}d~BJA{^v8Wi88c=rUctM5^zG7Fhr-6fv3!~LRu7lu%0F_kurvZ~Km zi06SB?*5VJXco|CRBd{ERJ+cENWXn$@YDuGtFOQ zu9A-*GVR}73n1!~X$tr!-Z9OOc1B&CKZh-k7#m=B@#nVQsz5utKqs!JMsI2+2Ijjbx!S z?4w)>Q)Mm}qpD#)wad(Tb-)C*IxUxwP8MqbUVF=YrVmQY+*@463(EC8^hyy+26$i-oFEJ`CIN( z0S@Y!oVkidujxwU;DH8jW{D@Jmg;FZeQ2;Flp%MK;qt}!MqO{d%c#fLlWZr&Iw0Y@ zzKJgZWne9zPv1l4_uO#r+VCpI5wDk<`fpBhT^#6WgVa!Sd@oEmm?IV`OZ(I`PN}+n zAAjEy$p~=V{5i7q_2%(BJa~zm8|Qs|uBu<=nd(nb9_sCEwa__vS%6>66(MyB=p3tM z4P74M`TxB%FDe)wuZQRgT|~#Txr(KUiJyP^S&uyGoyXHTdvi*z5DJNih;~k`o%rS( zJg@f(b17 zf@Wb3F?s$14(&^lahxr@~XASF768x7k`}BpYo24bU!34G7VO z>?4J8u)G7Gy}xBU3aD2!QXIepUkPO$N@ES|M%8ux5=D0mKWbJ7q-|@T|FE=l`bEVUY;(y2`fX1@<3J=(QB}Ys+^bE)BG5|95iVD|XUj_Zz;vs>Dk@YkChTg}sa~M>v)ht8O`R z0}c%hLj&IgUAKVsV#Coobnp~e&wVMO`Y;+s(Prj2&)l#j7WPj$|EK!=C)Hp8gFwg? zFC-*{FyIFXZzXi|aA^n!1H(`U#AQd1xqQ{GHHpk7+?)IxHDPTG87d%xBC(T4nCsFU zG25=f790eAI95d&{IzcD8y0A~sMECw9hw?3%Ogben8&h6hh#QQ?1si{kX^dd@*vsR zNXAs>r>CsG&!l+p>|M%eZ_!U|MkmXaWcyQk{b$u@S+bC#QnXHyq+9Y^l;hhuRxg=- z4DeXIvIzj{Nk|h*(;&tFEy1Ta*hWu^hh}|M?cAyJNL$5PQD5`-ceClK?Ukmf)ft9_ zEY|H_Y5o`J_n%$9)>v4A*Ir`2eEc zZltWRGDc@D+~MpKad};(H95=V*r)vLx0~#~Vg)WRN`;o3MC(NF1%|I9Ap=$f^JF>}xvyIdh=e&3 zKP@9KmzR^rclc2ckmkHaJVeix4W_6Kho5Hay%0<-r?5t+CkD_(9Ni536?RiXT@XB< zZppw^ubzKCn9#XBNPqxQetf!_Mc=qBU+o7qNj31;2LVs9}!N5@3yqlT~L? z(hsoR1SLtoFM5w)e5DxYXFyW?8CUft=Aaz;?P?ooA1lN{{$hApauR0l6vy5jyB?D2k({G)TZR3ydSG`)TWD z@SJp2_$6+knaK4Brp&j;-h82m8|mP-Xrcp_|w zsoJO=el$k7CFSamm)`ifE`02wy&uHyB6jj?SS3sVTq?Ul*k$r5LoDAJ zfca)yY@)~3{XWJ{jr%v!IyVqE$ttM39rToUc-=_5E|YnD1fwzNiPbB@ zJm$)E#Wzhz{6Wd87m}^sj9g8^p-+;>v4AY--hFR+=4NK$v3@S$*c@IC_v!{WC%SUc z4Rx@8YB)2uZM&rbVWAijX(|N4x2koWYd*(&`wn-W!N*z`8<@-_DC$E(M1wQ>B>q%gr-=rTEDP(z< zNs-p1^T)RN!kz}^_tafL^`Zu91SB3DBO!ba5bk(a8DktCPhfK=RsE9$imLmOl)YAn z_K3z7K^jjw_59)!-Vr<^^yntUg>+*zcvk3DGcV^8hEGQ_=N3*fY|3FqQ0XaxKVo%U zJI|_$vTKtvS#L7wjB&cfWHho2z=+L*aG%^!-DhhMlQ7eQGi6^_cL(ZBR#?-_Qj7H_ zcC5X!T!>mkZ>b+092a}pj-Bx8{6~a;!z}8Gp2sma5LGx*pqqFbj@4jtHe)EI^17KB z-QUX@lG!sEGc1Mv??akjz6sFLOmKegFyp-N7JkzZIOj`<* z)0JR0Q>`GT-cyH;O{=g$rlzr1fYZONNPj#&@dbUK=-UKpD~*XFjCnORj7#G&L%WH#2)01@WUgx9rf zo6d!=DvZMpwv>>L(;Up(3K?_{H?|uyEIz?*^Bc&jgyKtdk4sm3LD_Yc56N+ zAj7yXjChm!IN;C9q2T86iq%7f8b2dja78M22z@_$QB^)nS;odBkEN;E9v%gXJMEqb zdknLYlbHlfkY!$e#V4siDnTb|G$w1BSwf{Apj<@@w`$sm)5~ur&%$bB@k=8ymHp^F*h`(@UJrhjZis z-R!W|KiSk8z*zBI$e{s`Fy(&oslESpj_E#ucX< zbC!?MuN$qm5xP~j^;9;acbQUEiej`$rlCJdQQfYeXce2DBKRDiWpGgY=B}e(r6F>- z!EHA>49MC}KvNRw9AbJ1vegccN0{;t6wb3A0i;QMu4g!}PnNdQ6kuRW#nvm{iB3+P zZh&mP-5veS6KwBz&=GTOfqegOXWs+`1O*e1T=WjI3WGG;_Y6)xdIV3?cWek{;D$?O zZth_Gq+F6?GC70=heF{POC{e=F+83e+6r>F?)0JK7Uw9F{I^EsXz^>nFRN)dg4tTN z40#nPlV1UsYv$uXXViy#a9i}OD0F~=RbV+Q{msQXL1Ol@c)mN0JKG^H*h{19HgcI<;qe1Ist znZn#emxp2DBQ$l;Ri|wGD2w} zVJLsG+%x>5uS&n49^FuqBSj{lmygMl{-{q(z-}b0vSKTEHx&PYfj9RIqMRj&Au6v` zI*qbyF44MM%ORNSSvPdO6Mv2nHEnVr!&AG?%?s^KH{zdd>|&ryG?-^+BX=yoqI~P- z!%SsoeJ1Bh)S1VhdeKh6ZkX#|e9r9MQsh=h%T~{_Hn7idnTbw9@RElESt)7x7 zipne(kfPG!{sE_U5fcTX`l>n}tYMT4u=#fRW3Br+C1c;CdWqQG1;G`gqRv_*p%=eo zuZ$kPALcH^^EiWF-N_1>FWqyQZn5kTaqr=BK}x@Vt=W_15pwLmPHXnoRY+mtz?D77 z{UAyQ2($$B_s3ahvxUhk$XM#&BJt{@M`kFvT*_oxT`Tu%C}cIIn;p3u6;qBSz8tK# zr?EI6=_7>_`=k%&&;156T+HXo^bqjT!>}vGfQ2?|q3F?{9)_{CBtDW@E-f518a3@c z*!{25PC%J7njt&cnMILw1*zfe?9Z4f5z&jQavheXA=k9(Y}RNTOme6Ime`M6Y!ne? z+tyS~Kqxpngg&_jO>cJGfe%IDcCaCc@I4VB(`^#vQsuPOEzW0Fdh2m=S~EUVw4Ft` zlbWDQ?8}YqEB9`80k1n6;-U^vBV_a8@{OOa)a#5V5Dn^v_)Zgi0RIRQ9U23{+*r2QDQ!Fl%KbIt{6&9VnP2dV34(x6PKY zcfx|B<~uH^|Kh0y1{~$GYE{BzA@d>u@>1dJd=iG%<5P;sxg>*z#&nG~v-6mho`VJ1 z5@;$V=8^eay+USl=1!oHgr#eQ<61{$u!^mY^@U7)Z|O4kd@2Ifnf9u9yv;ty_C3YA z_2-k0dMnndz_@q(Y(kw`I30dOK5X(GC*00TdJM*Ib{PD6jz2LLHJ5aNR*Sn~ykK*3 zA9_QB=e8U{6whf2w@a`a7yA(o^+UI^f18iJao|W{VWqglD;0E@j06M@c)3>6uiju+ z2X&dO4u3!YYdiPl-`T<|#m3Tapy9Ye4%zf3F)dkLad6+h@WQDA*%2DvhsQwmid*bf zm-2k!;DS=3N>d=ku4btqiC7Q_J`vfrOOamRfMEV}PcE>&+%r{)}XK1$x%T+Sjpk9vvTe zh2(By^##{3z7)$yZs2=uX~S_0(Y=sCz-iPQqSN_G7}{fXv?7vpgG}f!V^Of$-lhJt z4t&7zwE)ZdG2pF_Mv@~=I8?iFgiAWp1FX=I02LKSN(4OJn%6RiEgvqZdgWMS%esBw zf#~*$G&sK%XsN4Y1RM?a*s;n~`QL3KHmgE$i*nMuzFY)A$GvAf(>E8-(`AK957a@# z|A>P+%~?CHSk8Fb%4mccuxKzS-Tp-i*BiDbHdWLUyYZ7!=Kd%i&Et2r$%eWbOwsMm zQb?C*1E<;Vg|x%vz=8grdrouaF9mGv*NQF;Lz2V6;+y>aA%)nq%+(z-1sQ+;gX6RB zqKoq7cdWorZb|IC&KOQq4ElgWf0JX{HJPL(eY~VnHW&AJhpJv{!lzcO-Tbt(Wnjc0 zl7BcOju&4)I1%0b?$6m1-qD!DGpZ!DgSuE_%n3}Bjy{G#h2Y`s&0Vt({1;l@1mVjE zdP0c`k@ACpZGyo=H7CZyJ91hHiTHdh1(7cJ3H5W+;VMfq0Dbkd2Fs0R>Xxy7##GGh zbBN54X*0lI$-v||6&js9=VFGAh78TI@40*#B|pAHXQ=eKb;mLdicn4i4V*YouN2WQ?UJwWj@VGv@^9H!gm>e!- zKcCI8QG_EIIC*LIO3>SdKx{B}B81Y45+7;lr*u8T|HpQ`0WxxK=0WVu+4??I{cQZd zjlg0i@_bv%xw=X%J>Aa9X|q65oE=Xj{0Zgwzp(m3m~qaqW%zo=>Xl{r;XH^#eW{~i z(_nrKnw~ufv)IN?eQ8eXIW?F|g;HE=STbVKOEVKw%>ELa!Z;+rdFwIRyuer@##lM6+E`wt_L82Y->L0 z_UE2$_tf#Yi)gGZlukPI=jsX1Bmt2b+4wSTv4D5o7G^CwBD`=qU=$q9X>qI-T75+g zv4Hq)-uobOyGeZQrazm^^2;nBiNjjLzP}Fb)jP&gm07{S*WO7G5f}d<`8=FMbIpP` z05XG(Y!)qQ%0g7OyNk}fNF`%5m0&^F&wVM=$pTG=bYM2QiqF#WTJ}6U093C^G;@_5 zpU(AF*#O$r=!`*`=A|BC7v|5oPYUTF3_jxtY_jaHgIaPJrLez$kDT5H^7Y@-0?lIq zh2lGdN7-v`4Y_iK=04C*&0fycrfe)`T02lhFj;KOkB5k$5*iPH>H0=+_6T4qK_cK} zl+Zd4!s@n)arC|~dLZ(CH}Eu!Ju#5Vs1G04h?={e_?>qsp4LpdW7W7kV$IcUSYsD^ zzF5m^&u4BLbi}LLWSlR42GCALln)9o^J!RxWmPc1LW;!RZ9`w|XeGJLgJR zx2G-sT`?vt()_9A)5V%vGX~UN$S8CS zy(cJ!nN&6>uR`N#Ekq7$ytP&=J`XN?9f^G`h7-3uT~jf%;n*%$Lixs2uIKJ%AS9L3 zP#qQ?zl}>v5ZK|2&bI4=zZ-iGb8ZjYOcl{!?(|IDcD4qr2tO?(j7Y1=JHdYp0 zp8+B#-vB9UU`7AR@b|EuXP>vyis9-k;_(lWqGla2-5KnyGiKcK5b7jkd0IJj$=JNL zOquk-e&v1+;N#XT6x7bEH`si-Y7Uct36xK7gpqI*&c1C=%W!XB=P8*9(@B zh9C{=dfdK8*hm{d))lS8*U$s7L${w@cc;{x19zL7Z9B(lq9GFxmm7}GfeA)?;*lRh z>ab~&dzKh(6btz+KJ5JAgM;{vsx%Fv>T%*`gR~m=uy+m5pZGSN(*k2(oB)@7XucgW ztQ-aE@~pvDnETIxJBMai8C>iK@j1a3by5yRnQ zl%AcDdPTZFb#%*Bo2#k>s)*_p$WlkOcBulwUkP)j>B#d!@QI>i#|j=K2T z#Q9Pu(sv0DN0LNc)yLLR17I7-^Kq1}mieg^KfrevV*gv5n|2#1nis?$gjMK~zyy1a z%Z7JiFd;ftgL^8cVyl40>zHGTejcYQvLVxhI5{-`>Xip(RWlgf@1pLigFW>Hw_m-D zxo6nW8O+XsXx=%_XhjL^QDofB#`~rTpB$qH)HRm-=m%J^uKYwWy*W2vg>xontuOT3z zQ-$=vB3G)$_%07}dsx%it3s8E*ZQ>-&lpkWOM0xvv17Qgm(5Mfw^!ycSPjpP+j}XN z#}-VGxya8Kq4vrmZ{@wig{bP&c6gJ+{mYl~r*@_p_h*=-i2K)x81ka1k1^t7vz3NS zI;qC_m`YTOWVAhv)`03jN)tGwPn|W?GWS#r++PqGl#N8O2?h-0YnU6%B0RUp9l4zT&=DF&KrqI7KqC$F}JoJ6y$+X6IMXZfHjATYwV||)wQgXOuwvwYtf~Ke< zqZ_%ruN_1@-l7ysbk|B&tJiA8%r9sPdoq$Ky*9DP{dqrckxpWh0Pj+^{{Rp$0l1zZ z;V0s0CzT9juqn50j@b=C`sWSgsO}wv?P@T6oF!_4M=N~DfS)lUlg~4kV{pK*-_MDL zswX%e_B9OcZ*Sms_LthSSYQZ{%QBr<4up2vT1LMzIj7F7A5Dpk7SCC9Y3(aWbUfxW zoj4L+_IW|#^|(rPFuo-9LT7+7de2F9c@_-3>T65bX6(Hm9P+%`yp$eJW@CJ;x+l#6 z^_kwA0GVe`!N#p8Jf4l(kV;*HSy-?)8&992gZ;olS98T`|Ps4(fqu> zG`=xln?^fGj&C|*lZW!KP-C}wF8}7OYQw^YN$!grVT&}jJT9);@Z4=-*U8Y~hF`!T1^AEy{);ZMZsl#2@~X6u+h>AAHkoQijas=;<*P!m|;zO1)^SIfq!g zzXrPSvsv}7SdDA4UnL+-vfpj@>sTTpn(t=^fEd%O4XEJhkWBi+54F~WoFKhWlr##C z=A2&DfeLGJiYO1DtD9ql-;oduPMj~0KGERO?gemOhn;pinjXjp=|l?m%X0UDnSD zWoP2vB$+pNV?Is^yp7is#Mz*^+=0OamvGj4&MBq-HyvYYlz@yxy92m+{Tu)7A_BiZ zTg$sW#CmE40z8Y<^aV8`Lh{EZ{qF+RLM5*vyr`R6!q zT2x7TO^|Pupb#d{=Ub|$;vpR8pxUe1tUH{&^e|3~cZO<~e3Kv&mFXHYNDc=~NogI+ zcSAe7H_tx?X;ByVDc#3#E6UD+)^e>*dnDacd>@&A<=smQ9WuEqv_Z_Xx3+yxL)K*w z&Zf$$&I%2U`>hh&n2dUPv(@U9#(6XlelAD3R1h3NGh0uwQfFKsu;hJxW!PeW$f#oq zov9VEmB0lCX(TOY1AfO5K^$xya4FL_LEd3gUZJ_c-M{R8H^|#E#Fnbvc@vPy>n=L9 zcrK=E#m9&6*;a4gC+I|d4F6lz{NH<2;KWD!Vu{Q@heb;D#unc=v$$Qdba_0juf0nV z8Qv+7^@=4&aPK(*>{Hg_%Ng?1T)23R+Y?RcVWaKF)QtFx8}R6g2%qJb3c3tEZJ21X zCb=7M8C9M{;JgnP#b{&n`P@3|z*k9r5_jYow)Q?rjXe$Narrl-_AbaTYA4(u-h6EF zyiU2IGamjUY3_x_oSWwk<-O@}eTCi<;d*}~9ELw3%SiLcVA3i=WM4LZKUUg!6Jaef z7v{l_vV4!=2;MZt=H4HAEJry_o>%txEymL-6AZDeulqIE%Q;J1@6r9?Np-8!;q1qj0XEVv#`q0(?=OqzG#1S+y$OGac=qOGnfyLdy+O{5JFTSK^W~*ZXRC}{ zj(Z_YHhZI-lwZGM_wr^G_wsr;WsPU?1BdbT9wvE&#olQ1S79ihQVv6KGw8q;s>?s* zzaB2p(Y!l|_}cvGbhw0n5^tDJNLdWWlGEqQPrUPm?;rI9#+`JWsGeyE2(aDp7=OMo zlxLvJgx~b{3}e%%*)ch-t$n64;StR;3wF08mqerA-Cy1wKsI}S8!Owmmg2Rw)w{DY z&5pojwk#EYdRnR-aC&**8}WQ3Ua3!G%5S{VHJQ+jZfpA{?t!r6`wyI62h(NF0;pyk z%&rZO>T#cROy`j@7zonsm8mkMQ@21aPFM+8r5o|7}B5Kn0&E)2LCR zuelpS!`8J#^9sx^i)P#kiLk!?%W$cu0&w<>YF<|1l6iEo?8`C7g7rWF30yH&60R^> zcp;&o$5LU?(!6a_{NbHpx|ic{aRBj%W=}jx{F3biw1`vo2Shv{P-EEJhubp zN?k7zwdB`7(<#spX;4lF>!U$n++tTOu+7y?;~IaaUa_@R{Q|0oGOR!NdEXvXq&fL7 zVgEXp8*<7nZd;ltE6Xf60zyiT{kiEexS{4Rf1pAM5yNu_$qRflep`iLg+(s#iI~_c z4KtewFP(>e-4BerKg618-!Q_r|HGJuzn7lAq$_k)z_bWH%el;L*a6+JzFCq#mYfXr zq8o+AoFfK9@JHOIHGz$`^;h29yBtx|Y1QW!$AXVOW0{2QMJ;yLd{$6R@|wOKyf!a( zC-ufTmdxk9{;d-Uh-60`RV(BY7zNk-po6Gg-meoUd?m7S<;R0d8Y_OELC41&mdFCF zt|x>lqV2>XVP038ge~4N?TuworeP0Zv;vmwqWA|(6`7vDPvTuMXh7g`RBTf1h|;He zo-SC#b<87)`5E#e6_=}7hIg^}C+&f2@r#Ct`06$Bmz2!cls#hTl*Gv34>oJdsD^jgEfO4x(>2tqTG%g? z?*@OslH2Gr{+Nd6hZ8>eEWSU9QKs;I`@08IYfi6EZ0g_A&xuSJ;^sM0==Kc zWL`~dU@0tWlddTuoPGMWs@>w4<@zD~z~Ps&LjW&D&g<|K;17cZd(>gRw-M%NH6Mqq zm_d>iS}edANKn?^Mow2}ey{7!CRBRzFk&2s)#V<;7{#quEk#LX-TTFs##y?an>3+^ zkKv+#68UbxeX8vlSXwDxca#uJ#WqT#Og8c0&JO@hVU|p$8~ZHA7dE9^3Z?AGIm>!R zxdB$p*bu|rfW5MrZ}vCft}1eA(2%MT0{K|X6#mfsP4InVnrCn4{r-1uBF`&iynnvs z(_icfF{&H7L8tI9gCLvgIw7i(2B~#5s-Bf!3;0r(Gugc8l|(Mbjg~z$uJV3y=^53R zJS@!5>FIr!o6-b>GRDPkQ{5yxn}~mOW2*zF(DQ+2gAg>|D7X=_R@%k8CSoY3xi5=2 zs!nmKY#pBKOaD??-bZ=S4A=5I^H$AFePK-dY?6P3`a0`z#CxkXwo;kPVwttEMp9(O zZhP91MrfGM8Y@j&cDeHwPZqyrVHz7Job(6=8FKq3=Gkm+!?fwG9`kzyr-*Rzc9g_gEK9Q zhTVw$GT1caOdscuJ(-AVW)cIDiCoL!WAEBz1kpCV;R-QuxCxdo!Lrm3LoPyE#{86snn~==BmpuyX@Pwn;FO}H47~lw!)MgJH5*>11nQ2Gq-3WxgM{RRBLJW z@mOn62qIOA)hY?oRvK@5k!j5U8pnbyfmUlGr$}rMfMc3nmTE9R5oZZVgKobs*JT~d zmtSmfrn0@J7RVD9W2HIt_XiUDSPiAuG{QVz-^dIRJg$7Z-s44((lGc)ozi7~bD+!O z^F~I^T7~I=dlMaPQNRa_b%TZ0SPfaHH|r>ibk> z=iMHI_{Xoq;t>yN)-HpVW|bx<>+P8y_ck*3XSkgyMR6p;AL=a^vWDY}bJ?oX*=@9? zcs*uo^XwTxPc8))>%xWu-loaO!{Gp_1SCjrWd`?0UTc9Uj^@XLOon-|Xkc8!er&%3 zd3Z}LowZ!9b)^c~VD;uR!T89J|6!W>*Bv_nMq;g%(=4r+-CFZ=SGweoE*W^%SMaM= zle0*@)>A5-*4x!-%9C>IBT<{Uw*1 z7SedA*RKz~HU>Uj=vdNEC9kg_-p;vj2rsFH3tdXm!iQpc`bqox!?1|w<~Zcy(9E%cADXJh6afXCooSeQ}GEH3PHL5Ui(BgX6s^>XMn%SR8e(fPQ! zKflTom$-X0NjWEwz2~$N zsvfg?21N^bmU1OiOuwV)hZ&yvoQm24Jol*CV3e)I3_DiDpc{j2Nxy#Urs#>fh^eLx znYW>(uetkSBKo%1LZ`utu44S;Y873Dh4~nMeog&Ui|Lb@my(NYhm*rxseD;HK%dQa zsvpxmK%+I!ew&29?ui*OKG!Qzq5l!DwJtTaoa4<_-7CtZW=w4UM65`=tOj}RnX0wh z#pF;z_czYJK-LZK6$Sg81~&6W;#8JHvFvOB)!RG(!hm}Z8#lbs5r{X70#y}jO( z)+&~LZ@IL=B4l;C_7Hy%4KDzUn#$%q`7vDu;0G-#zU3bSB<4>LE!soUt0hg>+d?B) zisI%>?5NJ?ts#6jDVWf3JwIIFv6V5izw`uBm>o`U1Wv%8d;*dYMMw<6v!~fNW{vas z`-=pt{GCoX7E3^1Y8sLXWZ~`4MWz4`Z|iyg)cpoKMYQ-KPLN;!SmGS z$;x$)KqMv;IF(rD#E441iJUKntB*{E8uRe++1?SMqlxjQ#DnqJp_s-<7+gp8^10gL zo(y76rS+o~=sZVqujcfK=Pi;3sshWIGI&)ZCFnfMbZ@T^k*Bp|(}+DeT%?tHLCpNC z&6n{V)@{=TfS*0!^J`5CMX_EQD-@QUct6Hm=)qGRs7%i(vBS2yzXV*m+|%pa8t-D) zgM73wb^k!4QsfTtj*OlCGrW7li`cw!RS7q#KFEBDxiOn+uN|bq@%c4IhaExg!fdue zPcS%RQ&CU+w;zor^ErYnQQgX~L1nllAG?n887oX*#!U3??FQ#Q(FRJR!nuy7J5@A& z@%NT-ezuIwDW~me_}PwWv_O?rDK)m*?ypzRRlGNb$MtLpDGX&1Cmn$WNK4kW{PlKc z0m7Vld_QreXMVXHOYA`Wyu5sq$7@PU zoPc9@3Bmbs<0z^*!s8Rl_}p09^c#33%RgQ%t~PmZ7Aju=OORwJC(MtzR?+Yr;F+rN zzHXaL5cQ@9jo{-*D0Da-Iz9af|E5altq2VcX%UQ+V79`RTTNRQbo(ya0?(l7bt!5N zJd1~4oO7!SCPo9&F>i3fkK%mCeT#JTwxY3Eav!^;f5DOxKbAyFWsnieg%ku*Dc@h1 z<4V+!>x$!pK0f>oeNFcFv5yM;LVEqdU(z4S-3oA?bncFr){LPE4lkeHBKPC#(>nNx zYjLdlI}^)YC`4Pb(-Z+RC9wE5ZkICPlrpXAh`^i6SZ<&-orxhBWp4Li)fa<7b?m%^ zKIjQlif5?KdDOoErcp8s+{8w#@p;9q*P7n>EPSvGSM`3WX1zV%Sl(MzB!)AKa6Q#r zEcjwg{NFGx#lRo4?P>mXvuUaRxhJBe`cQvMJ5-nfNML}|=nq&$uWjeBWEaPmVcLEa zIy4l~MYJl-1w*ULl2ajG|0Z#2ecvoLCuGFanx%?$tN{8o3wjEUG5p^T`!pHGUVALaxPqu$I?5d)}KV%K>!9(>0iPPiaFn8$I?8p^Qe4mPRchMAv zloe}tS;8fe#p!MPlICqVm7HxFEYQhjy{Kzl?GQPJqLM129~U-sFUX&*M#FUd>+YD7 z!VN^|X|A?}C9>VpL-b?cM4|N-V5dgGAVjP1yJ3kuJa}?Fnmw;MJv-EQXC*GG$xg!l-=zw8>W^hcO7RPP09)q;-<@dztA@;@zC(_NY z?U6cjaC_b+%e81VYj#|P8Phk@SSSY%PZ1N;p`1Er3V$jPr0KPLkd%C9mNy!BI4nd` z1@;jxj1%w$69BocUa8vqnA3oD0GDI^M=QL^qxG#iL{rE12*MHcNnm)6S-4|eM+LKa zG`{nR`qJO(5ZKFhR!JLdp|zc6h|Cbh;C>}gIstD*51a)8p%Do934^diyyKa?n$@KS z!asDi4Lk)Qz!r5yT%7T21W|gCk9Q z0*I}hX-CMw3rIHUhMr}5r)Q&%R7OQO@4+ikP(tp0Z8O-qw?1lvFw7j*dP)*BcWAgkTP_V)?1i50O?)Au575Fc#jU zKcCyJ0kcWZuCe3?ghu*vHj=Z2=mDt*c1`^8@sf09rq8@Crsx)XT(z6eqp%wpm}2fm z@Hx6Mcl!NNAAZ5bhKK@CrWLw`47mTCOQD*KXmGzrMECVCH-#+zxaitF!d*(Kx8D3^ zE3J=VF`;gD0vo~h+^DE0mO1GZ#FVnd1yP~RDgFM&y|VYg&Y_W_^JTiX;<}{)Ibe{0y#m=V{vnGT-+<`( zmYK?&783$HgG(hpfttS7E}~yn%I`?ER{0BDDdsx1*E@*0pUYvf08K5AVBsD<2!n&$ zGT%t@?OgV2VKCBZQF)L0p?K9roLY0+Ala|qXDf|?y*dKfaJvuDBn?@(px{MX3s=v@Z-(ZF?uzfO})9-w0F@whxCzT32n#yswrKzw0LKqRtk^&T2LLX6Ff_PTS% zpG$>CN74dsaF1T)pqgy%F@Gyu+NJ!Y<&ZWuTNKSkT`g2)`%_oD56ya77i7&j$aXR12PnThU!JB)4^->v-#RQ=aW+gJ2xZQFcO{)4i z>*!chT6~Z1;90wMuwzYjLoCZB!CH_%s`LcRZT@5^;4SLV|Bb+N62-Si1h z!EH3%o@xlJePJ-U$KflCWz*6X2LmRpzV{LYmfL##h33{Yy0%m&X00nxowgpY5^`9~ znbb>=aHqQ~KDXyHwf;cz>{NlVsHt-U2GjyT&cpfqQ}4~FsrIk}i3 z`p%Ay3nr8h+#`vyjXNrp>p}+XjYi3Kj9zYPt-pnr4Foiq9SevdA$O-uR@z+>c)Kz) ziR9K|_2}J*02;3eL)Jq6V&bQBh zek=tjqNezlJ)3{pq+8CvH>c65`1TLO%82d@kPUy0>@;z<8WhpPn70jihRcuy{ayMw z*fFFEdIK>x8xfBhWbQ&U3I(6!!40p@5NSlH4ddsB*JT%A5jHd+W&SLC962|DQ&N`y zgznzb%=;zeCdko4VKn|6$+i3IeX-Lg`McJH>SUFYOTP3dq5~lAUG7hZy!ERJ@m-3W z0T8nTfI%T}sCRJs2_jEv=xDbTUnsn*waK2cZ`IcEp;BWPu*d840wDTO?QA!DItnS4J6-=87y~w`OC7>75OYy%PjHmC2QtO_p|6KD$PmfDpJ-4Zt$x8_x^alzExeMG;qx7W z*}xFVNoIayP(uuAK?5|Lta^7cf=H)L1`k-^x&0XqvB9Q1yPhnre++u>P41sYrcpD6 z7j9u0G>q!P>`w8R$V61`c-hz18~^gH;HIF>UAn3&D6GE|?4V1n@CHsX+LtLXluD_D<(JDx&{8f5 zAk#qxKYHFOObx}gMn6j@u1YyFWJVCJjZCW8+38{tHQ=?oU#DWxrkbc@Ctov~whMMm zqK^C^CEDh7zmj-BvR-dX2eNbOt>#bgvA*AZfmdstzMz{cf5yz0h>7CiiPZ>7Vcu68 zsrQ%yFabD2WSo$$og8)-eSf(1{n_G~QrR7ie>TTI~0AZo0HN%x)%XV>9S`d3bzHk)inl=FaE0}K|jKqLJ{(t;SL|(gD0o3mLk;R zLG7M=-Iv(6`g3!>l1eQD} zy{XlON4Hx7KBVHRn~hTp=di7=cXyX2=xniFg1e`5Tg(V#_VKlL-5o#qa}V{2k~~pI z9zg$24L7%;Q_Q2Hin|?{*)PUDDn%e=G>s6K; z(JZggs5q(6MLVN=Srea~EN7IE?sdGFF>+0>-Fiouu1D4y(&?lgqKn6U!mqk*5Kea| zsA70Gg}x-O50~v&o*$h0J-!hh%sk(uNbvFb-W*JUp7yhFBV^ATd+{m^a>L&I6SWy3zzMkA9LwNmcW z5ZU$Y@o;OX1NL`AyFMR~sBU^!N;d2zV3waG9Na)1b@BGLgk$;o0gG}c63swei(WYB zL&>-N;#f=!3={vQddmz0C0GWF4ew9n(usacf9TK`Z>qrrsMFYJ6QV9O1$OGCQKBO* z=zLXNo~lWs*~vKTEeFpJq|Pw!px*{sOq%Hc)yAhMl4wpPv{c5jCgbmbq_|#t;5R%& zLpX%g{NV81H8P@<)XkE+1PNUEAIrlIK6qTtrxj}`Q}eGe!G9@ZL%GR@yz{(o01|`; z3>0=a##~5B$-zUTsKB4Ih*N3iYkGxJVhiT>?(VL4!Oz6qc?tED(mC~+ONCUdJ#(kg zVAX_D8bQ0}+{KEYr_XFJaDyc!ru;;DtVH2_gffvH^%69i!e&3sV65WNnYWWd%6Dti zplO|xj%?LRL?Z51QT^%iV%^cjwh>Rt&w-pmey@!P)%U9Pk-Zrau8x0z2+d{2ogsYo zVjbmE8A`WIRsXDI+S+u9NPQ^_@MzL_*mk4PA0^Y>UdFvErk_PFOK>Tpt0BpyakIV^ zx>_lHxR~Wu&TY*yVca8WU=&Ws*`=q95V649zYU)d9YpnJbZlA(z8+PhA_Os0&HmXz zf;4}pU$93yQh*{}bB^<*sK4%FMZohfNHPpFK+cEB7KHX{*42yTjH56rlftCAx9{Hh z%wjnzSdr<19$9+X-PT=J$=6z`jP`9W^ySjh@<`rx`w}G@v_ z3@xD{vzdIZYCE8J9N(E`c-O92uW(jVhk&D#{__0;c%d92!)T5DcExP|2!piuOo1MA zUnzT`NRZMaFwFU@pdUj69cnK`e1T-F8|CZ6lS%ep{_=R9oaXh<_kk$Z0*)T+uL?n& zeDnr#7Y6+2n;~nz1c_|MagaxuHKA~JLLtK1nT#!{k+zA5GDn*^ylS^z9@|LUq!PYp zW5ZOIIDZRfY8MU9)Xo_E(qQwtfHIG^{o|78IDSef_K-{rYV8?{agB0vkWyX zM?o*AHht%Nhl-+d>ErJ3oN?Ku^K71jZp#gFYt%)u;Xsr@5X#s6ZnDZXfnP3*tZpG3ny^YjcJW$XeFYrnexo_hLf8p~rUms8Pi%Digbulv{g z^yNsjJci7IX>1@G;{5h`*_ZBf(<<27@LTv zsW-4pl)H@|Aap~eMVU;;CF%7#6iAti+&MnBmZaII%f5*XYmpL2i}cLK5eq{9j6_8xSyp^* zbkXJ9$FSBjCKKri3J8&^wY{n`_1if3!0S0sB8_m5KR(Q=6(r&WQ+k`xjI7FS2>Hp8 z@^|J>r%_ZM#n>jm%ORvSM{8VBN2?BHfL+l5J!{m^*{|aWgtr>x>3&=(M@O^owo$1! z$1%sSs^Gmb zlW(F{Tqx>$n@IzDJ1um8F}dY~#?erCvhR<O490^fcTvS-`vBAc6VXGMYk(eRb)#Ev zd;4-du~C$i$lULXD1~`2fX_BMGKP(h<#9(~`03NPm4k_jI_R!5CgWi-c<;Yh(+S)Y z=Z_0$7jzC^;R0EFi^&bWCX^1}fw>0kQG8S*r2>TTo!6^fN8@#m0Vh_?k8J*RR!hZ2YR(K%s(sBzTZDSDM5HeFUGkkGAXzdcYu?=xs? z7l3RCqmS_QuqXXhWLwpi$jGjVws*1UHtIrO&R*;fMTD%K`n;4oR?jzTdc8JdxXUIe z^1bkVThddQ^OGkLe1iVFb*cYD*H;F`*>1}Q0)gP}7DxyV!CeCccXtTx?jGENLvVL@ zCpd!)?hNiSz~Fl4yXWk^&$(5%>iseEW2)x0^*pP)S2wO8Q1-8304 zd7s2SCE$y>!_y5Mr&AZZXAS?{!K#dgRZ_ZJW!nIce$>il_MXWHm>lHAl& zorEf3q9IsZgTFpg?||Fap25hhY&F)+$e4Y3e7ni${ltCK!$Kh=0C< zlGav^zrvjB^&f9-M>YFbSqC+giu7+!Lo9Yj9&|alhci9}BLEw@{}u?*kmhYJgx-9A zh0aMj)YB@u#gTO-nH;JU2e|4~kc6^gqL6FDjKL?RmAjntCt*>etU46zi{GMraOv`S z^^}al<_BO5!ewCMK_$)W9S@MNwDN0p#%)I(EQR7mmVY(h@3pH#b5BwI;gt_WV5dTf3^Rvc2n}&i$E|h`vsx*m+tX<)l|2+QH#Ia87CS$& z4ejDPsMtg~j;x?esldmIZLibk!Df=+FK#_t+NMs^W9*m&jRHUN%2O;7G@~i5HV_W& z-0LqJ!iW2M(-XSM*QE=k1~d9W5q%x}O2+?CV*hwqyo1?CP-*@9>W z(|UI_D~vfdlAGbra-ro@)%?3~dF!xafz`NRe%ue#4}SAdci_B!W?iQDj9HlHE)mZq zas6im)dRyeI(YllxWC^Y!7DlYlQ)MUrQUt&!C>A?4`q}jZ=w>T04J4!C5sEb!nQf# z76b&4q>Mg1neY8`j`mIZ!rBE$KV+_1y>AI5G82`ICO+_>JsvJQGdPDHY>+AdaUP2? z<;C!;zZydCZt_1qF0##By}2T6l?f<}dVq#96OyX6cGu)6ZX|Cc%HBV;y~MXTU32C0 zf5xtEZ%rAt-&;V{7rKHZ#iU<+9g75VjR!+BV+sua*24`ubj5tw<$-cActIl#J3}{H z3)j1&Vlpf9&8gs?oS%+C!?}(-LnT)8+63WV)p{Kk=_=ouuA4C$2Zzws+?ReE4hN!| z0g_OoKM7?9$XORejCj;nf2W{CZ0+c1o2I`-ZvL;z_b*!%a#)yJ?r=CjbnB%pTV2$O zC8YW|DUqfk*9d-UL2jGSz>#~AJ`48)4l#6>uGOzqJO8T8B3`DIz~OI&SIzE&ji(7> z&TLf}zFW`NSIxwBk<5R?*~$ zp-NACZMWEOf9>(z5KKg%I>tU$?{qr`8=I%s5CU(2a)sL8mGydju&#MieK34FAtaiH z6&hB5bGHGKSQOox;e$Fcmw`HM*zX6-kd~T|CkfQAw#%l&9is(WVxiMCNh2BrkePYp zEdV{rZhP z#j@fYt2Zz}TfzUsPKSQ4M@_ek;pQj+q(RaaSyTiUL237E>W+DKlIk6N5hDy+JL70Z z3!L*VlTjIL${kcPfQ1vbSYwb&CFzE}Bz78+V}N)xyaFA-+6w4xBLN%-!H+4PuQ8xJ zdhfe=zwj!f!8ZZ;YpUXSj^Lx$Zg)GtuU}n)Bs-vFjpM}0+(RT!W*A>mj-YRviD8&1 zPNZ_f9+Z9wW!mWU6pzAJVA~yz!q0L36&7^0Jvd8(+?F@}6iu%&Nf-&;-qMpuLZfFw z5zuV5k_J{LH@T~O>}i#M)g@Y-9A%v=g?d-x+bk)<--$3A>cC{gse`&Xh$a@QbbbiE za;xdYbPR=}*|1I_iv}HUB)fy7Urtt!W(~oZA4%TS4*Y*9Or$rkc;^7V6?M2cU~hea zKsB#{Zw>Bb7fPx+1VW$lpmDHRh@Vhl^A@K%uJz%R;`0M2_!Tk583KM_!VuGGDi(6# zCeeu>{Q#4S9)5&@%?rZ-2?RIDuC6an*dh7cK~s>{_*y3X%@-Xe+cw}~Fy8&=aK~U- zd4Qas72=?q;&|w@&g6(j5+c}e`fx(`<;-9SYg<;E*+h3MwS|wg<>XWkLb(2>0&I(2 zAb%V5gRFPkzre=BE6zC1w;$?qLSG0;Dvx7#Pa#?=E^iMidD_fq&5+dUgdFK8inkH3 zkk3Uv8k27;$$Zy02pN{ZVgkJuez%-FHP<~eWV(6U&v2@dQ-8$0o?_P|TUzLpYya|5 zY`9;ao*o$5W-f-RInyC#KRK7!i|`n@Kfb>wo$|AX;Eq(2=(jVeGv!o|k02y^fa`4M zvV3%p#%2W4Xh!JWxH+Xs?wJwZG|E*JPi;;VaIN8`11}|Rlu~%%G^4{p+$xLi@5kt# znv?|a*yFL^?n2xV^m_b0*1h}*UT_D4aLl z>F(TwJe1FunI9AHdYXD~vhhklKl(t{c+lN;JF5_i^)ul%pohKRrlLBD456N&F_-UB z&|f9<#BjicVK#sJwp0Ewm;7IMJ+LGrpy^D^(|feHcAPfIYIT?j^EM{@$-ESX`n`szO*6}N2eQm(F+a}X<{a6(X zdNPVn`lg>ltSe$<*EY}4{7JfE78uw$FY5bVWbD;hrW0@Ip0i!ZRpX0&iw!0wt2Au~ z36rr-s1oGcM5|A-W-q}%(gE_Nx4h z7I|Q@7bJ;He%y>i1=9O#p|9R9To;8{dug#fFv#hBvf2_MZ?swpgK)v5#hR#IooVi} zYDZ}Q2@q;-9nr$HXYzRcG2s$ilnTmCH6UXsQ`EFJY38TQ?=(^~S6hn0rMF%(Fy0n` z#-y6h3z+`qdjmz2t{S-JppGo~1*_tcj%V@|pdIe-3q7;UxG;bGs3A8m;C^0MP1Zxd z5@4@a^oEo~cecXY=6pixA~ED9)H93>vnQAog}%$Gb@}zvCw)`h6mT5TykvKyr&&Kc zLM?I3U(_fZTGJ(vTBt5ri&z+F0PwPq@JuNChh28%CD$EnG*@qmZ#}Ps221hoe;=Ea zu<&Bnej(iS9qYWTh12r&A|W?|2yMXU5BUP-3jFGX$tSeHJayIIo?O}*#lP-58h<`w zYI7cx=GiVLo6tTv-9Cs88V7GWkXOJwc4D9G_e@F;$Cih1dz(k6HhA(^vBdzP8h(c21#h(S=G)qg>TCieW%v-7(a)O&;nD z9Sk2s#~NZ3@zzp)MxaZR^>Snz-ceemLvgzJz$i zZ(XLYHnz2m=`fl$Bih<+PJzejCkyLpd<%A~%@SS`ZCFp~9>XdcCCK>Klya#|Sq1S_ z-Wf#4`xDm1$8D?Rn>z&tENNe&{J1D|q6DJJ2C};G{Ysu+-n;fYwN)r*@m8sJAXz-^ z<$4JI6q1a>W1bFxF+n*fvW?iXz8>O5a2c9D=H%!|cqV;|E4)e0npgi!^3rPP4j_cb_u6*XyP8+w#U-pU#?%Vo zFXTDPw_AInztqIOi?Rk28%;a=Z5@Va&XKmRZf$wMR>;e>X`bRfFlV9AE|?OvWyINK z{Le%sGz7ZnqsG^QUR(3wsq%2{8FaAd?}VRj6{{6)-R@@lzca57-Fs#AH0B+@l2i4! zRO~a}_0#>bmF;?;sf(iPQ1t6bdS&uvyeJsGQ!0~1$un~ z_;RwGAuUhV#-PV{_Oa*AS!+32V_8zVW|>pyG+FI>H7U4^2o4BwyM5^Vu6UUXJXW;$ z#ZxrX&$n~f#YAuUVAJJQMfJS!N+?e5Vqi*;tKTj0PEK>G?~C-=sY)qB(rJt`e4Pq2 zGH)9z&l3;9L||}gmR#A9jzfu1@A5&*f;`$UcP?^`$NEYEnHG`cQtf^U>lU@Wx&yn2 zEd1uFUm_<*d5OovP!yeIX;ot8j$3^0qC17 zU!SE`bmaH+EHx&xR@)VRZuOC?L9FM?E9h!)*|)RQlLb+_1${IbQwBLL+9m_vud2Ri zjSUQCH@J?8cr=^)cBQnn=ZSO0xuZFuETqhC(A~ zjf;7Y1B4?HJxl=u{+qnrrp}QP;wJxE)?hFcF%X%KWh&p*$Y#9p%I%fotKuD1^B{KX zOu)Ba<-B^PI6Zl){GHO}H8BoSyu4ukS?OY27`>%2VIC1<~Y5c*iI^zs{{Zc zhj2vmclO2wDV*O*T80+$aw+#wR#t8$9%yrHxad*<#W5RFFM`!ZH-6e1BbkGq@7?wN zyV?<%e~unm`vyi_dB)&n(16DlyC0(Q)6!g}YgwhK6}pZi)_OW7%La$_AP0UG97uUs z$K2wRQ5~YLK0)grF})!ZELC<+(PApTxqE+d*(ahU-asg zVtZqYxlPt@s%5_jI1X3c;Vx4$dtPn@7YlUEI)>N9#m2?0!(@mf6;f-ZB zV%Tk0JXTsA)7SIVYhIJ-G^!Oy`EKiU@NxgU(*3*Sy@Si7!Z6I8^!<5f+V1lDVUPKz zjwxgjd^Z~eMlb(hH_a2|)vYn;P%?PW4ITAZ{vCY0OjaUGLXaa+!t+#FpG99I*?rrR zSI-Nq(!F^XNpm0>G9X`JvY`nE7^$pjIC$96Uihr%lvspZm}GJr`hV@B%{j$6zJRBx zY~>-2;Fm({GYzrQXE03C_v+$rI#U9e!W<9Z88p)o4fK6?)yBi?VAa|1?X`$4Q!{Jy zu2`;9nQi%v`2tfmI(%xrZy^AhFuq8=IlP$E)V_y^thBx6uQN4Q^2^XyWakzGC6}tA zkdC5+-(DGAZXL2ettwThS<{p?hlNT^<%*En$Y029#_=}MZLHC^%Hza2ctA<%PyGj`d`s( zw}DW_FpF)SQMY0qW$4~YU-U8hNO(6s;Pj;cXcXG4k zchKh`_NFq6^iE|A{?ss>2|1f+_2MXVyKrHT9idvFq=ANday3tyAmeO%(c@Xa$7&B+ z*Vu1^-W`gU7~?HrFWF9CQ*hpV>A{oQ;lL}si-RP?+0z~6*@oAsL%U6MyZ2MK7)(>8 z8$_vQzI_mfUwAcDM$sJeH}IILTKWV}a{9IhsB>mF(MMAb;{HW9|4m(|ZiVIj8VhT5 zk6J+xzMd3ynd( zua3z8_*y)xAZGPSw3td1w_9{V*j8ht!~~Y4wpxyq_qv!&08{`APey-i(K0*N91kI} z;r}_pbv?;vF!MKf)pD^KwbAv&igrq;x`5yL;{0q?sMxz;ac>OYa=l$BSp`3G-B#^k zZXAbJS=)4Kzpg}n{l{|s5k6hfPyJ7yyh^5q$rX}et5s%d_*MXs7dD3G!R_i@Z^Pto zj>ENfXNm5lo59I9JT%$pp!CiA%a_m{=)l(dz>CnIiirPvQ}_vfL0^oeZTbLd{b85Xt^C_NmV&u(%QyN_In=hQFLL(17W`uD=KGFHKjTpz3MxTQd zp||J7lax-S=$)sj-_Hyo*CoH-ZRLro01pq3DYmn|qMhTS)da@t-djyfU2o9TLGP?F z43*^jEsb#jGXgl^@qv9zjhy({ecNdS_MkV{<;w);)6--B1iO_sb}Bq*m4pG;zPQ-_ z6V>B;<;1uSAvgWseF#s8=-|l2?v4)5?2_5xXn(Oqqzy9YYVuOn+U&1Rb@0yK)c6VN z<_n==ttT^uLfh@G*N0@LxD|^~5(+wkz;$HEak<{X6)t>n(`eMs+U_!{IhY9_GNVG# z*%YSJY*T!^&;Y!pdI4ByaCjarqw?KNiEVzki$1YaRlY zut{t#9*;kn7W$d&I}{#pdz4f0b~$!u{Sn@Y2bZo=qd}ANjwTs!t8X1%)?VJQJ(xe*qCGet1xc_(W+VbH4ixlCDpKc;#l^>Vv0={lRr{9%3v@^N zbb$3DDiwA~N%sM0;8>Nd>(N9t8_Sb4%DD3PU)iALaFkZt){X5`d^cgaTkNmM!S9_7 z@9C*Hj&MHVDSn;JBTXGp&(Cn(XoK{Lgb908c+Bp_yga?BO+>}0lZ;fqfTP^#WlgC# zwdFE6MYO)lWe6(B{c*{6+OwJ(V@6Lw(YIcF73mM{$}b-#R1alV%FnOn7r?c^hi$Zk z)Z^Hk0eHMTkKQKh7W}vRzPPk_$SAABEka}0TV~cfS{Mf_-So>6+VnI;MX!gf(1tjP zzVbfOShjp()N8LF-B~6x%ycU zdiD<(qEs%zJ<^qcXjL;qp{X{R9)?anb6RtE=vKAoZTg0dH=4SU(NOLJZN@P*{RXEb zx~={?s_9MEPAO^^V+Hn1(_1vZG^T!Jy|tym@1TP;Txl zK{AC|q17U6CtlyaI&&W9XQH}I8>d2ZuFF&?>lCryQ)fIJ^pX-GNmT@A$p>@xY1siF8*YP(!i33;(l z38|7?;D%>mw0Ho#d*jhrOV1SiOg4Ynt(5g@ZN0b6>5i~FTJGEZj=?YTU|&MutTz}l zi`y}v`|(&03j7t7^Q4yCC8c?!LWMcyf6O5JS!$jdKa+Z>njevoX6W1PSDS}eFkX-6 zHF-U+ewXv16`~_fz3CN|UmH+E3kPcoSX@rv)2D2xmZ*r1LAw#PAhDL--MKQVc5qEO+fQMN5V+2Po?hi8_&-8&a9H5475aoAC5nbcladC^s6qtv zLG-Lg0SJ~Yzqn4}9M#1Wz(V_Ck#;Nq$i*yrMiiobrO+30`*M9f{#}1t7#0TO#~U0{ z=%ZqW-Iqh)kyXI}i1FLCe2MAYUBR*O$+%C_JRh55?LJ5j-{LB~DikmgI195DNsY-oaqC1cM)7}%Dv2Eq4U#F zd8@lBVB7zNL&LmuJK_@jsYEuN-^J<(pD8l|cB=?!`%N>iNIZ&(-DC(V46s%J4!?da&s}{N>5s9hob7b)w>-1O!rV$eV*54mbG`X;Sk5Twv3Twb0jIy_jHi%KoSAc8 z(F*~Ixwc(5N;WqgydV>fDQ)*p2+|##7Vozn8pNTG*ndI09^W?uG5n>am5IMEh5hYD z(K*sLu0Q5n<*fh7X`iFWAu&V?YqQ~h(|?n6zj2Vt7!_9AKbKdz{apCD;0_|?)%|tP zxz9NufCH8pg~r|@itK5~>zKl=#rEEYdk-BzyNqBwxmI{&a5}TR+6o>b?W1*GkbL6= zexB2gfjwP*y96!X(AvMArX1PmIfwq!RE>_|&l3sUMvR!A^A8V~4Jt5YEmq8Z9k6~T za!<6FR&~wI;5)D=21l7=xxk#?=ol0NZpgt&1lkPf9R78!)no`0dI5v|Z^LDR#^kDl;0dGDAHHD8>?KOU=MZ>No+wNI|x>APC#r=8e3{h7b zJ6s59@TFd9ZjCI&^+EprSOQO{6)9fLOsok)!hW&QyWSBo0`v+xyyn1cm4kjms4p<{ zb<2Ip5?G@Mz&2O83}N?ef9yEgGnHph;Gkip*n9`WTyOj2oJ+6jJM(d^Gx7Uh*Mmbc zHa~9-z4ZFcG0P{*>)kE@rB{uDC(&9(W;qJs9oD_iu^(@#W)`R-Sg`i*Dav{HyK#GS z($APexzda$A&N)F`$4O!sCvJ*c;FY*5f~foNu-71q+Tev@%ho(-vyQ=^fq*jO_guR zAHtfO0N3;5ysF~UYKhu8S#&>EEPBVQ3KQ_}4#3zwKB(H;}zqIhaUW_LOo)pv9og zo*n$(Vg#>L77rvh?#D0H^kt$|@91#Tb7IdrWce2KrzRM<*M?m%__^%vq-oci|E__p zJx2G>#7|NPm|?d_5oaiisB@A8$>~pln31Y&PUc1Bs6jOTdAc}Cg5;KJN}(?L zBBc6^Vx113rQp6~3gXp3C&Jq<%;StYdBz3O)6r5W_sm5T!&9tln=^bINjC_BGCR7$m)vk=9lU&wop&^+^aBcpo9 z(+?moBZR9_%|BXk#dx`0MK9;MB++T2lLqq7s%yK{+AKqz$;mo1M68O*{Rn82K<>(c zupyuKJ{7EEF7Y@VOfpJgX5*d!v-qSc)ETGb8q}-I+F5b_8++@Wl{P#@_qOa)IPMXU zM2{}?%c^=i8#NY$X)-(dt+%w4-uZ^CI=B8kaO`8{vQzdeP6q;@Ou4L+7AeS}DeeDa))FtsLm8x1eAYbg`((X@&RWnz&m;w29$ic z9yy*LKDE9Xw)YFl-LRm!y(;PQ)vQ+yd3-~1SDI{n`-ZggD2ltz*X5(R>59;1OEkM` zO+ME%U_}Ud*!#Z?QCU93h{?}EpN{pG?zd7V1nHK8kW)v*N@53QpGTiuy5Ci&GPvc+ zRtyNH31rL#ZW*90e?Rk1!FnYWlBzvC9X)AuNzummb)X|39+DiO@bxY)B9%jyYp!Iz>Y!up#>Gwh>r5vtqCM0i8 zTJm~(S?F|{|JYsjjWI7K!1-uS2+`6iJ<3}_Vde*VwQ?$xUP8DvQDFwpvE7u|>7zu+ z-sHb7cZoCS&l5o0s^F9+k6_7H@4;vA%DH7verVXC_K~y z?5LA?8jpDLP^wXX9q^LC0m7P8tBii*gxj5U(*qzxHr{mhRRB5`5<~;#QX7Nt?mQy9 z%jklEu&x1-SLs0l?;AZ=A)VWC%J8;AMGsFB{t;jBhL;qtP0Z|b2qI>oxxSofLm)I8 z;SJ!8J}3&?KzrUN_lMo4qS~v1X^@tizojs(Z^#$zUZdDva7`2&za=lTZ~C#YkjY*h zLp~KU5T%+F5%VDN!M6>KnZV)mWqyjFSjE&GRZGPmy@)Q8`>YiDc?&FAHR8~bWmTaP zJaS30=;XDSOoyJ=3kytCgnnlVeuPf{8UL(u>(|V$sEa*M0O?Ne&}mo#Q&Qa$Ay|?r z-~DmN`2jQFYvg@xD%2v63BIl2QLM$z9$78yV>$I|%bS?bk~DOI2&Lo zqRiKs*oVCm@5QzWtTek4b=~K$SWY4nE`{4LI%4>PL1IKPs*6+Mh3zm89+MQ9VdP=ftRIeA<1uxE^V&W9Z8bNbFEGarE$%a`OzZxjWcG2?L*sA=B0N}hBh?}gHYVfo>QGAjJwb!9OPX_SRCIzoIMVv%GY-rooG5L#?@|CDr z&E=&P*48|a-+SE-_;QvExTzOebxr`b_sK513)PJ=zf`Sf51(I|iPP5h9Q+2b+iiZB zce&To=Y$|Oo!j|N=a!3n7%7C}FanS7qXj8JXFuf2MkUE+$CtTsUhnodZ)FA$kc;N6 z$Rvs?a9D7zwc0j+$!kjS?X%h5h|I6>j+8LNVq)UDU>oy+GwfGdcJzB*HPedqIQ4GTv&W16V95JNJ(Go8Vhj2qMD*efZMxQ+jjA7Y<}o ziX`0)NrtISK`pIyoAO28=nv6u3>J$v8vrj{s}FPyp^KgCbj0dsU)24ki<96Yte>3N z-}2MADrEQ)q%}qrfXFw*4M*o^Pn_W{5h~t|g z@a<*EdsK8X^B|p5TUD1$A+TXBWe0}u_b$kZ6yud+yU^D>cMc*W^x($%XdYTN{mBOP;U5 z*95eZs|P@9!9u3*BZ_;WRQaNuY^O{Vo}60DYNeLBnqU=cWiPAE63%RYL*V5TSVLtp zv%7fJL0tdvV)Sc6MDXFnXSBm?c1w6d+KdcKa5?YU>Rp1{XO>k1D3b~gBp+9IFV7|{ z!Uo~FjHfLZcc%iRpo1fV^Oei8TEDFS!}N?ez%fN+y-%(5t^5E)pK4Yrodte#nqktz z=$y^UGYb9Oi;Pmt$e(v*RJoD2ar8}0kc2v`7_Q#SJY}vyix3n)0EM}z=S%uNSPiWK zoqflo{$7|p{an4C0_W7NF{ z(5sqs`dHq_4nQG${sPV1oxMJ^@8Sr|_CDvBz1fRAnkJ_JPOR{BJ?ay2`Yfy@G453^ z0b#XbKi`tZkiRX}9RARmgNKns^!V*`ltaa+gb#f!#4uV@(`0Ay%ah6YIGDGu*EXJP zI%aLBG5)E9;~0oBBVs%IO}PttH^=8Iw|SV|>|S0b$3o@BEM7ksK+U^tz)Q#}+-W^{AMSn_I2$h}E^B;e zK33k)=q?j&r@am;|IwKkm?^FCkwTqnky`yhs2g10ykrHsaAxtiYkdz2T2Q~bo?-TZ z)Xom6`(JhC8(S~gr!>Yl!={WyPj)u+rABEhwGlmLS+ElNxbw3EN27GS(5oa&%%OQ2 zq??Y*m;Gpn1skyFbw5Nhq6s_*=h(K*_dCvz1Xu2apL~;ZMnQZOizlD(tX%xMGAA~O zt5?-*CZ4x;AC0$y^hc%6WdvKmN|D`~OEcR);>R4_7-7(2=c$>QpO6dd&6>J22RDQi zkz)5LEDHwqdS1N(9&esB-7{K<O0iH;8#8gh7(@Q|M?l$i>&{y+&oQ=&m4NK~MempS=y#)`#{9P~8zvc*R{8{T7-$I5Vj{k7$KYzgbSH`MObeJ@-0J6sF{MklG zq0ogEQa}oX`yUcMpyFdKjr3Q{RzWifY?XXLQm^DJx$W&=IZvS8DW)9RKGdm%M&8IOJ|%QFXT?d#YZ^&T+YAu3B% zU%etfF3M`{CuB*u@TW(-dh__e#PseXBtvENhrcIcg*hnsK59cpd2l79a^YKhJcj>b z>C&EQqNL6I&8RV_ZM#sjcjezq{H$OU{k?@R!NR>m8=;VdVOreh@}CL}6GAK{yE&{o5L0qG zW>K%a9#9wnBI-m6qoB}LwfaXujch(zuSuf@eL%jXf0w$Q?TMMYw;2jNmwQ7!)boYj zo(GkO0OwGwsw5KES_e?6$8PtW`SYY&tD&rkPJJvUdwJx1lGMT%XGK2d-r~uiVc%V? zA%(G{{3_P0(Zp)lxv*!g?ZpC+)Fk?BTHHuq-qdWvmLuT()%E;26wyfSsVQd3@?gML z`52vr4Gp+4!c~%mzSwZL>_n<@uipsy_6Iqx=tZ|}oCcNXI{o5f?A(J)qbt;^274<* z(^wj(%=^yW;(TpgH8+6g@0T;pk>CI-IM%FiUaId*iDjCt5Hvl>5A5X~E|WH1JsGdV zOMD&pRhp1aCG4akn805;ceIVKy6YpCt2a4W^)tV6?xqh%y_3bS-GKGif2|*FZB&zg zJAMq@I{_*mh*&fFs^JA%R}^!$?2@)=(&1>ecS2g3ZC7avP87qajaIf*M>o1NzK0x- zE5*!LD0VNYZMMNW#29 zIewupjjR>%bTzo@vcK!f>&&0+!8ZXL;-r}Cv553B_=jKKcVnCpQ|q0wzl}Xo#P+XbYH+OMx&2s7@N3o+lf2NAXEJ(RlxPTPTz zo)CL*a@y+5T}pEL{oRZK&6hpwja7=j`fV5kD;}{a%w!G0Zj6e|+q;7GNT$c5{sxYX zd~;kP6g&?wr2MVH`2}%U_60`=8owzi0ZNzKv2WgI=j*FDxUOl&CO~D`ajnR>p6{Ml zyQwI;HoL}OAPidd>1nv-+@hOM8sG?Ga(j8{NO{;!fJ>|VM^rTOBPb5vhG(WkNe_gT zxRIz*68l3(uRcK@N&*ck^%*sJ7QC)24fzWV4rF3N1Sz{R(8)f3l^BdUzjDsB_IwSG%7s>GOKIFn{^1`{mU;9>4a&y-YaJkWRZX zdh%QD*^cF?J*Lj-D|cnaxnq~}exd(P%?Uv1|H|_wu`IOmTkl&&KI%cbU+AZinFY{T zX4Hg5%CYY6SM{z3nGw`X{GKJg5KR>|mdmC~c~QLb>G&4c|7aI%c@+nIdL{j}>j&Fp zpeu`@pxc0Qy}F26CUPSz13HEGPl;(~F6xrpLCyGu3y|u-Z{q4qB?`&$=}8@uTP`88 z+BscNZP&C@BV?uhYIxnX5057lTbXz@IW5-y<)Zo`?6u<|{;|R-p)I8Rf!M!M8L#d7 zk2{m8LH{y#U)(3ZG49QV-?ee|=mojlPLJGXRD1q!b?5=Mx#kM`1=bqp%7&%r@Qyj+ zJ}z$jse;illS|nnoP8ZFiG}9l2@{F1zlb_nr?SD4r0vr93SWP~^xMcVs4!AY?Az=( zLTR~kC?q1&UCPHb$c7{oV}opJ>f?X8fl(MOMZw{Vd^ACBxQ{I(Cup|c?`pRpizB!t z*~8VTNSSe;k?Ec@MO}^0*xgbRt6P+@i2db>5IK4eyMyBXV~=F`J+Tb2X2n^5`twM+ zGwnn7lAlCkHZe=epB{EamB>nZ#DPJ*e?noVmm9u*&R{~0to4wKC5b&_1v+na$C9m! zK}W#~by~1~StzJO6HqZZ7e)Kzb=bv&xT`%DzShM(-C-92&*#ee6XHH;RIlM}z&;h~ zCeIWI^^P|bacv-j(@3zLW9>kl)P7A%;x&p^{>qAzz zgAA)C!lQYKD7=$mjPwJoH|GKB5{ZlNlYD}yrhZY*fe_Wu5roU~;P04=I^5IV#cg42 zudZsbhyD>E11b)_`*#-Q=Nn0@NGHeK`<+Y`A5lu%HB2f2v3Lq{D=i_-YU4+Kh{8>r zMT^^@Y>S$D=a+2vi~A}7Ws9Bx&QMnW*PWxO2#@wJnyGOkGJ7(QN+OuoM*tT&0-IRG z_cQUO)-L)4T-@OunFRE@1d05ps#be-ZF&ias>z8h9;r*85*Th*)|#2+z3kiMVf@Tx z_6sQg_$LWq+{8vvtYw95e-}b8Q=R5dfQCfzjc&xLnA=ZHt1KS=PV;s%`dLn~l!Pf# z*o@=Uy50nLcSC$<39-A(j&SFag2TmQN{XWF^P%k<6glG>aQ6gn(HmzH zV^Qr7gL(a1Dw42alpio~zVxVY3sL`;8cAz#jE@^>k1ey|KNt5+7WXmtd{VO3`CV>Z zA|D&WB1n0Wg>D`3{+bH#Boo%ed(@Pf9+o=!mGsCpHZ2tb=fU+soqwx8E5aZBD(X$* zGCsb!R+drFY#EZD7_@Cq>K)-37AY?3Lm{vX$BofyvurgJKl610x>PIHg(t-(-G_ywj8~bYpYfZ^8>k>7 zw37K3pTFu(OcLMxTYWVLw^ZyUea|9~q;qzJ%rz=`sygI0+1D3q&smy&Hqe1SdOJhx zBF4aOPHg%2nbJb>S%;x0pDL9Dsj>`X`Jm~!iLC_WepPf_i#jI6zsQ!YE8Nv zwT}Cu3U@Wm%gjE!O~}T1G#{ES-tDtBj`IWOjaT)rL5+NBGl3QbI40WQ&6lg6MeS;t z$f0SsPv0s;E*8u;?@4b@NbvLDg@xE}6<(O&V&!P@Y$4Fm+Po~onP_}5e}U;#xrot; zD7U*ThUvH%vTb9Ffs6OF0?&N7!<= z60-@GVIYQ}Zv1X{Bi&W?A+awMNFoi=$_+(#FTOrRXMuDcP2BYbsek&^?EZfHz!=1^ z)Qg<(9(|5s zto0O`>Q;s0Vll55==VmxmbCSHnVMJ9L0;Ah0F;sI&j~m*g0^|Q7rNEgUW>{z9xKAH z@MHOhG2h+*^M#PDJyOdiMFx+zhBG`oU<=Ku0|h^ENoo~e$#I8kFoqh)W*<`abQGjW zmx3(>93Twat%Q0?h>ACFgvT_RDr6e@+q?tMmvN(>G9`WHkkIP?ppNwLLL}&jAon6y z!%4?w1ftrglzsgmQWioyo>(DuFj>`Kwh>iS3amw?3+~cAN=6$zY;doLN_P6Lf=7}U z*RyQ#aA9Yr+ zzKUPaoh74%%CON%p8|UG=}^<5ztboP>RHlj$y5Oxo)Xi5Tmqf6*^eiFlq*t_-;lRy zVTmiPI?>VNR zy#g7eC#4)2Qz4iom8}!~mTtT5w7u@g=51PkZ2$X&$I0wlW{y9fG&p+Iu1U-G(*V1d zAugQ;r76Ij_7XwXz(w}HV$ON(!JF?-)BUeJaI`Noq{oJaF?3V-q1xEoP=7O5?u9os zcdO6ba6%|ic|%4lZ{L4=9CfT#&I&i58B`zd3mpK_CGivoQ_*gdxsPfFuPlX@>O)v%)Uw3J*YUVl10^iTz~h4%fPH8l z=6?XU|9J`j`5egpwbs(S@+Vs}E*voNC*Rprjhhq_8)7bq6E3RyQls{m|3+XTUhuchil37T`W0Uk&W3)5e9XsWPDk z0`}PO$_7KSt+fYaRrInjH%?WT+lRYU=CMesDo&v)45q{TN2?5xi#0|Bo5hoMRXEig zH)EUxoF^4%<4NSPmefTj6jzV-r0><)qkp>V6|8;tFvfzIeWLKk_t(me4((ZNg$;1= z{Gv-2w}aN*%Ou7KgFkMxJ4GbNu7G@>bgMGy{G)$Cx{E>G3E1fJyulx!j=_I7+HqVT zXFh)CJ&qE{`#;~?e}69f-rMge7oz%yFm#j!Zl765ygBwovS!5>kMr4Svu=4{s45lo zDyiW+wdAj6h1rhuC+LRH_`Bhaom`poGNFH|noP8p(fbE`(HoHd<7QOWZFyR|kQz-Q z%`7rwU$U0Uq793`B%BQWtVJHu4#4H&p5ypw+H6+uoYzH-0f)uF;QYRsWzwDWIZjQi zM^`}*;(hzv+Y`~U)0liOpj^I3JnySg`nswd5x*LpUFlD59!}xK$&w)_G|opC1ddy- z5?lwI!vjKX-kIa#m}3Liw?utxI*05^Z{x7c1GJ(iUp@=Xee=&f{sM zIY#tovI`;M@d->ne@sZ?F^)v?05MBksb~GQ@Ox~P$M3}m6$$;8#^JIIepwaYl`GZR2-Bxg0Ms73>}=yVt-QF)t<=C-=pXO^RZ zQCN28PjHl$G}P?qAvfc}p)e<%G1d-$NkYMd#QtxZq;h(Lx~E@2cx;s8scxk0HYzWf zEorFut2?g{uy}HO&eH*fKjR3K1p-84+yG~ZlZjDn0;?&zlKXScRmjsMoV)3)8@CH0r;?`p?F(x)*tU<=x1L}^-Pf7#cvT(`%=#x!CII90&nz&E)V}ynIgH^ zES{|L&DGanX=LN^dspjbaCfq76Nipv!4hikh#RO_ghf2%vLvl_mEAK|e|-P{w06}2 zQDto(kQhQjN<~Vgb3syKR7#PCA%+qK>5icpLKILMLi=|L0ZZgN*cba zySncC`nulT&%dtRd+vSC)4%6D=S-q5!9r7{OFQY6>`AgLwO1(@3-;{4o&|tN#mVoI z6>HHSs?Lf-$WI0N)@O~Y)H0^Zl@jSbdNHz9ziy!;7~vI&zA=)l$T<-&anHu&m7w167yXYCx}P6uJ@a~r>fRZI zzqZ^R9t(@lmsV7=B;crQ`mU<_r=mkoQ(&$s3zy64pY9-Qkix?qe4Z(^=xF?rF*-3K z*rdyU#O>%Q^-@0(F~I|abo~BfEZ5*$hCSUL1-dsZVIqU-hUs}JywIekxfKSn7Z=kl zCPnUN`IP#{*~)pLLTetozKxm>O})I@XzfuWdm~5xO*jq!LnoOSQ5|U>>v1WX{pg{h z7Y!qRtfLhwXrfq3F$eqIM85b~6t7s}u(NV`K1(=iWxzG2X7gOlgEh0#k z{pOa-!Pah!b}X=)sBxnIl;q2gIoTa7ZyYOc)1{>Io|awHRa7269_Wjze0cLn{*B9c zXKeYBCNERO9-}0G)UFFJi|I|?iKlnQ;upt{P_%ZsIFq%>4yv@=6VD9EmG(+%Sem_| z13f08qy3x#7cTM%hXkE$-BL>qSd&Oo5%VY?xOs=KM3hYvbEF1~oNLNqA40iDF5gw4 ziF3@K-0Gjdrs~jGAILTEaiLG|8r7$#3{wU%8ZS?{-|LT&^QnaMMs+Tg8>MyT)4O?O ziC@D1uSVc+9l=<*`cS_{#S)9o+nZX41f-gU6E4{~!=1+#T}Nb}7YKU~mY<#7?BJx1 zXP@HQ#~$z7@2}PDKO9{F>;A6&aJA@0)bK=R?P$jvUG|Oy@9X#TSTcAv3a4aHR{vG!BeTJ%HB%i?LS zxc2=6hTIl}{__%icekOU$1(_GBk4lA_U9)a^t{(@OCZ8XJ4+;2s}#4=DvU{o(bd4%$qWxf14h|OP(C-IsbHCwez~n5RUP*P1`ns6Vju2v+h+CHd^x3At&ZKwVO^~y`*tOda8s+F zXr`^8^m^#F}Ty3b~Lgq`?4YQQ7r~dR?Pe{8dKZ^^%)|t=in_q*-$nX9CgJ zEeF_NFEAk(>^A4tbNp5RW$&Nwm-qq;pG%HrnH-FI4#lk)RyOV){v)ESblg%_KI>l^a> zlJ4|4zQZ3|zmG9yZ1dikDlkzCj-=Rmp$Zq{O@=4vJT*v++xNc1db}{5r)(xSuB%Yb z{_I4Ssz3@Nn73JXVIS(nN3Vo^_@?s|>3)2`5#7C&>pKTCjhxg$r-@JeC_(#b-7g;b z@kq-}9S~F)Tr8d5qCzfuH7^toBAt6XPg;}@WjTr5jUCrjZ8YShNKPy%TZ~QYq-1VNT0ndG0BErCd(2NU?0f4|BXy2M#F<$y0^7)Gm2K5-!U0qMe(fgK#>GP5!}s z_*VYd+1kskaQX$m_lt( zh3v{*&8_DhaL{y-x6PDXfAKk`)9}4mP;J`MmV$jG_grw%;3?+lvSJfGLma8YPirhh zIE=MwSC`-G>Zv`ex#hrcv?$mggfCXG)rOW|$i$P+-(@CwlpP{2%yXUVU5Z95hagav zWRg{;TA>$&a!F9$k>$22p`GTC2j>hi! z3QhZGt;!8!_9fRIa#!@Exkfy88QN(NqN}Lk)vvfx7(V=rON8@bZ zYV_cw$7?@@26u~*vt&Q2Ek%gqRxwCjS5AuLCxQ$zVJcO9(L1m3>4cUiOk%^cZ!T#R zlD+GFMLDo6JXp;3;L(PT^7MH2*|Jvd8|5|CL@&J`3)0YSCN_yAsug7y4|2I#42hRA zS8D~RYS_$Pi!U7O%^u*^^A6dLh>mP++6Zd07xDd*e3mq89vu!B9Kvs7FCYzTu%m_`ZCEg7b#~|BpZV5fj>zdmV*~bXS>% z+st%2PdvSPlKx(W9Hq~&UAY)>QdnYfxQ~&0NSulVt82B(xLsYq?5azWe(l{`Q%;LTdl8wJ?+YSZ-Q9xd}h5*oc+Gc67)8F$J4` za@XR#TK=U z8d@u(0{XEkb_pjYU%DVMT$#d|%R~BB65OY!Qav}&a(x-%^i*aSkFJ*FtlgcYR8y=^t0j9;D!plnZBQNn~ zC0a!osTicOJOOP5F4U|x)TJ&!tQHPN}F1bcMlE7dZ`a|l{xzqI&#jrHG5 zJ&gjJPq>{KNaat6A|wn&_R}Uy@0Op_%sAH657*&DuWr6y9r@t3%%kWK*Uqppc31p0 z{QladD|bngUdUak9lqpybHJ@nXKJ1iao%opB84iiyk73exXTao zl}^p+yyKgSwYjp#*Gpeio&6ZmeI>sc$MN0VP_s_8yGq`qXcKVYhoQ{V>(q*SS1&EJ z3b+~EjygBlh?O2P`!wHkn)`~)5G5O4w3XH&nvgmvzy3qc^&Q9C=UlVWv>w%9L=RXV zpKjflw4%W5XP65IO%&^jri zNen0_n0H#wCy7q`Ey99XgBElrYmFVAAo=1WactxBC9f17pGeDTN{G&+%<{TdRk0QZ z`?0tg&Z?BE}T~r-TsqaMc!4hSG$&HdZ@t^3sdBEA`XwRrupLX0_&-3Oti$ zI=5q3GFk}iLOi;!>N{|ay5mZf)iE^m;94-`ng=FAnD|wG4WmupJsbMang>t>kLJvu z$JTrUwM@q$2zPgP^=FVLGC0VRvrkA27?z! z(?hy|HC;Oyz2UX8>;R9L{*EMR_|L(V70zwm(LN=l&C}tHoxX$Nvs%eEn|hucBdKEK zhRW8o-T;Ex^>}k>%=VFC@*JM|%wf++0-iZ6PkST`-bdzO`N*zX{Xs?9z2`$^pO?oT z__!DMJhMwwumSd?-4~x8vuYrkO*3EBZMlbMRg>TzvFpacL))F?N%kTA($NPQ!x}{4 z>7v3n*IN}1v3A@AqR_Km?Lcml>`RJnuRx#omEV&vs-H?(TWgnb$;DM|$_Q$_OjG(q zWsdFrMGoHwKeA+~94ml;!uA&=nVtA(<`j>t@2g36uuY&dwt^g;550!&#tKO#m%v?& zI@#SW+~uCmB(_IOI-jd%*3>+!xT98`@;5sCh<|aLr<$>cA4+~c*TtR6GUo8+J z4a<{OtR!7u{*16>C-vgET(y*F>pfw?OlS>YZj! z1bM3qK@49y->6eew^+1m(sG&;i?ceQD?PF72%zmMQ#Emx6Sj4hm$MV34sMXG6i~__ zcbpU|`D6GBwAoB%x8QCcHG1_f#b~p?OA&VqpecCpP6?%!fBDaSPXFZrKf6MxC19-T zXv4PY^#T;okbY@1mnO!wdf#XK#;%KOiLGTGD^F$=zOY+aX!%OzwX8OIRX-!w3da>* zn_=qI;&>-d(g+per!?*#3&i>Ksq+_etVZlJ-Ap=CMCgERIJ2uQ&nl7xJPmW@v zZFgHem$bZ(A6Uk3x2vd8-k=Pxb_}LDq|T=toW#FY#HvMrpAa@f7Ll0}>BU#&cVolN zlfBRm#=J;%{izA^is>Z0@1yF-cI+&$mTbzQpS8m_u0k`>gfJkNkoN5enRDfi)14&w zQEUPlS`5i-u1ZzjzP>?(!-*3s6CoZcE}C#``e>*^U#pAV$$(wCEjw{gYQ1tOSnJLk zy7FlAzBEZk>IOl&h{OTUywi3%9LJ~%#+{Wnmw_xpL9S{;$^|xkbT}=pS-Eb~uepN~ zbUf1>i?i<8TUiK;KxK3`1QvPpXRLG1(%3dD6HDc#qhIf<3$9+q6{DuP1A~Qi=b+b3 zkc@NLwIYsmQsq`umwA>d(bc>W18}j&&eaYBEK>nmF5(dCRJ};-J94*3mk02-tS&AA z(H$Nr@iR!9CXU3p{5*K2+d5h+1N&S#JFrzidy0{(X`bpCtDQ5KtdWv-=w{vo#pnGo z)q%~SnUs~a!;6AEAC-&{*M*itc|vp67KC~W)Yy3r*xVP&-L73K`&sBEz4Q&9OKP}=lTC=4$? zI4A&?08a7Ps<{M`{D^vJ4mm~@Uq7Su?nzwS3%KHUS8#JaCtx?nSM_JA~=c?S=^-=2aHO^FFKtO`y24h)gM(3qJ(Xzk_P~W5~kbcO@fM-4Xgd zAD^{`oC&P79l1$CGfo&!okf4OlxsGVxtg9b$RX{hFQ;HK_!0>Rk_5&Lq2dY!D23-rZRAv>4;>8}G}`R^6D#@G3eC0n*lwKyB2#@ZZ0lfEf0ks=f+#dcAj%;-&0)9;7yA_JximQ<*fHGVJMG z(ZRvN0$c`k-mJ0L=2KT{OH-M>TNf6&h!K;Gaqc#sYyYh#3y-v9Vp@7Nq+QMUQUwCK zhTpHVbr7+1`h2n#(i18bTRn?qb#Gos3pd&KVqOOC_UMNs0nX`~`*Q{KD4mDlB??bu zZZNL!Xi4dCs}3D{dQS1!uCVx&;TZ`(+1k71mHD9hcBA)?vhqK0#sDQo;CY+JsX@Eg zbBPY#7qS=>G~@;%T^lsZVhe|Ai_G*C1x{ooV0xuqo?a!*R`fOJT)}CrA%ovT`%0yq z=Gm?&>g$9VtVo_4DHI}=lOnS5)sqg6RMH=jOEfCFw9@dQr;Pp75TORm`&ERv(<DwPivuAfOjd7r;pa_&MGWwyD zk~dy6Y3j00OJ|M^OGT)_zqDaLhE{e9WqF12H+X()29bwwf9W_DEwG9b@>G7bzeQ~z zM)XH&ZQR!CzA^pWR-AlX%&Dh#b--no{fkq$O<2OM_P8@i;;Gemxi5>yWO`JEBb7t~ zu1<_uQ;ledcp}u=XfDYzH+DU~^MTarO667O3yb1O;nlM9uPNA;rw-jwYRr=j#?o>N zcO_yvfH&j;_mBRoH@l944S@*xYubB3~Pcl2ekLQN245)Q$vmyN%DbrIO9V?0OE zlZXy-i<4q>3N4sWpbF(v@G7ig=V!XOGoy+YZ?6i!i=GKOs)?qZvL*Hnj^8+#Lz~{i zjK(yvg`UFAik8D2r!R>+9P=_1@vxnw^_dQb0mOeTkO<<+!IBhPwip*5Nr@P93;9|+ zDL8}oM>_-XQz_{jpYp~FL@~V%8*A_YtY$pis%^$i1*Ql?h&`47V}XLTHAxn|jM8f) zaB9K~?HeB{OIaHGrPsQblC-XEH?!ZPcDyWGcaibFCG|Dh-cKzFy4+`;A63tK>?c(0 zIVsVq@)Q1VGe)vKfGXSZSySRr2f7BGMTcoNd*iUU_TnL1v7~OD=Yidb z?Gkn#>(vQs4c~8eG|Q96{`+Vh0DLe_lVdPz7Y2j*rrQ|>za3ikeMJ@2HKG-Q zI4>P|c3kQRD9~>U!nsj`?F$M&uvN|69#PhAGvB3Zze{ zZol>8?9f`#GO7ad~aA=H4u z@N~0S3pO~8e)?q-AoTqLtG&>t_TJ-ID!_{(=5S$!VHv6N1sOXM{9hFoF--Va)XZAD z!exiFgVFzCK<2?g-TLqQxFF}H$(5Y~HqaqrRNjdASM*fBw;V~V;1(F&_a3Hraj;Vz z6&*b$br*F^g4|bPzgm2FXwIDAYx)#Hgv+~J3p7Rl(W>NE3A|F_*-GwwWv=JOUuCIG zDeAM#xr)U7Hdu;qv^Gg5_^2KS0N4x0u+L5~Vilr88Gg~zi|_jAl$6T+VmA>rlENE(j+b!tDiouAyr%uryO zH)bR$lOg}weY_`ReII-8$|Pgj3MKxXJ8Hekd}%A^`Nh9t2|p#Ay)v8h;FewK)0abK zRX=+92#-v${&D*uGr8}qK$4ixb?yF!hUivUeg>C3_8&KO&PDW(j12R9Ih5B=19!31 zQYoI#&^d?a^tYz97b(!uMq=kl=Mvi|c|%p%GC?7K34;%ohm^KHj&}>q4vHN4Bfq;R zgd=+`uEdpimh?ok^I9Kp1RKM7jUtRHLE^J-#Q!x5`+H0N?s=fVPX>E;{`u%HsnUrM3`5;dwBwg=ox0=K)DW!tx%y1=lj`%Y8V;0U zgPiK$Ml&GLeNF|QTt9WY_#wwvCHzhCJ!XEBlqz6<*e{=bv9Us5fgg*fw(pM1u3 zmCg0($$y(QVc`m#1Qx+lUYUh0{6BE4|1-xlefJTFe>=-&1g=C>G&5n!s+4g1$$#rY zlMQhg43m>p(7n?w-?Qv+qLcp2RgwmN@)G0ulvQ;%izkDpj34<_$oOGl0Q%%~zxp5t#b zX2tx|36K&2rulKsW96@6`US#O*&;MVhK#|ohJ27a;xkvb2EZB!QXJWSeat+7@b8WH zuahw@ghsj9RXc(vHH#8+)@!VVg!Mm_@oy-ovqc`!PNLvYNziJpNjNi}ecGBE^uT4B zW+%5}S!^Il#X|BTB{Hp^m;RMyzaanTa3;azz=;Dw(BFi{Lu=40de%IkCz(fsIlI5O z9tw7r&=ly$YVTYgu!6)wmg0!rwyCrStvc*f+rAVv_N6W0`3kEG#Q^8GKK{i(_3Z;m zI0>ugz2+D4z|OKx8R>PkvHJQStC*zY1LGx7{Z|c%^XM-+iQuA@#9~e!RGZ=}073FC>0m1u~Hej_C3z?3y zQ{!e)VGY_S&8eFAs~{5yQP9GCuEpeL_0ksf7qh6Evme`lwho9jb@M9M{=1PSu-t*B zL(MWAU;G2r(O_!%hduy7QL|<(2iCBR3+s+TF+=b|(2fpw5!VsjcVGA+Xdm#2Xy&Eo zbSzj>{ebw9+$%C{0$ToqToI>K>d}(5pp%ac!kNACntsq(t6!ZSHeY?AWHsoRI3k>2uAyL66$`2UIZaZGPXz09uyMHXsvxy%%g?|%<+oqtv6b8fS{}=k z&095lIn>~FCm%ZJK~eL`;||!N#!sPUKEx;%u;cuoxzNt5ORkk*xdR2h7=_}83xIXy zkP;xAm)Vvlg0@H+z#@GDjSz026QwNbW+%&XI=EfGnPt;IP@O&8sH%VF1N>+wtyFG{ zf;B8xqWxM(O44i&Xj6Wo&a|aGYo7{wpm8QNxl-^SfB_(Wh$S`TgO~po;IiM@n+tX* zH1ji%)VBo>T=J*@AlZrn9kZZ?0FbO+;sFalv5+nRkTgJnFPE^>dfn+Y9xdd1Xi6aDnB)2Xv*l~~x!~l>) zmnVS^S#gBt#oD4~!PeRQpeO)HuKSFDx5$JqK+;qs9&AAoX$b&HkJ?qR0gLJR^)Em& zNgp&{D?S6skH_>>eiifP1lN#aVd3vymARz(2c1Zp-$5n40UubgJ_0~x$DR#X&Ba2R zeg>7g#rj}l3As7V7f|U11ufX;N=$C>BJkDsESl!*WINE70U~T2O%KIbP?d4n;*i{>BoKl>ZZvQYTSR3{3 z4gpIl6i6q#7U=E7z^XU|fFfaRlUqp!zkwa<0W2@rp*`9_(`uXqBrpR>`k>csD)1c9 zE4L2CevaOSaAwFziSpXh(qfHjH88dmkQZ`)ysa10HL@7>+l$^SkM9)#&c92rdtN@S!Hpqc0SY3{zk#C zUcBW6Rwu`er9m@?`$V(T0i^cBxH?!JQ00r5?-PN5l_M5X7RP;`u=c|!=r6wI!w|qQ WbY%)l?vYr)zpJv!G6k27{r?XW!l4oX literal 0 HcmV?d00001 diff --git a/docs/imgs/kai_rhs_packing_pattern_2.png.license b/docs/imgs/kai_rhs_packing_pattern_2.png.license new file mode 100644 index 00000000..efa11a94 --- /dev/null +++ b/docs/imgs/kai_rhs_packing_pattern_2.png.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates + +# SPDX-License-Identifier: Apache-2.0 diff --git a/kai/ukernels/matmul/BUILD.bazel b/kai/ukernels/matmul/BUILD.bazel index 85635fd6..156c7afe 100644 --- a/kai/ukernels/matmul/BUILD.bazel +++ b/kai/ukernels/matmul/BUILD.bazel @@ -390,9 +390,9 @@ kai_c_library( ) kai_c_library( - name = "rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme", - srcs = ["pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c"], - hdrs = ["pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h"], + name = "rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme", + srcs = ["pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c"], + hdrs = ["pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h"], cpu_uarch = kai_cpu_sme(), ) @@ -590,7 +590,7 @@ kai_c_library( ":rhs_pack_kxn_qsi4cxp_qs4cxs1s0", ":rhs_pack_kxn_qsi8cxp_qsi8cx_neon", ":rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme", - ":rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme", + ":rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme", ":rhs_pack_nxk_qsi4c32p_qsu4c32s1s0", ":rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0", ":rhs_pack_nxk_qsi4cxp_qs4cxs1s0", diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h index b5c3132d..a7636eec 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h @@ -17,7 +17,7 @@ extern "C" { /// Micro-kernel dependencies /// /// -# kai_lhs_pack_x8p2vlx4_x8_sme to pack the LHS matrix. -/// -# kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme to pack the RHS matrix. +/// -# kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme to pack the RHS matrix. /// Gets m step value. /// diff --git a/kai/ukernels/matmul/pack/README.md b/kai/ukernels/matmul/pack/README.md index 5cb7133c..80f34b75 100644 --- a/kai/ukernels/matmul/pack/README.md +++ b/kai/ukernels/matmul/pack/README.md @@ -26,6 +26,20 @@ The pattern of the packed output is shown below Each block has bias and weights arranged as expected by the micro kernel to produce a mr x nr output matrix. There can be padding involved in the blocks depending on the combination of underlying instruction used for the optimization in the micro kernel, the chosen values of mr and nr and input dimensions, M, N and K. +#### kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme() + +Pack RHS(weights), bias and scaling factor together into X blocks. Details of the input are below. + +1. Values calculated using the bias, reduce_sum and lhs_zero point such that; Value\[n\] = Bias\[n\] - (lhs_zero_point * reduce_sum\[n\]). Each block has nr/4 elements, including padding. +1. Non-transposed RHS of dimension KxN. Each block contains 2 x nr elements. +1. Scale values calculated as Scale\[n\] = (rhs_scale\[n\] * lhs_scale) / dst_scale. Each block has nr/4 elements, including any padding. + +The pattern of the packed output is shown below. + +![rhs_pack_pattern_2](../../../../docs/imgs/kai_rhs_packing_pattern_2.png)
+ +Padding may be involved in the blocks depending on the values of mr, nr and kr and the input dimensions, M, N and K. + ## Packing for int4 matmul micro-kernels For optimal cache utilization, the operands are packed for the matmul operations. There are 2 types of packing functions used in int4 matmul micro-kernels: diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c similarity index 91% rename from kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c rename to kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c index 449b1141..1c2ad308 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.c +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c @@ -8,7 +8,7 @@ #error This file must be compiled for AArch64, FEAT_SVE2. #else // Architectural features check. -#include "kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" +#include "kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h" #include #include @@ -23,41 +23,41 @@ static const size_t kai_num_bytes_output = 1; static const size_t kai_num_bytes_bias = 4; static const size_t kai_num_bytes_scale = 4; -size_t kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(void) { +size_t kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(void) { return kai_nr * kai_get_sme_vector_length_u8() / kai_kr; } -size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { +size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8() / kai_kr) == 0); return n_idx * kai_num_bytes_input; } -size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { +size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { return n_idx * kai_num_bytes_bias; } -size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { +size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { return n_idx * kai_num_bytes_scale; } -size_t kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t k) { +size_t kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t k) { return kai_nr * kai_get_sme_vector_length_u8() / kai_kr * (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); } -size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k) { +size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k) { KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8() / kai_kr) == 0); return n_idx * (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); } -size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k) { - return kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( +size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k) { + return kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( kai_roundup(n, kai_nr * kai_get_sme_vector_length_u8() / kai_kr), k); } -void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( +void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, const struct kai_rhs_pack_qsi8_params* params) { @@ -82,7 +82,7 @@ void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( memset(pad_row, 0, nr * sizeof(uint8_t)); } - size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(height); + size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(height); const int32_t lhs_zero_point = params->lhs_zero_point; const float scale_multiplier = params->scale_multiplier; diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h similarity index 76% rename from kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h rename to kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h index 7af07d6d..e8745292 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h @@ -19,28 +19,28 @@ extern "C" { /// The starting row index must be divisible by `n_step`. /// /// @return The n step value. -size_t kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(void); +size_t kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(void); /// Gets the offset in bytes to the data element in the RHS matrix buffer. /// /// @param[in] n_idx Column index. /// /// @return The offset in bytes to the data element. -size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); /// Gets the offset in bytes to the data element in the bias buffer. /// /// @param[in] n_idx Column index. /// /// @return The offset in bytes to the data element. -size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); /// Gets the offset in bytes to the data element in the scale buffer. /// /// @param[in] n_idx Column index. /// /// @return The offset in bytes to the data element. -size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); /// Gets the offset in bytes to the data element in the packed RHS buffer. /// @@ -48,7 +48,7 @@ size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n /// @param[in] k Number of columns. /// /// @return The offset in bytes to the data element. -size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k); +size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k); /// Gets the size in bytes of the packed RHS buffer. /// @@ -56,17 +56,17 @@ size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(siz /// @param[in] k Number of columns. /// /// @return The size in bytes of the packed RHS buffer. -size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k); +size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k); /// Runs the RHS packing function for matrix multiplication. /// /// The pointer of each buffers (RHS, bias and packed RHS) needs to be added with offset /// calculated using the following functions: /// -/// * RHS: @ref kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. -/// * Bias: @ref kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. -/// * Scale: @ref kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. -/// * Output: @ref kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme. +/// * RHS: @ref kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. +/// * Bias: @ref kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. +/// * Scale: @ref kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. +/// * Output: @ref kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. /// /// @param[in] num_groups Number of groups. It must be 1. /// @param[in] n Number of columns of the output matrix. @@ -81,7 +81,7 @@ size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme(size_ /// @param[out] rhs_packed Packed RHS matrix. /// @param[in] extra_bytes Extra bytes to append to the end of each row of the packed RHS matrix. It must be 0. /// @param[in] params Extra packing parameters. It must be NULL. -void kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme( +void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, const struct kai_rhs_pack_qsi8_params* params); diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp index 4adbc03e..44363021 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp @@ -14,7 +14,7 @@ #include "kai/kai_common.h" #include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h" #include "kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h" -#include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme.h" +#include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h" #include "test/common/cpu_info.hpp" #include "test/common/matrix_portion.hpp" #include "test/common/memory.hpp" @@ -95,13 +95,13 @@ const std::array gemm_variants = { .fn_pack_lhs_get_packed_lhs_size = kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme, .fn_pack_lhs_run = kai_run_lhs_pack_x8p2vlx4_x8_sme, - .fn_pack_rhs_get_n_step = kai_get_n_step_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_rhs_offset = kai_get_rhs_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_bias_offset = kai_get_bias_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_scale_offset = kai_get_scale_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_packed_rhs_offset = kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_n_step = kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_rhs_offset = kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_bias_offset = kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_scale_offset = kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_packed_rhs_offset = kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, -- GitLab From 210f4f2b14fe33169dc8af7c71eb875a6861b421 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Thu, 21 Nov 2024 02:01:16 +0000 Subject: [PATCH 06/12] Minor changes - Rename kernel, fix int8 rhs packing kernel documentation Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 2 +- docs/imgs/kai_rhs_packing_pattern_2.png | Bin 137081 -> 70910 bytes kai/kai_common.h | 2 +- kai/ukernels/matmul/BUILD.bazel | 8 +-- ...p_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c | 2 + ...p_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h | 2 +- ...mul_clamp_qai8_qai8p_qsi8cxpsb_interface.h | 58 ++++++++++++++++++ kai/ukernels/matmul/pack/README.md | 4 +- .../pack/kai_lhs_pack_x8p2vlx4_x8_sme.c | 18 ++++-- ...ck_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c} | 29 ++++----- ...ck_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h} | 22 +++---- .../matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp | 17 ++--- 12 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h rename kai/ukernels/matmul/pack/{kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c => kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c} (88%) rename kai/ukernels/matmul/pack/{kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h => kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h} (81%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a4f91e2..b1479196 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ set(KLEIDIAI_FILES_SME kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_kxn_f32pb_f32_f32_16vlx1_sme.c kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c - kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c + kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c ) set(KLEIDIAI_FILES_SME2 diff --git a/docs/imgs/kai_rhs_packing_pattern_2.png b/docs/imgs/kai_rhs_packing_pattern_2.png index 700da43e57406dbb4a1f39343b1655cedf0b9c4d..6c6b15beb1f78f58e978aae79f4bbffdbbb4c937 100644 GIT binary patch literal 70910 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

1$dpOx-NnG8Y1mcMBF}HGBS&`LDO2WMFN%| zk63^1Ic9sI#@{wh2v&%esD#u|aaw#}ESKW-D5u{2eX58U&1Qvz1 zWe`hM>qHJd?^XY&{&7YUve#Blq+mH70~U6Yj)~V=2<2N9A-^>PdOj%@g??=sgT1P8 zcKn`>`FFd^Uh2m>EeR|YZC35-O>|H_CKUfH=>W%a%?0d(sPfN6BS;4aHdU|VwIK%R z#N0W``WwVuA8UwRcpk#B12x=oRniQnAt(Mw0VdjZF=TJ{2t`xfJrM;S)jzVv@J$Q` zXy`(wg#{zJxLlJf3&|R+_saIjCsbbg9BbXCAQ>EnD3^S$qi2SNKc+Squ5YekmJirG z6m(nH-H9tsoL(SnV&<7ogp%rt^oClPP^K<7)dKDbT0q=&cKj_jK1^X}rZN_fFEbv3 zB?C(nfNK_sPlw=D)IL?PPnH;}=|RkfgFW_Q`it9kUhG#A9&)J+E%AbZv#Lbq-+$W{ zZ$0ck#>*s$M&{gZYpwlOWL0S!)ZDi+kce_{GeGTEq_*bW1QDCU=~!rCGVD#evu>D| ze5~{4!!tH+7*^!`j`Yz>kP^H$kr1kSrjgy$*=X*O1%JQe2QIaV;S*6h`CN-bY_t)Q zlu;eQ9CBziLSAT~??RydH0_M_?Wv`tKX~#K&2O`-8UaZ3hkCDt?|t%A1rO#`7)!Qo z!=T@ZKjH`?kEJmPXE2zo+xfMS{@#j!B70r?)<2HXEAQmpcBRwQAP3#nnpyuzcTlRF z@8j$zJ4)}j`bIRONl zTA)Za)H+aIbV`3TBw$`*1JTl2@hO41k*?k5!p4usQ_9C5E0kfMa#Z;HWQGOWdsl1| z%~!8kb_q0S*F50a9zAafu^661M_UPza}}y9Yu%Be(I$otS9d(hU8p6eY_tuyc ziA$A)7$;Djv-gC-o{Y;{duYwA-0i+MoHZ*E(HiK9S6J|wAx>!Jchw9yka#towXf5d zLpx)}`Uty04(s6n%b?vG#jyAFnRc zG>{kR=DAxQ0SlMx?SQzXYdNT8meaBzJGu4VU6RH@{%~O=$|fw!hj| z?=*(7*PD8ER93qDQ_i_r;{7x)Z?0JH`hh~{hp4~!-raE^zGW3n#-8NKc$PfOR=WX8ygCyv3kk=_?pk%QX+n&|3~jz1anU)uu8Pgn#f z6sk4jO~u-oqLIJla_+{ELiUo(X(2Ze>le3%Lb9g}x}b;` zvQS}oPyRzMMIVN)1M5u_$2!A2s>b7$;|R+s+;5IiX5I{a$(onXfr3@_xwG(yUTKm; z;PbFn?Ne}p|4lz{!}aV|r*BrW^e87r@?fI=Qs-u}PI8%N_sg=nEBy`BYhXpAjMfl#ouY7)7NvjOPe9($7T{TjQNaU4U+BIvO(eg^g^y^9^8cQEV%LF}vgy@MjO=sPRH4QSUOO(v|D@#_=uai+DRqF~U_JjG)oFfWgn@f|*=_ zDF6FRC)*p&@;dC!D%6thC`pPQc2QR;9EqAo_lWh3=;nWs_11As#^2kxBGRFBgHqBZ zprne5gn)E+cSsD_5GkdlM)yE+bdHde?uJp)jP4Q7t)JiL`+Q%|@4vlZ+iSab?(@FR zbVPU5tZ>T@43zBZ9euwC(4-8Hdr2`0DF34Ws3uml=Fv2E^22{^)B^JRPkCXj zX{gEG2ZK(w__UV7fihgWAGlt)ARpD>0gAp@qfPMKP*jirSAO8BSj(17!GgcqS<9~M zsKREm(kMCNoO!dgZP@0a98Ov?xmsv*3gnGm=WpJKB}{x`5AeAxe{mFD4aRr&b${`#_Wq>& zGcJd@hkln_GiYL?Ux|wo(8O;;d+$D)%ptnU)}p$c69;q@b`u+W86D@J2X>5}QEJVm z1P$Ptpbz&e)}0SCJidTR0s|#Ky?I!AQNZhaQxJZWd#8cHea^v4np}kvFo4Uyp02H; z=1x<^p4ZvTm!el_9ct%nzh4vFPmS{FXL@2YueD|}(xJPw5sMAP3+_dPA}=7Desz!@ zWqiJ3>Fj7mi_1muIyjJn<-teutm7&{Z{wGBK2==v(+eY;#WtravFS}Kp>NA<0b_!B zsqu#xp6-E0vYPP?>&P77xEudLnAG7dl9)o0Zg*`DC?a3K-0*Hf(l_19c;fZML$}`d zCQAhWQk#miB(>hvCXYu^qp_bD22cDo)SKh zsg9L(Qk5b<27VjithpuE9+8vT2rjHq*1gwr%HT}>fk0wDSZC1a{~dS*M!7NnwnFzv zfXO@Ve%QZ+a5M8i0Yv*ZfNpMMz+7ABUo8LWg&Vi--dKqtV_ zBk?CQ|0zL9a5gbKm#q0_AGnXHlQ_PDY&q|(DZFftFG0O^Pf*15A?q3xdr+i9C00_L zY{-a^q>JY_{6Mwxl>uxUC+hXFCt8Z|1f{Ktd%t@J>QIN3;mOfcxf1y{BMm?KtKIT$ zHi6^$7m`q`ijyg#+Q(h&1PC9!a-E4WdyrA)Sx2V8Q_ja#XIU~zqIJywy z^R+E}^UO7Kd2OvAjI#$=4eiN;yXNbP-$Y{X6moJDv@D%QW)Y$!`wSe4!9|kO%uP%* zLug2UfJ7L6suR2WYW@AmCQyyOLn7bWI87XXqVbTBcHFUX()yAM`{*WhkEPRPgj5yF zgYRMFOM)F&zzqU?PTL_34zF;yTSR2q2H9sqc+c*d-CO5ftdVIgMZ#~@B z;(0bGkbaB@JNl3@$JcoIYPNQ$jwoWmm>&Or_u5vaHK6IPce6-1TK+Kvldu4ofYvXq z?Co56*3ab27D*360rv^wq>VQA@avrpgtFLX`u!gF9}K%2=pnll_NlEA#Q$xSe|hwQ z1c(odFM#fRl3p4Z<|lszh*n-bVFji%e!ZrC!=&*P80O4f&;;Uy=k~eSe^Lzn{dn@| zS*>LG$&fArBffa1uYGThfR3{bR3{x&FoGBJa=rLOiE%(3gON1zJ z5-Tj&{$*RV<$-Jd4zRPp;s);3bX@tzz-xQo00%jTU5jmlK>{%ndjUxA}(eGy@y-uW5M+$sE5WOLqTx{m@(d)76A`K8hs9V=5=8i_0DKIn{ zZJlE`N!Z9VFPIrVRcUj7vDw4(b2XRQVjf`@PQR@~vN10R!tN34F_15FD}^!{Bubz_ zBe4l(sMrnoYh}~J!{4xd3cU3JTXl+zuUg!17n-nxCA%Q=_mFJ5oEyp|V^HBwC)A%2FpH^Xd1%uIVdyprbyXr5hAmhjT`?PK8|*MTz2lH?W3s(M7J$a z=xv;6PjxY%fmXSqncaRR&?e72%vJq=CZw1+9!DDlu&gOs?_$atcg2Jaucm-|C(gna zzN8!R)9F--y)!=hNrPAaMtsvQ&6CC<4fZ@HON?Na2Z!OOUeOy!;?+lsR;LoCq(GxY z&;B2;@zO*bMpzGsRV9~G>wOthi?{=PCla`{Xz8Eyj_UJs>($37y;=J`(^wV#i2!0jK7re zd4X#VSAEYWA{W2Ubo`7uXyM4TgSEt0dNud=%H@^)pZhMB2fK7q^jVL7yP9iUCTRsQ z2px@m9C18fc7EZpSNeO0^skZFJE~h0z+baQtws!|F=zcIv7n+>=8C<2!Bz1gPaJfZ zz7m9w=DwbkiI&_UO`AC{4LO6dvpUSkNDre0_QmC%d&FN`O90R-k0QXDVPC~o?H);(j`(RDNHfoMDc4I zC2d*u#6=d(el}`&qH@TY<4|??hyIzmTTsmhxz!hcS=;}5b6s)Li(mlWT$h{_(e}CV zQOQ2ml`eVR1jY9Tu6`mjn&~{r*6-GcsX})irBFib!ozA)9(=a8)w@HK)T4im{a6BO zskp=5hoXt>oG34A^G#%zn$319mr(eo9#+?oRQ{h&aKk7bl^lJ;->q#CC`k`lnw8kI zbd`CHvr!ffmRD@Cy#0W-G4l08BP)udAw@R$DqblU*R3Lh!(12`VQ5G^c9q3jBL}9hSLxixe2u zJ093~(#DTH#sC3%HbkIeBPn3{QGc6Xu)YsYQblfvX?WGvHVV@bS)XZ*;T@w8Ih{7` zLmG+Fl=~(bqWNW)sOt$^?CN~%PU_wQ1gmkf@a-ZX{?7U)JNQS;Aer(B(<2;Q+%F}1 zcVw6H@!PNKU8~=wv)`KW65m~z5gZR&;|Og3i6a*KyxCo}*oX!^H8Oq9IeQz@Qjgjq z>AM)7F+X*D{ke*!cY3D&9$=&uVxjb5NyI@H!^A`sIk(2_J7aeDSBr3h0*e66{^#j( zp8#tB0O6g~Gov-%Fw5VtQeXg?gc#5+&xFucKgEpU`kSju#bfUsZ!gYZ-_Fvoe0aCH zV6p8x6F|&pX)Z0DUUeDC_q8~Fx;6_TCgL9AfbH0JWssF!kp44(oXWA(?b!59ck

2oot0=9AyrZh`&yh%seICpuu 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 H^GE*yYP=?~ literal 137081 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 WbY%)l?vYr)zpJv!G6k27{r?XW!l4oX diff --git a/kai/kai_common.h b/kai/kai_common.h index 92c9cc1d..3536ce1a 100644 --- a/kai/kai_common.h +++ b/kai/kai_common.h @@ -178,7 +178,7 @@ struct kai_rhs_pack_qs4cxs1s0_param { /// RHS packing parameter for static 8-bit quantization. struct kai_rhs_pack_qsi8_params { int32_t lhs_zero_point; ///< LHS quantization zero point. - float scale_multiplier; ///< Product of input and output quantization scales. + float scale_multiplier; ///< Product of input (refers to lhs and rhs) and output quantization scales. }; /// Requantization and clamp parameters for GEMM output stage. diff --git a/kai/ukernels/matmul/BUILD.bazel b/kai/ukernels/matmul/BUILD.bazel index 156c7afe..b19fba44 100644 --- a/kai/ukernels/matmul/BUILD.bazel +++ b/kai/ukernels/matmul/BUILD.bazel @@ -390,9 +390,9 @@ kai_c_library( ) kai_c_library( - name = "rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme", - srcs = ["pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c"], - hdrs = ["pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h"], + name = "rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme", + srcs = ["pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c"], + hdrs = ["pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h"], cpu_uarch = kai_cpu_sme(), ) @@ -590,7 +590,7 @@ kai_c_library( ":rhs_pack_kxn_qsi4cxp_qs4cxs1s0", ":rhs_pack_kxn_qsi8cxp_qsi8cx_neon", ":rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme", - ":rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme", + ":rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme", ":rhs_pack_nxk_qsi4c32p_qsu4c32s1s0", ":rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0", ":rhs_pack_nxk_qsi4cxp_qs4cxs1s0", diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c index ee074ac3..6316aaee 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c @@ -8,6 +8,8 @@ #error This file must be compiled for AArch64, FEAT_SVE2. #else // Architectural features check. +#include "kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h" + #include #include diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h index a7636eec..342251fb 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h @@ -17,7 +17,7 @@ extern "C" { /// Micro-kernel dependencies /// /// -# kai_lhs_pack_x8p2vlx4_x8_sme to pack the LHS matrix. -/// -# kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme to pack the RHS matrix. +/// -# kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme to pack the RHS matrix. /// Gets m step value. /// diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h new file mode 100644 index 00000000..cf060bea --- /dev/null +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h @@ -0,0 +1,58 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// +#pragma once + +#if !defined(__aarch64__) +#error This file must be compiled for AArch64 +#else // Architectural features check. + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// All micro-kernels variants of the same type share the same interfaces +// In this case, the micro-kernel type is: matmul_clamp_qai8_qai8p_qsi8cxpsb + +/// Micro-kernel helper functions ("get" methods) +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_m_step_func_t)(void); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_n_step_func_t)(void); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_mr_func_t)(void); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_nr_func_t)(void); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_kr_func_t)(void); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_sr_func_t)(void); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_lhs_packed_offset_func_t)(size_t m_idx, size_t k); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_rhs_packed_offset_func_t)(size_t n_idx, size_t k); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_dst_offset_func_t)( + size_t m_idx, size_t n_idx, size_t dst_stride); +typedef size_t (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_dst_size_func_t)(size_t m, size_t n); + +/// Micro-kernel core function ("run" method) +typedef void (*kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_run_matmul_func_t)( + size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, + size_t dst_stride_col, float scalar_min, float scalar_max); + +/// Micro-kernel interface +struct kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_ukernel { + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_m_step_func_t get_m_step; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_n_step_func_t get_n_step; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_mr_func_t get_mr; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_nr_func_t get_nr; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_nr_func_t get_kr; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_sr_func_t get_sr; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_lhs_packed_offset_func_t get_lhs_packed_offset; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_rhs_packed_offset_func_t get_rhs_packed_offset; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_dst_offset_func_t get_dst_offset; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_dst_size_func_t get_dst_size; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_run_matmul_func_t run_matmul; +}; + +#ifdef __cplusplus +} +#endif + +#endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/README.md b/kai/ukernels/matmul/pack/README.md index 80f34b75..94e42fc5 100644 --- a/kai/ukernels/matmul/pack/README.md +++ b/kai/ukernels/matmul/pack/README.md @@ -26,12 +26,12 @@ The pattern of the packed output is shown below Each block has bias and weights arranged as expected by the micro kernel to produce a mr x nr output matrix. There can be padding involved in the blocks depending on the combination of underlying instruction used for the optimization in the micro kernel, the chosen values of mr and nr and input dimensions, M, N and K. -#### kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme() +#### kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme() Pack RHS(weights), bias and scaling factor together into X blocks. Details of the input are below. 1. Values calculated using the bias, reduce_sum and lhs_zero point such that; Value\[n\] = Bias\[n\] - (lhs_zero_point * reduce_sum\[n\]). Each block has nr/4 elements, including padding. -1. Non-transposed RHS of dimension KxN. Each block contains 2 x nr elements. +1. Non-transposed RHS of dimension KxN. Each block contains nr elements. 1. Scale values calculated as Scale\[n\] = (rhs_scale\[n\] * lhs_scale) / dst_scale. Each block has nr/4 elements, including any padding. The pattern of the packed output is shown below. diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c index b16ef82a..4157ff99 100644 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c @@ -8,6 +8,8 @@ #error This file must be compiled for AArch64, FEAT_SVE2. #else // Architectural features check. +#include "kai_lhs_pack_x8p2vlx4_x8_sme.h" + #include #include @@ -17,6 +19,10 @@ static const size_t kai_mr = 2; static const size_t kai_kr = 4; static const size_t kai_sr = 1; +static inline size_t kai_get_m_step() { + return (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; +} + size_t kai_get_m_step_lhs_pack_x8p2vlx4_x8_sme(size_t mr) { KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8() / kai_kr); KAI_UNUSED(mr); @@ -25,13 +31,13 @@ size_t kai_get_m_step_lhs_pack_x8p2vlx4_x8_sme(size_t mr) { } size_t kai_get_lhs_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t lhs_stride) { - KAI_ASSUME(m_idx % (kai_mr * kai_get_sme_vector_length_u8() / kai_kr) == 0); + KAI_ASSUME(m_idx % (kai_get_m_step()) == 0); return m_idx * lhs_stride; } size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t k, size_t mr, size_t kr, size_t sr) { - const size_t scaled_mr = kai_mr * kai_get_sme_vector_length_u8() / kai_kr; + const size_t scaled_mr = kai_get_m_step(); KAI_ASSUME(m_idx % scaled_mr == 0); KAI_ASSUME(mr == scaled_mr); KAI_ASSUME(kr == kai_kr); @@ -45,7 +51,7 @@ size_t kai_get_lhs_packed_offset_lhs_pack_x8p2vlx4_x8_sme(size_t m_idx, size_t k } size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme(size_t m, size_t k, size_t mr, size_t kr, size_t sr) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8() / kai_kr); + KAI_ASSUME(mr == kai_get_m_step()); KAI_ASSUME(kr == kai_kr); KAI_ASSUME(sr == kai_sr); @@ -53,13 +59,13 @@ size_t kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme(size_t m, size_t k, size KAI_UNUSED(kr); KAI_UNUSED(sr); - return (kai_roundup(m, kai_mr * kai_get_sme_vector_length_u8() / kai_kr) * kai_roundup(k, kai_kr) * sizeof(int8_t)); + return (kai_roundup(m, kai_get_m_step()) * kai_roundup(k, kai_kr) * sizeof(int8_t)); } void kai_run_lhs_pack_x8p2vlx4_x8_sme( size_t m, size_t k, size_t mr, size_t kr, size_t sr, size_t m_idx_start, const void* lhs, size_t lhs_stride, void* lhs_packed) { - KAI_ASSUME(mr == kai_mr * kai_get_sme_vector_length_u8() / kai_kr); + KAI_ASSUME(mr == kai_get_m_step()); KAI_ASSUME(kr == kai_kr); KAI_ASSUME(sr == kai_sr); KAI_ASSUME(lhs != NULL); @@ -67,7 +73,7 @@ void kai_run_lhs_pack_x8p2vlx4_x8_sme( KAI_ASSUME(m_idx_start == 0); - const size_t block_height = kai_mr * kai_get_sme_vector_length_u8() / kai_kr; + const size_t block_height = kai_get_m_step(); const size_t width = k; const size_t row_offset = 0; diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c similarity index 88% rename from kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c rename to kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c index 1c2ad308..c50ea625 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.c +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c @@ -8,7 +8,7 @@ #error This file must be compiled for AArch64, FEAT_SVE2. #else // Architectural features check. -#include "kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h" +#include "kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h" #include #include @@ -23,41 +23,42 @@ static const size_t kai_num_bytes_output = 1; static const size_t kai_num_bytes_bias = 4; static const size_t kai_num_bytes_scale = 4; -size_t kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(void) { +size_t kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(void) { return kai_nr * kai_get_sme_vector_length_u8() / kai_kr; } -size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { +size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx) { KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8() / kai_kr) == 0); return n_idx * kai_num_bytes_input; } -size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { +size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx) { return n_idx * kai_num_bytes_bias; } -size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx) { +size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx) { return n_idx * kai_num_bytes_scale; } -size_t kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t k) { - return kai_nr * kai_get_sme_vector_length_u8() / kai_kr * +size_t kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t k) { + return kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme() * (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); } -size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k) { - KAI_ASSUME(n_idx % (kai_nr * kai_get_sme_vector_length_u8() / kai_kr) == 0); +size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx, size_t k) { + KAI_ASSUME(n_idx % kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme() == 0); - return n_idx * (kai_num_bytes_bias + kai_roundup(k, kai_kr) * kai_num_bytes_output + kai_num_bytes_scale); + return (n_idx / kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme()) * + kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(k); } -size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k) { - return kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( +size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n, size_t k) { + return kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme( kai_roundup(n, kai_nr * kai_get_sme_vector_length_u8() / kai_kr), k); } -void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( +void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme( size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, const struct kai_rhs_pack_qsi8_params* params) { @@ -82,7 +83,7 @@ void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( memset(pad_row, 0, nr * sizeof(uint8_t)); } - size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(height); + size_t out_stride = kai_get_rhs_packed_stride_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(height); const int32_t lhs_zero_point = params->lhs_zero_point; const float scale_multiplier = params->scale_multiplier; diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h similarity index 81% rename from kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h rename to kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h index e8745292..a4b0fa1a 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h @@ -19,28 +19,28 @@ extern "C" { /// The starting row index must be divisible by `n_step`. /// /// @return The n step value. -size_t kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(void); +size_t kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(void); /// Gets the offset in bytes to the data element in the RHS matrix buffer. /// /// @param[in] n_idx Column index. /// /// @return The offset in bytes to the data element. -size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +size_t kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx); /// Gets the offset in bytes to the data element in the bias buffer. /// /// @param[in] n_idx Column index. /// /// @return The offset in bytes to the data element. -size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +size_t kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx); /// Gets the offset in bytes to the data element in the scale buffer. /// /// @param[in] n_idx Column index. /// /// @return The offset in bytes to the data element. -size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx); +size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx); /// Gets the offset in bytes to the data element in the packed RHS buffer. /// @@ -48,7 +48,7 @@ size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t /// @param[in] k Number of columns. /// /// @return The offset in bytes to the data element. -size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n_idx, size_t k); +size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx, size_t k); /// Gets the size in bytes of the packed RHS buffer. /// @@ -56,17 +56,17 @@ size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(si /// @param[in] k Number of columns. /// /// @return The size in bytes of the packed RHS buffer. -size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size_t n, size_t k); +size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n, size_t k); /// Runs the RHS packing function for matrix multiplication. /// /// The pointer of each buffers (RHS, bias and packed RHS) needs to be added with offset /// calculated using the following functions: /// -/// * RHS: @ref kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. -/// * Bias: @ref kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. -/// * Scale: @ref kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. -/// * Output: @ref kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme. +/// * RHS: @ref kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(. +/// * Bias: @ref kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(. +/// * Scale: @ref kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(. +/// * Output: @ref kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(. /// /// @param[in] num_groups Number of groups. It must be 1. /// @param[in] n Number of columns of the output matrix. @@ -81,7 +81,7 @@ size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme(size /// @param[out] rhs_packed Packed RHS matrix. /// @param[in] extra_bytes Extra bytes to append to the end of each row of the packed RHS matrix. It must be 0. /// @param[in] params Extra packing parameters. It must be NULL. -void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme( +void kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme( size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t rhs_stride, const void* rhs, const void* bias, const void* scale, void* rhs_packed, size_t extra_bytes, const struct kai_rhs_pack_qsi8_params* params); diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp index 44363021..b7f56720 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp @@ -14,7 +14,7 @@ #include "kai/kai_common.h" #include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h" #include "kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h" -#include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme.h" +#include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h" #include "test/common/cpu_info.hpp" #include "test/common/matrix_portion.hpp" #include "test/common/memory.hpp" @@ -95,13 +95,13 @@ const std::array gemm_variants = { .fn_pack_lhs_get_packed_lhs_size = kai_get_lhs_packed_size_lhs_pack_x8p2vlx4_x8_sme, .fn_pack_lhs_run = kai_run_lhs_pack_x8p2vlx4_x8_sme, - .fn_pack_rhs_get_n_step = kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_rhs_offset = kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_bias_offset = kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_scale_offset = kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_packed_rhs_offset = kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, - .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qsi8_f32_i32_sme, + .fn_pack_rhs_get_n_step = kai_get_n_step_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, + .fn_pack_rhs_get_rhs_offset = kai_get_rhs_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, + .fn_pack_rhs_get_bias_offset = kai_get_bias_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, + .fn_pack_rhs_get_scale_offset = kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, + .fn_pack_rhs_get_packed_rhs_offset = kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, + .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, + .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, @@ -129,6 +129,7 @@ const std::array gemm_shapes = { GemmShape{23, 1, 43}, // GemmShape{32, 14, 1}, // GemmShape{123, 85, 45}, // + GemmShape{130, 130, 6}, }; const std::array output_portions = { -- GitLab From 8f4dd660439c8aeef0fb926208d8dfb4b27a7ed5 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Thu, 28 Nov 2024 11:46:04 +0000 Subject: [PATCH 07/12] Improve documentation, make minor changes Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 1 + docs/imgs/kai_rhs_packing_pattern_2.png | Bin 70910 -> 110828 bytes kai/kai_common.h | 4 +- kai/ukernels/matmul/BUILD.bazel | 8 +-- ...p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c} | 32 ++++----- ...p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h} | 28 ++++---- ...mul_clamp_qai8_qai8p_qsi8cxpsb_interface.h | 8 +-- kai/ukernels/matmul/pack/README.md | 8 +-- test/reference/clamp.hpp | 6 ++ test/reference/fill.cpp | 61 +----------------- test/reference/fill.hpp | 35 ---------- test/reference/quantize.hpp | 8 +++ test/reference/reduce.hpp | 16 +++++ .../matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp | 28 ++++---- 14 files changed, 88 insertions(+), 155 deletions(-) rename kai/ukernels/matmul/{matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c => matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c} (91%) rename kai/ukernels/matmul/{matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h => matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h} (73%) rename kai/ukernels/matmul/{matmul_clamp_qai8_qai8p_qsi8cp => matmul_clamp_qai8_qai8p_qsi8cxp}/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1479196..03e2862d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,7 @@ set(KLEIDIAI_FILES_SME2 kai/ukernels/matmul/matmul_clamp_f16_f16_f16p/kai_matmul_clamp_f16_f16_f16p2vlx2b_1x16vl_sme2_dot.c kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c + kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c ) add_library(kleidiai) diff --git a/docs/imgs/kai_rhs_packing_pattern_2.png b/docs/imgs/kai_rhs_packing_pattern_2.png index 6c6b15beb1f78f58e978aae79f4bbffdbbb4c937..d10d1fc038c8f118dbb07d316f7eb49dfdffe8ac 100644 GIT binary patch literal 110828 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

+7h;mx~!;+ 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%0D)D*%oF}-dBF3KbEvR`*}h@7^I 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 x{B|EB=7<3s`iD5-w7uJ0zq*x{;Jdx>LHl;dies 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

1$dpOx-NnG8Y1mcMBF}HGBS&`LDO2WMFN%| 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

2oot0=9AyrZh`&yh%seICpuu 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 H^GE*yYP=?~ diff --git a/kai/kai_common.h b/kai/kai_common.h index 3536ce1a..cbd03a95 100644 --- a/kai/kai_common.h +++ b/kai/kai_common.h @@ -175,13 +175,13 @@ struct kai_rhs_pack_qs4cxs1s0_param { uint8_t rhs_zero_point; /**< RHS Matrix quantization zero-point */ }; -/// RHS packing parameter for static 8-bit quantization. +/// RHS packing parameter for 8-bit quantization. struct kai_rhs_pack_qsi8_params { int32_t lhs_zero_point; ///< LHS quantization zero point. float scale_multiplier; ///< Product of input (refers to lhs and rhs) and output quantization scales. }; -/// Requantization and clamp parameters for GEMM output stage. +/// Requantization and clamp parameters for GEMM/GEMV output stage. struct kai_matmul_requantize32_params { int32_t min_value; ///< Minimum output value. int32_t max_value; ///< Maximum output value. diff --git a/kai/ukernels/matmul/BUILD.bazel b/kai/ukernels/matmul/BUILD.bazel index b19fba44..11dfe551 100644 --- a/kai/ukernels/matmul/BUILD.bazel +++ b/kai/ukernels/matmul/BUILD.bazel @@ -153,9 +153,9 @@ kai_c_library( ) kai_c_library( - name = "clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa", - srcs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c"], - hdrs = ["matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h"], + name = "clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa", + srcs = ["matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c"], + hdrs = ["matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h"], cpu_uarch = kai_cpu_sme(), ) @@ -574,7 +574,7 @@ kai_c_library( ":lhs_pack_f32p2vlx1_f32_sme", ":lhs_quant_pack_bf16p1x4_f32_neon", ":lhs_quant_pack_bf16p8x4_f32_neon", - ":clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa", + ":clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa", ":lhs_pack_f32p2vlx1_f32_sme", ":lhs_pack_x8p2vlx4_x8_sme", ":lhs_quant_pack_bf16p_f32_neon", diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c similarity index 91% rename from kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c rename to kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c index 6316aaee..ef38be5f 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c @@ -8,7 +8,7 @@ #error This file must be compiled for AArch64, FEAT_SVE2. #else // Architectural features check. -#include "kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h" +#include "kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h" #include #include @@ -20,53 +20,53 @@ static const size_t kai_nr = 2; static const size_t kai_kr = 4; static const size_t kai_sr = 1; -size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_m_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void) { return kai_mr * kai_get_sme_vector_length_u32(); } -size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_n_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void) { return kai_nr * kai_get_sme_vector_length_u32(); } -size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_mr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void) { return kai_mr * kai_get_sme_vector_length_u32(); } -size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_nr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void) { return kai_nr * kai_get_sme_vector_length_u32(); } -size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_kr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void) { return kai_kr; } -size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void) { +size_t kai_get_sr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void) { return kai_sr; } -size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k) { - KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); +size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(size_t m_idx, size_t k) { + KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa() == 0); return m_idx * kai_roundup(k, kai_kr) * sizeof(int8_t); } -size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k) { - KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); +size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(size_t n_idx, size_t k) { + KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa() == 0); return n_idx * (sizeof(int32_t) + kai_roundup(k, kai_kr) * sizeof(int8_t) + sizeof(float)); } -size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( +size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa( size_t m_idx, size_t n_idx, size_t dst_stride) { - KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); - KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa() == 0); + KAI_ASSUME(m_idx % kai_get_m_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa() == 0); + KAI_ASSUME(n_idx % kai_get_n_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa() == 0); return m_idx * dst_stride + n_idx * sizeof(int8_t); } -size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m, size_t n) { +size_t kai_get_dst_size_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(size_t m, size_t n) { return m * n * sizeof(int8_t); } -void kai_run_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( +void kai_run_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, size_t dst_stride_col, const struct kai_matmul_requantize32_params* params) { KAI_ASSUME(dst_stride_col == sizeof(int8_t)); diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h similarity index 73% rename from kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h rename to kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h index 342251fb..e8e2868b 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h @@ -24,42 +24,42 @@ extern "C" { /// The starting row index must be divisible by `m_step`. /// /// @return The m step value. -size_t kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_m_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void); /// Gets n step value. /// /// The starting column index must be divisible by `n_step`. /// /// @return The n step value. -size_t kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_n_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void); /// Gets mr value. /// /// This is the packing parameter which must be used to pack the LHS matrix. /// /// @return The mr value. -size_t kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_mr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void); /// Gets nr value. /// /// This is the packing parameter which must be used to pack the RHS matrix. /// /// @return The nr value. -size_t kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_nr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void); /// Gets kr value. /// /// This is the packing parameter which must be used to pack the LHS and RHS matrix. /// /// @return The kr value. -size_t kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_kr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void); /// Gets sr value. /// /// This is the packing parameter which must be used to pack the LHS and RHS matrix. /// /// @return The sr value. -size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); +size_t kai_get_sr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(void); /// Gets the offset in bytes to the data element in the packed LHS matrix buffer. /// @@ -67,7 +67,7 @@ size_t kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(void); /// @param[in] k Number of columns in the unpacked LHS matrix. /// /// @return The offset in bytes to the data element. -size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m_idx, size_t k); +size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(size_t m_idx, size_t k); /// Gets the offset in bytes to the data element in the packed RHS matrix buffer. /// @@ -75,7 +75,7 @@ size_t kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_ /// @param[in] k Number of rows in the unpacked RHS matrix. /// /// @return The offset in bytes to the data element. -size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t n_idx, size_t k); +size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(size_t n_idx, size_t k); /// Gets the offset in bytes to the data element in the destination matrix buffer. /// @@ -84,7 +84,7 @@ size_t kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_ /// @param[in] dst_stride Row stride in bytes. /// /// @return The offset in bytes to the data element. -size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( +size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa( size_t m_idx, size_t n_idx, size_t dst_stride); /// Gets the size in bytes of the destination matrix buffer. @@ -93,16 +93,16 @@ size_t kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( /// @param[in] n Number of columns. /// /// @return The size in bytes of the destination matrix buffer. -size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size_t m, size_t n); +size_t kai_get_dst_size_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa(size_t m, size_t n); /// Runs the matrix multiplication microkernel followed by a clamp operation. /// /// The pointer of each buffers (packed LHS, packed RHS and output) needs to be added with offset /// calculated using the following functions: /// -/// * Packed LHS: @ref kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa. -/// * Packed RHS: @ref kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa. -/// * Output: @ref kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa. +/// * Packed LHS: @ref kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa. +/// * Packed RHS: @ref kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa. +/// * Output: @ref kai_get_dst_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa. /// /// @param[in] m Number of output rows to be computed. /// @param[in] n Number of output columns to be computed. @@ -113,7 +113,7 @@ size_t kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa(size /// @param[in] dst_stride_row Row stride in bytes of the output matrix. /// @param[in] dst_stride_col Column stride in bytes of the output matrix. /// @param[in] params Requantization and clamp parmaters. -void kai_run_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa( +void kai_run_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa( size_t m, size_t n, size_t k, const void* lhs_packed, const void* rhs_packed, void* dst, size_t dst_stride_row, size_t dst_stride_col, const struct kai_matmul_requantize32_params* params); diff --git a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h similarity index 91% rename from kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h rename to kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h index cf060bea..c09dc3e9 100644 --- a/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h +++ b/kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_interface.h @@ -5,10 +5,6 @@ // #pragma once -#if !defined(__aarch64__) -#error This file must be compiled for AArch64 -#else // Architectural features check. - #include #ifdef __cplusplus @@ -42,7 +38,7 @@ struct kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_ukernel { kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_n_step_func_t get_n_step; kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_mr_func_t get_mr; kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_nr_func_t get_nr; - kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_nr_func_t get_kr; + kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_kr_func_t get_kr; kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_sr_func_t get_sr; kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_lhs_packed_offset_func_t get_lhs_packed_offset; kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_get_rhs_packed_offset_func_t get_rhs_packed_offset; @@ -54,5 +50,3 @@ struct kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_ukernel { #ifdef __cplusplus } #endif - -#endif // Architectural features check. diff --git a/kai/ukernels/matmul/pack/README.md b/kai/ukernels/matmul/pack/README.md index 94e42fc5..950a69ac 100644 --- a/kai/ukernels/matmul/pack/README.md +++ b/kai/ukernels/matmul/pack/README.md @@ -28,11 +28,11 @@ Each block has bias and weights arranged as expected by the micro kernel to prod #### kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme() -Pack RHS(weights), bias and scaling factor together into X blocks. Details of the input are below. +Pack RHS(weights), bias and scaling factor together into X number of blocks that are a combination of scale, bias and RHS. Details of the input are below. -1. Values calculated using the bias, reduce_sum and lhs_zero point such that; Value\[n\] = Bias\[n\] - (lhs_zero_point * reduce_sum\[n\]). Each block has nr/4 elements, including padding. -1. Non-transposed RHS of dimension KxN. Each block contains nr elements. -1. Scale values calculated as Scale\[n\] = (rhs_scale\[n\] * lhs_scale) / dst_scale. Each block has nr/4 elements, including any padding. +1. Values calculated using the bias, reduce_sum and lhs_zero point such that; Value\[n\] = Bias\[n\] - (lhs_zero_point * reduce_sum\[n\]). Each block has nr elements, including padding. +1. Non-transposed RHS of dimension KxN. Each block contains nr\*kr elements, including any padding. +1. Scale values calculated as Scale\[n\] = (rhs_scale\[n\] * lhs_scale) / dst_scale. Each block has nr elements, including any padding. The pattern of the packed output is shown below. diff --git a/test/reference/clamp.hpp b/test/reference/clamp.hpp index d28484f3..24d3ac6c 100644 --- a/test/reference/clamp.hpp +++ b/test/reference/clamp.hpp @@ -12,6 +12,12 @@ namespace kai::test { +/// Clamps the matrix. +/// +/// @param[in] src Data buffer of the source matrix. +/// @param[in] len Number of values in the source matrix. +/// @param[in] min_value Lower bound of clamp. +/// @param[in] width Upper bound of clamp. template std::vector clamp(const void* src, size_t len, T min_value, T max_value); diff --git a/test/reference/fill.cpp b/test/reference/fill.cpp index faad8547..49b16987 100644 --- a/test/reference/fill.cpp +++ b/test/reference/fill.cpp @@ -20,9 +20,6 @@ #include "test/common/float16.hpp" #include "test/common/int4.hpp" #include "test/common/memory.hpp" -#include "test/common/numeric_limits.hpp" -#include "test/common/round.hpp" -#include "test/common/type_traits.hpp" namespace kai::test { @@ -121,67 +118,11 @@ std::vector fill_matrix_random(size_t height, size_t width, const DataF } } -template -Value get_random(uint64_t seed, Value min_value, Value max_value) { - static_assert(is_floating_point || is_integral); - static_assert(size_in_bits <= 32); - - using Distribution = std::conditional_t< - is_floating_point, std::uniform_real_distribution, - std::conditional_t< - is_signed, std::uniform_int_distribution, std::uniform_int_distribution>>; - - std::mt19937 rnd(seed); - Distribution dist(min_value, max_value); - - return static_cast(dist(rnd)); -} - -template -Value get_random(uint64_t seed) { - if constexpr (is_floating_point) { - return get_random(seed, static_cast(0.0F), static_cast(1.0F)); - } else { - return get_random(seed, numeric_lowest, numeric_highest); - } -} - -template float get_random(uint64_t seed); -template int32_t get_random(uint64_t seed); - -template -std::vector fill_random(size_t length, uint64_t seed, Value min_value, Value max_value) { - static_assert(is_floating_point || is_integral); - static_assert(size_in_bits <= 32); - - using Distribution = std::conditional_t< - is_floating_point, std::uniform_real_distribution, - std::conditional_t< - is_signed, std::uniform_int_distribution, std::uniform_int_distribution>>; - - std::mt19937 rnd(seed); - Distribution dist(min_value, max_value); - - std::vector data(round_up_division(length * size_in_bits, 8)); - - for (size_t i = 0; i < length; ++i) { - write_array(data.data(), i, static_cast(dist(rnd))); - } - - return data; -} - template std::vector fill_random(size_t length, uint64_t seed) { - if constexpr (is_floating_point) { - return fill_random(length, seed, static_cast(0.0F), static_cast(1.0F)); - } else { - return fill_random(length, seed, numeric_lowest, numeric_highest); - } + return fill_matrix_random_raw(1, length, seed); } template std::vector fill_random(size_t length, uint64_t seed); -template std::vector fill_random(size_t length, uint64_t seed); -template std::vector fill_random(size_t length, uint64_t seed); } // namespace kai::test diff --git a/test/reference/fill.hpp b/test/reference/fill.hpp index df5f03d3..80093952 100644 --- a/test/reference/fill.hpp +++ b/test/reference/fill.hpp @@ -24,28 +24,6 @@ class DataFormat; /// @return The data buffer for the matrix. std::vector fill_matrix_random(size_t height, size_t width, const DataFormat& format, uint64_t seed); -/// Gets a random value. -/// -/// @tparam Value The data type. -/// -/// @param[in] seed The random seed. -/// -/// @return The random value. -template -Value get_random(uint64_t seed); - -/// Gets a random value. -/// -/// @tparam Value The data type. -/// -/// @param[in] seed The random seed. -/// @param[in] min_value The minimum value. -/// @param[in] max_value The maximum value. -/// -/// @return The random value. -template -Value get_random(uint64_t seed, Value min_value, Value max_value); - /// Creates a new data buffer filled with random data. /// /// @tparam Value The data type. @@ -57,17 +35,4 @@ Value get_random(uint64_t seed, Value min_value, Value max_value); template std::vector fill_random(size_t length, uint64_t seed); -/// Creates a new data buffer filled with random data. -/// -/// @tparam Value The data type. -/// -/// @param[in] length The number of elements. -/// @param[in] seed The random seed. -/// @param[in] min_value The minimum value. -/// @param[in] max_value The maximum value. -/// -/// @return The data buffer. -template -std::vector fill_random(size_t length, uint64_t seed, Value min_value, Value max_value); - } // namespace kai::test diff --git a/test/reference/quantize.hpp b/test/reference/quantize.hpp index 42645796..3e2f162d 100644 --- a/test/reference/quantize.hpp +++ b/test/reference/quantize.hpp @@ -19,6 +19,14 @@ enum class QuantizationMethod : uint32_t { PER_ROW, ///< Per-row, i.e. one quantization scale and zero point for each row. }; +/// Quantized a float value to an integer datatype using a provided scale. +/// +/// @tparam IntType Quantized integer datatype. +/// +/// @param[in] float The value to quantize +/// @param[in] scale The scale used to quantize the provided float value. +/// +/// @return The quantized data matrix, the quantization scale matrix and the quantization zero point matrix. template IntType quantize_symmetric(float value, float scale); diff --git a/test/reference/reduce.hpp b/test/reference/reduce.hpp index f5455acb..a3ccec7c 100644 --- a/test/reference/reduce.hpp +++ b/test/reference/reduce.hpp @@ -46,9 +46,25 @@ std::vector reduce_add( template std::vector reduce_add_x(const void* src, size_t height, size_t width); +/// Retrieve the minimum value in a provided matrix. +/// +/// @tparam T Datatype of source matrix +/// +/// @param[in] src The input data +/// @param[in] len The number of values within the source matrix. +/// +/// @return The quantized data matrix, the quantization scale matrix and the quantization zero point matrix. template T reduce_min(const void* src, size_t len); +/// Retrieve the maximum value in a provided matrix. +/// +/// @tparam T Datatyoe of source matrix +/// +/// @param[in] src The input data +/// @param[in] len The number of values within the source matrix. +/// +/// @return The quantized data matrix, the quantization scale matrix and the quantization zero point matrix. template T reduce_max(const void* src, size_t len); diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp index b7f56720..985e8166 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp @@ -12,7 +12,7 @@ #include #include "kai/kai_common.h" -#include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.h" +#include "kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.h" #include "kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.h" #include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h" #include "test/common/cpu_info.hpp" @@ -103,17 +103,19 @@ const std::array gemm_variants = { .fn_pack_rhs_get_packed_rhs_size = kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, .fn_pack_rhs_run = kai_run_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme, - .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_mr = kai_get_mr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_nr = kai_get_nr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_kr = kai_get_kr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_sr = kai_get_sr_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_packed_lhs_offset = kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_packed_rhs_offset = kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_dst_offset = kai_get_dst_offset_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_get_dst_size = kai_get_dst_size_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, - .fn_main_run = kai_run_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa, + .fn_main_get_m_step = kai_get_m_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_n_step = kai_get_n_step_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_mr = kai_get_mr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_nr = kai_get_nr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_kr = kai_get_kr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_sr = kai_get_sr_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_packed_lhs_offset = + kai_get_lhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_packed_rhs_offset = + kai_get_rhs_packed_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_dst_offset = kai_get_dst_offset_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_get_dst_size = kai_get_dst_size_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, + .fn_main_run = kai_run_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa, }, }; @@ -370,7 +372,7 @@ TEST_P(ThisTest, EndToEnd) { } // namespace INSTANTIATE_TEST_SUITE_P( - matmul_clamp_qai8_qai8p_qsi8cp, ThisTest, + matmul_clamp_qai8_qai8p_qsi8cxp, ThisTest, testing::Combine( testing::ValuesIn(gemm_variants), testing::ValuesIn(gemm_shapes), testing::ValuesIn(output_portions))); -- GitLab From f314a5e06c8a274502faad9229882ef8ff1347b1 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Thu, 28 Nov 2024 14:28:55 +0000 Subject: [PATCH 08/12] Fix issues caused by rebase. Remove unnecessary clamp reference function. Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 2 -- test/reference/clamp.cpp | 32 ------------------- test/reference/clamp.hpp | 24 -------------- test/reference/quantize.cpp | 4 +-- ...matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp | 2 +- .../matmul_clamp_f32_qai8dxp_qsi4cxp_test.cpp | 4 +-- .../matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp | 1 - 7 files changed, 4 insertions(+), 65 deletions(-) delete mode 100644 test/reference/clamp.cpp delete mode 100644 test/reference/clamp.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 03e2862d..9b801bda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,6 @@ set(KLEIDIAI_FILES_SME kai/ukernels/matmul/pack/kai_rhs_pack_kxn_x16p2vlx2b_x16_x16_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_nxk_x16p2vlx2b_x16_x16_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.c - kai/ukernels/matmul/pack/kai_rhs_pack_kxn_f32pb_f32_f32_16vlx1_sme.c kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.c ) @@ -233,7 +232,6 @@ if(KLEIDIAI_BUILD_TESTS) test/reference/reduce.cpp test/reference/transpose.cpp test/reference/cast.cpp - test/reference/clamp.cpp test/reference/reorder.cpp ) diff --git a/test/reference/clamp.cpp b/test/reference/clamp.cpp deleted file mode 100644 index a4ba773c..00000000 --- a/test/reference/clamp.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates -// -// SPDX-License-Identifier: Apache-2.0 -// - -#include "test/reference/clamp.hpp" - -#include -#include -#include -#include - -#include "test/common/memory.hpp" -#include "test/common/round.hpp" - -namespace kai::test { - -template -std::vector clamp(const void* src, size_t len, T min_value, T max_value) { - std::vector dst(round_up_division(len * size_in_bits, 8)); - - for (size_t i = 0; i < len; ++i) { - write_array(dst.data(), i, std::clamp(read_array(src, i), min_value, max_value)); - } - - return dst; -} - -template std::vector clamp(const void* src, size_t len, float min_value, float max_value); - -} // namespace kai::test diff --git a/test/reference/clamp.hpp b/test/reference/clamp.hpp deleted file mode 100644 index 24d3ac6c..00000000 --- a/test/reference/clamp.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates -// -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include -#include -#include - -namespace kai::test { - -/// Clamps the matrix. -/// -/// @param[in] src Data buffer of the source matrix. -/// @param[in] len Number of values in the source matrix. -/// @param[in] min_value Lower bound of clamp. -/// @param[in] width Upper bound of clamp. -template -std::vector clamp(const void* src, size_t len, T min_value, T max_value); - -} // namespace kai::test diff --git a/test/reference/quantize.cpp b/test/reference/quantize.cpp index ba5b6a28..82a09fd7 100644 --- a/test/reference/quantize.cpp +++ b/test/reference/quantize.cpp @@ -148,7 +148,7 @@ std::vector quantize_symmetric_per_block( } template std::vector quantize_symmetric_per_block( - const void* src, const void* scales, size_t height, size_t width, size_t quant_width, bool is_transposed); + const void* src, const void* scales, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> quantize_symmetric_per_block_dynamic( @@ -180,8 +180,6 @@ template std::tuple, std::vector> quantize_symmetr float, int8_t, float>(const void* src, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> quantize_symmetric_per_block_dynamic< float, int32_t, float>(const void* src, size_t height, size_t width, size_t quant_width); -template std::tuple, std::vector> quantize_symmetric_per_block_dynamic< - float, int8_t, float>(const void* src, size_t height, size_t width, size_t quant_width); template std::tuple, std::vector> compute_asymmetric_per_block_quantization_info( diff --git a/test/tests/matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp b/test/tests/matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp index 0155d7d8..c24361f5 100644 --- a/test/tests/matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp +++ b/test/tests/matmul_clamp_f32_qai8dxp_qsi4c32p_test.cpp @@ -182,7 +182,7 @@ TEST_P(MatMulTest_f32_qmatmul_clamp_f32_qai8dxp_qsi4c32p, EndToEnd_RHS_kxn) { const auto [ref_rhs_qsi4_transposed, ref_rhs_scales] = quantize_symmetric_per_block_dynamic(ref_rhs_transposed.data(), N, K, bl); - auto ref_rhs_qsi4 = transpose( + auto ref_rhs_qsi4 = transpose_with_padding( ref_rhs_qsi4_transposed.data(), N, K, ref_rhs_qsi4_nxk_stride, ref_rhs_qsi4_kxn_stride, ref_rhs_qsi4_kxn_size_bytes); diff --git a/test/tests/matmul_clamp_f32_qai8dxp_qsi4cxp_test.cpp b/test/tests/matmul_clamp_f32_qai8dxp_qsi4cxp_test.cpp index b245e7f5..ecc7f703 100644 --- a/test/tests/matmul_clamp_f32_qai8dxp_qsi4cxp_test.cpp +++ b/test/tests/matmul_clamp_f32_qai8dxp_qsi4cxp_test.cpp @@ -253,7 +253,7 @@ TEST_P(MatMulTest_f32_qai8dxp_qsi4cxp, EndToEnd_RHS_kxn_qsi4cx) { const auto [ref_rhs_qsi4_transposed, ref_rhs_scales] = quantize_symmetric_per_block_dynamic(ref_rhs.data(), N, K, K); - const auto ref_rhs_qsi4 = transpose( + const auto ref_rhs_qsi4 = transpose_with_padding( ref_rhs_qsi4_transposed.data(), N, K, ref_rhs_qsi4_nxk_stride, ref_rhs_qsi4_kxn_stride, ref_rhs_qsi4_kxn_size_bytes); @@ -342,7 +342,7 @@ TEST_P(MatMulTest_f32_qai8dxp_qsi4cxp, EndToEnd_RHS_kxn_qsu4cx) { const auto [ref_rhs_qsi4_transposed, ref_rhs_scales] = quantize_symmetric_per_block_dynamic(ref_rhs.data(), N, K, K); - const auto ref_rhs_qsi4 = transpose( + const auto ref_rhs_qsi4 = transpose_with_padding( ref_rhs_qsi4_transposed.data(), N, K, ref_rhs_qsi4_nxk_stride, ref_rhs_qsi4_kxn_stride, ref_rhs_qsi4_kxn_size_bytes); diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp index 985e8166..415f6a17 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp @@ -21,7 +21,6 @@ #include "test/common/rect.hpp" #include "test/common/sme.hpp" #include "test/reference/binary_elementwise.hpp" -#include "test/reference/clamp.hpp" #include "test/reference/fill.hpp" #include "test/reference/matmul.hpp" #include "test/reference/matmul_pack.hpp" -- GitLab From a46dd7b6e9754a1c3df77b9b27f7eb292cce0cb4 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Thu, 28 Nov 2024 14:53:14 +0000 Subject: [PATCH 09/12] Fix compilation errors Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 1 + .../pack/kai_lhs_pack_x8p2vlx4_x8_sme.c | 6 ++-- test/reference/clamp.cpp | 32 +++++++++++++++++++ test/reference/clamp.hpp | 24 ++++++++++++++ .../matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp | 1 + 5 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/reference/clamp.cpp create mode 100644 test/reference/clamp.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b801bda..aab6439b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -228,6 +228,7 @@ if(KLEIDIAI_BUILD_TESTS) test/reference/fill.cpp test/reference/pack.cpp test/reference/pad.cpp + test/reference/clamp.cpp test/reference/quantize.cpp test/reference/reduce.cpp test/reference/transpose.cpp diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c index 4157ff99..1a71b44c 100644 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c @@ -78,13 +78,15 @@ void kai_run_lhs_pack_x8p2vlx4_x8_sme( const size_t row_offset = 0; const void* in[block_height]; + const uint8_t* lhs_ptr = lhs; + uint8_t* lhs_packed_ptr = lhs_packed; for (size_t block_y = 0; block_y < m; block_y += block_height) { const size_t height = KAI_MIN(m - block_y, block_height); - void* out = lhs_packed + block_y * kai_roundup(k, kai_kr) * sizeof(int8_t); + void* out = lhs_packed_ptr + block_y * kai_roundup(k, kai_kr) * sizeof(int8_t); for (size_t y = 0; y < height; y++) { - in[y] = lhs + (block_y + y) * lhs_stride; + in[y] = lhs_ptr + (block_y + y) * lhs_stride; } __asm__ __volatile__( diff --git a/test/reference/clamp.cpp b/test/reference/clamp.cpp new file mode 100644 index 00000000..a4ba773c --- /dev/null +++ b/test/reference/clamp.cpp @@ -0,0 +1,32 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "test/reference/clamp.hpp" + +#include +#include +#include +#include + +#include "test/common/memory.hpp" +#include "test/common/round.hpp" + +namespace kai::test { + +template +std::vector clamp(const void* src, size_t len, T min_value, T max_value) { + std::vector dst(round_up_division(len * size_in_bits, 8)); + + for (size_t i = 0; i < len; ++i) { + write_array(dst.data(), i, std::clamp(read_array(src, i), min_value, max_value)); + } + + return dst; +} + +template std::vector clamp(const void* src, size_t len, float min_value, float max_value); + +} // namespace kai::test diff --git a/test/reference/clamp.hpp b/test/reference/clamp.hpp new file mode 100644 index 00000000..24d3ac6c --- /dev/null +++ b/test/reference/clamp.hpp @@ -0,0 +1,24 @@ +// +// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +namespace kai::test { + +/// Clamps the matrix. +/// +/// @param[in] src Data buffer of the source matrix. +/// @param[in] len Number of values in the source matrix. +/// @param[in] min_value Lower bound of clamp. +/// @param[in] width Upper bound of clamp. +template +std::vector clamp(const void* src, size_t len, T min_value, T max_value); + +} // namespace kai::test diff --git a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp index 415f6a17..985e8166 100644 --- a/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp +++ b/test/tests/matmul_clamp_qai8_qai8p_qsi8cxp_test.cpp @@ -21,6 +21,7 @@ #include "test/common/rect.hpp" #include "test/common/sme.hpp" #include "test/reference/binary_elementwise.hpp" +#include "test/reference/clamp.hpp" #include "test/reference/fill.hpp" #include "test/reference/matmul.hpp" #include "test/reference/matmul_pack.hpp" -- GitLab From fdb70a2608efbf8db7419b1bd99920b8db587c53 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Fri, 29 Nov 2024 10:43:22 +0000 Subject: [PATCH 10/12] Fix pipeline errors Signed-off-by: Mohammed Suhail Munshi --- CMakeLists.txt | 2 -- test/tests/matmul_clamp_f32_qai8dxp_qsi8cxp_test.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aab6439b..cb8c6a5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,8 +156,6 @@ set(KLEIDIAI_FILES_SME2 kai/ukernels/matmul/matmul_clamp_f32_f32_f32p/kai_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla.c kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa.c kai/ukernels/matmul/matmul_clamp_f16_f16_f16p/kai_matmul_clamp_f16_f16_f16p2vlx2b_1x16vl_sme2_dot.c - kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cpsb_2vlx2vl_sme2_mopa.c - kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cp/kai_matmul_clamp_qai8_qai8p_qsi8cxpsb_2vlx2vl_sme2_mopa.c kai/ukernels/matmul/matmul_clamp_qai8_qai8p_qsi8cxp/kai_matmul_clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa.c ) diff --git a/test/tests/matmul_clamp_f32_qai8dxp_qsi8cxp_test.cpp b/test/tests/matmul_clamp_f32_qai8dxp_qsi8cxp_test.cpp index 6360c433..db3c530f 100644 --- a/test/tests/matmul_clamp_f32_qai8dxp_qsi8cxp_test.cpp +++ b/test/tests/matmul_clamp_f32_qai8dxp_qsi8cxp_test.cpp @@ -162,7 +162,7 @@ TEST_P(MatMulTest_f32_qai8dxp_qsi8cxp, EndToEnd_RHS_kxn_qsi8cx) { const auto [ref_rhs_qsi8_transposed, ref_rhs_scales] = quantize_symmetric_per_block_dynamic(ref_rhs.data(), N, K, K); - const auto ref_rhs_qsi8 = transpose( + const auto ref_rhs_qsi8 = transpose_with_padding( ref_rhs_qsi8_transposed.data(), N, K, ref_rhs_qsi8_nxk_stride, ref_rhs_qsi8_kxn_stride, ref_rhs_qsi8_kxn_size_bytes); -- GitLab From b1e48b1b91f316356885a02ed18f911d039ce511 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Fri, 29 Nov 2024 11:09:20 +0000 Subject: [PATCH 11/12] Fix clang build issue Signed-off-by: Mohammed Suhail Munshi --- kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c index 1a71b44c..59fb328f 100644 --- a/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c +++ b/kai/ukernels/matmul/pack/kai_lhs_pack_x8p2vlx4_x8_sme.c @@ -19,7 +19,7 @@ static const size_t kai_mr = 2; static const size_t kai_kr = 4; static const size_t kai_sr = 1; -static inline size_t kai_get_m_step() { +static inline size_t kai_get_m_step(void) { return (kai_mr * kai_get_sme_vector_length_u8()) / kai_kr; } -- GitLab From 0f76a0a6cc40f96874a7dae597fe688d8b4dcc86 Mon Sep 17 00:00:00 2001 From: Mohammed Suhail Munshi Date: Fri, 29 Nov 2024 11:31:13 +0000 Subject: [PATCH 12/12] Fix bazel build, fix minor issue in documentation Signed-off-by: Mohammed Suhail Munshi --- kai/ukernels/matmul/BUILD.bazel | 17 ++++------------- ..._pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h | 8 ++++---- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/kai/ukernels/matmul/BUILD.bazel b/kai/ukernels/matmul/BUILD.bazel index 11dfe551..2d1134c7 100644 --- a/kai/ukernels/matmul/BUILD.bazel +++ b/kai/ukernels/matmul/BUILD.bazel @@ -317,7 +317,7 @@ kai_c_library( srcs = ["pack/kai_lhs_quant_pack_bf16p8x4_f32_neon.c"], hdrs = ["pack/kai_lhs_quant_pack_bf16p8x4_f32_neon.h"], cpu_uarch = kai_cpu_bf16(), -} +) kai_c_library( name = "lhs_pack_x8p2vlx4_x8_sme", @@ -326,13 +326,6 @@ kai_c_library( cpu_uarch = kai_cpu_sme(), ) -kai_c_library( - name = "lhs_quant_pack_bf16p_f32_neon", - srcs = ["pack/kai_lhs_quant_pack_bf16p_f32_neon.c"], - hdrs = ["pack/kai_lhs_quant_pack_bf16p_f32_neon.h"], - cpu_uarch = kai_cpu_bf16(), -) - kai_c_library( name = "rhs_pack_kxn_f16p16x1biasf16_f16_f16_neon", srcs = ["pack/kai_rhs_pack_kxn_f16p16x1biasf16_f16_f16_neon.c"], @@ -568,16 +561,14 @@ kai_c_library( ":clamp_f32_qsi8d32p_qsi4c32p_dotprod", ":clamp_f32_qsi8d32p_qsi4c32p_i8mm", ":clamp_f32_qsi8d32p_qsi4c32p_interface", + ":clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa", ":kai_files_sme", ":kai_files_sme2", ":lhs_pack_bf16p8x4_f16_neon", ":lhs_pack_f32p2vlx1_f32_sme", + ":lhs_pack_x8p2vlx4_x8_sme", ":lhs_quant_pack_bf16p1x4_f32_neon", ":lhs_quant_pack_bf16p8x4_f32_neon", - ":clamp_qai8_qai8p2vlx4_qsi8cxpsb2vlx4_2vlx2vl_sme2_mopa", - ":lhs_pack_f32p2vlx1_f32_sme", - ":lhs_pack_x8p2vlx4_x8_sme", - ":lhs_quant_pack_bf16p_f32_neon", ":lhs_quant_pack_qai8dxp_f32", ":lhs_quant_pack_qsi8d32p_f32", ":rhs_pack_kxn_bf16p12x4biasf16_f16_neon", @@ -588,9 +579,9 @@ kai_c_library( ":rhs_pack_kxn_f32pbiasf32_f32_f32_neon", ":rhs_pack_kxn_qsi4c32p_qsu4c32s1s0", ":rhs_pack_kxn_qsi4cxp_qs4cxs1s0", + ":rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme", ":rhs_pack_kxn_qsi8cxp_qsi8cx_neon", ":rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme", - ":rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme", ":rhs_pack_nxk_qsi4c32p_qsu4c32s1s0", ":rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0", ":rhs_pack_nxk_qsi4cxp_qs4cxs1s0", diff --git a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h index a4b0fa1a..effb8da9 100644 --- a/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h +++ b/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme.h @@ -44,16 +44,16 @@ size_t kai_get_scale_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t /// Gets the offset in bytes to the data element in the packed RHS buffer. /// -/// @param[in] n_idx Row index. -/// @param[in] k Number of columns. +/// @param[in] n_idx Column index. +/// @param[in] k Number of rows. /// /// @return The offset in bytes to the data element. size_t kai_get_rhs_packed_offset_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n_idx, size_t k); /// Gets the size in bytes of the packed RHS buffer. /// -/// @param[in] n Number of rows. -/// @param[in] k Number of columns. +/// @param[in] n Number of columns. +/// @param[in] k Number of rows. /// /// @return The size in bytes of the packed RHS buffer. size_t kai_get_rhs_packed_size_rhs_pack_kxn_qsi8cxp2vlx4sb_qs8cx_f32_i32_sme(size_t n, size_t k); -- GitLab