onednn-src 0.1.13

Source of oneAPI Deep Neural Network Library (oneDNN)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
/*******************************************************************************
* Copyright 2024 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

#if USE_LARGE_INDEX == 1
typedef long idx_t;
#else
typedef int idx_t;
#endif

#include "gpu/intel/concat/common.h"
#define unroll_for __attribute__((opencl_unroll_hint)) for

#define SRC_PARAM(n) \
    CS_PARAM(__global const DATA_T *src##n, const idx_t src_ext_offset##n, \
            const idx_t offset##n, const idx_t padded_offset##n, \
            const idx_t src_concat_axis##n)
#define SRC_PARAMS REDUCE(N_INPUTS, JOIN_COMMA, SRC_PARAM)

idx_t get_concat_idx(idx_t inner_idx, idx_t inner_offset) {
    idx_t block = 1;
    idx_t idx = 0;
#define HANDLE(n) \
    idx += block * ((inner_idx / (BLOCK_S##n)) % (BLOCK_B##n)); \
    block *= BLOCK_B##n

    REDUCE(BLOCK_DEPTH, JOIN_SEMICOLON, HANDLE);
#undef HANDLE
    return idx + block * (inner_idx / inner_offset);
}

idx_t get_concat_offset(idx_t concat_idx, idx_t inner_offset) {
    idx_t block = 1;
    idx_t idx = 0;
#define HANDLE(n) \
    idx += (BLOCK_S##n) * ((concat_idx / block) % (BLOCK_B##n)); \
    block *= BLOCK_B##n

    REDUCE(BLOCK_DEPTH, JOIN_SEMICOLON, HANDLE);
#undef HANDLE
    return idx + inner_offset * (concat_idx / block);
}

struct write_info_t {
    idx_t idx;
    bool write;
};

struct write_info_t get_write_info(const idx_t src_idx,
        const idx_t src_ext_offset, const idx_t concat_offset,
        const idx_t dst_ext_offset, const idx_t thread_offset,
        const idx_t concat_axis, const idx_t last_concat_axis,
        const idx_t zero_pad_offset, const idx_t zero_pad_concat_axis,
        const idx_t inner_offset, bool must_compute_ext_idx) {

    idx_t inner_idx, ext_idx;
    if (must_compute_ext_idx) {
        // short circuit logic avoids significant assembly bloat
        // in the special case
        ext_idx = (src_ext_offset == 1) ? src_idx : src_idx / src_ext_offset;
        inner_idx = (src_ext_offset == 1) ? 0 : src_idx % src_ext_offset;
    } else {
        ext_idx = 0;
        inner_idx = src_idx;
    }

    struct write_info_t info;

#if BLOCK_DEPTH > 0
    idx_t concat_idx = get_concat_idx(inner_idx, inner_offset);
    bool write_value = concat_offset + concat_idx < concat_axis;

    idx_t write_offset;
    if (last_concat_axis < zero_pad_concat_axis) {
        idx_t zero_pad_start = zero_pad_offset - concat_axis + thread_offset;
        bool write_zeropad = zero_pad_start + concat_idx < zero_pad_concat_axis;

        write_offset = write_value ? concat_offset : zero_pad_start;
        info.write = write_value || write_zeropad;
    } else {
        write_offset = concat_offset;
        info.write = write_value;
    }
    info.idx = ext_idx * dst_ext_offset + inner_idx
            + get_concat_offset(concat_idx + write_offset, inner_offset)
            - get_concat_offset(concat_idx, inner_offset);
#else

    info.write = true;
    info.idx = ext_idx * dst_ext_offset + inner_offset * concat_offset
            + inner_idx;

#endif
    return info;
}

#if SIMD != 1
__attribute__((intel_reqd_sub_group_size(SIMD)))
#endif
__kernel void
reusable_simple_concat(__global DATA_T *dst, const ulong dst_offset0,
        const ulong dst_ext_offset, SRC_PARAMS, const idx_t zero_pad_offset,
        const idx_t zero_pad_concat_axis, const idx_t read_overlap,
        const idx_t gws0_block, const idx_t inner_offset,
        const uchar must_compute_ext_idx) {
    __global const DATA_T *src;
    idx_t src_ext_offset, input_offset;
    idx_t input_padded_offset, concat_axis_size;

#define CHECK_AND_GET(n) \
    if (get_global_id(2) >= padded_offset##n) { \
        src = src##n; \
        input_offset = offset##n; \
        input_padded_offset = padded_offset##n; \
        concat_axis_size = src_concat_axis##n; \
        src_ext_offset = src_ext_offset##n; \
    }
    REDUCE(N_INPUTS, JOIN_ELSE, CHECK_AND_GET);
#undef CHECK_AND_GET

    const idx_t block_offset
            = gws0_block * (get_global_id(2) - input_padded_offset)
            + (get_global_id(0) / SIMD) * READ_BLOCK;
    const idx_t thr_elems = READ_BLOCK / SIMD;
    src += read_overlap * get_global_id(1) * src_ext_offset + block_offset;

#define CONCAT_AXIS(n) src_concat_axis##n
#define RIGHT(x, y) y
#define WRITE_INFO(idx) \
    get_write_info(block_offset + (idx), src_ext_offset, input_offset, \
            dst_ext_offset, input_padded_offset, concat_axis_size, \
            REDUCE(N_INPUTS, RIGHT, CONCAT_AXIS), zero_pad_offset, \
            zero_pad_concat_axis, inner_offset, must_compute_ext_idx)

#if SIMD == 1
    dst += dst_offset0 + read_overlap * get_global_id(1) * dst_ext_offset;
    for (int i = 0; i < thr_elems; ++i) {
        struct write_info_t info = WRITE_INFO(i);
        if (info.write) dst[info.idx] = src[i];
    }
#else
    const uint lane = get_sub_group_local_id() % SIMD;
    buffer_t buf;

    if (READ_BLOCK * DATA_TYPE_SIZE % 4 != 0) {
        for (int i = 0; i < thr_elems; ++i)
            buf.v1[i] = src[i * SIMD + lane];
    } else {
#define MAYBE_BLOCK_READ(n, read, elems) \
    do { \
        if ((elems) & (n)) { \
            const uint rel = (elems) & ~(((n) << 1) - 1); \
            buf.v##n[rel / (n)] = read(&src[rel * SIMD]); \
        } \
    } while (0)

        for (int i = 0; i < thr_elems / 16; ++i)
            buf.v16[i] = BLOCK_READ16(&src[16 * i * SIMD]);
        MAYBE_BLOCK_READ(8, BLOCK_READ8, thr_elems);
        MAYBE_BLOCK_READ(4, BLOCK_READ4, thr_elems);
        MAYBE_BLOCK_READ(2, BLOCK_READ2, thr_elems);
        MAYBE_BLOCK_READ(1, BLOCK_READ, thr_elems);
#undef MAYBE_BLOCK_READ
    }
    if (lane < READ_BLOCK % SIMD)
        buf.v1[thr_elems] = src[thr_elems * SIMD + lane];

    dst += dst_offset0 + read_overlap * get_global_id(1) * dst_ext_offset;
    if (WRITE_BLOCK * DATA_TYPE_SIZE % 16 != 0) {
        for (int i = 0; i < thr_elems; ++i) {
            struct write_info_t info = WRITE_INFO(i * SIMD + lane);
            if (info.write) dst[info.idx] = buf.v1[i];
        }
        if (lane < READ_BLOCK % SIMD) {
            struct write_info_t info = WRITE_INFO(thr_elems * SIMD + lane);
            if (info.write) dst[info.idx] = buf.v1[thr_elems];
        }
    } else {
        // Break up the data that was read into several blocks that may be written
        // sequentially. If the block size is not divisible by the subgroup size,
        // borrow values from the next block(s), if available, to fill a scattered
        // write.
        const uint elems_per_iteration = MAX(SIMD, WRITE_BLOCK);
        const uint iterations = DIV_UP(READ_BLOCK, elems_per_iteration);

        unroll_for(int j = 0; j < iterations; ++j) {
            const uint buf_off = DIV_UP(j * elems_per_iteration, SIMD);
            const uint block_off = buf_off * SIMD;
            // Accounting for any values borrowed from the last iteration, this
            // block only has `iter_elems` values to write:
            const uint iter_elems = WRITE_BLOCK - (block_off % WRITE_BLOCK);
            const uint thr_iter_elems = iter_elems / SIMD;
            struct write_info_t info = WRITE_INFO(block_off);
            __global DATA_T *iter_dst = dst + info.idx;
#define MAYBE_BLOCK_WRITE(n, write, elems) \
    do { \
        if ((elems) & (n)) { \
            const uint rel = (elems) & ~(((n) << 1) - 1); \
            write(&iter_dst[rel * SIMD], load_vec##n(&buf, buf_off + rel)); \
        } \
    } while (0)

            if (info.write) {
                for (int i = 0; i < thr_iter_elems / 16; ++i)
                    BLOCK_WRITE16(&iter_dst[16 * i * SIMD],
                            load_vec16(&buf, buf_off + i * 16));
                MAYBE_BLOCK_WRITE(8, BLOCK_WRITE8, thr_iter_elems);
                MAYBE_BLOCK_WRITE(4, BLOCK_WRITE4, thr_iter_elems);
                MAYBE_BLOCK_WRITE(2, BLOCK_WRITE2, thr_iter_elems);
                MAYBE_BLOCK_WRITE(1, BLOCK_WRITE, thr_iter_elems);
            }
#undef MAYBE_BLOCK_WRITE

            // Write tail elements + the leading elements of the next block
            if (iter_elems % SIMD) {
                const uint written = block_off + thr_iter_elems * SIMD;
                struct write_info_t info = WRITE_INFO(written + lane);
                if (info.write && lane < MIN(SIMD, READ_BLOCK - written))
                    dst[info.idx] = buf.v1[buf_off + thr_iter_elems];
            }
        }
    }
#endif // SIMD == 1
}

#if BYTES_PER_WORKITEM == 16

inline ulong2 shiftl(ulong2 v, unsigned s) {
    ulong mm0, mm1;
    s &= 127;
    mm0 = ((((s + 127) | s) & 64) >> 6) - 1UL;
    mm1 = (s >> 6) - 1UL;
    s &= 63;
    ulong2 res;
    res.s1 = (v.s0 << s) & (~mm1);
    res.s0 = (v.s0 << s) & (mm1);
    res.s1 |= ((v.s1 << s) | ((v.s0 >> (64 - s)) & mm0)) & mm1;
    return res;
}

inline ulong2 shiftr(ulong2 v, unsigned s) {
    ulong mm0, mm1;
    s &= 127;
    mm0 = ((((s + 127) | s) & 64) >> 6) - 1UL;
    mm1 = (s >> 6) - 1UL;
    s &= 63;
    ulong2 res;
    res.s0 = (v.s1 >> s) & (~mm1);
    res.s1 = (v.s1 >> s) & (mm1);
    res.s0 |= ((v.s0 >> s) | ((v.s1 << (64 - s)) & mm0)) & mm1;
    return res;
}

#define AS_FULL as_ulong2
#define COMPUTE_T ulong2
#define FULLMASK ((ulong2)(0xFFFFFFFFFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL))

#if DATA_TYPE_SIZE == 8
#define NPERSG 2
#define DDATA_T DATA2_T
#define AS_VEC as_ulong2
#define BLOCK_READ_UL BLOCK_READ2
#define BLOCK_WRITE_UL BLOCK_WRITE2
#elif DATA_TYPE_SIZE == 4
#define NPERSG 4
#define DDATA_T DATA4_T
#define AS_VEC as_uint4
#define BLOCK_READ_UL BLOCK_READ4
#define BLOCK_WRITE_UL BLOCK_WRITE4
#elif DATA_TYPE_SIZE == 2
#define NPERSG 8
#define DDATA_T DATA8_T
#define AS_VEC as_ushort8
#define BLOCK_READ_UL BLOCK_READ8
#define BLOCK_WRITE_UL BLOCK_WRITE8
#elif DATA_TYPE_SIZE == 1
#define NPERSG 16
#define DDATA_T DATA16_T
#define AS_VEC as_uchar16
#define BLOCK_READ_UL BLOCK_READ16
#define BLOCK_WRITE_UL BLOCK_WRITE16
#endif

#define SHUFFLE_UP(p, c, dt) \
    (ulong2)(intel_sub_group_shuffle_up((p).s0, (c).s0, (dt)), \
            intel_sub_group_shuffle_up((p).s1, (c).s1, (dt)))
#define SHUFFLE_DOWN(c, n, dt) \
    (ulong2)(intel_sub_group_shuffle_down((c).s0, (n).s0, (dt)), \
            intel_sub_group_shuffle_down((c).s1, (n).s1, (dt)))
#define SHUFFLE(data, c) \
    (ulong2)(intel_sub_group_shuffle((data).s0, (c)), \
            intel_sub_group_shuffle((data).s1, (c)))

#define SHIFTL(v, s) shiftl((v), (s))
#define SHIFTR(v, s) shiftr((v), (s))

#elif BYTES_PER_WORKITEM == 8

#define AS_FULL as_ulong
#define COMPUTE_T ulong
#define FULLMASK 0xFFFFFFFFFFFFFFFFUL
#define SHUFFLE_UP intel_sub_group_shuffle_up
#define SHUFFLE_DOWN intel_sub_group_shuffle_down
#define SHUFFLE intel_sub_group_shuffle

#define DATA1_T DATA_T
#if DATA_TYPE_SIZE == 8
#define NPERSG 1
#define DDATA_T DATA1_T
#define AS_VEC as_ulong
#define BLOCK_READ_UL BLOCK_READ
#define BLOCK_WRITE_UL BLOCK_WRITE
#elif DATA_TYPE_SIZE == 4
#define NPERSG 2
#define DDATA_T DATA2_T
#define AS_VEC as_uint2
#define BLOCK_READ_UL BLOCK_READ2
#define BLOCK_WRITE_UL BLOCK_WRITE2
#elif DATA_TYPE_SIZE == 2
#define NPERSG 4
#define DDATA_T DATA4_T
#define AS_VEC as_ushort4
#define BLOCK_READ_UL BLOCK_READ4
#define BLOCK_WRITE_UL BLOCK_WRITE4
#elif DATA_TYPE_SIZE == 1
#define NPERSG 8
#define DDATA_T DATA8_T
#define AS_VEC as_uchar8
#define BLOCK_READ_UL BLOCK_READ8
#define BLOCK_WRITE_UL BLOCK_WRITE8
#endif

#define SHIFTL(v, s) ((v) << (s))
#define SHIFTR(v, s) ((v) >> (s))

#else

#define SKIP_IP_CONCAT2

#endif

/*
 * This kernel handles internal padding cases by abstracting the problem into 3 cases:
 *     - Reads and writes fall completely into the first source and can be copied as is
 *
 *     - Reads and writes fall across the boundary between two sources, neighboring "read blocks"
 *       across the concat-axis boundary will need to be combined to form a single "write block" that will
 *       consist halfway of each of the source blocks
 *
 *     - Reads and writes that fall completely within the second source will continue to misalign along
 *       the halfway cutoff and will likewise require two reads per write, just with both reads within
 *       the same second source
 * The kernel will perform the reads/writes on aligned boundaries through additional logic that shifts
 * misaligned values in registers to form full cache-lines
 */
