| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 |
| ; RUN: llc -mtriple=aarch64 < %s | FileCheck %s |
| |
| define void @bool_ne_1(ptr %p) { |
| ; CHECK-LABEL: bool_ne_1: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldrb w8, [x0] |
| ; CHECK-NEXT: cbnz w8, .LBB0_2 |
| ; CHECK-NEXT: // %bb.1: // %if.else |
| ; CHECK-NEXT: b bar |
| ; CHECK-NEXT: .LBB0_2: // %if.then |
| ; CHECK-NEXT: b foo |
| %v = load i8, ptr %p, align 1, !range !0, !noundef !1 |
| %cmp = icmp ne i8 %v, 1 |
| br i1 %cmp, label %if.else, label %if.then |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| define void @bool_ne_1_with_memcpy(ptr %this, ptr %v) { |
| ; CHECK-LABEL: bool_ne_1_with_memcpy: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldrb w8, [x0, #40] |
| ; CHECK-NEXT: ldr q0, [x1] |
| ; CHECK-NEXT: str q0, [x0] |
| ; CHECK-NEXT: cbz w8, .LBB1_2 |
| ; CHECK-NEXT: // %bb.1: // %if.then |
| ; CHECK-NEXT: b foo |
| ; CHECK-NEXT: .LBB1_2: // %if.else |
| ; CHECK-NEXT: b bar |
| %engaged = getelementptr inbounds nuw i8, ptr %this, i64 40 |
| %val = load i8, ptr %engaged, align 8, !range !0, !noundef !1 |
| %loadedv = trunc nuw i8 %val to i1 |
| tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 8 dereferenceable(40) %this, ptr noundef nonnull align 8 dereferenceable(40) %v, i64 16, i1 false) |
| br i1 %loadedv, label %if.then, label %if.else |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| define void @bool_eq_1(ptr %p) { |
| ; CHECK-LABEL: bool_eq_1: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldrb w8, [x0] |
| ; CHECK-NEXT: cbz w8, .LBB2_2 |
| ; CHECK-NEXT: // %bb.1: // %if.then |
| ; CHECK-NEXT: b foo |
| ; CHECK-NEXT: .LBB2_2: // %if.else |
| ; CHECK-NEXT: b bar |
| %v = load i8, ptr %p, align 1, !range !0, !noundef !1 |
| %cmp = icmp eq i8 %v, 1 |
| br i1 %cmp, label %if.then, label %if.else |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| define void @bool_ne_1_i32(ptr %p) { |
| ; CHECK-LABEL: bool_ne_1_i32: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldr w8, [x0] |
| ; CHECK-NEXT: cbnz w8, .LBB3_2 |
| ; CHECK-NEXT: // %bb.1: // %if.else |
| ; CHECK-NEXT: b bar |
| ; CHECK-NEXT: .LBB3_2: // %if.then |
| ; CHECK-NEXT: b foo |
| %v = load i32, ptr %p, align 4, !range !2, !noundef !1 |
| %cmp = icmp ne i32 %v, 1 |
| br i1 %cmp, label %if.else, label %if.then |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| define void @bool_eq_1_i32(ptr %p) { |
| ; CHECK-LABEL: bool_eq_1_i32: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldr w8, [x0] |
| ; CHECK-NEXT: cbz w8, .LBB4_2 |
| ; CHECK-NEXT: // %bb.1: // %if.then |
| ; CHECK-NEXT: b foo |
| ; CHECK-NEXT: .LBB4_2: // %if.else |
| ; CHECK-NEXT: b bar |
| %v = load i32, ptr %p, align 4, !range !2, !noundef !1 |
| %cmp = icmp eq i32 %v, 1 |
| br i1 %cmp, label %if.then, label %if.else |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| ; Negative test: value not in [0, 1] |
| define void @not_bool_ne_1(ptr %p) { |
| ; CHECK-LABEL: not_bool_ne_1: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldrb w8, [x0] |
| ; CHECK-NEXT: cmp w8, #1 |
| ; CHECK-NEXT: b.eq .LBB5_2 |
| ; CHECK-NEXT: // %bb.1: // %if.then |
| ; CHECK-NEXT: b foo |
| ; CHECK-NEXT: .LBB5_2: // %if.else |
| ; CHECK-NEXT: b bar |
| %v = load i8, ptr %p, align 1 |
| %cmp = icmp ne i8 %v, 1 |
| br i1 %cmp, label %if.then, label %if.else |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| ; Negative test: conjunction tree should use CCMP chain, not CBZ. |
| define void @conjunction_ccmp(ptr %x_ptr, ptr %y_ptr, ptr %z_ptr) { |
| ; CHECK-LABEL: conjunction_ccmp: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: ldrh w8, [x1] |
| ; CHECK-NEXT: ldr w9, [x0] |
| ; CHECK-NEXT: cmp w8, #10 |
| ; CHECK-NEXT: ccmp w9, #0, #0, ne |
| ; CHECK-NEXT: ccmp w8, #14, #4, ne |
| ; CHECK-NEXT: b.eq .LBB6_2 |
| ; CHECK-NEXT: // %bb.1: // %if.then |
| ; CHECK-NEXT: str wzr, [x2] |
| ; CHECK-NEXT: .LBB6_2: // %common.ret |
| ; CHECK-NEXT: ret |
| %x = load i32, ptr %x_ptr, align 4 |
| %cmp1 = icmp ne i32 %x, 0 |
| %y = load i16, ptr %y_ptr, align 2 |
| %cmp2 = icmp eq i16 %y, 10 |
| %or1 = select i1 %cmp1, i1 true, i1 %cmp2 |
| %cmp3 = icmp ne i16 %y, 14 |
| %or2 = select i1 %or1, i1 %cmp3, i1 false |
| br i1 %or2, label %if.then, label %if.else |
| |
| if.then: |
| store i32 0, ptr %z_ptr, align 8 |
| ret void |
| |
| if.else: |
| ret void |
| } |
| |
| ; Negative test: overflow result should use direct flag-consuming branch |
| define void @overflow_bool(i32 %a, i32 %b) { |
| ; CHECK-LABEL: overflow_bool: |
| ; CHECK: // %bb.0: |
| ; CHECK-NEXT: cmn w0, w1 |
| ; CHECK-NEXT: b.vc .LBB7_2 |
| ; CHECK-NEXT: // %bb.1: // %if.then |
| ; CHECK-NEXT: b foo |
| ; CHECK-NEXT: .LBB7_2: // %if.else |
| ; CHECK-NEXT: b bar |
| %res = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b) |
| %ov = extractvalue {i32, i1} %res, 1 |
| br i1 %ov, label %if.then, label %if.else |
| |
| if.then: |
| tail call void @foo() |
| ret void |
| |
| if.else: |
| tail call void @bar() |
| ret void |
| } |
| |
| declare void @foo() |
| declare void @bar() |
| declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) |
| |
| !0 = !{i8 0, i8 2} |
| !1 = !{} |
| !2 = !{i32 0, i32 2} |