1// RUN: mlir-opt -allow-unregistered-dialect -split-input-file -verify-diagnostics %s | FileCheck %s
2
3//===----------------------------------------------------------------------===//
4// spv.Branch
5//===----------------------------------------------------------------------===//
6
7func @branch() -> () {
8  // CHECK: spv.Branch ^bb1
9  spv.Branch ^next
10^next:
11  spv.Return
12}
13
14// -----
15
16func @branch_argument() -> () {
17  %zero = spv.constant 0 : i32
18  // CHECK: spv.Branch ^bb1(%{{.*}}, %{{.*}} : i32, i32)
19  spv.Branch ^next(%zero, %zero: i32, i32)
20^next(%arg0: i32, %arg1: i32):
21  spv.Return
22}
23
24// -----
25
26func @missing_accessor() -> () {
27  // expected-error @+2 {{expected block name}}
28  spv.Branch
29}
30
31// -----
32
33func @wrong_accessor_count() -> () {
34  %true = spv.constant true
35  // expected-error @+1 {{requires 1 successor but found 2}}
36  "spv.Branch"()[^one, ^two] : () -> ()
37^one:
38  spv.Return
39^two:
40  spv.Return
41}
42
43// -----
44
45//===----------------------------------------------------------------------===//
46// spv.BranchConditional
47//===----------------------------------------------------------------------===//
48
49func @cond_branch() -> () {
50  %true = spv.constant true
51  // CHECK: spv.BranchConditional %{{.*}}, ^bb1, ^bb2
52  spv.BranchConditional %true, ^one, ^two
53// CHECK: ^bb1
54^one:
55  spv.Return
56// CHECK: ^bb2
57^two:
58  spv.Return
59}
60
61// -----
62
63func @cond_branch_argument() -> () {
64  %true = spv.constant true
65  %zero = spv.constant 0 : i32
66  // CHECK: spv.BranchConditional %{{.*}}, ^bb1(%{{.*}}, %{{.*}} : i32, i32), ^bb2
67  spv.BranchConditional %true, ^true1(%zero, %zero: i32, i32), ^false1
68^true1(%arg0: i32, %arg1: i32):
69  // CHECK: spv.BranchConditional %{{.*}}, ^bb3, ^bb4(%{{.*}}, %{{.*}} : i32, i32)
70  spv.BranchConditional %true, ^true2, ^false2(%zero, %zero: i32, i32)
71^false1:
72  spv.Return
73^true2:
74  spv.Return
75^false2(%arg3: i32, %arg4: i32):
76  spv.Return
77}
78
79// -----
80
81func @cond_branch_with_weights() -> () {
82  %true = spv.constant true
83  // CHECK: spv.BranchConditional %{{.*}} [5, 10]
84  spv.BranchConditional %true [5, 10], ^one, ^two
85^one:
86  spv.Return
87^two:
88  spv.Return
89}
90
91// -----
92
93func @missing_condition() -> () {
94  // expected-error @+1 {{expected SSA operand}}
95  spv.BranchConditional ^one, ^two
96^one:
97  spv.Return
98^two:
99  spv.Return
100}
101
102// -----
103
104func @wrong_condition_type() -> () {
105  // expected-note @+1 {{prior use here}}
106  %zero = spv.constant 0 : i32
107  // expected-error @+1 {{use of value '%zero' expects different type than prior uses: 'i1' vs 'i32'}}
108  spv.BranchConditional %zero, ^one, ^two
109^one:
110  spv.Return
111^two:
112  spv.Return
113}
114
115// -----
116
117func @wrong_accessor_count() -> () {
118  %true = spv.constant true
119  // expected-error @+1 {{requires 2 successors but found 1}}
120  "spv.BranchConditional"(%true)[^one] {operand_segment_sizes = dense<[1, 0, 0]>: vector<3xi32>} : (i1) -> ()
121^one:
122  spv.Return
123^two:
124  spv.Return
125}
126
127// -----
128
129func @wrong_number_of_weights() -> () {
130  %true = spv.constant true
131  // expected-error @+1 {{must have exactly two branch weights}}
132  "spv.BranchConditional"(%true)[^one, ^two] {branch_weights = [1 : i32, 2 : i32, 3 : i32],
133                                              operand_segment_sizes = dense<[1, 0, 0]>: vector<3xi32>} : (i1) -> ()
134^one:
135  spv.Return
136^two:
137  spv.Return
138}
139
140// -----
141
142func @weights_cannot_both_be_zero() -> () {
143  %true = spv.constant true
144  // expected-error @+1 {{branch weights cannot both be zero}}
145  spv.BranchConditional %true [0, 0], ^one, ^two
146^one:
147  spv.Return
148^two:
149  spv.Return
150}
151
152// -----
153
154//===----------------------------------------------------------------------===//
155// spv.FunctionCall
156//===----------------------------------------------------------------------===//
157
158spv.module Logical GLSL450 {
159  spv.func @fmain(%arg0 : vector<4xf32>, %arg1 : vector<4xf32>, %arg2 : i32) -> i32 "None" {
160    // CHECK: {{%.*}} = spv.FunctionCall @f_0({{%.*}}, {{%.*}}) : (vector<4xf32>, vector<4xf32>) -> vector<4xf32>
161    %0 = spv.FunctionCall @f_0(%arg0, %arg1) : (vector<4xf32>, vector<4xf32>) -> vector<4xf32>
162    // CHECK: spv.FunctionCall @f_1({{%.*}}, {{%.*}}) : (vector<4xf32>, vector<4xf32>) -> ()
163    spv.FunctionCall @f_1(%0, %arg1) : (vector<4xf32>, vector<4xf32>) ->  ()
164    // CHECK: spv.FunctionCall @f_2() : () -> ()
165    spv.FunctionCall @f_2() : () -> ()
166    // CHECK: {{%.*}} = spv.FunctionCall @f_3({{%.*}}) : (i32) -> i32
167    %1 = spv.FunctionCall @f_3(%arg2) : (i32) -> i32
168    spv.ReturnValue %1 : i32
169  }
170
171  spv.func @f_0(%arg0 : vector<4xf32>, %arg1 : vector<4xf32>) -> (vector<4xf32>) "None" {
172    spv.ReturnValue %arg0 : vector<4xf32>
173  }
174
175  spv.func @f_1(%arg0 : vector<4xf32>, %arg1 : vector<4xf32>) -> () "None" {
176    spv.Return
177  }
178
179  spv.func @f_2() -> () "None" {
180    spv.Return
181  }
182
183  spv.func @f_3(%arg0 : i32) -> (i32) "None" {
184    spv.ReturnValue %arg0 : i32
185  }
186}
187
188// -----
189
190// Allow calling functions in other module-like ops
191spv.func @callee() "None" {
192  spv.Return
193}
194
195func @caller() {
196  // CHECK: spv.FunctionCall
197  spv.FunctionCall @callee() : () -> ()
198  spv.Return
199}
200
201// -----
202
203spv.module Logical GLSL450 {
204  spv.func @f_invalid_result_type(%arg0 : i32, %arg1 : i32) -> () "None" {
205    // expected-error @+1 {{result group starting at #0 requires 0 or 1 element, but found 2}}
206    %0:2 = spv.FunctionCall @f_invalid_result_type(%arg0, %arg1) : (i32, i32) -> (i32, i32)
207    spv.Return
208  }
209}
210
211// -----
212
213spv.module Logical GLSL450 {
214  spv.func @f_result_type_mismatch(%arg0 : i32, %arg1 : i32) -> () "None" {
215    // expected-error @+1 {{has incorrect number of results has for callee: expected 0, but provided 1}}
216    %1 = spv.FunctionCall @f_result_type_mismatch(%arg0, %arg0) : (i32, i32) -> (i32)
217    spv.Return
218  }
219}
220
221// -----
222
223spv.module Logical GLSL450 {
224  spv.func @f_type_mismatch(%arg0 : i32, %arg1 : i32) -> () "None" {
225    // expected-error @+1 {{has incorrect number of operands for callee: expected 2, but provided 1}}
226    spv.FunctionCall @f_type_mismatch(%arg0) : (i32) -> ()
227    spv.Return
228  }
229}
230
231// -----
232
233spv.module Logical GLSL450 {
234  spv.func @f_type_mismatch(%arg0 : i32, %arg1 : i32) -> () "None" {
235    %0 = spv.constant 2.0 : f32
236    // expected-error @+1 {{operand type mismatch: expected operand type 'i32', but provided 'f32' for operand number 1}}
237    spv.FunctionCall @f_type_mismatch(%arg0, %0) : (i32, f32) -> ()
238    spv.Return
239  }
240}
241
242// -----
243
244spv.module Logical GLSL450 {
245  spv.func @f_type_mismatch(%arg0 : i32, %arg1 : i32) -> i32 "None" {
246    %cst = spv.constant 0: i32
247    // expected-error @+1 {{result type mismatch: expected 'i32', but provided 'f32'}}
248    %0 = spv.FunctionCall @f_type_mismatch(%arg0, %arg0) : (i32, i32) -> f32
249    spv.ReturnValue %cst: i32
250  }
251}
252
253// -----
254
255spv.module Logical GLSL450 {
256  spv.func @f_foo(%arg0 : i32, %arg1 : i32) -> i32 "None" {
257    // expected-error @+1 {{op callee function 'f_undefined' not found in nearest symbol table}}
258    %0 = spv.FunctionCall @f_undefined(%arg0, %arg0) : (i32, i32) -> i32
259    spv.ReturnValue %0: i32
260  }
261}
262
263// -----
264
265//===----------------------------------------------------------------------===//
266// spv.loop
267//===----------------------------------------------------------------------===//
268
269// for (int i = 0; i < count; ++i) {}
270func @loop(%count : i32) -> () {
271  %zero = spv.constant 0: i32
272  %one = spv.constant 1: i32
273  %var = spv.Variable init(%zero) : !spv.ptr<i32, Function>
274
275  // CHECK: spv.loop {
276  spv.loop {
277    // CHECK-NEXT: spv.Branch ^bb1
278    spv.Branch ^header
279
280  // CHECK-NEXT: ^bb1:
281  ^header:
282    %val0 = spv.Load "Function" %var : i32
283    %cmp = spv.SLessThan %val0, %count : i32
284    // CHECK: spv.BranchConditional %{{.*}}, ^bb2, ^bb4
285    spv.BranchConditional %cmp, ^body, ^merge
286
287  // CHECK-NEXT: ^bb2:
288  ^body:
289    // Do nothing
290    // CHECK-NEXT: spv.Branch ^bb3
291    spv.Branch ^continue
292
293  // CHECK-NEXT: ^bb3:
294  ^continue:
295    %val1 = spv.Load "Function" %var : i32
296    %add = spv.IAdd %val1, %one : i32
297    spv.Store "Function" %var, %add : i32
298    // CHECK: spv.Branch ^bb1
299    spv.Branch ^header
300
301  // CHECK-NEXT: ^bb4:
302  ^merge:
303    spv.mlir.merge
304  }
305  return
306}
307
308// -----
309
310// CHECK-LABEL: @empty_region
311func @empty_region() -> () {
312  // CHECK: spv.loop
313  spv.loop {
314  }
315  return
316}
317
318// -----
319
320// CHECK-LABEL: @loop_with_control
321func @loop_with_control() -> () {
322  // CHECK: spv.loop control(Unroll)
323  spv.loop control(Unroll) {
324  }
325  return
326}
327
328// -----
329
330func @wrong_merge_block() -> () {
331  // expected-error @+1 {{last block must be the merge block with only one 'spv.mlir.merge' op}}
332  spv.loop {
333    spv.Return
334  }
335  return
336}
337
338// -----
339
340func @missing_entry_block() -> () {
341  // expected-error @+1 {{must have an entry block branching to the loop header block}}
342  spv.loop {
343    spv.mlir.merge
344  }
345  return
346}
347
348// -----
349
350func @missing_header_block() -> () {
351  // expected-error @+1 {{must have a loop header block branched from the entry block}}
352  spv.loop {
353  ^entry:
354    spv.Branch ^merge
355  ^merge:
356    spv.mlir.merge
357  }
358  return
359}
360
361// -----
362
363func @entry_should_branch_to_header() -> () {
364  // expected-error @+1 {{entry block must only have one 'spv.Branch' op to the second block}}
365  spv.loop {
366  ^entry:
367    spv.Branch ^merge
368  ^header:
369    spv.Branch ^merge
370  ^merge:
371    spv.mlir.merge
372  }
373  return
374}
375
376// -----
377
378func @missing_continue_block() -> () {
379  // expected-error @+1 {{requires a loop continue block branching to the loop header block}}
380  spv.loop {
381  ^entry:
382    spv.Branch ^header
383  ^header:
384    spv.Branch ^merge
385  ^merge:
386    spv.mlir.merge
387  }
388  return
389}
390
391// -----
392
393func @continue_should_branch_to_header() -> () {
394  // expected-error @+1 {{second to last block must be the loop continue block that branches to the loop header block}}
395  spv.loop {
396  ^entry:
397    spv.Branch ^header
398  ^header:
399    spv.Branch ^continue
400  ^continue:
401    spv.Branch ^merge
402  ^merge:
403    spv.mlir.merge
404  }
405  return
406}
407
408// -----
409
410func @only_entry_and_continue_branch_to_header() -> () {
411  // expected-error @+1 {{can only have the entry and loop continue block branching to the loop header block}}
412  spv.loop {
413  ^entry:
414    spv.Branch ^header
415  ^header:
416    spv.Branch ^cont1
417  ^cont1:
418    spv.Branch ^header
419  ^cont2:
420    spv.Branch ^header
421  ^merge:
422    spv.mlir.merge
423  }
424  return
425}
426
427// -----
428
429//===----------------------------------------------------------------------===//
430// spv.mlir.merge
431//===----------------------------------------------------------------------===//
432
433func @merge() -> () {
434  // expected-error @+1 {{expected parent op to be 'spv.selection' or 'spv.loop'}}
435  spv.mlir.merge
436}
437
438// -----
439
440func @only_allowed_in_last_block(%cond : i1) -> () {
441  %zero = spv.constant 0: i32
442  %one = spv.constant 1: i32
443  %var = spv.Variable init(%zero) : !spv.ptr<i32, Function>
444
445  spv.selection {
446    spv.BranchConditional %cond, ^then, ^merge
447
448  ^then:
449    spv.Store "Function" %var, %one : i32
450    // expected-error @+1 {{can only be used in the last block of 'spv.selection' or 'spv.loop'}}
451    spv.mlir.merge
452
453  ^merge:
454    spv.mlir.merge
455  }
456
457  spv.Return
458}
459
460// -----
461
462func @only_allowed_in_last_block() -> () {
463  %true = spv.constant true
464  spv.loop {
465    spv.Branch ^header
466  ^header:
467    spv.BranchConditional %true, ^body, ^merge
468  ^body:
469    // expected-error @+1 {{can only be used in the last block of 'spv.selection' or 'spv.loop'}}
470    spv.mlir.merge
471  ^continue:
472    spv.Branch ^header
473  ^merge:
474    spv.mlir.merge
475  }
476  return
477}
478
479// -----
480
481//===----------------------------------------------------------------------===//
482// spv.Return
483//===----------------------------------------------------------------------===//
484
485// CHECK-LABEL: func @in_selection
486func @in_selection(%cond : i1) -> () {
487  spv.selection {
488    spv.BranchConditional %cond, ^then, ^merge
489  ^then:
490    // CHECK: spv.Return
491    spv.Return
492  ^merge:
493    spv.mlir.merge
494  }
495  spv.Return
496}
497
498// CHECK-LABEL: func @in_loop
499func @in_loop(%cond : i1) -> () {
500  spv.loop {
501    spv.Branch ^header
502  ^header:
503    spv.BranchConditional %cond, ^body, ^merge
504  ^body:
505    // CHECK: spv.Return
506    spv.Return
507  ^continue:
508    spv.Branch ^header
509  ^merge:
510    spv.mlir.merge
511  }
512  spv.Return
513}
514
515// CHECK-LABEL: in_other_func_like_op
516func @in_other_func_like_op() {
517  // CHECK: spv.Return
518  spv.Return
519}
520
521// -----
522
523"foo.function"() ({
524  // expected-error @+1 {{op must appear in a function-like op's block}}
525  spv.Return
526})  : () -> ()
527
528// -----
529
530// Return mismatches function signature
531spv.module Logical GLSL450 {
532  spv.func @work() -> (i32) "None" {
533    // expected-error @+1 {{cannot be used in functions returning value}}
534    spv.Return
535  }
536}
537
538// -----
539
540spv.module Logical GLSL450 {
541  spv.func @in_nested_region(%cond: i1) -> (i32) "None" {
542    spv.selection {
543      spv.BranchConditional %cond, ^then, ^merge
544    ^then:
545      // expected-error @+1 {{cannot be used in functions returning value}}
546      spv.Return
547    ^merge:
548      spv.mlir.merge
549    }
550
551    %zero = spv.constant 0: i32
552    spv.ReturnValue %zero: i32
553  }
554}
555
556// -----
557
558//===----------------------------------------------------------------------===//
559// spv.ReturnValue
560//===----------------------------------------------------------------------===//
561
562func @ret_val() -> (i32) {
563  %0 = spv.constant 42 : i32
564  // CHECK: spv.ReturnValue %{{.*}} : i32
565  spv.ReturnValue %0 : i32
566}
567
568// CHECK-LABEL: func @in_selection
569func @in_selection(%cond : i1) -> (i32) {
570  spv.selection {
571    spv.BranchConditional %cond, ^then, ^merge
572  ^then:
573    %zero = spv.constant 0 : i32
574    // CHECK: spv.ReturnValue
575    spv.ReturnValue %zero : i32
576  ^merge:
577    spv.mlir.merge
578  }
579  %one = spv.constant 1 : i32
580  spv.ReturnValue %one : i32
581}
582
583// CHECK-LABEL: func @in_loop
584func @in_loop(%cond : i1) -> (i32) {
585  spv.loop {
586    spv.Branch ^header
587  ^header:
588    spv.BranchConditional %cond, ^body, ^merge
589  ^body:
590    %zero = spv.constant 0 : i32
591    // CHECK: spv.ReturnValue
592    spv.ReturnValue %zero : i32
593  ^continue:
594    spv.Branch ^header
595  ^merge:
596    spv.mlir.merge
597  }
598  %one = spv.constant 1 : i32
599  spv.ReturnValue %one : i32
600}
601
602// CHECK-LABEL: in_other_func_like_op
603func @in_other_func_like_op(%arg: i32) -> i32 {
604  // CHECK: spv.ReturnValue
605  spv.ReturnValue %arg: i32
606}
607
608// -----
609
610"foo.function"() ({
611  %0 = spv.constant true
612  // expected-error @+1 {{op must appear in a function-like op's block}}
613  spv.ReturnValue %0 : i1
614})  : () -> ()
615
616// -----
617
618spv.module Logical GLSL450 {
619  spv.func @value_count_mismatch() -> () "None" {
620    %0 = spv.constant 42 : i32
621    // expected-error @+1 {{op returns 1 value but enclosing function requires 0 results}}
622    spv.ReturnValue %0 : i32
623  }
624}
625
626// -----
627
628spv.module Logical GLSL450 {
629  spv.func @value_type_mismatch() -> (f32) "None" {
630    %0 = spv.constant 42 : i32
631    // expected-error @+1 {{return value's type ('i32') mismatch with function's result type ('f32')}}
632    spv.ReturnValue %0 : i32
633  }
634}
635
636// -----
637
638spv.module Logical GLSL450 {
639  spv.func @in_nested_region(%cond: i1) -> () "None" {
640    spv.selection {
641      spv.BranchConditional %cond, ^then, ^merge
642    ^then:
643      %cst = spv.constant 0: i32
644      // expected-error @+1 {{op returns 1 value but enclosing function requires 0 results}}
645      spv.ReturnValue %cst: i32
646    ^merge:
647      spv.mlir.merge
648    }
649
650    spv.Return
651  }
652}
653
654// -----
655
656//===----------------------------------------------------------------------===//
657// spv.selection
658//===----------------------------------------------------------------------===//
659
660func @selection(%cond: i1) -> () {
661  %zero = spv.constant 0: i32
662  %one = spv.constant 1: i32
663  %var = spv.Variable init(%zero) : !spv.ptr<i32, Function>
664
665  // CHECK: spv.selection {
666  spv.selection {
667    // CHECK-NEXT: spv.BranchConditional %{{.*}}, ^bb1, ^bb2
668    spv.BranchConditional %cond, ^then, ^merge
669
670  // CHECK: ^bb1
671  ^then:
672    spv.Store "Function" %var, %one : i32
673    // CHECK: spv.Branch ^bb2
674    spv.Branch ^merge
675
676  // CHECK: ^bb2
677  ^merge:
678    // CHECK-NEXT: spv.mlir.merge
679    spv.mlir.merge
680  }
681
682  spv.Return
683}
684
685// -----
686
687func @selection(%cond: i1) -> () {
688  %zero = spv.constant 0: i32
689  %one = spv.constant 1: i32
690  %two = spv.constant 2: i32
691  %var = spv.Variable init(%zero) : !spv.ptr<i32, Function>
692
693  // CHECK: spv.selection {
694  spv.selection {
695    // CHECK-NEXT: spv.BranchConditional %{{.*}}, ^bb1, ^bb2
696    spv.BranchConditional %cond, ^then, ^else
697
698  // CHECK: ^bb1
699  ^then:
700    spv.Store "Function" %var, %one : i32
701    // CHECK: spv.Branch ^bb3
702    spv.Branch ^merge
703
704  // CHECK: ^bb2
705  ^else:
706    spv.Store "Function" %var, %two : i32
707    // CHECK: spv.Branch ^bb3
708    spv.Branch ^merge
709
710  // CHECK: ^bb3
711  ^merge:
712    // CHECK-NEXT: spv.mlir.merge
713    spv.mlir.merge
714  }
715
716  spv.Return
717}
718
719// -----
720
721// CHECK-LABEL: @empty_region
722func @empty_region() -> () {
723  // CHECK: spv.selection
724  spv.selection {
725  }
726  return
727}
728
729// -----
730
731// CHECK-LABEL: @selection_with_control
732func @selection_with_control() -> () {
733  // CHECK: spv.selection control(Flatten)
734  spv.selection control(Flatten) {
735  }
736  return
737}
738
739// -----
740
741func @wrong_merge_block() -> () {
742  // expected-error @+1 {{last block must be the merge block with only one 'spv.mlir.merge' op}}
743  spv.selection {
744    spv.Return
745  }
746  return
747}
748
749// -----
750
751func @missing_entry_block() -> () {
752  // expected-error @+1 {{must have a selection header block}}
753  spv.selection {
754    spv.mlir.merge
755  }
756  return
757}
758
759// -----
760
761//===----------------------------------------------------------------------===//
762// spv.Unreachable
763//===----------------------------------------------------------------------===//
764
765// CHECK-LABEL: func @unreachable_no_pred
766func @unreachable_no_pred() {
767    spv.Return
768
769  ^next:
770    // CHECK: spv.Unreachable
771    spv.Unreachable
772}
773
774// CHECK-LABEL: func @unreachable_with_pred
775func @unreachable_with_pred() {
776    spv.Return
777
778  ^parent:
779    spv.Branch ^unreachable
780
781  ^unreachable:
782    // CHECK: spv.Unreachable
783    spv.Unreachable
784}
785
786// -----
787
788func @unreachable() {
789  // expected-error @+1 {{cannot be used in reachable block}}
790  spv.Unreachable
791}
792