#if SIMD != 1
__attribute__((intel_reqd_sub_group_size(SIMD)))
#endif
__kernel void
internal_padding_block_concat2(__global DATA_T *dst,
        const idx_t dst_concat_axis, const idx_t dst_padded_concat_axis,
        __global const DATA_T *src0, const idx_t offset0,
        const idx_t padded_offset0, const idx_t src_concat_axis0,
        const idx_t src_padded_concat_axis0, __global const DATA_T *src1,
        const idx_t offset1, const idx_t padded_offset1,
        const idx_t src_concat_axis1, const idx_t src_padded_concat_axis1,
        const idx_t inner_dim) {
#ifndef SKIP_IP_CONCAT2

    const int dtsize = DATA_TYPE_SIZE;
    const int loads_per_sg = NPERSG;
    const int elems_per_sg = SIMD * loads_per_sg;

#if BLOCK_DEPTH > 0
    const int B0 = BLOCK_B0;
#else
    const int B0 = 1;
#endif

    const idx_t first_boundary_block
            = ((DIV_UP(src_concat_axis0, B0) - 1) * inner_dim);
    const idx_t last_boundary_block = first_boundary_block + inner_dim - 1;
    const idx_t first_zp_block = (dst_concat_axis / B0 * inner_dim);
    const idx_t tot_boundary_block
            = (DIV_UP(dst_padded_concat_axis, B0) * inner_dim);

    const int blocks_per_sg = elems_per_sg
            / B0; // host side check ensures whole multiple of B0 and elems_per_sg > B0

    __global const DATA_T *src;

    // within block idx, ex: 0-7, 0-15, 0-23, 0-31
    idx_t blid = get_sub_group_local_id() % B0;

    idx_t id_start = (get_global_id(0) / SIMD) * elems_per_sg
            + get_sub_group_local_id();
    idx_t bii = id_start / B0;

    idx_t sg_first_bii = (get_global_id(0) / SIMD) * elems_per_sg / B0;

    // index along concat dimension
    idx_t ic = (bii / inner_dim) * B0 + blid;
    idx_t ic_end = ((bii + blocks_per_sg) / inner_dim) * B0 + blid;

    idx_t sg_last_bii = sg_first_bii + blocks_per_sg - 1;

    idx_t ccsz
            = src_concat_axis0; // concat dimension for calculating batched src
    idx_t padded_ccsz
            = padded_offset1; // padded concat dimension for calculating batched src

    idx_t batch_offset0 = inner_dim * padded_ccsz * get_global_id(1);
    idx_t batch_offset1
            = inner_dim * src_padded_concat_axis1 * get_global_id(1);
    idx_t ext_batch_offset
            = inner_dim * dst_padded_concat_axis * get_global_id(1);

    // completely aligned r/w blocks, no boundary src case nor misaligned reads

    if (sg_last_bii < first_boundary_block) {
        COMPUTE_T val = AS_FULL(
                BLOCK_READ_UL(src0 + batch_offset0 + sg_first_bii * B0));
        BLOCK_WRITE_UL(dst + ext_batch_offset + sg_first_bii * B0, AS_VEC(val));
    } else if (sg_first_bii > last_boundary_block) {
        // if sg_bii0 fully within second source, update src_bii+other idx vars to match src1

        idx_t src_blid = (ic - src_concat_axis0) % B0;
        idx_t src_bii = sg_first_bii
                - ((padded_offset1 / B0)
                        * inner_dim); // update inner logical block idx to 0-nblocks read range for src1
        if (src_bii < 0) { src_bii += inner_dim; }

        idx_t sg_last_src_bii = src_bii + blocks_per_sg - 1;

        const int cutoff = (B0 + (offset1 - padded_offset1) % B0)
                % B0; // positive modulo(offset / padded_offset, block size) |--|-----|
        // this cutoff will persist in following misaligned (multi-cacheline / block src)
        // form full write block by combining two read blocks |-A-|--B---|

        COMPUTE_T aVal
                = AS_FULL(BLOCK_READ_UL(src1 + batch_offset1 + src_bii * B0));

        idx_t next_block = src_bii
                + inner_dim; // offset to read next required block for second half of cutoff,
        idx_t next_block_last = next_block + blocks_per_sg - 1;
        const idx_t tot_src1_block
                = (DIV_UP(src_padded_concat_axis1, B0) * inner_dim);

        COMPUTE_T bVal;
        if (next_block_last > tot_src1_block) { // keep reads in bounds
            int n_blocks_tail = tot_src1_block - next_block;
            int rem_elems = n_blocks_tail * B0;

#if NPERSG > 1
            DDATA_T v = 0;
            for (int b = get_sub_group_local_id(), vid = 0; b < rem_elems;
                    b += SIMD, vid++) {
                v[vid] = src1[batch_offset1 + next_block * B0 + b];
            }
            bVal = AS_FULL(v);
#else
            for (int b = get_sub_group_local_id(); b < rem_elems; b += SIMD) {
                bVal = src1[batch_offset1 + next_block * B0 + b];
            }
#endif
        } else {
            bVal = AS_FULL(BLOCK_READ_UL(src1 + batch_offset1
                    + next_block
                            * B0)); //TODO: MAYBE_READ like in simple concat kernel? no perf difference.
        }
        bVal = SHUFFLE_UP(bVal, bVal, cutoff);

        // shift both halves of block to match cutoff
        int sg_shuffle_dt
                = (B0 - cutoff); // required shift to match block load to cutoff

        COMPUTE_T offset_vec_data = SHUFFLE_DOWN(aVal, aVal, sg_shuffle_dt);

        if ((ic % B0) < cutoff) {
            aVal = offset_vec_data;
        } else {
            aVal = bVal;
        }

#if NPERSG > 1
        if (sg_first_bii >= first_zp_block) {
            if ((ic % B0) >= cutoff) { aVal = 0; }
        } else if (sg_last_bii >= first_zp_block) {
            const int blocks_per_simd1 = SIMD / B0;
            const int leading_boundary_shift = first_zp_block - sg_first_bii;
            int block_scaled_leading_shift
                    = DIV_UP(leading_boundary_shift, blocks_per_simd1);
            int rollover = leading_boundary_shift % blocks_per_simd1;

            if (((get_sub_group_local_id() / B0) + rollover)
                    >= blocks_per_simd1)
                block_scaled_leading_shift++;

            if ((ic % B0) >= cutoff) {
                aVal = aVal
                        & SHIFTR(FULLMASK,
                                (NPERSG - block_scaled_leading_shift)
                                        * DATA_TYPE_SIZE * 8);
            }
        }
#else
        if (ic >= dst_concat_axis) { aVal = 0; }
#endif

        if (sg_last_bii >= tot_boundary_block) { // keep writes in bounds
            int n_blocks_tail = tot_boundary_block - sg_first_bii;
            int rem_elems = n_blocks_tail * B0;

#if NPERSG > 1
            for (int b = get_sub_group_local_id(), vid = 0; b < rem_elems;
                    b += SIMD, vid++) {
                //TODO: MAYBE_WRITE like in simple concat kernel? nonstandard CL
                dst[ext_batch_offset + sg_first_bii * B0 + b]
                        = AS_VEC(aVal)[vid];
            }
#else
            for (int b = get_sub_group_local_id(); b < rem_elems; b += SIMD) {
                dst[ext_batch_offset + sg_first_bii * B0 + b] = aVal;
            }
#endif
        } else {
            BLOCK_WRITE_UL(
                    dst + ext_batch_offset + sg_first_bii * B0, AS_VEC(aVal));
        }
    } else { // sg span falls within boundary blocks, handle leading/trailing overlaps
        // load blocks corresponding to first source
        COMPUTE_T aVal = AS_FULL(BLOCK_READ_UL(src0 + batch_offset0
                + sg_first_bii
                        * B0)); // TODO: does this read past end? logic similar to above

        // since these are "boundary" blocks load corresponding blocks from next source
        idx_t next_bii = sg_first_bii
                - ((padded_offset1 / B0)
                        * inner_dim); // update inner logical block idx to 0-nblocks read range for src1
        if (next_bii < 0) {
            next_bii += inner_dim; // bump negative index to next src
            next_bii = (next_bii < 0)
                    ? 0
                    : next_bii; // if next src begins still "negative" clamp to beginning of next src
            // can happen if sg spans [n-2, n-1] vertical blocks
        }

        // with certain inner_dim sizes, sg may span before or after border blocks
        // additional shifting logic will be required in these cases
        int leading_boundary_shift = (sg_first_bii < first_boundary_block)
                ? first_boundary_block - sg_first_bii
                : 0; // sg span begins before boundary span
        int trailing_boundary_shift = (sg_last_bii > last_boundary_block)
                ? sg_last_bii - last_boundary_block
                : 0; // sg span ends after boundary span

        const int cutoff = (B0 + (offset1 - padded_offset1) % B0)
                % B0; // positive modulo( offset/padded_offset , block size) |-|-------|

        int blocks_per_simd1 = SIMD / B0;
        COMPUTE_T bVal = AS_FULL(BLOCK_READ_UL(src1 + batch_offset1
                + next_bii * B0)); // load next blocks across concat boundary
        if (leading_boundary_shift) {
            int block_scaled_leading_shift
                    = leading_boundary_shift / blocks_per_simd1;
            if (blocks_per_simd1 > 1) {
                int rollover = leading_boundary_shift % blocks_per_simd1;

                if (((get_sub_group_local_id() / B0) + rollover)
                        >= blocks_per_simd1)
                    block_scaled_leading_shift++;

                bVal = SHIFTL(bVal,
                        (block_scaled_leading_shift * DATA_TYPE_SIZE * 8));

                // cannot directly shift values to corresponding location in sg since each
                // workitem is responsible for multiple blocks, figure out source block
                int src_bank
                        = ((get_sub_group_local_id() / B0)
                                  + leading_boundary_shift % blocks_per_simd1)
                        % blocks_per_simd1;

                bVal = SHUFFLE(
                        bVal, src_bank * B0 + (get_sub_group_local_id() % B0));

            } else {
                bVal = SHIFTL(bVal,
                        (block_scaled_leading_shift * DATA_TYPE_SIZE * 8));
            }
        }

        int sg_shuffle_dt = cutoff;
        COMPUTE_T offset_vec_data;
        const COMPUTE_T zero = 0;
        offset_vec_data = SHUFFLE_UP(zero, bVal,
                sg_shuffle_dt); // will move src1 block to cutoff

        COMPUTE_T cVal;
        if (trailing_boundary_shift) {
            const idx_t trailing_bii = 0;
            // trailing portion of sg span across boundary will roll over to beginning portion of next tensor
            cVal = AS_FULL(
                    BLOCK_READ_UL(src1 + batch_offset1 + trailing_bii * B0));

            int block_dt = (blocks_per_sg - trailing_boundary_shift);
            int block_dt_from_src1 = sg_first_bii - first_boundary_block;

            // offset trailing portion to match cutoff
            cVal = SHUFFLE_DOWN(cVal, zero, (B0 - cutoff));

            // cannot directly shift values to corresponding location in sg since each
            // workitem is responsible for multiple blocks, figure out source block
            int src_bank = ((get_sub_group_local_id() / B0)
                                   + trailing_boundary_shift % blocks_per_simd1)
                    % blocks_per_simd1;

            int ntrail = 0;
            for (int i = 0; i < NPERSG; ++i) {
                if ((bii + i * blocks_per_simd1) > last_boundary_block) {
                    ntrail = NPERSG - i;
                    break;
                }
            }

            COMPUTE_T trailmask = (ntrail < NPERSG)
                    ? SHIFTR(SHIFTL(FULLMASK, ntrail * DATA_TYPE_SIZE * 8),
                              ntrail * DATA_TYPE_SIZE * 8)
                    : zero;

            if (cutoff > 0 && (ic % B0) >= cutoff) {
                aVal = aVal
                        & trailmask; // zero out portions in anticipation of further trailing/cutoff values
                aVal = aVal | offset_vec_data;
            }

            // reorder according to source banks
            cVal = SHUFFLE(cVal, src_bank * B0 + get_sub_group_local_id() % B0);
            cVal = SHIFTL(cVal, ((NPERSG - ntrail) * DATA_TYPE_SIZE * 8));

            if (cutoff > 0 && (get_sub_group_local_id() % B0) < cutoff) {
                aVal = (aVal & trailmask) | (cVal & ~trailmask);
            }

#if NPERSG > 1
            if (sg_first_bii >= first_zp_block) {
                if ((ic % B0) >= cutoff) { aVal = 0; }
            } else if (sg_last_bii >= first_zp_block) {
                const int blocks_per_simd1 = SIMD / B0;
                const int leading_boundary_shift
                        = first_zp_block - sg_first_bii;
                int block_scaled_leading_shift
                        = (leading_boundary_shift) / blocks_per_simd1;
                int rollover = leading_boundary_shift % blocks_per_simd1;

                if (((get_sub_group_local_id() / B0) + rollover)
                        >= blocks_per_simd1)
                    block_scaled_leading_shift++;

                if ((ic % B0) >= cutoff) {
                    aVal = aVal
                            & SHIFTR(FULLMASK,
                                    (NPERSG - block_scaled_leading_shift)
                                            * DATA_TYPE_SIZE * 8);
                }
            }
#else
            if (ic >= dst_concat_axis) { aVal = 0; }
#endif
        } else {
            // boundary case w/no trailing overlap
            if (cutoff > 0 && (ic % B0) >= cutoff) {
                aVal = aVal | offset_vec_data;
            }
        }
        BLOCK_WRITE_UL(
                dst + ext_batch_offset + sg_first_bii * B0, AS_VEC(aVal));
    }

#endif
}