1// RUN: mlir-opt %s --test-next-access --split-input-file |\ 2// RUN: FileCheck %s --check-prefixes=CHECK,IP 3// RUN: mlir-opt %s --test-next-access='interprocedural=false' \ 4// RUN: --split-input-file |\ 5// RUN: FileCheck %s --check-prefixes=CHECK,LOCAL 6// RUN: mlir-opt %s --test-next-access='assume-func-reads=true' \ 7// RUN: --split-input-file |\ 8// RUN: FileCheck %s --check-prefixes=CHECK,IP_AR 9// RUN: mlir-opt %s \ 10// RUN: --test-next-access='interprocedural=false assume-func-reads=true' \ 11// RUN: --split-input-file | FileCheck %s --check-prefixes=CHECK,LC_AR 12 13// Check prefixes are as follows: 14// 'check': common for all runs; 15// 'ip_ar': interpocedural runs assuming calls to external functions read 16// all arguments; 17// 'ip': interprocedural runs not assuming function calls reading; 18// 'local': local (non-interprocedural) analysis not assuming calls reading; 19// 'lc_ar': local analysis assuming external calls reading all arguments. 20 21// CHECK-LABEL: @trivial 22func.func @trivial(%arg0: memref<f32>, %arg1: f32) -> f32 { 23 // CHECK: name = "store" 24 // CHECK-SAME: next_access = ["unknown", ["load"]] 25 memref.store %arg1, %arg0[] {name = "store"} : memref<f32> 26 // CHECK: name = "load" 27 // CHECK-SAME: next_access = ["unknown"] 28 %0 = memref.load %arg0[] {name = "load"} : memref<f32> 29 return %0 : f32 30} 31 32// CHECK-LABEL: @chain 33func.func @chain(%arg0: memref<f32>, %arg1: f32) -> f32 { 34 // CHECK: name = "store" 35 // CHECK-SAME: next_access = ["unknown", ["load 1"]] 36 memref.store %arg1, %arg0[] {name = "store"} : memref<f32> 37 // CHECK: name = "load 1" 38 // CHECK-SAME: next_access = {{\[}}["load 2"]] 39 %0 = memref.load %arg0[] {name = "load 1"} : memref<f32> 40 // CHECK: name = "load 2" 41 // CHECK-SAME: next_access = ["unknown"] 42 %1 = memref.load %arg0[] {name = "load 2"} : memref<f32> 43 %2 = arith.addf %0, %1 : f32 44 return %2 : f32 45} 46 47// CHECK-LABEL: @branch 48func.func @branch(%arg0: memref<f32>, %arg1: f32, %arg2: i1) -> f32 { 49 // CHECK: name = "store" 50 // CHECK-SAME: next_access = ["unknown", ["load 1", "load 2"]] 51 memref.store %arg1, %arg0[] {name = "store"} : memref<f32> 52 cf.cond_br %arg2, ^bb0, ^bb1 53 54^bb0: 55 %0 = memref.load %arg0[] {name = "load 1"} : memref<f32> 56 cf.br ^bb2(%0 : f32) 57 58^bb1: 59 %1 = memref.load %arg0[] {name = "load 2"} : memref<f32> 60 cf.br ^bb2(%1 : f32) 61 62^bb2(%phi: f32): 63 return %phi : f32 64} 65 66// CHECK-LABEL: @dead_branch 67func.func @dead_branch(%arg0: memref<f32>, %arg1: f32) -> f32 { 68 // CHECK: name = "store" 69 // CHECK-SAME: next_access = ["unknown", ["load 2"]] 70 memref.store %arg1, %arg0[] {name = "store"} : memref<f32> 71 cf.br ^bb1 72 73^bb0: 74 // CHECK: name = "load 1" 75 // CHECK-SAME: next_access = "not computed" 76 %0 = memref.load %arg0[] {name = "load 1"} : memref<f32> 77 cf.br ^bb2(%0 : f32) 78 79^bb1: 80 %1 = memref.load %arg0[] {name = "load 2"} : memref<f32> 81 cf.br ^bb2(%1 : f32) 82 83^bb2(%phi: f32): 84 return %phi : f32 85} 86 87// CHECK-LABEL: @loop 88func.func @loop(%arg0: memref<?xf32>, %arg1: f32, %arg2: index, %arg3: index, %arg4: index) -> f32 { 89 %c0 = arith.constant 0.0 : f32 90 // CHECK: name = "pre" 91 // CHECK-SAME: next_access = {{\[}}["outside", "loop"], "unknown"] 92 memref.load %arg0[%arg4] {name = "pre"} : memref<?xf32> 93 %l = scf.for %i = %arg2 to %arg3 step %arg4 iter_args(%ia = %c0) -> (f32) { 94 // CHECK: name = "loop" 95 // CHECK-SAME: next_access = {{\[}}["outside", "loop"], "unknown"] 96 %0 = memref.load %arg0[%i] {name = "loop"} : memref<?xf32> 97 %1 = arith.addf %ia, %0 : f32 98 scf.yield %1 : f32 99 } 100 %v = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32> 101 %2 = arith.addf %v, %l : f32 102 return %2 : f32 103} 104 105// CHECK-LABEL: @conditional 106func.func @conditional(%cond: i1, %arg0: memref<f32>) { 107 // CHECK: name = "pre" 108 // CHECK-SAME: next_access = {{\[}}["post", "then"]] 109 memref.load %arg0[] {name = "pre"}: memref<f32> 110 scf.if %cond { 111 // CHECK: name = "then" 112 // CHECK-SAME: next_access = {{\[}}["post"]] 113 memref.load %arg0[] {name = "then"} : memref<f32> 114 } 115 memref.load %arg0[] {name = "post"} : memref<f32> 116 return 117} 118 119// CHECK-LABEL: @two_sided_conditional 120func.func @two_sided_conditional(%cond: i1, %arg0: memref<f32>) { 121 // CHECK: name = "pre" 122 // CHECK-SAME: next_access = {{\[}}["then", "else"]] 123 memref.load %arg0[] {name = "pre"}: memref<f32> 124 scf.if %cond { 125 // CHECK: name = "then" 126 // CHECK-SAME: next_access = {{\[}}["post"]] 127 memref.load %arg0[] {name = "then"} : memref<f32> 128 } else { 129 // CHECK: name = "else" 130 // CHECK-SAME: next_access = {{\[}}["post"]] 131 memref.load %arg0[] {name = "else"} : memref<f32> 132 } 133 memref.load %arg0[] {name = "post"} : memref<f32> 134 return 135} 136 137// CHECK-LABEL: @dead_conditional 138func.func @dead_conditional(%arg0: memref<f32>) { 139 %false = arith.constant 0 : i1 140 // CHECK: name = "pre" 141 // CHECK-SAME: next_access = {{\[}}["post"]] 142 memref.load %arg0[] {name = "pre"}: memref<f32> 143 scf.if %false { 144 // CHECK: name = "then" 145 // CHECK-SAME: next_access = "not computed" 146 memref.load %arg0[] {name = "then"} : memref<f32> 147 } 148 memref.load %arg0[] {name = "post"} : memref<f32> 149 return 150} 151 152// CHECK-LABEL: @known_conditional 153func.func @known_conditional(%arg0: memref<f32>) { 154 %false = arith.constant 0 : i1 155 // CHECK: name = "pre" 156 // CHECK-SAME: next_access = {{\[}}["else"]] 157 memref.load %arg0[] {name = "pre"}: memref<f32> 158 scf.if %false { 159 // CHECK: name = "then" 160 // CHECK-SAME: next_access = "not computed" 161 memref.load %arg0[] {name = "then"} : memref<f32> 162 } else { 163 // CHECK: name = "else" 164 // CHECK-SAME: next_access = {{\[}}["post"]] 165 memref.load %arg0[] {name = "else"} : memref<f32> 166 } 167 memref.load %arg0[] {name = "post"} : memref<f32> 168 return 169} 170 171// CHECK-LABEL: @loop_cf 172func.func @loop_cf(%arg0: memref<?xf32>, %arg1: f32, %arg2: index, %arg3: index, %arg4: index) -> f32 { 173 %cst = arith.constant 0.000000e+00 : f32 174 // CHECK: name = "pre" 175 // CHECK-SAME: next_access = {{\[}}["loop", "outside"], "unknown"] 176 %0 = memref.load %arg0[%arg4] {name = "pre"} : memref<?xf32> 177 cf.br ^bb1(%arg2, %cst : index, f32) 178^bb1(%1: index, %2: f32): 179 %3 = arith.cmpi slt, %1, %arg3 : index 180 cf.cond_br %3, ^bb2, ^bb3 181^bb2: 182 // CHECK: name = "loop" 183 // CHECK-SAME: next_access = {{\[}}["loop", "outside"], "unknown"] 184 %4 = memref.load %arg0[%1] {name = "loop"} : memref<?xf32> 185 %5 = arith.addf %2, %4 : f32 186 %6 = arith.addi %1, %arg4 : index 187 cf.br ^bb1(%6, %5 : index, f32) 188^bb3: 189 %7 = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32> 190 %8 = arith.addf %7, %2 : f32 191 return %8 : f32 192} 193 194// CHECK-LABEL: @conditional_cf 195func.func @conditional_cf(%arg0: i1, %arg1: memref<f32>) { 196 // CHECK: name = "pre" 197 // CHECK-SAME: next_access = {{\[}}["then", "post"]] 198 %0 = memref.load %arg1[] {name = "pre"} : memref<f32> 199 cf.cond_br %arg0, ^bb1, ^bb2 200^bb1: 201 // CHECK: name = "then" 202 // CHECK-SAME: next_access = {{\[}}["post"]] 203 %1 = memref.load %arg1[] {name = "then"} : memref<f32> 204 cf.br ^bb2 205^bb2: 206 %2 = memref.load %arg1[] {name = "post"} : memref<f32> 207 return 208} 209 210// CHECK-LABEL: @two_sided_conditional_cf 211func.func @two_sided_conditional_cf(%arg0: i1, %arg1: memref<f32>) { 212 // CHECK: name = "pre" 213 // CHECK-SAME: next_access = {{\[}}["then", "else"]] 214 %0 = memref.load %arg1[] {name = "pre"} : memref<f32> 215 cf.cond_br %arg0, ^bb1, ^bb2 216^bb1: 217 // CHECK: name = "then" 218 // CHECK-SAME: next_access = {{\[}}["post"]] 219 %1 = memref.load %arg1[] {name = "then"} : memref<f32> 220 cf.br ^bb3 221^bb2: 222 // CHECK: name = "else" 223 // CHECK-SAME: next_access = {{\[}}["post"]] 224 %2 = memref.load %arg1[] {name = "else"} : memref<f32> 225 cf.br ^bb3 226^bb3: 227 %3 = memref.load %arg1[] {name = "post"} : memref<f32> 228 return 229} 230 231// CHECK-LABEL: @dead_conditional_cf 232func.func @dead_conditional_cf(%arg0: memref<f32>) { 233 %false = arith.constant false 234 // CHECK: name = "pre" 235 // CHECK-SAME: next_access = {{\[}}["post"]] 236 %0 = memref.load %arg0[] {name = "pre"} : memref<f32> 237 cf.cond_br %false, ^bb1, ^bb2 238^bb1: 239 // CHECK: name = "then" 240 // CHECK-SAME: next_access = "not computed" 241 %1 = memref.load %arg0[] {name = "then"} : memref<f32> 242 cf.br ^bb2 243^bb2: 244 %2 = memref.load %arg0[] {name = "post"} : memref<f32> 245 return 246} 247 248// CHECK-LABEL: @known_conditional_cf 249func.func @known_conditional_cf(%arg0: memref<f32>) { 250 %false = arith.constant false 251 // CHECK: name = "pre" 252 // CHECK-SAME: next_access = {{\[}}["else"]] 253 %0 = memref.load %arg0[] {name = "pre"} : memref<f32> 254 cf.cond_br %false, ^bb1, ^bb2 255^bb1: 256 // CHECK: name = "then" 257 // CHECK-SAME: next_access = "not computed" 258 %1 = memref.load %arg0[] {name = "then"} : memref<f32> 259 cf.br ^bb3 260^bb2: 261 // CHECK: name = "else" 262 // CHECK-SAME: next_access = {{\[}}["post"]] 263 %2 = memref.load %arg0[] {name = "else"} : memref<f32> 264 cf.br ^bb3 265^bb3: 266 %3 = memref.load %arg0[] {name = "post"} : memref<f32> 267 return 268} 269 270// ----- 271 272func.func private @callee1(%arg0: memref<f32>) { 273 // IP: name = "callee1" 274 // IP-SAME: next_access = {{\[}}["post"]] 275 // LOCAL: name = "callee1" 276 // LOCAL-SAME: next_access = ["unknown"] 277 memref.load %arg0[] {name = "callee1"} : memref<f32> 278 return 279} 280 281func.func private @callee2(%arg0: memref<f32>) { 282 // CHECK: name = "callee2" 283 // CHECK-SAME: next_access = "not computed" 284 memref.load %arg0[] {name = "callee2"} : memref<f32> 285 return 286} 287 288// CHECK-LABEL: @simple_call 289func.func @simple_call(%arg0: memref<f32>) { 290 // IP: name = "caller" 291 // IP-SAME: next_access = {{\[}}["callee1"]] 292 // LOCAL: name = "caller" 293 // LOCAL-SAME: next_access = ["unknown"] 294 // LC_AR: name = "caller" 295 // LC_AR-SAME: next_access = {{\[}}["call"]] 296 memref.load %arg0[] {name = "caller"} : memref<f32> 297 func.call @callee1(%arg0) {name = "call"} : (memref<f32>) -> () 298 memref.load %arg0[] {name = "post"} : memref<f32> 299 return 300} 301 302// ----- 303 304// CHECK-LABEL: @infinite_recursive_call 305func.func @infinite_recursive_call(%arg0: memref<f32>) { 306 // IP: name = "pre" 307 // IP-SAME: next_access = {{\[}}["pre"]] 308 // LOCAL: name = "pre" 309 // LOCAL-SAME: next_access = ["unknown"] 310 // LC_AR: name = "pre" 311 // LC_AR-SAME: next_access = {{\[}}["call"]] 312 memref.load %arg0[] {name = "pre"} : memref<f32> 313 func.call @infinite_recursive_call(%arg0) {name = "call"} : (memref<f32>) -> () 314 memref.load %arg0[] {name = "post"} : memref<f32> 315 return 316} 317 318// ----- 319 320// CHECK-LABEL: @recursive_call 321func.func @recursive_call(%arg0: memref<f32>, %cond: i1) { 322 // IP: name = "pre" 323 // IP-SAME: next_access = {{\[}}["post", "pre"]] 324 // LOCAL: name = "pre" 325 // LOCAL-SAME: next_access = ["unknown"] 326 // LC_AR: name = "pre" 327 // LC_AR-SAME: next_access = {{\[}}["post", "call"]] 328 memref.load %arg0[] {name = "pre"} : memref<f32> 329 scf.if %cond { 330 func.call @recursive_call(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> () 331 } 332 memref.load %arg0[] {name = "post"} : memref<f32> 333 return 334} 335 336// ----- 337 338// CHECK-LABEL: @recursive_call_cf 339func.func @recursive_call_cf(%arg0: memref<f32>, %cond: i1) { 340 // IP: name = "pre" 341 // IP-SAME: next_access = {{\[}}["pre", "post"]] 342 // LOCAL: name = "pre" 343 // LOCAL-SAME: next_access = ["unknown"] 344 // LC_AR: name = "pre" 345 // LC_AR-SAME: next_access = {{\[}}["call", "post"]] 346 %0 = memref.load %arg0[] {name = "pre"} : memref<f32> 347 cf.cond_br %cond, ^bb1, ^bb2 348^bb1: 349 call @recursive_call_cf(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> () 350 cf.br ^bb2 351^bb2: 352 %2 = memref.load %arg0[] {name = "post"} : memref<f32> 353 return 354} 355 356// ----- 357 358func.func private @callee1(%arg0: memref<f32>) { 359 // IP: name = "callee1" 360 // IP-SAME: next_access = {{\[}}["post"]] 361 // LOCAL: name = "callee1" 362 // LOCAL-SAME: next_access = ["unknown"] 363 memref.load %arg0[] {name = "callee1"} : memref<f32> 364 return 365} 366 367func.func private @callee2(%arg0: memref<f32>) { 368 // IP: name = "callee2" 369 // IP-SAME: next_access = {{\[}}["post"]] 370 // LOCAL: name = "callee2" 371 // LOCAL-SAME: next_access = ["unknown"] 372 memref.load %arg0[] {name = "callee2"} : memref<f32> 373 return 374} 375 376func.func @conditonal_call(%arg0: memref<f32>, %cond: i1) { 377 // IP: name = "pre" 378 // IP-SAME: next_access = {{\[}}["callee1", "callee2"]] 379 // LOCAL: name = "pre" 380 // LOCAL-SAME: next_access = ["unknown"] 381 // LC_AR: name = "pre" 382 // LC_AR-SAME: next_access = {{\[}}["call1", "call2"]] 383 memref.load %arg0[] {name = "pre"} : memref<f32> 384 scf.if %cond { 385 func.call @callee1(%arg0) {name = "call1"} : (memref<f32>) -> () 386 } else { 387 func.call @callee2(%arg0) {name = "call2"} : (memref<f32>) -> () 388 } 389 memref.load %arg0[] {name = "post"} : memref<f32> 390 return 391} 392 393// ----- 394 395 396// In this test, the "call" operation also accesses %arg0 itself before 397// transferring control flow to the callee. Therefore, the order of accesses is 398// "caller" -> "call" -> "callee" -> "post" 399 400func.func private @callee(%arg0: memref<f32>) { 401 // IP: name = "callee" 402 // IP-SAME: next_access = {{\[}}["post"]] 403 // LOCAL: name = "callee" 404 // LOCAL-SAME: next_access = ["unknown"] 405 memref.load %arg0[] {name = "callee"} : memref<f32> 406 return 407} 408 409// CHECK-LABEL: @call_and_store_before 410func.func @call_and_store_before(%arg0: memref<f32>) { 411 // IP: name = "caller" 412 // IP-SAME: next_access = {{\[}}["call"]] 413 // LOCAL: name = "caller" 414 // LOCAL-SAME: next_access = ["unknown"] 415 // LC_AR: name = "caller" 416 // LC_AR-SAME: next_access = {{\[}}["call"]] 417 memref.load %arg0[] {name = "caller"} : memref<f32> 418 // Note that the access after the entire call is "post". 419 // CHECK: name = "call" 420 // CHECK-SAME: next_access = {{\[}}["post"], ["post"]] 421 test.call_and_store @callee(%arg0), %arg0 {name = "call", store_before_call = true} : (memref<f32>, memref<f32>) -> () 422 // CHECK: name = "post" 423 // CHECK-SAME: next_access = ["unknown"] 424 memref.load %arg0[] {name = "post"} : memref<f32> 425 return 426} 427 428// ----- 429 430// In this test, the "call" operation also accesses %arg0 itself after getting 431// control flow back from the callee. Therefore, the order of accesses is 432// "caller" -> "callee" -> "call" -> "post" 433 434func.func private @callee(%arg0: memref<f32>) { 435 // IP: name = "callee" 436 // IP-SAME: next_access = {{\[}}["call"]] 437 // LOCAL: name = "callee" 438 // LOCAL-SAME: next_access = ["unknown"] 439 memref.load %arg0[] {name = "callee"} : memref<f32> 440 return 441} 442 443// CHECK-LABEL: @call_and_store_after 444func.func @call_and_store_after(%arg0: memref<f32>) { 445 // IP: name = "caller" 446 // IP-SAME: next_access = {{\[}}["callee"]] 447 // LOCAL: name = "caller" 448 // LOCAL-SAME: next_access = ["unknown"] 449 // LC_AR: name = "caller" 450 // LC_AR-SAME: next_access = {{\[}}["call"]] 451 memref.load %arg0[] {name = "caller"} : memref<f32> 452 // CHECK: name = "call" 453 // CHECK-SAME: next_access = {{\[}}["post"], ["post"]] 454 test.call_and_store @callee(%arg0), %arg0 {name = "call", store_before_call = false} : (memref<f32>, memref<f32>) -> () 455 // CHECK: name = "post" 456 // CHECK-SAME: next_access = ["unknown"] 457 memref.load %arg0[] {name = "post"} : memref<f32> 458 return 459} 460 461// ----- 462 463// In this test, the "region" operation also accesses %arg0 itself before 464// entering the region. Therefore: 465// - the next access of "pre" is the "region" operation itself; 466// - at the entry of the block, the next access is "post". 467// CHECK-LABEL: @store_with_a_region 468func.func @store_with_a_region_before(%arg0: memref<f32>) { 469 // CHECK: name = "pre" 470 // CHECK-SAME: next_access = {{\[}}["region"]] 471 memref.load %arg0[] {name = "pre"} : memref<f32> 472 // CHECK: name = "region" 473 // CHECK-SAME: next_access = {{\[}}["post"]] 474 // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["post"]]] 475 test.store_with_a_region %arg0 attributes { name = "region", store_before_region = true } { 476 test.store_with_a_region_terminator 477 } : memref<f32> 478 memref.load %arg0[] {name = "post"} : memref<f32> 479 return 480} 481 482// In this test, the "region" operation also accesses %arg0 itself after 483// exiting from the region. Therefore: 484// - the next access of "pre" is the "region" operation itself; 485// - at the entry of the block, the next access is "region". 486// CHECK-LABEL: @store_with_a_region 487func.func @store_with_a_region_after(%arg0: memref<f32>) { 488 // CHECK: name = "pre" 489 // CHECK-SAME: next_access = {{\[}}["region"]] 490 memref.load %arg0[] {name = "pre"} : memref<f32> 491 // CHECK: name = "region" 492 // CHECK-SAME: next_access = {{\[}}["post"]] 493 // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["region"]]] 494 test.store_with_a_region %arg0 attributes { name = "region", store_before_region = false } { 495 test.store_with_a_region_terminator 496 } : memref<f32> 497 memref.load %arg0[] {name = "post"} : memref<f32> 498 return 499} 500 501// In this test, the operation with a region stores to %arg0 before going to the 502// region. Therefore: 503// - the next access of "pre" is the "region" operation itself; 504// - the next access of the "region" operation (computed as the next access 505// *after* said operation) is the "post" operation; 506// - the next access of the "inner" operation is also "post"; 507// - the next access at the entry point of the region of the "region" operation 508// is the "inner" operation. 509// That is, the order of access is: "pre" -> "region" -> "inner" -> "post". 510// CHECK-LABEL: @store_with_a_region_before_containing_a_load 511func.func @store_with_a_region_before_containing_a_load(%arg0: memref<f32>) { 512 // CHECK: name = "pre" 513 // CHECK-SAME: next_access = {{\[}}["region"]] 514 memref.load %arg0[] {name = "pre"} : memref<f32> 515 // CHECK: name = "region" 516 // CHECK-SAME: next_access = {{\[}}["post"]] 517 // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["inner"]]] 518 test.store_with_a_region %arg0 attributes { name = "region", store_before_region = true } { 519 // CHECK: name = "inner" 520 // CHECK-SAME: next_access = {{\[}}["post"]] 521 memref.load %arg0[] {name = "inner"} : memref<f32> 522 test.store_with_a_region_terminator 523 } : memref<f32> 524 // CHECK: name = "post" 525 // CHECK-SAME: next_access = ["unknown"] 526 memref.load %arg0[] {name = "post"} : memref<f32> 527 return 528} 529 530// In this test, the operation with a region stores to %arg0 after exiting from 531// the region. Therefore: 532// - the next access of "pre" is "inner"; 533// - the next access of the "region" operation (computed as the next access 534// *after* said operation) is the "post" operation); 535// - the next access at the entry point of the region of the "region" operation 536// is the "inner" operation; 537// - the next access of the "inner" operation is the "region" operation itself. 538// That is, the order of access is "pre" -> "inner" -> "region" -> "post". 539// CHECK-LABEL: @store_with_a_region_after_containing_a_load 540func.func @store_with_a_region_after_containing_a_load(%arg0: memref<f32>) { 541 // CHECK: name = "pre" 542 // CHECK-SAME: next_access = {{\[}}["inner"]] 543 memref.load %arg0[] {name = "pre"} : memref<f32> 544 // CHECK: name = "region" 545 // CHECK-SAME: next_access = {{\[}}["post"]] 546 // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["inner"]]] 547 test.store_with_a_region %arg0 attributes { name = "region", store_before_region = false } { 548 // CHECK: name = "inner" 549 // CHECK-SAME: next_access = {{\[}}["region"]] 550 memref.load %arg0[] {name = "inner"} : memref<f32> 551 test.store_with_a_region_terminator 552 } : memref<f32> 553 // CHECK: name = "post" 554 // CHECK-SAME: next_access = ["unknown"] 555 memref.load %arg0[] {name = "post"} : memref<f32> 556 return 557} 558 559// ----- 560 561func.func private @opaque_callee(%arg0: memref<f32>) 562 563// CHECK-LABEL: @call_opaque_callee 564func.func @call_opaque_callee(%arg0: memref<f32>) { 565 // IP: name = "pre" 566 // IP-SAME: next_access = ["unknown"] 567 // IP_AR: name = "pre" 568 // IP_AR-SAME: next_access = {{\[}}["call"]] 569 // LOCAL: name = "pre" 570 // LOCAL-SAME: next_access = ["unknown"] 571 // LC_AR: name = "pre" 572 // LC_AR-SAME: next_access = {{\[}}["call"]] 573 memref.load %arg0[] {name = "pre"} : memref<f32> 574 func.call @opaque_callee(%arg0) {name = "call"} : (memref<f32>) -> () 575 memref.load %arg0[] {name = "post"} : memref<f32> 576 return 577} 578 579// ----- 580 581// CHECK-LABEL: @indirect_call 582func.func @indirect_call(%arg0: memref<f32>, %arg1: (memref<f32>) -> ()) { 583 // IP: name = "pre" 584 // IP-SAME: next_access = ["unknown"] 585 // IP_AR: name = "pre" 586 // IP_AR-SAME: next_access = ["unknown"] 587 // LOCAL: name = "pre" 588 // LOCAL-SAME: next_access = ["unknown"] 589 // LC_AR: name = "pre" 590 // LC_AR-SAME: next_access = {{\[}}["call"]] 591 memref.load %arg0[] {name = "pre"} : memref<f32> 592 func.call_indirect %arg1(%arg0) {name = "call"} : (memref<f32>) -> () 593 memref.load %arg0[] {name = "post"} : memref<f32> 594 return 595} 596