<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/118100>118100</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Vectorizer generates inefficient code for variable-step loop at -Os
</td>
</tr>
<tr>
<th>Labels</th>
<td>
vectorizers,
missed-optimization
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
ostannard
</td>
</tr>
</table>
<pre>
The loop vectorizer can generate very inefficient IR for this code at `-Os`:
```test.c
int printf(const char *format, ...);
#define ARR_SIZE 2
#define STEP 1
char arr[ARR_SIZE];
__attribute((noinline))
void test(int step) {
for (int i = 0; i < ARR_SIZE; i += step) {
arr[i] = 1;
}
}
int main() {
test(STEP);
for (int i = 0; i < ARR_SIZE; ++i)
printf("%d, ", arr[i]);
printf("\n");
}
```
```
$ /work/llvm/build/bin/clang --target=aarch64-none-elf -S test.c -emit-llvm -o - -S -emit-llvm -Os
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-none-elf"
@arr = dso_local local_unnamed_addr global [2 x i8] zeroinitializer, align 4
@.str = private unnamed_addr constant [5 x i8] c"%d, \00", align 1
; Function Attrs: nofree noinline norecurse nosync nounwind optsize memory(write, argmem: none, inaccessiblemem: none)
define dso_local void @test(i32 noundef %step) local_unnamed_addr #0 {
entry:
%0 = sext i32 %step to i64
%smax = tail call i64 @llvm.smax.i64(i64 %0, i64 2)
%1 = add nsw i64 %smax, -1
%2 = udiv i64 %1, %0
%3 = and i64 %2, -16
%broadcast.splatinsert5 = insertelement <16 x i64> poison, i64 %2, i64 0
%broadcast.splat6 = shufflevector <16 x i64> %broadcast.splatinsert5, <16 x i64> poison, <16 x i32> zeroinitializer
br label %vector.body
vector.body: ; preds = %pred.store.continue36, %entry
%index = phi i64 [ 0, %entry ], [ %index.next, %pred.store.continue36 ]
%offset.idx = mul i64 %index, %0
%broadcast.splatinsert = insertelement <16 x i64> poison, i64 %index, i64 0
%broadcast.splat = shufflevector <16 x i64> %broadcast.splatinsert, <16 x i64> poison, <16 x i32> zeroinitializer
%vec.iv = or disjoint <16 x i64> %broadcast.splat, <i64 0, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i64 8, i64 9, i64 10, i64 11, i64 12, i64 13, i64 14, i64 15>
%4 = icmp ule <16 x i64> %vec.iv, %broadcast.splat6
%5 = extractelement <16 x i1> %4, i64 0
br i1 %5, label %pred.store.if, label %pred.store.continue
pred.store.if: ; preds = %vector.body
%6 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %offset.idx
store i8 1, ptr %6, align 1, !tbaa !3
br label %pred.store.continue
pred.store.continue: ; preds = %pred.store.if, %vector.body
%7 = extractelement <16 x i1> %4, i64 1
br i1 %7, label %pred.store.if7, label %pred.store.continue8
pred.store.if7: ; preds = %pred.store.continue
%8 = add i64 %offset.idx, %0
%9 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %8
store i8 1, ptr %9, align 1, !tbaa !3
br label %pred.store.continue8
pred.store.continue8: ; preds = %pred.store.if7, %pred.store.continue
%10 = extractelement <16 x i1> %4, i64 2
br i1 %10, label %pred.store.if9, label %pred.store.continue10
pred.store.if9: ; preds = %pred.store.continue8
%offset.idx37 = or disjoint i64 %index, 2
%11 = mul i64 %offset.idx37, %0
%12 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %11
store i8 1, ptr %12, align 1, !tbaa !3
br label %pred.store.continue10
pred.store.continue10: ; preds = %pred.store.if9, %pred.store.continue8
%13 = extractelement <16 x i1> %4, i64 3
br i1 %13, label %pred.store.if11, label %pred.store.continue12
pred.store.if11: ; preds = %pred.store.continue10
%offset.idx38 = or disjoint i64 %index, 3
%14 = mul i64 %offset.idx38, %0
%15 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %14
store i8 1, ptr %15, align 1, !tbaa !3
br label %pred.store.continue12
pred.store.continue12: ; preds = %pred.store.if11, %pred.store.continue10
%16 = extractelement <16 x i1> %4, i64 4
br i1 %16, label %pred.store.if13, label %pred.store.continue14
pred.store.if13: ; preds = %pred.store.continue12
%offset.idx39 = or disjoint i64 %index, 4
%17 = mul i64 %offset.idx39, %0
%18 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %17
store i8 1, ptr %18, align 1, !tbaa !3
br label %pred.store.continue14
pred.store.continue14: ; preds = %pred.store.if13, %pred.store.continue12
%19 = extractelement <16 x i1> %4, i64 5
br i1 %19, label %pred.store.if15, label %pred.store.continue16
pred.store.if15: ; preds = %pred.store.continue14
%offset.idx40 = or disjoint i64 %index, 5
%20 = mul i64 %offset.idx40, %0
%21 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %20
store i8 1, ptr %21, align 1, !tbaa !3
br label %pred.store.continue16
pred.store.continue16: ; preds = %pred.store.if15, %pred.store.continue14
%22 = extractelement <16 x i1> %4, i64 6
br i1 %22, label %pred.store.if17, label %pred.store.continue18
pred.store.if17: ; preds = %pred.store.continue16
%offset.idx41 = or disjoint i64 %index, 6
%23 = mul i64 %offset.idx41, %0
%24 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %23
store i8 1, ptr %24, align 1, !tbaa !3
br label %pred.store.continue18
pred.store.continue18: ; preds = %pred.store.if17, %pred.store.continue16
%25 = extractelement <16 x i1> %4, i64 7
br i1 %25, label %pred.store.if19, label %pred.store.continue20
pred.store.if19: ; preds = %pred.store.continue18
%offset.idx42 = or disjoint i64 %index, 7
%26 = mul i64 %offset.idx42, %0
%27 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %26
store i8 1, ptr %27, align 1, !tbaa !3
br label %pred.store.continue20
pred.store.continue20: ; preds = %pred.store.if19, %pred.store.continue18
%28 = extractelement <16 x i1> %4, i64 8
br i1 %28, label %pred.store.if21, label %pred.store.continue22
pred.store.if21: ; preds = %pred.store.continue20
%offset.idx43 = or disjoint i64 %index, 8
%29 = mul i64 %offset.idx43, %0
%30 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %29
store i8 1, ptr %30, align 1, !tbaa !3
br label %pred.store.continue22
pred.store.continue22: ; preds = %pred.store.if21, %pred.store.continue20
%31 = extractelement <16 x i1> %4, i64 9
br i1 %31, label %pred.store.if23, label %pred.store.continue24
pred.store.if23: ; preds = %pred.store.continue22
%offset.idx44 = or disjoint i64 %index, 9
%32 = mul i64 %offset.idx44, %0
%33 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %32
store i8 1, ptr %33, align 1, !tbaa !3
br label %pred.store.continue24
pred.store.continue24: ; preds = %pred.store.if23, %pred.store.continue22
%34 = extractelement <16 x i1> %4, i64 10
br i1 %34, label %pred.store.if25, label %pred.store.continue26
pred.store.if25: ; preds = %pred.store.continue24
%offset.idx45 = or disjoint i64 %index, 10
%35 = mul i64 %offset.idx45, %0
%36 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %35
store i8 1, ptr %36, align 1, !tbaa !3
br label %pred.store.continue26
pred.store.continue26: ; preds = %pred.store.if25, %pred.store.continue24
%37 = extractelement <16 x i1> %4, i64 11
br i1 %37, label %pred.store.if27, label %pred.store.continue28
pred.store.if27: ; preds = %pred.store.continue26
%offset.idx46 = or disjoint i64 %index, 11
%38 = mul i64 %offset.idx46, %0
%39 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %38
store i8 1, ptr %39, align 1, !tbaa !3
br label %pred.store.continue28
pred.store.continue28: ; preds = %pred.store.if27, %pred.store.continue26
%40 = extractelement <16 x i1> %4, i64 12
br i1 %40, label %pred.store.if29, label %pred.store.continue30
pred.store.if29: ; preds = %pred.store.continue28
%offset.idx47 = or disjoint i64 %index, 12
%41 = mul i64 %offset.idx47, %0
%42 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %41
store i8 1, ptr %42, align 1, !tbaa !3
br label %pred.store.continue30
pred.store.continue30: ; preds = %pred.store.if29, %pred.store.continue28
%43 = extractelement <16 x i1> %4, i64 13
br i1 %43, label %pred.store.if31, label %pred.store.continue32
pred.store.if31: ; preds = %pred.store.continue30
%offset.idx48 = or disjoint i64 %index, 13
%44 = mul i64 %offset.idx48, %0
%45 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %44
store i8 1, ptr %45, align 1, !tbaa !3
br label %pred.store.continue32
pred.store.continue32: ; preds = %pred.store.if31, %pred.store.continue30
%46 = extractelement <16 x i1> %4, i64 14
br i1 %46, label %pred.store.if33, label %pred.store.continue34
pred.store.if33: ; preds = %pred.store.continue32
%offset.idx49 = or disjoint i64 %index, 14
%47 = mul i64 %offset.idx49, %0
%48 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %47
store i8 1, ptr %48, align 1, !tbaa !3
br label %pred.store.continue34
pred.store.continue34: ; preds = %pred.store.if33, %pred.store.continue32
%49 = extractelement <16 x i1> %4, i64 15
br i1 %49, label %pred.store.if35, label %pred.store.continue36
pred.store.if35: ; preds = %pred.store.continue34
%offset.idx50 = or disjoint i64 %index, 15
%50 = mul i64 %offset.idx50, %0
%51 = getelementptr inbounds [2 x i8], ptr @arr, i64 0, i64 %50
store i8 1, ptr %51, align 1, !tbaa !3
br label %pred.store.continue36
pred.store.continue36: ; preds = %pred.store.if35, %pred.store.continue34
%index.next = add i64 %index, 16
%52 = icmp eq i64 %index, %3
br i1 %52, label %for.cond.cleanup, label %vector.body, !llvm.loop !6
for.cond.cleanup: ; preds = %pred.store.continue36
ret void
}
; Function Attrs: nofree nounwind optsize
define dso_local noundef i32 @main() local_unnamed_addr #1 {
entry:
tail call void @test(i32 noundef 1) #4
%0 = load i8, ptr @arr, align 4, !tbaa !3
%conv = zext i8 %0 to i32
%call = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str, i32 noundef %conv) #4
%1 = load i8, ptr getelementptr inbounds (i8, ptr @arr, i64 1), align 1, !tbaa !3
%conv.c = zext i8 %1 to i32
%call.c = tail call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str, i32 noundef %conv.c) #4
%putchar = tail call i32 @putchar(i32 10)
ret i32 0
}
; Function Attrs: nofree nounwind optsize
declare dso_local noundef i32 @printf(ptr nocapture noundef readonly, ...) local_unnamed_addr #1
; Function Attrs: nofree nounwind
declare noundef i32 @putchar(i32 noundef) local_unnamed_addr #2
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i64 @llvm.smax.i64(i64, i64) #3
attributes #0 = { nofree noinline norecurse nosync nounwind optsize memory(write, argmem: none, inaccessiblemem: none) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+v8a" }
attributes #1 = { nofree nounwind optsize "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+v8a" }
attributes #2 = { nofree nounwind }
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #4 = { optsize }
!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"frame-pointer", i32 1}
!2 = !{!"clang version 20.0.0git (git@github.com:llvm/llvm-project.git 700a90545d68d7f4e40ccee5de67b2f7c7c85d9e)"}
!3 = !{!4, !4, i64 0}
!4 = !{!"omnipotent char", !5, i64 0}
!5 = !{!"Simple C/C++ TBAA"}
!6 = distinct !{!6, !7, !8, !9}
!7 = !{!"llvm.loop.mustprogress"}
!8 = !{!"llvm.loop.isvectorized", i32 1}
!9 = !{!"llvm.loop.unroll.runtime.disable"}
```
The induction variable has been vectorized with a VF of 16, but the body of the loop is done with lots of repeated scalar code. The code emitted at `-O2` and `-O3` is much smaller.
This affects at least `--target=aarch64-none-elf`, `--target=armv8a-none-eabihf` and `--target=x86_64-unknown-linux-gnu`.
This was found by a fuzzer (because a variation on it triggered an unrelated miscompilation in the ARM backend), not real-world code, but I think the code looks "normal" enough to consider this a missed optimisation bug.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzkW09v4zqS_zTMhbAhFiVbPuTgpDvAOwze4vXDHPYS0BJlc1qmvCSVdPrTL0jqnyVZssfG7mEajUQyi8Xij78iy5Ui01rsJefPKHpB0bcnVppDoZ4LbZiUTKVPuyL9ev77wHFeFCf8wRNTKPGbK5wwifdccsUMxx9cfWEheZaJRHBp8B9_4axQ2ByExkmRcswMRqtg8adGqwDRLQq29sH9N1ybZYKCrZAGn5SQJkMQJ4XUBicHpjCCbVaoIzMIXvFyuUSwQfTFqgi2CGjKMyE53v711_uPP_77O4azj3_8_f2_MPHCThtTCkUvtTSKvjW63t-ZMUrsSsMRxAhiWQiZC8ntgLBBwfajECm29iKIrbXa8BOCDUZrqwK7OVdNAiP6DQeIvrjH18a-6hN4se19BbgyT6Dom1NAvHUYo_U3a6X_6bE6MiGdoW3_yjY76Qal682yRsGL8HO1xjSrgQAQRKldAPf42rGzM9CZfPQqnWy9WN70etWr5eu-QogRvH0W6ieCtzz_OCJ425UiT-1vO9W3JGdyjxcLw9SeG0S_MaaSwypcyELyBc8zvPiBPZ_wgh-FWVg1eFHghW3pfvSntkPSF_yPIi1z_oeHG8G6oiOsUbDVRakS_p6JnEt25JUI1CKWad4UnDLDcvZVlKYW4osjolu-OMHaMp6C-2FfyfmrfVqF7sdCxIhuY98kyArRrfth3xoRAlaAQLyQ1Pdd_LBvb_a1a5NR4pQ3RtdQlfKnLD5lA5nvYv-HAVPKiae6eM-LhOXY_XwvpZ1--s7SVOF9XuxYjlH0AvgXFrGl6m-uCiGFESy3u4MjSC72Eode8VIbr_mkxIfdMc40Ol9n0lidUaMz6ZIueg2CmnlOceXSdgXfSpkYUUi8NUZpRLdYFpniHNf-i2WheFIqbZ_0l0ywLEr5KWSKi5PR4jfHR34s1BeC-FMJ6_6W4PsjP3pt0n0iJEsSrrXY5fysybpLtd20yLm9AoVBvV1QcKOmPMMIotrxR_BFQIPKobk06stvl9j2ChyGmv8y2Oqr9GBTYEuPSkgf2S8nZ5jIccLy3LZaUyzzl7Z5acUhdh9DFLjJrUIMtecjiIhTwdIUS_2JK0nb1wovSC0GTqxMxUctQ_wmEQW1CPWaZFpLgFexqgV2qmBpwrRZ6lPOjJCaKxO5Xv6ZW7wtPegrWVl-WF_4jk-F0IWsja8V2-fgguqVx-9QZlnO_WHWV3rRHjetSwY0DdYjvw_cwVqzUzhnO57bEfzQS3u4ehp3P6BbbFl9UjzVlfNG9mWpTaH4MimkEbLkdFUB7UlSTVjIlPvVPx2ExyV6wUFXFLs9-9U11D2Wkv8yldDoWK5TNUaRZZqbpUj9QMcyrxfA6eqv_yic_8bqNsqnVvjfX-A719ev6lJ8OAsKhVOh_1WI4byG41cD-GlV8yMdn_QPtH4I64eofljVD-v6Ia4fNo3CVnWjmzTKSaOdNOpJhOj3GubQL1hyPOHSnSn9OfnJV2vfd7tai3dq_ssolgzXnVSqwvNF3iksiOttP298qENTkV1qqQnsvey8C3Uhzty_gSue-66blt9W9rye0skoLOTO7vb67Jy0ZtpGf9S202xJ3vqWU-6MxSL2fHBdIVp1T0EHODE7xuxvOthqrgGjaZiDZGpf8oswDtD6tnUn5-u-vrzuF5vqKcUjK7--bumv2YTrCcbNaTlcx952uHkcWeLLHNncyZEhbm3LDHrTJFlfPmWa0CO4jS5wThe_043zZTPHFxKMEGbzOMLEw0OUrgdHRv_Eg-qEIaR_4nbV9JlG4HFUI-Qy1wjcSbYR0DtN09hPs21zmW3NShB6G91oj270Mt38UTs5dRjhGyFXEe4avpFghHDxLOFoTbhwgnDxgHDRAwkXThAuupdwQ9Q7TTMBeL2sc4CT1W28Cnu8Wk3w6iLnGjPCMV7Ra79cEBihzWaWNs13ULKe4M1mwJv4gbxZT_Amvpc3Q1Q7TfO8oRO8aRAnm9t4E_V4c_GMExm5GEI3ZqzGeBNdzZtwyJswmOVN1KQTgsu8CYM-b4A8jjcQXOYNkHt5M0S10zTPm2iCNw3iALfxZnXOG59cu8Cb2TibjAXaZH01b1YjvCGzvGl6AZ3gzSAjBeEDeUMneBPey5vLYTiJr-DNRLDdIg43fitf93gz8bX88l5UmwFj8TbZXM2bkXg6hFnerJu5ryZ4AwPerB_Im9UEb9Z38mYE1U7TPG8mwuYWcYhv403c4018mTcwGzfDWNwM5FrewEhYHNJZ3rRz30zwhvZ5Q4MH8mZzmTc0uJc3l-NiuCIuhom4uEWcktt4sznnDb3IDZHBbFwMY3ExXB0Xw0hcHIazvGn-qkJhgjfhgDf0cbyhMMEbei9vLsfFcEVcDBNxcYs4DW_MIvbSxzScIM5sYAxjgTFcHRjDWGAczRKn_TZJownmRAPmPDAlTaMJ5tybix6BtdM0z5yJyLiFnN6af-4loOlEBhpmQ2MYC43h6tAYxkLj1Txzmj_RVumeceasBsx5YH6aTiSo6b0Z6hFYO03zzJmIjVvIwxtT0aSXiw4nctEwGxzTseAYrg6OYSw4nk82t5mIcCLdHA7SzeED083hRLo5vDfdPAJrp2meORPRcQt5eGNWmfTSyuFEWvlyCNTMYyw8pleHx3QsPJ7PGldzsNZP5I3DQd44fGDeOJzIG4f35o1HYO00zTKHTsTHLeThjXlj0kschxOJYzobINOxAJleHSDTsQB5PnHcZrHCicxxOMgchw_MHIcTmePw3szxCKydpnnmTETILeThjZlj0ksdhxOpYzobIdOxCJleHSHTkQg5mk8dkyZ3HE3kjqNB7jh6YO44msgdR_fmjkdg7TTNM2ciQvaQn9WA9esYWpzbIh5oa4H4_4yVfvWOsug8s5wVylqQLpOcM1mezhq7xSMeLFe06KrPEdR59IGKK_5cemVNnbVccePKN7sF1zOFpuflpGP1oHXxpyvcDINO8fZ4DSgZqwFtSzonykuJqwiH1qG8Y-QFSy2jB3SuK3XHyIkgSgrpi9p-u7rT2Gs0hSuGq2WsSb2aU1egGp-MG8OX7dtBmxpxa0RtsiykLPMcp1zxjCsuE852OUcQk6rbUntFvTpaa1x_umRsupccHeIxSKoSvNnvKJUFy6QPEBkBqJL6_4BomfRBOpXGX64YWhQGVWPFKxLUdcHWMewnwX2ekeRMTbhGb_oJO5lS8UZKcZYWMv_qYHbBg26wr2NX35ozLKrGy4PC9KAW5R1Lfrbjn9ej6xNPypwZu7T4U-S54qZUsq1P71ace3svF3dXTK6WnnrLmjsuuqo0t7vh-uX_vGYeI4BMsSNfnOzBzhUCQPQbApCFXOScZQise4AsFkax00nI_eLIzKGRM6rklYw2LPm5OKnCuPNjsSuzjKuFY1wtHley_nbEIjmVTZO7yCSSc4GMM8s73UgheMlOC6aOH3bHQPAiuasORvDyETPX2fnEOcJkiHAPx_8QIOAiEGPStCP9KKcZHShsBmrWo9nZqujj6K4ILbOc7evogdjDGUgVVhJS9ak6iNQH3h1J6GoNOm1uh63UIIBPu9m81-tV7eZho570eq7bngMOVZ1b2-DcJATgL1R9cKXtPgXBMlgGe2HsqbQXBoX25VDulklhXbe6k2V_WYr9iydmaaXXQcA2QRRG6SpO11nIwyBJOI9SvlrvIFsn6ySO0o2_RweNOfTcnDoA6ZR_15LhwPDiKMWpMBZmvztXf6wl0bB3NOj9QxxPOcevCN5e_aU3_PfLdts1zn8JT4U2Qiam7V0lTOs_rZMqmUE2Tc_1YLgmhF0eS21OqtgrrnV3tHiij9DNtct0dFk3E51LqYo8X6pSGnHky1RoHzR06Hh-I-_vA8dCpqU_uT6YEs6rDkzjHeeyvQKa4k9hDpjhf77hIsMemF1psDlwbON3-6mpb44KjdNCct8nL4y2rYqfODM8xTphOVPupugSWwvcnVF-FMa21ndHAa0Cd4_IvVH7JjQ-lskB6yPLc66W9RyExizLeGK07Z1zpr2OyzcHLQB2Jc-F7BbHKhm2E4esa0Ir9ytevXeu1uVClr8We1miVdA16ZNpnNkAAu--MMNZ-fs3dzczdzxhpeaYecAd9IXEwt3h2--5sihIXErFcwfYUeikOJ5E7mWFdEhv__oHtvskl2kVu8rC2HgpX3wWKk8drPUy_YHNQcifrqODOy-Kn9qfM-rIcruNc1mU-4MNZZNCapHy6kovswZo7o4wcRTaW7Er98un9JmmG7phT_yZrCnEmxAC8nR4XiXhmkDK05QRTgiBHSMp7HgU0XVKw-hJPEMAISGwIRFsos2SZ1HIV5vdbr1ZrwlJUBjwIxP50rG7UPsnoXXJnwmJSRA8ua-Q2l1kBmgvKmvvMgjAm7zwJv92Jrtrqt-e1LPb0nblXttASmij20GMMDl__md787m-9azPbjw7CLNCNS6zcFfzHPmZwYs_9VOp8ueDMScbCiJ4Q_DW2V2bG6_d3RXBm5ujRvBWTfPjGf43AAD__7cWEpY">