1; RUN: opt -loop-accesses -analyze < %s | FileCheck %s 2 3target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" 4 5; Following cases are no dependence. 6 7; void nodep_Read_Write(int *A) { 8; int *B = A + 1; 9; for (unsigned i = 0; i < 1024; i+=3) 10; B[i] = A[i] + 1; 11; } 12 13; CHECK: function 'nodep_Read_Write': 14; CHECK-NEXT: for.body: 15; CHECK-NEXT: Memory dependences are safe 16; CHECK-NEXT: Dependences: 17; CHECK-NEXT: Run-time memory checks: 18 19define void @nodep_Read_Write(i32* nocapture %A) { 20entry: 21 %add.ptr = getelementptr inbounds i32, i32* %A, i64 1 22 br label %for.body 23 24for.cond.cleanup: ; preds = %for.body 25 ret void 26 27for.body: ; preds = %entry, %for.body 28 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 29 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 30 %0 = load i32, i32* %arrayidx, align 4 31 %add = add nsw i32 %0, 1 32 %arrayidx2 = getelementptr inbounds i32, i32* %add.ptr, i64 %indvars.iv 33 store i32 %add, i32* %arrayidx2, align 4 34 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 3 35 %cmp = icmp ult i64 %indvars.iv.next, 1024 36 br i1 %cmp, label %for.body, label %for.cond.cleanup 37} 38 39; int nodep_Write_Read(int *A) { 40; int sum = 0; 41; for (unsigned i = 0; i < 1024; i+=4) { 42; A[i] = i; 43; sum += A[i+3]; 44; } 45; 46; return sum; 47; } 48 49; CHECK: function 'nodep_Write_Read': 50; CHECK-NEXT: for.body: 51; CHECK-NEXT: Memory dependences are safe 52; CHECK-NEXT: Dependences: 53; CHECK-NEXT: Run-time memory checks: 54 55define i32 @nodep_Write_Read(i32* nocapture %A) { 56entry: 57 br label %for.body 58 59for.cond.cleanup: ; preds = %for.body 60 ret i32 %add3 61 62for.body: ; preds = %entry, %for.body 63 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 64 %sum.013 = phi i32 [ 0, %entry ], [ %add3, %for.body ] 65 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 66 %0 = trunc i64 %indvars.iv to i32 67 store i32 %0, i32* %arrayidx, align 4 68 %1 = or i64 %indvars.iv, 3 69 %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %1 70 %2 = load i32, i32* %arrayidx2, align 4 71 %add3 = add nsw i32 %2, %sum.013 72 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4 73 %cmp = icmp ult i64 %indvars.iv.next, 1024 74 br i1 %cmp, label %for.body, label %for.cond.cleanup 75} 76 77; void nodep_Write_Write(int *A) { 78; for (unsigned i = 0; i < 1024; i+=2) { 79; A[i] = i; 80; A[i+1] = i+1; 81; } 82; } 83 84; CHECK: function 'nodep_Write_Write': 85; CHECK-NEXT: for.body: 86; CHECK-NEXT: Memory dependences are safe 87; CHECK-NEXT: Dependences: 88; CHECK-NEXT: Run-time memory checks: 89 90define void @nodep_Write_Write(i32* nocapture %A) { 91entry: 92 br label %for.body 93 94for.cond.cleanup: ; preds = %for.body 95 ret void 96 97for.body: ; preds = %entry, %for.body 98 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 99 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 100 %0 = trunc i64 %indvars.iv to i32 101 store i32 %0, i32* %arrayidx, align 4 102 %1 = or i64 %indvars.iv, 1 103 %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %1 104 %2 = trunc i64 %1 to i32 105 store i32 %2, i32* %arrayidx3, align 4 106 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 107 %cmp = icmp ult i64 %indvars.iv.next, 1024 108 br i1 %cmp, label %for.body, label %for.cond.cleanup 109} 110 111; Following cases are unsafe depdences and are not vectorizable. 112 113; void unsafe_Read_Write(int *A) { 114; for (unsigned i = 0; i < 1024; i+=3) 115; A[i+3] = A[i] + 1; 116; } 117 118; CHECK: function 'unsafe_Read_Write': 119; CHECK-NEXT: for.body: 120; CHECK-NEXT: Report: unsafe dependent memory operations in loop 121; CHECK-NEXT: Dependences: 122; CHECK-NEXT: Backward: 123; CHECK-NEXT: %0 = load i32, i32* %arrayidx, align 4 -> 124; CHECK-NEXT: store i32 %add, i32* %arrayidx3, align 4 125 126define void @unsafe_Read_Write(i32* nocapture %A) { 127entry: 128 br label %for.body 129 130for.cond.cleanup: ; preds = %for.body 131 ret void 132 133for.body: ; preds = %entry, %for.body 134 %i.010 = phi i32 [ 0, %entry ], [ %add1, %for.body ] 135 %idxprom = zext i32 %i.010 to i64 136 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom 137 %0 = load i32, i32* %arrayidx, align 4 138 %add = add nsw i32 %0, 1 139 %add1 = add i32 %i.010, 3 140 %idxprom2 = zext i32 %add1 to i64 141 %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %idxprom2 142 store i32 %add, i32* %arrayidx3, align 4 143 %cmp = icmp ult i32 %add1, 1024 144 br i1 %cmp, label %for.body, label %for.cond.cleanup 145} 146 147; int unsafe_Write_Read(int *A) { 148; int sum = 0; 149; for (unsigned i = 0; i < 1024; i+=4) { 150; A[i] = i; 151; sum += A[i+4]; 152; } 153; 154; return sum; 155; } 156 157; CHECK: function 'unsafe_Write_Read': 158; CHECK-NEXT: for.body: 159; CHECK-NEXT: Report: unsafe dependent memory operations in loop 160; CHECK-NEXT: Dependences: 161; CHECK-NEXT: Backward: 162; CHECK-NEXT: store i32 %0, i32* %arrayidx, align 4 -> 163; CHECK-NEXT: %1 = load i32, i32* %arrayidx2, align 4 164 165define i32 @unsafe_Write_Read(i32* nocapture %A) { 166entry: 167 br label %for.body 168 169for.cond.cleanup: ; preds = %for.body 170 ret i32 %add3 171 172for.body: ; preds = %entry, %for.body 173 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 174 %sum.013 = phi i32 [ 0, %entry ], [ %add3, %for.body ] 175 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 176 %0 = trunc i64 %indvars.iv to i32 177 store i32 %0, i32* %arrayidx, align 4 178 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4 179 %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv.next 180 %1 = load i32, i32* %arrayidx2, align 4 181 %add3 = add nsw i32 %1, %sum.013 182 %cmp = icmp ult i64 %indvars.iv.next, 1024 183 br i1 %cmp, label %for.body, label %for.cond.cleanup 184} 185 186; void unsafe_Write_Write(int *A) { 187; for (unsigned i = 0; i < 1024; i+=2) { 188; A[i] = i; 189; A[i+2] = i+1; 190; } 191; } 192 193; CHECK: function 'unsafe_Write_Write': 194; CHECK-NEXT: for.body: 195; CHECK-NEXT: Report: unsafe dependent memory operations in loop 196; CHECK-NEXT: Dependences: 197; CHECK-NEXT: Backward: 198; CHECK-NEXT: store i32 %0, i32* %arrayidx, align 4 -> 199; CHECK-NEXT: store i32 %2, i32* %arrayidx3, align 4 200 201define void @unsafe_Write_Write(i32* nocapture %A) { 202entry: 203 br label %for.body 204 205for.cond.cleanup: ; preds = %for.body 206 ret void 207 208for.body: ; preds = %entry, %for.body 209 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 210 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 211 %0 = trunc i64 %indvars.iv to i32 212 store i32 %0, i32* %arrayidx, align 4 213 %1 = or i64 %indvars.iv, 1 214 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 215 %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv.next 216 %2 = trunc i64 %1 to i32 217 store i32 %2, i32* %arrayidx3, align 4 218 %cmp = icmp ult i64 %indvars.iv.next, 1024 219 br i1 %cmp, label %for.body, label %for.cond.cleanup 220} 221 222; Following cases check that strided accesses can be vectorized. 223 224; void vectorizable_Read_Write(int *A) { 225; int *B = A + 4; 226; for (unsigned i = 0; i < 1024; i+=2) 227; B[i] = A[i] + 1; 228; } 229 230; CHECK: function 'vectorizable_Read_Write': 231; CHECK-NEXT: for.body: 232; CHECK-NEXT: Memory dependences are safe 233; CHECK-NEXT: Dependences: 234; CHECK-NEXT: BackwardVectorizable: 235; CHECK-NEXT: %0 = load i32, i32* %arrayidx, align 4 -> 236; CHECK-NEXT: store i32 %add, i32* %arrayidx2, align 4 237 238define void @vectorizable_Read_Write(i32* nocapture %A) { 239entry: 240 %add.ptr = getelementptr inbounds i32, i32* %A, i64 4 241 br label %for.body 242 243for.cond.cleanup: ; preds = %for.body 244 ret void 245 246for.body: ; preds = %entry, %for.body 247 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 248 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 249 %0 = load i32, i32* %arrayidx, align 4 250 %add = add nsw i32 %0, 1 251 %arrayidx2 = getelementptr inbounds i32, i32* %add.ptr, i64 %indvars.iv 252 store i32 %add, i32* %arrayidx2, align 4 253 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 254 %cmp = icmp ult i64 %indvars.iv.next, 1024 255 br i1 %cmp, label %for.body, label %for.cond.cleanup 256} 257 258; int vectorizable_Write_Read(int *A) { 259; int *B = A + 4; 260; int sum = 0; 261; for (unsigned i = 0; i < 1024; i+=2) { 262; A[i] = i; 263; sum += B[i]; 264; } 265; 266; return sum; 267; } 268 269; CHECK: function 'vectorizable_Write_Read': 270; CHECK-NEXT: for.body: 271; CHECK-NEXT: Memory dependences are safe 272; CHECK-NEXT: Dependences: 273; CHECK-NEXT: BackwardVectorizable: 274; CHECK-NEXT: store i32 %0, i32* %arrayidx, align 4 -> 275; CHECK-NEXT: %1 = load i32, i32* %arrayidx2, align 4 276 277define i32 @vectorizable_Write_Read(i32* nocapture %A) { 278entry: 279 %add.ptr = getelementptr inbounds i32, i32* %A, i64 4 280 br label %for.body 281 282for.cond.cleanup: ; preds = %for.body 283 ret i32 %add 284 285for.body: ; preds = %entry, %for.body 286 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 287 %sum.013 = phi i32 [ 0, %entry ], [ %add, %for.body ] 288 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 289 %0 = trunc i64 %indvars.iv to i32 290 store i32 %0, i32* %arrayidx, align 4 291 %arrayidx2 = getelementptr inbounds i32, i32* %add.ptr, i64 %indvars.iv 292 %1 = load i32, i32* %arrayidx2, align 4 293 %add = add nsw i32 %1, %sum.013 294 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 295 %cmp = icmp ult i64 %indvars.iv.next, 1024 296 br i1 %cmp, label %for.body, label %for.cond.cleanup 297} 298 299; void vectorizable_Write_Write(int *A) { 300; int *B = A + 4; 301; for (unsigned i = 0; i < 1024; i+=2) { 302; A[i] = i; 303; B[i] = i+1; 304; } 305; } 306 307; CHECK: function 'vectorizable_Write_Write': 308; CHECK-NEXT: for.body: 309; CHECK-NEXT: Memory dependences are safe 310; CHECK-NEXT: Dependences: 311; CHECK-NEXT: BackwardVectorizable: 312; CHECK-NEXT: store i32 %0, i32* %arrayidx, align 4 -> 313; CHECK-NEXT: store i32 %2, i32* %arrayidx2, align 4 314 315define void @vectorizable_Write_Write(i32* nocapture %A) { 316entry: 317 %add.ptr = getelementptr inbounds i32, i32* %A, i64 4 318 br label %for.body 319 320for.cond.cleanup: ; preds = %for.body 321 ret void 322 323for.body: ; preds = %entry, %for.body 324 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 325 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 326 %0 = trunc i64 %indvars.iv to i32 327 store i32 %0, i32* %arrayidx, align 4 328 %1 = or i64 %indvars.iv, 1 329 %arrayidx2 = getelementptr inbounds i32, i32* %add.ptr, i64 %indvars.iv 330 %2 = trunc i64 %1 to i32 331 store i32 %2, i32* %arrayidx2, align 4 332 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 333 %cmp = icmp ult i64 %indvars.iv.next, 1024 334 br i1 %cmp, label %for.body, label %for.cond.cleanup 335} 336 337; void vectorizable_unscaled_Read_Write(int *A) { 338; int *B = (int *)((char *)A + 14); 339; for (unsigned i = 0; i < 1024; i+=2) 340; B[i] = A[i] + 1; 341; } 342 343; FIXME: This case looks like previous case @vectorizable_Read_Write. It sould 344; be vectorizable. 345 346; CHECK: function 'vectorizable_unscaled_Read_Write': 347; CHECK-NEXT: for.body: 348; CHECK-NEXT: Report: unsafe dependent memory operations in loop 349; CHECK-NEXT: Dependences: 350; CHECK-NEXT: BackwardVectorizableButPreventsForwarding: 351; CHECK-NEXT: %2 = load i32, i32* %arrayidx, align 4 -> 352; CHECK-NEXT: store i32 %add, i32* %arrayidx2, align 4 353 354define void @vectorizable_unscaled_Read_Write(i32* nocapture %A) { 355entry: 356 %0 = bitcast i32* %A to i8* 357 %add.ptr = getelementptr inbounds i8, i8* %0, i64 14 358 %1 = bitcast i8* %add.ptr to i32* 359 br label %for.body 360 361for.cond.cleanup: ; preds = %for.body 362 ret void 363 364for.body: ; preds = %entry, %for.body 365 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 366 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 367 %2 = load i32, i32* %arrayidx, align 4 368 %add = add nsw i32 %2, 1 369 %arrayidx2 = getelementptr inbounds i32, i32* %1, i64 %indvars.iv 370 store i32 %add, i32* %arrayidx2, align 4 371 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 372 %cmp = icmp ult i64 %indvars.iv.next, 1024 373 br i1 %cmp, label %for.body, label %for.cond.cleanup 374} 375 376; int vectorizable_unscaled_Write_Read(int *A) { 377; int *B = (int *)((char *)A + 17); 378; int sum = 0; 379; for (unsigned i = 0; i < 1024; i+=2) { 380; A[i] = i; 381; sum += B[i]; 382; } 383; 384; return sum; 385; } 386 387; CHECK: for function 'vectorizable_unscaled_Write_Read': 388; CHECK-NEXT: for.body: 389; CHECK-NEXT: Memory dependences are safe 390; CHECK-NEXT: Dependences: 391; CHECK-NEXT: BackwardVectorizable: 392; CHECK-NEXT: store i32 %2, i32* %arrayidx, align 4 -> 393; CHECK-NEXT: %3 = load i32, i32* %arrayidx2, align 4 394 395define i32 @vectorizable_unscaled_Write_Read(i32* nocapture %A) { 396entry: 397 %0 = bitcast i32* %A to i8* 398 %add.ptr = getelementptr inbounds i8, i8* %0, i64 17 399 %1 = bitcast i8* %add.ptr to i32* 400 br label %for.body 401 402for.cond.cleanup: ; preds = %for.body 403 ret i32 %add 404 405for.body: ; preds = %entry, %for.body 406 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 407 %sum.013 = phi i32 [ 0, %entry ], [ %add, %for.body ] 408 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 409 %2 = trunc i64 %indvars.iv to i32 410 store i32 %2, i32* %arrayidx, align 4 411 %arrayidx2 = getelementptr inbounds i32, i32* %1, i64 %indvars.iv 412 %3 = load i32, i32* %arrayidx2, align 4 413 %add = add nsw i32 %3, %sum.013 414 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 415 %cmp = icmp ult i64 %indvars.iv.next, 1024 416 br i1 %cmp, label %for.body, label %for.cond.cleanup 417} 418 419; void unsafe_unscaled_Read_Write(int *A) { 420; int *B = (int *)((char *)A + 11); 421; for (unsigned i = 0; i < 1024; i+=2) 422; B[i] = A[i] + 1; 423; } 424 425; CHECK: function 'unsafe_unscaled_Read_Write': 426; CHECK-NEXT: for.body: 427; CHECK-NEXT: Report: unsafe dependent memory operations in loop 428; CHECK-NEXT: Dependences: 429; CHECK-NEXT: Backward: 430; CHECK-NEXT: %2 = load i32, i32* %arrayidx, align 4 -> 431; CHECK-NEXT: store i32 %add, i32* %arrayidx2, align 4 432 433define void @unsafe_unscaled_Read_Write(i32* nocapture %A) { 434entry: 435 %0 = bitcast i32* %A to i8* 436 %add.ptr = getelementptr inbounds i8, i8* %0, i64 11 437 %1 = bitcast i8* %add.ptr to i32* 438 br label %for.body 439 440for.cond.cleanup: ; preds = %for.body 441 ret void 442 443for.body: ; preds = %entry, %for.body 444 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 445 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 446 %2 = load i32, i32* %arrayidx, align 4 447 %add = add nsw i32 %2, 1 448 %arrayidx2 = getelementptr inbounds i32, i32* %1, i64 %indvars.iv 449 store i32 %add, i32* %arrayidx2, align 4 450 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 451 %cmp = icmp ult i64 %indvars.iv.next, 1024 452 br i1 %cmp, label %for.body, label %for.cond.cleanup 453} 454 455; CHECK: function 'unsafe_unscaled_Read_Write2': 456; CHECK-NEXT: for.body: 457; CHECK-NEXT: Report: unsafe dependent memory operations in loop 458; CHECK-NEXT: Dependences: 459; CHECK-NEXT: Backward: 460; CHECK-NEXT: %2 = load i32, i32* %arrayidx, align 4 -> 461; CHECK-NEXT: store i32 %add, i32* %arrayidx2, align 4 462 463; void unsafe_unscaled_Read_Write2(int *A) { 464; int *B = (int *)((char *)A + 1); 465; for (unsigned i = 0; i < 1024; i+=2) 466; B[i] = A[i] + 1; 467; } 468 469define void @unsafe_unscaled_Read_Write2(i32* nocapture %A) { 470entry: 471 %0 = bitcast i32* %A to i8* 472 %add.ptr = getelementptr inbounds i8, i8* %0, i64 1 473 %1 = bitcast i8* %add.ptr to i32* 474 br label %for.body 475 476for.cond.cleanup: ; preds = %for.body 477 ret void 478 479for.body: ; preds = %entry, %for.body 480 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 481 %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv 482 %2 = load i32, i32* %arrayidx, align 4 483 %add = add nsw i32 %2, 1 484 %arrayidx2 = getelementptr inbounds i32, i32* %1, i64 %indvars.iv 485 store i32 %add, i32* %arrayidx2, align 4 486 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 487 %cmp = icmp ult i64 %indvars.iv.next, 1024 488 br i1 %cmp, label %for.body, label %for.cond.cleanup 489} 490 491; Following case checks that interleaved stores have dependences with another 492; store and can not pass dependence check. 493 494; void interleaved_stores(int *A) { 495; int *B = (int *) ((char *)A + 1); 496; for(int i = 0; i < 1024; i+=2) { 497; B[i] = i; // (1) 498; A[i+1] = i + 1; // (2) 499; B[i+1] = i + 1; // (3) 500; } 501; } 502; 503; The access (2) has overlaps with (1) and (3). 504 505; CHECK: function 'interleaved_stores': 506; CHECK-NEXT: for.body: 507; CHECK-NEXT: Report: unsafe dependent memory operations in loop 508; CHECK-NEXT: Dependences: 509; CHECK-NEXT: Backward: 510; CHECK-NEXT: store i32 %4, i32* %arrayidx5, align 4 -> 511; CHECK-NEXT: store i32 %4, i32* %arrayidx9, align 4 512; CHECK: Backward: 513; CHECK-NEXT: store i32 %2, i32* %arrayidx2, align 4 -> 514; CHECK-NEXT: store i32 %4, i32* %arrayidx5, align 4 515 516define void @interleaved_stores(i32* nocapture %A) { 517entry: 518 %0 = bitcast i32* %A to i8* 519 %incdec.ptr = getelementptr inbounds i8, i8* %0, i64 1 520 %1 = bitcast i8* %incdec.ptr to i32* 521 br label %for.body 522 523for.cond.cleanup: ; preds = %for.body 524 ret void 525 526for.body: ; preds = %entry, %for.body 527 %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] 528 %2 = trunc i64 %indvars.iv to i32 529 %arrayidx2 = getelementptr inbounds i32, i32* %1, i64 %indvars.iv 530 store i32 %2, i32* %arrayidx2, align 4 531 %3 = or i64 %indvars.iv, 1 532 %arrayidx5 = getelementptr inbounds i32, i32* %A, i64 %3 533 %4 = trunc i64 %3 to i32 534 store i32 %4, i32* %arrayidx5, align 4 535 %arrayidx9 = getelementptr inbounds i32, i32* %1, i64 %3 536 store i32 %4, i32* %arrayidx9, align 4 537 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 538 %cmp = icmp slt i64 %indvars.iv.next, 1024 539 br i1 %cmp, label %for.body, label %for.cond.cleanup 540} 541