blob: ffe4b7b34aaaabbd94871a31f440f85dfab54dac [file] [edit]
; 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
}