forked from OSchip/llvm-project
893 lines
18 KiB
LLVM
893 lines
18 KiB
LLVM
; Test that compares are omitted if CC already has the right value
|
|
; (z10 version).
|
|
;
|
|
; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z10 -no-integrated-as \
|
|
; RUN: -verify-machineinstrs| FileCheck %s
|
|
|
|
declare void @foo()
|
|
|
|
; Addition provides enough for equality comparisons with zero. First teest
|
|
; the EQ case.
|
|
define i32 @f1(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f1:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: ber %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
%cmp = icmp eq i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and again with NE.
|
|
define i32 @f2(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f2:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: bner %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; SLT requires a comparison.
|
|
define i32 @f3(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f3:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: cibl %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
%cmp = icmp slt i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...SLE too.
|
|
define i32 @f4(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f4:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: cible %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
%cmp = icmp sle i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...SGT too.
|
|
define i32 @f5(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f5:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: cibh %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
%cmp = icmp sgt i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...SGE too.
|
|
define i32 @f6(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f6:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: cibhe %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
%cmp = icmp sge i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Subtraction also provides enough for equality comparisons with zero.
|
|
define i32 @f7(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f7:
|
|
; CHECK: s %r2, 0(%r4)
|
|
; CHECK-NEXT: bner %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%cur = load i32, i32 *%dest
|
|
%res = sub i32 %a, %cur
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...but not for ordered comparisons.
|
|
define i32 @f8(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f8:
|
|
; CHECK: s %r2, 0(%r4)
|
|
; CHECK-NEXT: cibl %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%cur = load i32, i32 *%dest
|
|
%res = sub i32 %a, %cur
|
|
%cmp = icmp slt i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Logic register-register instructions also provide enough for equality
|
|
; comparisons with zero.
|
|
define i32 @f9(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f9:
|
|
; CHECK: nr %r2, %r3
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = and i32 %a, %b
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...but not for ordered comparisons.
|
|
define i32 @f10(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f10:
|
|
; CHECK: nr %r2, %r3
|
|
; CHECK-NEXT: cibl %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = and i32 %a, %b
|
|
%cmp = icmp slt i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Logic register-immediate instructions also provide enough for equality
|
|
; comparisons with zero if the immediate covers the whole register.
|
|
define i32 @f11(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f11:
|
|
; CHECK: nilf %r2, 100000001
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = and i32 %a, 100000001
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Partial logic register-immediate instructions do not provide simple
|
|
; zero results.
|
|
define i32 @f12(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f12:
|
|
; CHECK: nill %r2, 65436
|
|
; CHECK-NEXT: ciblh %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = and i32 %a, -100
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; SRA provides the same CC result as a comparison with zero.
|
|
define i32 @f13(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f13:
|
|
; CHECK: sra %r2, 0(%r3)
|
|
; CHECK-NEXT: ber %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = ashr i32 %a, %b
|
|
%cmp = icmp eq i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and again with NE.
|
|
define i32 @f14(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f14:
|
|
; CHECK: sra %r2, 0(%r3)
|
|
; CHECK-NEXT: blhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = ashr i32 %a, %b
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and SLT.
|
|
define i32 @f15(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f15:
|
|
; CHECK: sra %r2, 0(%r3)
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = ashr i32 %a, %b
|
|
%cmp = icmp slt i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and SLE.
|
|
define i32 @f16(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f16:
|
|
; CHECK: sra %r2, 0(%r3)
|
|
; CHECK-NEXT: bler %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = ashr i32 %a, %b
|
|
%cmp = icmp sle i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and SGT.
|
|
define i32 @f17(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f17:
|
|
; CHECK: sra %r2, 0(%r3)
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = ashr i32 %a, %b
|
|
%cmp = icmp sgt i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and SGE.
|
|
define i32 @f18(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f18:
|
|
; CHECK: sra %r2, 0(%r3)
|
|
; CHECK-NEXT: bher %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = ashr i32 %a, %b
|
|
%cmp = icmp sge i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; RISBG provides the same result as a comparison against zero.
|
|
; Test the EQ case.
|
|
define i64 @f19(i64 %a, i64 %b, i64 *%dest) {
|
|
; CHECK-LABEL: f19:
|
|
; CHECK: risbg %r2, %r3, 0, 190, 0
|
|
; CHECK-NEXT: ber %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = and i64 %b, -2
|
|
%cmp = icmp eq i64 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %b, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %res
|
|
}
|
|
|
|
; ...and the SLT case.
|
|
define i64 @f20(i64 %a, i64 %b, i64 *%dest) {
|
|
; CHECK-LABEL: f20:
|
|
; CHECK: risbg %r2, %r3, 0, 190, 0
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = and i64 %b, -2
|
|
%cmp = icmp slt i64 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %b, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %res
|
|
}
|
|
|
|
; Test a case where the register we're testing is set by a non-CC-clobbering
|
|
; instruction.
|
|
define i32 @f21(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f21:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: cibe %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i32 %a, 1000000
|
|
%res = call i32 asm "blah $0", "=r,0" (i32 %add)
|
|
%cmp = icmp eq i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; ...and again with a CC-clobbering instruction.
|
|
define i32 @f22(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f22:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: cibe %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i32 %a, 1000000
|
|
%res = call i32 asm "blah $0", "=r,0,~{cc}" (i32 %add)
|
|
%cmp = icmp eq i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Check that stores do not interfere.
|
|
define i32 @f23(i32 %a, i32 %b, i32 *%dest1, i32 *%dest2) {
|
|
; CHECK-LABEL: f23:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: st %r2, 0(%r4)
|
|
; CHECK-NEXT: bner %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = add i32 %a, 1000000
|
|
store i32 %res, i32 *%dest1
|
|
%cmp = icmp ne i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest2
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Check that calls do interfere.
|
|
define void @f24(i32 *%ptr) {
|
|
; CHECK-LABEL: f24:
|
|
; CHECK: afi [[REG:%r[0-9]+]], 1000000
|
|
; CHECK-NEXT: brasl %r14, foo@PLT
|
|
; CHECK-NEXT: cijlh [[REG]], 0, .L{{.*}}
|
|
; CHECK: br %r14
|
|
entry:
|
|
%val = load i32, i32 *%ptr
|
|
%xor = xor i32 %val, 1
|
|
%add = add i32 %xor, 1000000
|
|
call void @foo()
|
|
%cmp = icmp eq i32 %add, 0
|
|
br i1 %cmp, label %store, label %exit, !prof !1
|
|
|
|
store:
|
|
store i32 %add, i32 *%ptr
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Check that inline asms don't interfere if they don't clobber CC.
|
|
define void @f25(i32 %a, i32 *%ptr) {
|
|
; CHECK-LABEL: f25:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: bner %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i32 %a, 1000000
|
|
call void asm sideeffect "blah", "r"(i32 %add)
|
|
%cmp = icmp ne i32 %add, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %add, i32 *%ptr
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; ...but do interfere if they do clobber CC.
|
|
define void @f26(i32 %a, i32 *%ptr) {
|
|
; CHECK-LABEL: f26:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: ciblh %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i32 %a, 1000000
|
|
call void asm sideeffect "blah", "r,~{cc}"(i32 %add)
|
|
%cmp = icmp ne i32 %add, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %add, i32 *%ptr
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Test a case where CC is set based on a different register from the
|
|
; compare input.
|
|
define i32 @f27(i32 %a, i32 %b, i32 *%dest1, i32 *%dest2) {
|
|
; CHECK-LABEL: f27:
|
|
; CHECK: afi %r2, 1000000
|
|
; CHECK-NEXT: sr %r3, %r2
|
|
; CHECK-NEXT: st %r3, 0(%r4)
|
|
; CHECK-NEXT: cibe %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i32 %a, 1000000
|
|
%sub = sub i32 %b, %add
|
|
store i32 %sub, i32 *%dest1
|
|
%cmp = icmp eq i32 %add, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %sub, i32 *%dest2
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %add
|
|
}
|
|
|
|
; Make sure that we don't confuse a base register for a destination.
|
|
define void @f28(i64 %a, i64 *%dest) {
|
|
; CHECK-LABEL: f28:
|
|
; CHECK: xi 0(%r2), 15
|
|
; CHECK: cgibe %r2, 0, 0(%r14)
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ptr = inttoptr i64 %a to i8 *
|
|
%val = load i8, i8 *%ptr
|
|
%xor = xor i8 %val, 15
|
|
store i8 %xor, i8 *%ptr
|
|
%cmp = icmp eq i64 %a, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %a, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Test that L gets converted to LT where useful.
|
|
define i32 @f29(i64 %base, i64 %index, i32 *%dest) {
|
|
; CHECK-LABEL: f29:
|
|
; CHECK: lt %r2, 0({{%r2,%r3|%r3,%r2}})
|
|
; CHECK-NEXT: bler %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i64 %base, %index
|
|
%ptr = inttoptr i64 %add to i32 *
|
|
%res = load i32, i32 *%ptr
|
|
%cmp = icmp sle i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %res, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Test that LY gets converted to LT where useful.
|
|
define i32 @f30(i64 %base, i64 %index, i32 *%dest) {
|
|
; CHECK-LABEL: f30:
|
|
; CHECK: lt %r2, 100000({{%r2,%r3|%r3,%r2}})
|
|
; CHECK-NEXT: bler %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add1 = add i64 %base, %index
|
|
%add2 = add i64 %add1, 100000
|
|
%ptr = inttoptr i64 %add2 to i32 *
|
|
%res = load i32, i32 *%ptr
|
|
%cmp = icmp sle i32 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %res, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; Test that LG gets converted to LTG where useful.
|
|
define i64 @f31(i64 %base, i64 %index, i64 *%dest) {
|
|
; CHECK-LABEL: f31:
|
|
; CHECK: ltg %r2, 0({{%r2,%r3|%r3,%r2}})
|
|
; CHECK-NEXT: bher %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i64 %base, %index
|
|
%ptr = inttoptr i64 %add to i64 *
|
|
%res = load i64, i64 *%ptr
|
|
%cmp = icmp sge i64 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %res, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %res
|
|
}
|
|
|
|
; Test that LGF gets converted to LTGF where useful.
|
|
define i64 @f32(i64 %base, i64 %index, i64 *%dest) {
|
|
; CHECK-LABEL: f32:
|
|
; CHECK: ltgf %r2, 0({{%r2,%r3|%r3,%r2}})
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i64 %base, %index
|
|
%ptr = inttoptr i64 %add to i32 *
|
|
%val = load i32, i32 *%ptr
|
|
%res = sext i32 %val to i64
|
|
%cmp = icmp sgt i64 %res, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %res, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %res
|
|
}
|
|
|
|
; Test that LR gets converted to LTR where useful.
|
|
define i32 @f33(i32 %dummy, i32 %val, i32 *%dest) {
|
|
; CHECK-LABEL: f33:
|
|
; CHECK: ltr %r2, %r3
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
call void asm sideeffect "blah $0", "{r2}"(i32 %val)
|
|
%cmp = icmp slt i32 %val, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %val, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %val
|
|
}
|
|
|
|
; Test that LGR gets converted to LTGR where useful.
|
|
define i64 @f34(i64 %dummy, i64 %val, i64 *%dest) {
|
|
; CHECK-LABEL: f34:
|
|
; CHECK: ltgr %r2, %r3
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
call void asm sideeffect "blah $0", "{r2}"(i64 %val)
|
|
%cmp = icmp sgt i64 %val, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %val, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %val
|
|
}
|
|
|
|
; Test that LGFR gets converted to LTGFR where useful.
|
|
define i64 @f35(i64 %dummy, i32 %val, i64 *%dest) {
|
|
; CHECK-LABEL: f35:
|
|
; CHECK: ltgfr %r2, %r3
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ext = sext i32 %val to i64
|
|
call void asm sideeffect "blah $0", "{r2}"(i64 %ext)
|
|
%cmp = icmp sgt i64 %ext, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %ext, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %ext
|
|
}
|
|
|
|
; Test a case where it is the source rather than destination of LR that
|
|
; we need.
|
|
define i32 @f36(i32 %val, i32 %dummy, i32 *%dest) {
|
|
; CHECK-LABEL: f36:
|
|
; CHECK: ltr %r3, %r2
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r3
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
call void asm sideeffect "blah $0", "{r3}"(i32 %val)
|
|
%cmp = icmp slt i32 %val, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %val, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %val
|
|
}
|
|
|
|
; Test a case where it is the source rather than destination of LGR that
|
|
; we need.
|
|
define i64 @f37(i64 %val, i64 %dummy, i64 *%dest) {
|
|
; CHECK-LABEL: f37:
|
|
; CHECK: ltgr %r3, %r2
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r3
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
call void asm sideeffect "blah $0", "{r3}"(i64 %val)
|
|
%cmp = icmp slt i64 %val, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %val, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %val
|
|
}
|
|
|
|
; Test a case where it is the source rather than destination of LGFR that
|
|
; we need.
|
|
define i32 @f38(i32 %val, i64 %dummy, i32 *%dest) {
|
|
; CHECK-LABEL: f38:
|
|
; CHECK: ltgfr %r3, %r2
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r3
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ext = sext i32 %val to i64
|
|
call void asm sideeffect "blah $0", "{r3}"(i64 %ext)
|
|
%cmp = icmp slt i32 %val, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %val, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %val
|
|
}
|
|
|
|
; Test f35 for in-register extensions.
|
|
define i64 @f39(i64 %dummy, i64 %a, i64 *%dest) {
|
|
; CHECK-LABEL: f39:
|
|
; CHECK: ltgfr %r2, %r3
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%val = trunc i64 %a to i32
|
|
%ext = sext i32 %val to i64
|
|
call void asm sideeffect "blah $0", "{r2}"(i64 %ext)
|
|
%cmp = icmp sgt i64 %ext, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %ext, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %ext
|
|
}
|
|
|
|
; ...and again with what InstCombine would produce for f40.
|
|
define i64 @f40(i64 %dummy, i64 %a, i64 *%dest) {
|
|
; CHECK-LABEL: f40:
|
|
; CHECK: ltgfr %r2, %r3
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %r2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%shl = shl i64 %a, 32
|
|
%ext = ashr i64 %shl, 32
|
|
call void asm sideeffect "blah $0", "{r2}"(i64 %ext)
|
|
%cmp = icmp sgt i64 %shl, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %ext, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %ext
|
|
}
|
|
|
|
; Try a form of f7 in which the subtraction operands are compared directly.
|
|
define i32 @f41(i32 %a, i32 %b, i32 *%dest) {
|
|
; CHECK-LABEL: f41:
|
|
; CHECK: s %r2, 0(%r4)
|
|
; CHECK-NEXT: bner %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%cur = load i32, i32 *%dest
|
|
%res = sub i32 %a, %cur
|
|
%cmp = icmp ne i32 %a, %cur
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i32 %b, i32 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i32 %res
|
|
}
|
|
|
|
; A version of f32 that tests the unextended value.
|
|
define i64 @f42(i64 %base, i64 %index, i64 *%dest) {
|
|
; CHECK-LABEL: f42:
|
|
; CHECK: ltgf %r2, 0({{%r2,%r3|%r3,%r2}})
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = add i64 %base, %index
|
|
%ptr = inttoptr i64 %add to i32 *
|
|
%val = load i32, i32 *%ptr
|
|
%res = sext i32 %val to i64
|
|
%cmp = icmp sgt i32 %val, 0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store i64 %res, i64 *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret i64 %res
|
|
}
|
|
|
|
!1 = !{!"branch_weights", i32 2, i32 1}
|