1; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-NOFP,CHECK-ATPCS 2; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=all %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-FP-ATPCS,CHECK-ATPCS 3; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none -mattr=+aapcs-frame-chain %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-NOFP,CHECK-AAPCS 4; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=all -mattr=+aapcs-frame-chain %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-FP-AAPCS,CHECK-AAPCS 5 6; struct S { int x[128]; } s; 7; int f(int *, int, int, int, struct S); 8; int g(int *, int, int, int, int, int); 9; int h(int *, int *, int *); 10; int u(int *, int *, int *, struct S, struct S); 11 12%struct.S = type { [128 x i32] } 13%struct.__va_list = type { ptr } 14 15@s = common dso_local global %struct.S zeroinitializer, align 4 16 17declare void @llvm.va_start(ptr) 18declare dso_local i32 @i(i32) local_unnamed_addr 19declare dso_local i32 @g(ptr, i32, i32, i32, i32, i32) local_unnamed_addr 20declare dso_local i32 @f(ptr, i32, i32, i32, ptr byval(%struct.S) align 4) local_unnamed_addr 21declare dso_local i32 @h(ptr, ptr, ptr) local_unnamed_addr 22declare dso_local i32 @u(ptr, ptr, ptr, ptr byval(%struct.S) align 4, ptr byval(%struct.S) align 4) local_unnamed_addr 23 24; 25; Test access to arguments, passed on stack (including varargs) 26; 27 28; Usual case, access via SP if FP is not available 29; int test_args_sp(int a, int b, int c, int d, int e) { 30; int v[4]; 31; return g(v, a, b, c, d, e); 32; } 33define dso_local i32 @test_args_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 34entry: 35 %v = alloca [4 x i32], align 4 36 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 37 ret i32 %call 38} 39; CHECK-LABEL: test_args_sp 40; Load `e` 41; CHECK-NOFP: ldr r0, [sp, #32] 42; CHECK-FP-ATPCS: ldr r0, [r7, #8] 43; CHECK-FP-AAPCS: mov r0, r11 44; CHECK-FP-AAPCS: ldr r0, [r0, #8] 45; CHECK-NEXT: str r3, [sp] 46; Pass `e` on stack 47; CHECK-NEXT: str r0, [sp, #4] 48; CHECK: bl g 49 50; int test_varargs_sp(int a, ...) { 51; int v[4]; 52; __builtin_va_list ap; 53; __builtin_va_start(ap, a); 54; return g(v, a, 0, 0, 0, 0); 55; } 56define dso_local i32 @test_varargs_sp(i32 %a, ...) local_unnamed_addr { 57entry: 58 %v = alloca [4 x i32], align 4 59 %ap = alloca %struct.__va_list, align 4 60 call void @llvm.va_start(ptr nonnull %ap) 61 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 0, i32 0, i32 0, i32 0) 62 ret i32 %call 63} 64; CHECK-LABEL: test_varargs_sp 65; Three incoming varargs in registers 66; CHECK: sub sp, #12 67; CHECK: sub sp, #28 68; Incoming arguments area is accessed via SP if FP is not available 69; CHECK-NOFP: add r0, sp, #36 70; CHECK-NOFP: stm r0!, {r1, r2, r3} 71; CHECK-FP-ATPCS: mov r0, r7 72; CHECK-FP-ATPCS: adds r0, #8 73; CHECK-FP-ATPCS: stm r0!, {r1, r2, r3} 74; CHECK-FP-AAPCS: mov r0, r11 75; CHECK-FP-AAPCS: mov r7, r0 76; CHECK-FP-AAPCS: adds r7, #8 77; CHECK-FP-AAPCS: stm r7!, {r1, r2, r3} 78; Re-aligned stack, access via FP 79; int test_args_realign(int a, int b, int c, int d, int e) { 80; __attribute__((aligned(16))) int v[4]; 81; return g(v, a, b, c, d, e); 82; } 83; Function Attrs: nounwind 84define dso_local i32 @test_args_realign(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 85entry: 86 %v = alloca [4 x i32], align 16 87 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 88 ret i32 %call 89} 90; CHECK-LABEL: test_args_realign 91; Setup frame pointer 92; CHECK-ATPCS: add r7, sp, #8 93; CHECK-AAPCS: mov r11, sp 94; Align stack 95; CHECK: mov r4, sp 96; CHECK-NEXT: lsrs r4, r4, #4 97; CHECK-NEXT: lsls r4, r4, #4 98; CHECK-NEXT: mov sp, r4 99; Load `e` via FP 100; CHECK-ATPCS: ldr r0, [r7, #8] 101; CHECK-AAPCS: mov r0, r11 102; CHECK-AAPCS: ldr r0, [r0, #8] 103; CHECK-NEXT: str r3, [sp] 104; Pass `e` as argument 105; CHECK-NEXT: str r0, [sp, #4] 106; CHECK: bl g 107 108; int test_varargs_realign(int a, ...) { 109; __attribute__((aligned(16))) int v[4]; 110; __builtin_va_list ap; 111; __builtin_va_start(ap, a); 112; return g(v, a, 0, 0, 0, 0); 113; } 114define dso_local i32 @test_varargs_realign(i32 %a, ...) local_unnamed_addr { 115entry: 116 %v = alloca [4 x i32], align 16 117 %ap = alloca %struct.__va_list, align 4 118 call void @llvm.va_start(ptr nonnull %ap) 119 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 0, i32 0, i32 0, i32 0) 120 ret i32 %call 121} 122; CHECK-LABEL: test_varargs_realign 123; Three incoming register varargs 124; CHECK: sub sp, #12 125; Setup frame pointer 126; CHECK-ATPCS: add r7, sp, #8 127; CHECK-AAPCS: mov r11, sp 128; Align stack 129; CHECK: mov r4, sp 130; CHECK-NEXT: lsrs r4, r4, #4 131; CHECK-NEXT: lsls r4, r4, #4 132; CHECK-NEXT: mov sp, r4 133; Incoming register varargs stored via FP 134; CHECK-ATPCS: mov r0, r7 135; CHECK-ATPCS-NEXT: adds r0, #8 136; CHECK-ATPCS-NEXT: stm r0!, {r1, r2, r3} 137; CHECK-AAPCS: mov r0, r11 138; CHECK-AAPCS: mov r7, r0 139; CHECK-AAPCS: adds r7, #8 140; CHECK-AAPCS: stm r7!, {r1, r2, r3} 141; VLAs present, access via FP 142; int test_args_vla(int a, int b, int c, int d, int e) { 143; int v[a]; 144; return g(v, a, b, c, d, e); 145; } 146define dso_local i32 @test_args_vla(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 147entry: 148 %vla = alloca i32, i32 %a, align 4 149 %call = call i32 @g(ptr nonnull %vla, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 150 ret i32 %call 151} 152; CHECK-LABEL: test_args_vla 153; Setup frame pointer 154; CHECK-ATPCS: add r7, sp, #12 155; CHECK-AAPCS: mov r11, sp 156; Allocate outgoing stack arguments space 157; CHECK: sub sp, #8 158; Load `e` via FP 159; CHECK-ATPCS: ldr r5, [r7, #8] 160; CHECK-AAPCS: mov r5, r11 161; CHECK-AAPCS: ldr r5, [r5, #8] 162; Pass `d` and `e` as arguments 163; CHECK-NEXT: str r3, [sp] 164; CHECK-NEXT: str r5, [sp, #4] 165; CHECK: bl g 166 167; int test_varargs_vla(int a, ...) { 168; int v[a]; 169; __builtin_va_list ap; 170; __builtin_va_start(ap, a); 171; return g(v, a, 0, 0, 0, 0); 172; } 173define dso_local i32 @test_varargs_vla(i32 %a, ...) local_unnamed_addr { 174entry: 175 %ap = alloca %struct.__va_list, align 4 176 %vla = alloca i32, i32 %a, align 4 177 call void @llvm.va_start(ptr nonnull %ap) 178 %call = call i32 @g(ptr nonnull %vla, i32 %a, i32 0, i32 0, i32 0, i32 0) 179 ret i32 %call 180} 181; CHECK-LABEL: test_varargs_vla 182; Three incoming register varargs 183; CHECK: sub sp, #12 184; Setup frame pointer 185; CHECK-ATPCS: add r7, sp, #8 186; CHECK-AAPCS: mov r11, sp 187; Register varargs stored via FP 188; CHECK-ATPCS-DAG: str r3, [r7, #16] 189; CHECK-ATPCS-DAG: str r2, [r7, #12] 190; CHECK-ATPCS-DAG: str r1, [r7, #8] 191; CHECK-AAPCS-DAG: mov r5, r11 192; CHECK-AAPCS-DAG: str r1, [r5, #8] 193; CHECK-AAPCS-DAG: mov r1, r11 194; CHECK-AAPCS-DAG: str r3, [r1, #16] 195; CHECK-AAPCS-DAG: mov r1, r11 196; CHECK-AAPCS-DAG: str r2, [r1, #12] 197 198; Moving SP, access via SP 199; int test_args_moving_sp(int a, int b, int c, int d, int e) { 200; int v[4]; 201; return f(v, a, b + c + d, e, s) + h(v, v+1, v+2); 202; } 203define dso_local i32 @test_args_moving_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 204entry: 205 %v = alloca [4 x i32], align 4 206 %add = add nsw i32 %c, %b 207 %add1 = add nsw i32 %add, %d 208 %call = call i32 @f(ptr nonnull %v, i32 %a, i32 %add1, i32 %e, ptr byval(%struct.S) nonnull align 4 @s) 209 %add.ptr = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 1 210 %add.ptr5 = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 2 211 %call6 = call i32 @h(ptr nonnull %v, ptr nonnull %add.ptr, ptr nonnull %add.ptr5) 212 %add7 = add nsw i32 %call6, %call 213 ret i32 %add7 214} 215; CHECK-LABEL: test_args_moving_sp 216; 20 bytes callee-saved area without FP 217; CHECK-NOFP: push {r4, r5, r6, r7, lr} 218; 20 bytes callee-saved area for ATPCS 219; CHECK-FP-ATPCS: push {r4, r5, r6, r7, lr} 220; 24 bytes callee-saved area for AAPCS as codegen prefers an even number of GPRs spilled 221; CHECK-FP-AAPCS: push {lr} 222; CHECK-FP-AAPCS: mov lr, r11 223; CHECK-FP-AAPCS: push {lr} 224; CHECK-FP-AAPCS: push {r4, r5, r6, r7} 225; 20 bytes locals without FP 226; CHECK-NOFP: sub sp, #20 227; 28 bytes locals with FP for ATPCS 228; CHECK-FP-ATPCS: sub sp, #28 229; 24 bytes locals with FP for AAPCS 230; CHECK-FP-AAPCS: sub sp, #24 231; Setup base pointer 232; CHECK: mov r6, sp 233; Allocate outgoing arguments space 234; CHECK: sub sp, #508 235; CHECK: sub sp, #4 236; Load `e` via BP if FP is not present (40 = 20 + 20) 237; CHECK-NOFP: ldr r3, [r6, #40] 238; Load `e` via FP otherwise 239; CHECK-FP-ATPCS: ldr r3, [r7, #8] 240; CHECK-FP-AAPCS: mov r0, r11 241; CHECK-FP-AAPCS: ldr r3, [r0, #8] 242; CHECK: bl f 243; Stack restored before next call 244; CHECK-NEXT: add sp, #508 245; CHECK-NEXT: add sp, #4 246; CHECK: bl h 247 248; int test_varargs_moving_sp(int a, ...) { 249; int v[4]; 250; __builtin_va_list ap; 251; __builtin_va_start(ap, a); 252; return f(v, a, 0, 0, s) + h(v, v+1, v+2); 253; } 254define dso_local i32 @test_varargs_moving_sp(i32 %a, ...) local_unnamed_addr { 255entry: 256 %v = alloca [4 x i32], align 4 257 %ap = alloca %struct.__va_list, align 4 258 call void @llvm.va_start(ptr nonnull %ap) 259 %call = call i32 @f(ptr nonnull %v, i32 %a, i32 0, i32 0, ptr byval(%struct.S) nonnull align 4 @s) 260 %add.ptr = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 1 261 %add.ptr5 = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 2 262 %call6 = call i32 @h(ptr nonnull %v, ptr nonnull %add.ptr, ptr nonnull %add.ptr5) 263 %add = add nsw i32 %call6, %call 264 ret i32 %add 265} 266; CHECK-LABEL: test_varargs_moving_sp 267; Three incoming register varargs 268; CHECK: sub sp, #12 269; 16 bytes callee-saves without FP 270; CHECK-NOFP: push {r4, r5, r6, lr} 271; 24 bytes callee-saves with FP 272; CHECK-FP-ATPCS: push {r4, r5, r6, r7, lr} 273; CHECK-FP-AAPCS: push {lr} 274; CHECK-FP-AAPCS: mov lr, r11 275; CHECK-FP-AAPCS: push {lr} 276; CHECK-FP-AAPCS: push {r4, r5, r6, r7} 277; Locals area 278; CHECK-NOFP: sub sp, #20 279; CHECK-FP-ATPCS: sub sp, #24 280; CHECK-FP-AAPCS: sub sp, #20 281; Incoming varargs stored via BP if FP is not present (36 = 20 + 16) 282; CHECK-NOFP: mov r0, r6 283; CHECK-NOFP-NEXT: adds r0, #36 284; CHECK-NOFP-NEXT: stm r0!, {r1, r2, r3} 285; Incoming varargs stored via FP otherwise 286; CHECK-FP-ATPCS: mov r0, r7 287; CHECK-FP-ATPCS-NEXT: adds r0, #8 288; CHECK-FP-ATPCS-NEXT: stm r0!, {r1, r2, r3} 289; CHECK-FP-AAPCS: mov r0, r11 290; CHECK-FP-AAPCS-NEXT: mov r5, r0 291; CHECK-FP-AAPCS-NEXT: adds r5, #8 292; CHECK-FP-AAPCS-NEXT: stm r5!, {r1, r2, r3} 293 294; struct S { int x[128]; } s; 295; int test(S a, int b) { 296; return i(b); 297; } 298define dso_local i32 @test_args_large_offset(ptr byval(%struct.S) align 4 %0, i32 %1) local_unnamed_addr { 299 %3 = alloca i32, align 4 300 store i32 %1, ptr %3, align 4 301 %4 = load i32, ptr %3, align 4 302 %5 = call i32 @i(i32 %4) 303 ret i32 %5 304} 305; CHECK-LABEL: test_args_large_offset 306; Without FP: Access to large offset is made using SP 307; CHECK-NOFP: ldr r0, [sp, #520] 308; With FP: Access to large offset is made through a const pool using FP 309; CHECK-FP: ldr r0, .LCPI0_0 310; CHECK-FP-ATPCS: ldr r0, [r0, r7] 311; CHECK-FP-AAPCS: add r0, r11 312; CHECK-FP-AAPCS: ldr r0, [r0] 313; CHECK: bl i 314 315; 316; Access to locals 317; 318 319; Usual case, access via SP. 320; int test_local(int n) { 321; int v[4]; 322; int x, y, z; 323; h(&x, &y, &z); 324; return g(v, x, y, z, 0, 0); 325; } 326define dso_local i32 @test_local(i32 %n) local_unnamed_addr { 327entry: 328 %v = alloca [4 x i32], align 4 329 %x = alloca i32, align 4 330 %y = alloca i32, align 4 331 %z = alloca i32, align 4 332 %call = call i32 @h(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z) 333 %0 = load i32, ptr %x, align 4 334 %1 = load i32, ptr %y, align 4 335 %2 = load i32, ptr %z, align 4 336 %call1 = call i32 @g(ptr nonnull %v, i32 %0, i32 %1, i32 %2, i32 0, i32 0) 337 ret i32 %call1 338} 339; CHECK-LABEL: test_local 340; Arguments to `h` relative to SP 341; CHECK: add r0, sp, #20 342; CHECK-NEXT: add r1, sp, #16 343; CHECK-NEXT: add r2, sp, #12 344; CHECK-NEXT: bl h 345; Load `x`, `y`, and `z` via SP 346; CHECK: ldr r1, [sp, #20] 347; CHECK-NEXT: ldr r2, [sp, #16] 348; CHECK-NEXT: ldr r3, [sp, #12] 349; CHECK: bl g 350 351; Re-aligned stack, access via SP. 352; int test_local_realign(int n) { 353; __attribute__((aligned(16))) int v[4]; 354; int x, y, z; 355; h(&x, &y, &z); 356; return g(v, x, y, z, 0, 0); 357; } 358define dso_local i32 @test_local_realign(i32 %n) local_unnamed_addr { 359entry: 360 %v = alloca [4 x i32], align 16 361 %x = alloca i32, align 4 362 %y = alloca i32, align 4 363 %z = alloca i32, align 4 364 %call = call i32 @h(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z) 365 %0 = load i32, ptr %x, align 4 366 %1 = load i32, ptr %y, align 4 367 %2 = load i32, ptr %z, align 4 368 %call1 = call i32 @g(ptr nonnull %v, i32 %0, i32 %1, i32 %2, i32 0, i32 0) 369 ret i32 %call1 370} 371; CHECK-LABEL: test_local_realign 372; Setup frame pointer 373; CHECK-ATPCS: add r7, sp, #8 374; CHECK-AAPCS: mov r11, sp 375; Re-align stack 376; CHECK: mov r4, sp 377; CHECK-NEXT: lsrs r4, r4, #4 378; CHECK-NEXT: lsls r4, r4, #4 379; CHECK-NEXT: mov sp, r4 380; Arguments to `h` computed relative to SP 381; CHECK: add r0, sp, #28 382; CHECK-NEXT: add r1, sp, #24 383; CHECK-NEXT: add r2, sp, #20 384; CHECK-NEXT: bl h 385; Load `x`, `y`, and `z` via SP for passing to `g` 386; CHECK: ldr r1, [sp, #28] 387; CHECK-NEXT: ldr r2, [sp, #24] 388; CHECK-NEXT: ldr r3, [sp, #20] 389; CHECK: bl g 390 391; VLAs, access via BP. 392; int test_local_vla(int n) { 393; int v[n]; 394; int x, y, z; 395; h(&x, &y, &z); 396; return g(v, x, y, z, 0, 0); 397; } 398define dso_local i32 @test_local_vla(i32 %n) local_unnamed_addr { 399entry: 400 %x = alloca i32, align 4 401 %y = alloca i32, align 4 402 %z = alloca i32, align 4 403 %vla = alloca i32, i32 %n, align 4 404 %call = call i32 @h(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z) 405 %0 = load i32, ptr %x, align 4 406 %1 = load i32, ptr %y, align 4 407 %2 = load i32, ptr %z, align 4 408 %call1 = call i32 @g(ptr nonnull %vla, i32 %0, i32 %1, i32 %2, i32 0, i32 0) 409 ret i32 %call1 410} 411; CHECK-LABEL: test_local_vla 412; Setup frame pointer 413; CHECK-ATPCS: add r7, sp, #12 414; CHECK-AAPCS: mov r11, sp 415; Locas area 416; CHECK-ATPCS: sub sp, #12 417; CHECK-AAPCS: sub sp, #16 418; Setup base pointer 419; CHECK: mov r6, sp 420; CHECK-ATPCS: mov r5, r6 421; CHECK-AAPCS: adds r5, r6, #4 422; Arguments to `h` compute relative to BP 423; CHECK: adds r0, r6, #7 424; CHECK-ATPCS-NEXT: adds r0, #1 425; CHECK-ATPCS-NEXT: adds r1, r6, #4 426; CHECK-ATPCS-NEXT: mov r2, r6 427; CHECK-AAPCS-NEXT: adds r0, #5 428; CHECK-AAPCS-NEXT: adds r1, r6, #7 429; CHECK-AAPCS-NEXT: adds r1, #1 430; CHECK-AAPCS-NEXT: adds r2, r6, #4 431; CHECK-NEXT: bl h 432; Load `x`, `y`, `z` via BP (r5 should still have the value of r6 from the move 433; above) 434; CHECK: ldr r3, [r5] 435; CHECK-NEXT: ldr r2, [r5, #4] 436; CHECK-NEXT: ldr r1, [r5, #8] 437; CHECK: bl g 438 439; Moving SP, access via SP. 440; int test_local_moving_sp(int n) { 441; int v[4]; 442; int x, y, z; 443; return u(v, &x, &y, s, s) + u(v, &y, &z, s, s); 444; } 445define dso_local i32 @test_local_moving_sp(i32 %n) local_unnamed_addr { 446entry: 447 %v = alloca [4 x i32], align 4 448 %x = alloca i32, align 4 449 %y = alloca i32, align 4 450 %z = alloca i32, align 4 451 %call = call i32 @u(ptr nonnull %v, ptr nonnull %x, ptr nonnull %y, ptr byval(%struct.S) nonnull align 4 @s, ptr byval(%struct.S) nonnull align 4 @s) 452 %call2 = call i32 @u(ptr nonnull %v, ptr nonnull %y, ptr nonnull %z, ptr byval(%struct.S) nonnull align 4 @s, ptr byval(%struct.S) nonnull align 4 @s) 453 %add = add nsw i32 %call2, %call 454 ret i32 %add 455} 456; CHECK-LABEL: test_local_moving_sp 457; Locals area 458; CHECK-NOFP: sub sp, #36 459; CHECK-FP-ATPCS: sub sp, #44 460; CHECK-FP-AAPCS: sub sp, #40 461; Setup BP 462; CHECK: mov r6, sp 463; Outoging arguments 464; CHECK: sub sp, #508 465; CHECK-NEXT: sub sp, #508 466; CHECK-NEXT: sub sp, #8 467; Argument addresses computed relative to BP 468; CHECK-NOFP: adds r4, r6, #7 469; CHECK-NOFP-NEXT: adds r4, #13 470; CHECK-NOFP: adds r1, r6, #7 471; CHECK-NOFP-NEXT: adds r1, #9 472; CHECK-NOFP: adds r5, r6, #7 473; CHECK-NOFP-NEXT: adds r5, #5 474; CHECK-FP-ATPCS: adds r0, r6, #7 475; CHECK-FP-ATPCS-NEXT: adds r0, #21 476; CHECK-FP-ATPCS: adds r1, r6, #7 477; CHECK-FP-ATPCS-NEXT: adds r1, #17 478; CHECK-FP-ATPCS: adds r5, r6, #7 479; CHECK-FP-ATPCS-NEXT: adds r5, #13 480; CHECK-FP-AAPCS: adds r4, r6, #7 481; CHECK-FP-AAPCS-NEXT: adds r4, #17 482; CHECK-FP-AAPCS: adds r1, r6, #7 483; CHECK-FP-AAPCS-NEXT: adds r1, #13 484; CHECK-FP-AAPCS: adds r5, r6, #7 485; CHECK-FP-AAPCS-NEXT: adds r5, #9 486; CHECK: bl u 487; Stack restored before next call 488; CHECK: add sp, #508 489; CHECK-NEXT: add sp, #508 490; CHECK-NEXT: add sp, #8 491; CHECK: bl u 492