1// RUN: mlir-opt -split-input-file -test-written-to %s 2>&1 |\ 2// RUN: FileCheck %s --check-prefixes=CHECK,IP 3// RUN: mlir-opt -split-input-file -test-written-to='interprocedural=false' %s \ 4// RUN: 2>&1 | FileCheck %s --check-prefixes=CHECK,LOCAL 5// RUN: mlir-opt -split-input-file \ 6// RUN: -test-written-to='assume-func-writes=true' %s 2>&1 |\ 7// RUN: FileCheck %s --check-prefixes=CHECK,IP_AW 8// RUN: mlir-opt -split-input-file \ 9// RUN: -test-written-to='interprocedural=false assume-func-writes=true' \ 10// RUN: %s 2>&1 | FileCheck %s --check-prefixes=CHECK,LC_AW 11 12// Check prefixes are as follows: 13// 'check': common for all runs; 14// 'ip': interprocedural runs; 15// 'ip_aw': interpocedural runs assuming calls to external functions write to 16// all arguments; 17// 'local': local (non-interprocedural) analysis not assuming calls writing; 18// 'lc_aw': local analysis assuming external calls writing to all arguments. 19 20// Note that despite the name of the test analysis being "written to", it is set 21// up in a peculiar way where passing a value through a block or region argument 22// (via visitCall/BranchOperand) is considered as "writing" that value to the 23// corresponding operand, which is itself a value and not necessarily "memory". 24// This is arguably okay for testing purposes, but may be surprising for readers 25// trying to interpret this test using their intuition. 26 27// CHECK-LABEL: test_tag: constant0 28// CHECK: result #0: [a] 29// CHECK-LABEL: test_tag: constant1 30// CHECK: result #0: [b] 31func.func @test_two_writes(%m0: memref<i32>, %m1: memref<i32>) -> (memref<i32>, memref<i32>) { 32 %c0 = arith.constant {tag = "constant0"} 0 : i32 33 %c1 = arith.constant {tag = "constant1"} 1 : i32 34 memref.store %c0, %m0[] {tag_name = "a"} : memref<i32> 35 memref.store %c1, %m1[] {tag_name = "b"} : memref<i32> 36 return %m0, %m1 : memref<i32>, memref<i32> 37} 38 39// ----- 40 41// CHECK-LABEL: test_tag: c0 42// CHECK: result #0: [b] 43// CHECK-LABEL: test_tag: c1 44// CHECK: result #0: [b] 45// CHECK-LABEL: test_tag: condition 46// CHECK: result #0: [brancharg0] 47// CHECK-LABEL: test_tag: c2 48// CHECK: result #0: [a] 49// CHECK-LABEL: test_tag: c3 50// CHECK: result #0: [a] 51func.func @test_if(%m0: memref<i32>, %m1: memref<i32>, %condition: i1) { 52 %c0 = arith.constant {tag = "c0"} 2 : i32 53 %c1 = arith.constant {tag = "c1"} 3 : i32 54 %condition2 = arith.addi %condition, %condition {tag = "condition"} : i1 55 %0, %1 = scf.if %condition2 -> (i32, i32) { 56 %c2 = arith.constant {tag = "c2"} 0 : i32 57 scf.yield %c2, %c0: i32, i32 58 } else { 59 %c3 = arith.constant {tag = "c3"} 1 : i32 60 scf.yield %c3, %c1: i32, i32 61 } 62 memref.store %0, %m0[] {tag_name = "a"} : memref<i32> 63 memref.store %1, %m1[] {tag_name = "b"} : memref<i32> 64 return 65} 66 67// ----- 68 69// CHECK-LABEL: test_tag: c0 70// CHECK: result #0: [a c] 71// CHECK-LABEL: test_tag: c1 72// CHECK: result #0: [b c] 73// CHECK-LABEL: test_tag: br 74// CHECK: operand #0: [brancharg0] 75func.func @test_blocks(%m0: memref<i32>, 76 %m1: memref<i32>, 77 %m2: memref<i32>, %cond : i1) { 78 %0 = arith.constant {tag = "c0"} 0 : i32 79 %1 = arith.constant {tag = "c1"} 1 : i32 80 cf.cond_br %cond, ^a(%0: i32), ^b(%1: i32) {tag = "br"} 81^a(%a0: i32): 82 memref.store %a0, %m0[] {tag_name = "a"} : memref<i32> 83 cf.br ^c(%a0 : i32) 84^b(%b0: i32): 85 memref.store %b0, %m1[] {tag_name = "b"} : memref<i32> 86 cf.br ^c(%b0 : i32) 87^c(%c0 : i32): 88 memref.store %c0, %m2[] {tag_name = "c"} : memref<i32> 89 return 90} 91 92// ----- 93 94// CHECK-LABEL: test_tag: two 95// CHECK: result #0: [a] 96func.func @test_infinite_loop(%m0: memref<i32>) { 97 %0 = arith.constant 0 : i32 98 %1 = arith.constant 1 : i32 99 %2 = arith.constant {tag = "two"} 2 : i32 100 %3 = arith.constant -1 : i32 101 cf.br ^loop(%0, %1, %2: i32, i32, i32) 102^loop(%a: i32, %b: i32, %c: i32): 103 memref.store %a, %m0[] {tag_name = "a"} : memref<i32> 104 cf.br ^loop(%b, %c, %3 : i32, i32, i32) 105} 106 107// ----- 108 109// CHECK-LABEL: test_tag: c0 110// CHECK: result #0: [a b c] 111func.func @test_switch(%flag: i32, %m0: memref<i32>) { 112 %0 = arith.constant {tag = "c0"} 0 : i32 113 cf.switch %flag : i32, [ 114 default: ^a(%0 : i32), 115 42: ^b(%0 : i32), 116 43: ^c(%0 : i32) 117 ] 118^a(%a0: i32): 119 memref.store %a0, %m0[] {tag_name = "a"} : memref<i32> 120 cf.br ^c(%a0 : i32) 121^b(%b0: i32): 122 memref.store %b0, %m0[] {tag_name = "b"} : memref<i32> 123 cf.br ^c(%b0 : i32) 124^c(%c0 : i32): 125 memref.store %c0, %m0[] {tag_name = "c"} : memref<i32> 126 return 127} 128 129// ----- 130 131// CHECK-LABEL: test_tag: add 132// IP: result #0: [a] 133// LOCAL: result #0: [callarg0] 134// LC_AW: result #0: [func.call] 135func.func @test_caller(%m0: memref<f32>, %arg: f32) { 136 %0 = arith.addf %arg, %arg {tag = "add"} : f32 137 %1 = func.call @callee(%0) : (f32) -> f32 138 %2 = arith.mulf %1, %1 : f32 139 %3 = arith.mulf %2, %2 : f32 140 %4 = arith.mulf %3, %3 : f32 141 memref.store %4, %m0[] {tag_name = "a"} : memref<f32> 142 return 143} 144 145func.func private @callee(%0 : f32) -> f32 { 146 %1 = arith.mulf %0, %0 : f32 147 %2 = arith.mulf %1, %1 : f32 148 func.return %2 : f32 149} 150 151// ----- 152 153func.func private @callee(%0 : f32) -> f32 { 154 %1 = arith.mulf %0, %0 : f32 155 func.return %1 : f32 156} 157 158// CHECK-LABEL: test_tag: sub 159// IP: result #0: [a] 160// LOCAL: result #0: [callarg0] 161// LC_AW: result #0: [func.call] 162func.func @test_caller_below_callee(%m0: memref<f32>, %arg: f32) { 163 %0 = arith.subf %arg, %arg {tag = "sub"} : f32 164 %1 = func.call @callee(%0) : (f32) -> f32 165 memref.store %1, %m0[] {tag_name = "a"} : memref<f32> 166 return 167} 168 169// ----- 170 171func.func private @callee1(%0 : f32) -> f32 { 172 %1 = func.call @callee2(%0) : (f32) -> f32 173 func.return %1 : f32 174} 175 176func.func private @callee2(%0 : f32) -> f32 { 177 %1 = func.call @callee3(%0) : (f32) -> f32 178 func.return %1 : f32 179} 180 181func.func private @callee3(%0 : f32) -> f32 { 182 func.return %0 : f32 183} 184 185// CHECK-LABEL: test_tag: mul 186// IP: result #0: [a] 187// LOCAL: result #0: [callarg0] 188// LC_AW: result #0: [func.call] 189func.func @test_callchain(%m0: memref<f32>, %arg: f32) { 190 %0 = arith.mulf %arg, %arg {tag = "mul"} : f32 191 %1 = func.call @callee1(%0) : (f32) -> f32 192 memref.store %1, %m0[] {tag_name = "a"} : memref<f32> 193 return 194} 195 196// ----- 197 198// CHECK-LABEL: test_tag: zero 199// CHECK: result #0: [c] 200// CHECK-LABEL: test_tag: init 201// CHECK: result #0: [a b c] 202// CHECK-LABEL: test_tag: condition 203// CHECK: operand #0: [brancharg0] 204// CHECK: operand #2: [a b c] 205func.func @test_while(%m0: memref<i32>, %init : i32, %cond: i1) { 206 %zero = arith.constant {tag = "zero"} 0 : i32 207 %init2 = arith.addi %init, %init {tag = "init"} : i32 208 %0, %1 = scf.while (%arg1 = %zero, %arg2 = %init2) : (i32, i32) -> (i32, i32) { 209 memref.store %arg2, %m0[] {tag_name = "a"} : memref<i32> 210 scf.condition(%cond) {tag = "condition"} %arg1, %arg2 : i32, i32 211 } do { 212 ^bb0(%arg1: i32, %arg2: i32): 213 memref.store %arg1, %m0[] {tag_name = "c"} : memref<i32> 214 %res = arith.addi %arg2, %arg2 : i32 215 scf.yield %res, %res: i32, i32 216 } 217 memref.store %1, %m0[] {tag_name = "b"} : memref<i32> 218 return 219} 220 221// ----- 222 223// CHECK-LABEL: test_tag: zero 224// CHECK: result #0: [] 225// CHECK-LABEL: test_tag: one 226// CHECK: result #0: [a] 227// CHECK-LABEL: test_tag: condition 228// CHECK: operand #0: [brancharg0] 229// 230// The important thing to note in this test is that the sparse backward dataflow 231// analysis framework also works on complex region branch ops like this one 232// where the number of operands in the `scf.yield` op don't match the number of 233// results in the parent op. 234func.func @test_complex_while(%m0: memref<i32>, %cond: i1) { 235 %zero = arith.constant {tag = "zero"} 0 : i32 236 %one = arith.constant {tag = "one"} 1 : i32 237 %0 = scf.while (%arg1 = %zero, %arg2 = %one) : (i32, i32) -> (i32) { 238 scf.condition(%cond) {tag = "condition"} %arg2 : i32 239 } do { 240 ^bb0(%arg1: i32): 241 scf.yield %arg1, %arg1: i32, i32 242 } 243 memref.store %0, %m0[] {tag_name = "a"} : memref<i32> 244 return 245} 246 247// ----- 248 249// CHECK-LABEL: test_tag: zero 250// CHECK: result #0: [brancharg0] 251// CHECK-LABEL: test_tag: ten 252// CHECK: result #0: [brancharg1] 253// CHECK-LABEL: test_tag: one 254// CHECK: result #0: [brancharg2] 255// CHECK-LABEL: test_tag: x 256// CHECK: result #0: [a] 257func.func @test_for(%m0: memref<i32>) { 258 %zero = arith.constant {tag = "zero"} 0 : index 259 %ten = arith.constant {tag = "ten"} 10 : index 260 %one = arith.constant {tag = "one"} 1 : index 261 %x = arith.constant {tag = "x"} 0 : i32 262 %0 = scf.for %i = %zero to %ten step %one iter_args(%ix = %x) -> (i32) { 263 scf.yield %ix : i32 264 } 265 memref.store %0, %m0[] {tag_name = "a"} : memref<i32> 266 return 267} 268 269// ----- 270 271// CHECK-LABEL: test_tag: default_a 272// CHECK: result #0: [a] 273// CHECK-LABEL: test_tag: default_b 274// CHECK: result #0: [b] 275// CHECK-LABEL: test_tag: 1a 276// CHECK: result #0: [a] 277// CHECK-LABEL: test_tag: 1b 278// CHECK: result #0: [b] 279// CHECK-LABEL: test_tag: 2a 280// CHECK: result #0: [a] 281// CHECK-LABEL: test_tag: 2b 282// CHECK: result #0: [b] 283// CHECK-LABEL: test_tag: switch 284// CHECK: operand #0: [brancharg0] 285func.func @test_switch(%arg0 : index, %m0: memref<i32>) { 286 %0, %1 = scf.index_switch %arg0 {tag="switch"} -> i32, i32 287 case 1 { 288 %2 = arith.constant {tag="1a"} 10 : i32 289 %3 = arith.constant {tag="1b"} 100 : i32 290 scf.yield %2, %3 : i32, i32 291 } 292 case 2 { 293 %4 = arith.constant {tag="2a"} 20 : i32 294 %5 = arith.constant {tag="2b"} 200 : i32 295 scf.yield %4, %5 : i32, i32 296 } 297 default { 298 %6 = arith.constant {tag="default_a"} 30 : i32 299 %7 = arith.constant {tag="default_b"} 300 : i32 300 scf.yield %6, %7 : i32, i32 301 } 302 memref.store %0, %m0[] {tag_name = "a"} : memref<i32> 303 memref.store %1, %m0[] {tag_name = "b"} : memref<i32> 304 return 305} 306 307// ----- 308 309// The point of this test is to ensure the analysis doesn't crash in presence of 310// external functions. 311 312// CHECK-LABEL: llvm.func @decl(i64) 313// CHECK-LABEL: llvm.func @func(%arg0: i64) { 314// CHECK-NEXT: llvm.call @decl(%arg0) : (i64) -> () 315// CHECK-NEXT: llvm.return 316 317llvm.func @decl(i64) 318 319llvm.func @func(%lb : i64) -> () { 320 llvm.call @decl(%lb) : (i64) -> () 321 llvm.return 322} 323 324// ----- 325 326func.func private @callee(%arg0 : i32, %arg1 : i32) -> i32 { 327 func.return %arg0 : i32 328} 329 330// CHECK-LABEL: test_tag: a 331 332// IP: operand #0: [b] 333// LOCAL: operand #0: [callarg0] 334// LC_AW: operand #0: [test.call_on_device] 335 336// IP: operand #1: [] 337// LOCAL: operand #1: [callarg1] 338// LC_AW: operand #1: [test.call_on_device] 339 340// IP: operand #2: [callarg2] 341// LOCAL: operand #2: [callarg2] 342// LC_AW: operand #2: [test.call_on_device] 343 344// CHECK: result #0: [b] 345func.func @test_call_on_device(%arg0: i32, %arg1: i32, %device: i32, %m0: memref<i32>) { 346 %0 = test.call_on_device @callee(%arg0, %arg1), %device {tag = "a"} : (i32, i32, i32) -> (i32) 347 memref.store %0, %m0[] {tag_name = "b"} : memref<i32> 348 return 349} 350 351// ----- 352 353func.func private @external_callee(%arg0: i32) -> i32 354 355// CHECK-LABEL: test_tag: add_external 356// IP: operand #0: [callarg0] 357// LOCAL: operand #0: [callarg0] 358// LC_AW: operand #0: [func.call] 359// IP_AW: operand #0: [func.call] 360 361func.func @test_external_callee(%arg0: i32, %m0: memref<i32>) { 362 %0 = arith.addi %arg0, %arg0 { tag = "add_external"}: i32 363 %1 = func.call @external_callee(%arg0) : (i32) -> i32 364 memref.store %1, %m0[] {tag_name = "a"} : memref<i32> 365 return 366} 367