| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 |
| ; RUN: llc -mtriple=x86_64-- < %s | FileCheck %s |
| |
| ; Issue #51707: add2 = add1 + b should reuse the already-materialized value of |
| ; add1 (a + b + 17) instead of rematerializing a + b. |
| |
| define i32 @reduced(i32 %a, i32 %b, ptr %p) { |
| ; CHECK-LABEL: reduced: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal 17(%rdi,%rsi), %eax |
| ; CHECK-NEXT: movl %eax, (%rdx) |
| ; CHECK-NEXT: addl %esi, %eax |
| ; CHECK-NEXT: retq |
| %add = add i32 %a, 17 |
| %add1 = add i32 %add, %b |
| store i32 %add1, ptr %p, align 4 |
| %add2 = add nsw i32 %add1, %b |
| ret i32 %add2 |
| } |
| |
| ; int32_t f(int32_t & __restrict a, const int32_t & __restrict b) { |
| ; a += b + 17; |
| ; return a + b; |
| ; } |
| define i32 @f(ptr noalias %a, ptr noalias readonly %b) { |
| ; CHECK-LABEL: f: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: movl (%rsi), %ecx |
| ; CHECK-NEXT: movl (%rdi), %eax |
| ; CHECK-NEXT: leal 17(%rcx,%rax), %eax |
| ; CHECK-NEXT: movl %eax, (%rdi) |
| ; CHECK-NEXT: addl %ecx, %eax |
| ; CHECK-NEXT: retq |
| %lb = load i32, ptr %b, align 4 |
| %la = load i32, ptr %a, align 4 |
| %t = add i32 %lb, 17 |
| %sum = add i32 %t, %la |
| store i32 %sum, ptr %a, align 4 |
| %ret = add i32 %sum, %lb |
| ret i32 %ret |
| } |
| |
| ; Commuted: the reused value is the second operand of the final add. |
| define i32 @commuted(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: commuted: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal 17(%rdi,%rsi), %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: addl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %add1 = add i32 %s, 17 |
| store i32 %add1, ptr %p |
| %r = add i32 %c, %add1 |
| ret i32 %r |
| } |
| |
| ; Deeper nesting: v = ((a + b) + c) + 17 is materialized, then reused for v + d. |
| define i32 @deeper(i32 %a, i32 %b, i32 %c, i32 %d, ptr %p) { |
| ; CHECK-LABEL: deeper: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $edx killed $edx def $rdx |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: addl %esi, %edi |
| ; CHECK-NEXT: leal 17(%rdx,%rdi), %eax |
| ; CHECK-NEXT: movl %eax, (%r8) |
| ; CHECK-NEXT: addl %ecx, %eax |
| ; CHECK-NEXT: retq |
| %s1 = add i32 %a, %b |
| %s2 = add i32 %s1, %c |
| %v = add i32 %s2, 17 |
| store i32 %v, ptr %p |
| %r = add i32 %v, %d |
| ret i32 %r |
| } |
| |
| ; A multi-use shl-by-constant of an add folds to a scaled index; the materialized |
| ; value m = (a + b) << 2 should be reused for m + c, not recomputed. |
| define i32 @shl_reuse(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: shl_reuse: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal (%rdi,%rsi), %eax |
| ; CHECK-NEXT: shll $2, %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: addl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = shl i32 %s, 2 |
| store i32 %m, ptr %p |
| %r = add i32 %m, %c |
| ret i32 %r |
| } |
| |
| ; Boundary/other-operand coverage: the following already produce optimal code |
| ; (with or without the reuse fix); they guard against a future matcher change |
| ; re-introducing the de-CSE. |
| |
| ; A shift by 4 cannot fold into an LEA scale (max is <<3 == scale 8), so m is |
| ; kept whole regardless - nothing to split. |
| define i32 @shl_by_4(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: shl_by_4: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal (%rdi,%rsi), %eax |
| ; CHECK-NEXT: shll $4, %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: addl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = shl i32 %s, 4 |
| store i32 %m, ptr %p |
| %r = add i32 %m, %c |
| ret i32 %r |
| } |
| |
| ; mul by 3/5/9 folds to lea (X, X, {2,4,8}), consuming both base and index, so |
| ; the sibling add operand cannot be folded and m is reused whole. |
| define i32 @mul_3(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: mul_3: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: addl %esi, %edi |
| ; CHECK-NEXT: leal (%rdi,%rdi,2), %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: addl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = mul i32 %s, 3 |
| store i32 %m, ptr %p |
| %r = add i32 %m, %c |
| ret i32 %r |
| } |
| |
| define i32 @mul_5(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: mul_5: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: addl %esi, %edi |
| ; CHECK-NEXT: leal (%rdi,%rdi,4), %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: addl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = mul i32 %s, 5 |
| store i32 %m, ptr %p |
| %r = add i32 %m, %c |
| ret i32 %r |
| } |
| |
| define i32 @mul_9(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: mul_9: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: addl %esi, %edi |
| ; CHECK-NEXT: leal (%rdi,%rdi,8), %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: addl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = mul i32 %s, 9 |
| store i32 %m, ptr %p |
| %r = add i32 %m, %c |
| ret i32 %r |
| } |
| |
| ; A real subtract (variable subtrahend) folds to base + neg-index, again |
| ; consuming both slots, so m = (a + b) - c is reused whole for m + d. |
| define i32 @sub_var(i32 %a, i32 %b, i32 %c, i32 %d, ptr %p) { |
| ; CHECK-LABEL: sub_var: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal (%rdi,%rsi), %eax |
| ; CHECK-NEXT: subl %edx, %eax |
| ; CHECK-NEXT: movl %eax, (%r8) |
| ; CHECK-NEXT: addl %ecx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = sub i32 %s, %c |
| store i32 %m, ptr %p |
| %r = add i32 %m, %d |
| ret i32 %r |
| } |
| |
| ; Subtracting a constant is canonicalized to an add of a negative constant, so |
| ; this is the add-like case with a negative displacement and is covered by it. |
| define i32 @sub_const(i32 %a, i32 %b, ptr %p) { |
| ; CHECK-LABEL: sub_const: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal -17(%rdi,%rsi), %eax |
| ; CHECK-NEXT: movl %eax, (%rdx) |
| ; CHECK-NEXT: addl %esi, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = sub i32 %s, 17 |
| store i32 %m, ptr %p |
| %r = add i32 %m, %b |
| ret i32 %r |
| } |
| |
| ; The materialized value used as the minuend of a subtract is reused directly. |
| define i32 @minuend(i32 %a, i32 %b, i32 %c, ptr %p) { |
| ; CHECK-LABEL: minuend: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi |
| ; CHECK-NEXT: # kill: def $edi killed $edi def $rdi |
| ; CHECK-NEXT: leal 17(%rdi,%rsi), %eax |
| ; CHECK-NEXT: movl %eax, (%rcx) |
| ; CHECK-NEXT: subl %edx, %eax |
| ; CHECK-NEXT: retq |
| %s = add i32 %a, %b |
| %m = add i32 %s, 17 |
| store i32 %m, ptr %p |
| %r = sub i32 %m, %c |
| ret i32 %r |
| } |
| |
| ; idx = (a+b)+4 is multi-use but consumed only by foldable-address uses (the two |
| ; returned GEP pointers and the load), so it is never materialized in a register |
| ; as a value. hasMaterializingUse() sees no such use, so idx is not reused; each |
| ; pointer folds base+idx+disp fresh (no redundant materialization). |
| define { ptr, ptr } @two_ptrs(i64 %a, i64 %b, ptr %base, ptr %sink) nounwind { |
| ; CHECK-LABEL: two_ptrs: |
| ; CHECK: # %bb.0: |
| ; CHECK-NEXT: addq %rsi, %rdi |
| ; CHECK-NEXT: leaq 4(%rdx,%rdi), %rax |
| ; CHECK-NEXT: leaq 136(%rdx,%rdi), %rsi |
| ; CHECK-NEXT: movl 4(%rdx,%rdi), %edx |
| ; CHECK-NEXT: movl %edx, (%rcx) |
| ; CHECK-NEXT: movq %rsi, %rdx |
| ; CHECK-NEXT: retq |
| %s = add i64 %a, %b |
| %idx = add i64 %s, 4 |
| %p0 = getelementptr i8, ptr %base, i64 %idx |
| %idx1 = add i64 %idx, 132 |
| %p1 = getelementptr i8, ptr %base, i64 %idx1 |
| %v0 = load i32, ptr %p0 |
| store i32 %v0, ptr %sink |
| %r0 = insertvalue { ptr, ptr } poison, ptr %p0, 0 |
| %r1 = insertvalue { ptr, ptr } %r0, ptr %p1, 1 |
| ret { ptr, ptr } %r1 |
| } |