| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py |
| ; RUN: llc < %s -mtriple=aarch64-linux-gnu | FileCheck %s |
| |
| ; getJumpConditionMergingParams keeps two integer branch conditions split when |
| ; both compare values loaded from memory: merging would pin the loaded operands |
| ; in registers for the CMP/CCMP chain, which the latency-based heuristic prices |
| ; as cheap but can cause spills under register pressure. Other conditions still |
| ; merge into a CCMP chain. |
| |
| declare void @sink_a() |
| declare void @sink_b() |
| |
| ; Both sides compare loads (e.g. a bounds guard imin[d] < imax[d]): keep split. |
| define void @both_loaded(ptr %imin, ptr %imax) { |
| ; CHECK-LABEL: both_loaded: |
| ; CHECK: // %bb.0: // %entry |
| ; CHECK-NEXT: ldr w8, [x0] |
| ; CHECK-NEXT: ldr w9, [x1] |
| ; CHECK-NEXT: cmp w8, w9 |
| ; CHECK-NEXT: b.ge .LBB0_3 |
| ; CHECK-NEXT: // %bb.1: // %entry |
| ; CHECK-NEXT: ldr w8, [x0, #4] |
| ; CHECK-NEXT: ldr w9, [x1, #4] |
| ; CHECK-NEXT: cmp w8, w9 |
| ; CHECK-NEXT: b.ge .LBB0_3 |
| ; CHECK-NEXT: // %bb.2: // %taken |
| ; CHECK-NEXT: b sink_a |
| ; CHECK-NEXT: .LBB0_3: // %fall |
| ; CHECK-NEXT: b sink_b |
| entry: |
| %imin0 = load i32, ptr %imin |
| %imax0 = load i32, ptr %imax |
| %c0 = icmp slt i32 %imin0, %imax0 |
| %imin1p = getelementptr inbounds i32, ptr %imin, i64 1 |
| %imax1p = getelementptr inbounds i32, ptr %imax, i64 1 |
| %imin1 = load i32, ptr %imin1p |
| %imax1 = load i32, ptr %imax1p |
| %c1 = icmp slt i32 %imin1, %imax1 |
| %and = and i1 %c0, %c1 |
| br i1 %and, label %taken, label %fall |
| taken: |
| tail call void @sink_a() |
| ret void |
| fall: |
| tail call void @sink_b() |
| ret void |
| } |
| |
| ; Negative control: register operands, not loads -> still merge into a CCMP chain. |
| define void @both_reg(i32 %a0, i32 %b0, i32 %a1, i32 %b1) { |
| ; CHECK-LABEL: both_reg: |
| ; CHECK: // %bb.0: // %entry |
| ; CHECK-NEXT: cmp w2, w3 |
| ; CHECK-NEXT: ccmp w0, w1, #0, lt |
| ; CHECK-NEXT: b.ge .LBB1_2 |
| ; CHECK-NEXT: // %bb.1: // %taken |
| ; CHECK-NEXT: b sink_a |
| ; CHECK-NEXT: .LBB1_2: // %fall |
| ; CHECK-NEXT: b sink_b |
| entry: |
| %c0 = icmp slt i32 %a0, %b0 |
| %c1 = icmp slt i32 %a1, %b1 |
| %and = and i1 %c0, %c1 |
| br i1 %and, label %taken, label %fall |
| taken: |
| tail call void @sink_a() |
| ret void |
| fall: |
| tail call void @sink_b() |
| ret void |
| } |
| |
| ; Only one side is a load -> "both" requirement not met -> still merges. |
| define void @one_loaded(ptr %imin, ptr %imax, i32 %a1, i32 %b1) { |
| ; CHECK-LABEL: one_loaded: |
| ; CHECK: // %bb.0: // %entry |
| ; CHECK-NEXT: ldr w8, [x1] |
| ; CHECK-NEXT: ldr w9, [x0] |
| ; CHECK-NEXT: cmp w2, w3 |
| ; CHECK-NEXT: ccmp w9, w8, #0, lt |
| ; CHECK-NEXT: b.ge .LBB2_2 |
| ; CHECK-NEXT: // %bb.1: // %taken |
| ; CHECK-NEXT: b sink_a |
| ; CHECK-NEXT: .LBB2_2: // %fall |
| ; CHECK-NEXT: b sink_b |
| entry: |
| %imin0 = load i32, ptr %imin |
| %imax0 = load i32, ptr %imax |
| %c0 = icmp slt i32 %imin0, %imax0 |
| %c1 = icmp slt i32 %a1, %b1 |
| %and = and i1 %c0, %c1 |
| br i1 %and, label %taken, label %fall |
| taken: |
| tail call void @sink_a() |
| ret void |
| fall: |
| tail call void @sink_b() |
| ret void |
| } |