mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
b5b6e8e953
This adds support for constrained floating-point comparison intrinsics. Specifically, we add: declare <ty2> @llvm.experimental.constrained.fcmp(<type> <op1>, <type> <op2>, metadata <condition code>, metadata <exception behavior>) declare <ty2> @llvm.experimental.constrained.fcmps(<type> <op1>, <type> <op2>, metadata <condition code>, metadata <exception behavior>) The first variant implements an IEEE "quiet" comparison (i.e. we only get an invalid FP exception if either argument is a SNaN), while the second variant implements an IEEE "signaling" comparison (i.e. we get an invalid FP exception if either argument is any NaN). The condition code is implemented as a metadata string. The same set of predicates as for the fcmp instruction is supported (except for the "true" and "false" predicates). These new intrinsics are mapped by SelectionDAG codegen onto two new ISD opcodes, ISD::STRICT_FSETCC and ISD::STRICT_FSETCCS, again representing quiet vs. signaling comparison operations. Otherwise those nodes look like SETCC nodes, with an additional chain argument and result as usual for strict FP nodes. The patch includes support for the common legalization operations for those nodes. The patch also includes full SystemZ back-end support for the new ISD nodes, mapping them to all available SystemZ instruction to fully implement strict semantics (scalar and vector). Differential Revision: https://reviews.llvm.org/D69281
525 lines
16 KiB
LLVM
525 lines
16 KiB
LLVM
; Test that floating-point strict compares are omitted if CC already has the
|
|
; right value.
|
|
;
|
|
; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z10 \
|
|
; RUN: -enable-misched=0 -no-integrated-as | FileCheck %s
|
|
;
|
|
; We need -enable-misched=0 to make sure f12 and following routines really
|
|
; test the compare elimination pass.
|
|
|
|
|
|
declare float @llvm.fabs.f32(float %f)
|
|
|
|
; Test addition followed by EQ, which can use the CC result of the addition.
|
|
define float @f1(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f1:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: ber %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"oeq",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; ...and again with LT.
|
|
define float @f2(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f2:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; ...and again with GT.
|
|
define float @f3(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f3:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"ogt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; ...and again with UEQ.
|
|
define float @f4(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f4:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: bnlhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"ueq",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Subtraction also provides a zero-based CC value.
|
|
define float @f5(float %a, float %b, float *%dest) {
|
|
; CHECK-LABEL: f5:
|
|
; CHECK: seb %f0, 0(%r2)
|
|
; CHECK-NEXT: bnher %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%cur = load float, float *%dest
|
|
%res = call float @llvm.experimental.constrained.fsub.f32(
|
|
float %a, float %cur,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"ult",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Test the result of LOAD POSITIVE. We cannot omit the LTEBR.
|
|
define float @f6(float %dummy, float %a, float *%dest) #0 {
|
|
; CHECK-LABEL: f6:
|
|
; CHECK: lpdfr %f0, %f2
|
|
; CHECK-NEXT: ltebr %f0, %f0
|
|
; CHECK-NEXT: bhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.fabs.f32(float %a)
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"ogt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %res, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Test the result of LOAD NEGATIVE. We cannot omit the LTEBR.
|
|
define float @f7(float %dummy, float %a, float *%dest) #0 {
|
|
; CHECK-LABEL: f7:
|
|
; CHECK: lndfr %f0, %f2
|
|
; CHECK-NEXT: ltebr %f0, %f0
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%abs = call float @llvm.fabs.f32(float %a)
|
|
%res = fneg float %abs
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %res, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Test the result of LOAD COMPLEMENT. We cannot omit the LTEBR.
|
|
define float @f8(float %dummy, float %a, float *%dest) #0 {
|
|
; CHECK-LABEL: f8:
|
|
; CHECK: lcdfr %f0, %f2
|
|
; CHECK-NEXT: ltebr %f0, %f0
|
|
; CHECK-NEXT: bler %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = fneg float %a
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"ole",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %res, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Multiplication (for example) does not modify CC.
|
|
define float @f9(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f9:
|
|
; CHECK: meebr %f0, %f2
|
|
; CHECK-NEXT: ltebr %f0, %f0
|
|
; CHECK-NEXT: blhr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fmul.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"one",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Test a combination involving a CC-setting instruction followed by
|
|
; a non-CC-setting instruction.
|
|
define float @f10(float %a, float %b, float %c, float *%dest) #0 {
|
|
; CHECK-LABEL: f10:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: debr %f0, %f4
|
|
; CHECK-NEXT: ltebr %f0, %f0
|
|
; CHECK-NEXT: bner %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%res = call float @llvm.experimental.constrained.fdiv.f32(
|
|
float %add, float %c,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"une",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Test a case where CC is set based on a different register from the
|
|
; compare input.
|
|
define float @f11(float %a, float %b, float %c, float *%dest1, float *%dest2) #0 {
|
|
; CHECK-LABEL: f11:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: sebr %f4, %f0
|
|
; CHECK-DAG: ste %f4, 0(%r2)
|
|
; CHECK-DAG: ltebr %f0, %f0
|
|
; CHECK-NEXT: ber %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%add = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%sub = call float @llvm.experimental.constrained.fsub.f32(
|
|
float %c, float %add,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
store float %sub, float *%dest1
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %add, float 0.0,
|
|
metadata !"oeq",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %sub, float *%dest2
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %add
|
|
}
|
|
|
|
; Test that LER gets converted to LTEBR where useful.
|
|
define float @f12(float %dummy, float %val) #0 {
|
|
; CHECK-LABEL: f12:
|
|
; CHECK: ltebr %f0, %f2
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %f0
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ret = call float asm "blah $1", "=f,{f0}"(float %val)
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %val, float 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
call void asm sideeffect "blah", ""()
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %ret
|
|
}
|
|
|
|
; Test that LDR gets converted to LTDBR where useful.
|
|
define double @f13(double %dummy, double %val) #0 {
|
|
; CHECK-LABEL: f13:
|
|
; CHECK: ltdbr %f0, %f2
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %f0
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ret = call double asm "blah $1", "=f,{f0}"(double %val)
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f64(
|
|
double %val, double 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
call void asm sideeffect "blah", ""()
|
|
br label %exit
|
|
|
|
exit:
|
|
ret double %ret
|
|
}
|
|
|
|
; Test that LXR gets converted to LTXBR where useful.
|
|
define void @f14(fp128 *%ptr1, fp128 *%ptr2) #0 {
|
|
; CHECK-LABEL: f14:
|
|
; CHECK: ltxbr
|
|
; CHECK-NEXT: dxbr
|
|
; CHECK-NEXT: std
|
|
; CHECK-NEXT: std
|
|
; CHECK-NEXT: mxbr
|
|
; CHECK-NEXT: std
|
|
; CHECK-NEXT: std
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%val1 = load fp128, fp128 *%ptr1
|
|
%val2 = load fp128, fp128 *%ptr2
|
|
%div = fdiv fp128 %val1, %val2
|
|
store fp128 %div, fp128 *%ptr1
|
|
%mul = fmul fp128 %val1, %val2
|
|
store fp128 %mul, fp128 *%ptr2
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f128(
|
|
fp128 %val1, fp128 0xL00000000000000000000000000000000,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
call void asm sideeffect "blah", ""()
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Test a case where it is the source rather than destination of LER that
|
|
; we need.
|
|
define float @f15(float %val, float %dummy) #0 {
|
|
; CHECK-LABEL: f15:
|
|
; CHECK: ltebr %f2, %f0
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %f2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ret = call float asm "blah $1", "=f,{f2}"(float %val)
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %val, float 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
call void asm sideeffect "blah", ""()
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %ret
|
|
}
|
|
|
|
; Test a case where it is the source rather than destination of LDR that
|
|
; we need.
|
|
define double @f16(double %val, double %dummy) #0 {
|
|
; CHECK-LABEL: f16:
|
|
; CHECK: ltdbr %f2, %f0
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %f2
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ret = call double asm "blah $1", "=f,{f2}"(double %val)
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f64(
|
|
double %val, double 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
call void asm sideeffect "blah", ""()
|
|
br label %exit
|
|
|
|
exit:
|
|
ret double %ret
|
|
}
|
|
|
|
; Repeat f2 with a comparison against -0.
|
|
define float @f17(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f17:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float -0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Verify that we cannot omit the compare if there may be an intervening
|
|
; change to the exception flags.
|
|
define float @f18(float %a, float %b, float *%dest) #0 {
|
|
; CHECK-LABEL: f18:
|
|
; CHECK: aebr %f0, %f2
|
|
; CHECK: ltebr %f0, %f0
|
|
; CHECK-NEXT: ber %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%res = call float @llvm.experimental.constrained.fadd.f32(
|
|
float %a, float %b,
|
|
metadata !"round.dynamic",
|
|
metadata !"fpexcept.strict") #0
|
|
call void asm sideeffect "blah", ""()
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %res, float 0.0,
|
|
metadata !"oeq",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
store float %b, float *%dest
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %res
|
|
}
|
|
|
|
; Verify that we cannot convert LER to LTEBR and omit the compare if
|
|
; there may be an intervening change to the exception flags.
|
|
define float @f19(float %dummy, float %val) #0 {
|
|
; CHECK-LABEL: f19:
|
|
; CHECK: ler %f0, %f2
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: blah %f0
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: ltebr %f2, %f2
|
|
; CHECK-NEXT: blr %r14
|
|
; CHECK: br %r14
|
|
entry:
|
|
%ret = call float asm sideeffect "blah $1", "=f,{f0}"(float %val)
|
|
%cmp = call i1 @llvm.experimental.constrained.fcmp.f32(
|
|
float %val, float 0.0,
|
|
metadata !"olt",
|
|
metadata !"fpexcept.strict") #0
|
|
br i1 %cmp, label %exit, label %store
|
|
|
|
store:
|
|
call void asm sideeffect "blah", ""()
|
|
br label %exit
|
|
|
|
exit:
|
|
ret float %ret
|
|
}
|
|
|
|
attributes #0 = { strictfp }
|
|
|
|
declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata)
|
|
declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, metadata)
|
|
declare float @llvm.experimental.constrained.fmul.f32(float, float, metadata, metadata)
|
|
declare float @llvm.experimental.constrained.fdiv.f32(float, float, metadata, metadata)
|
|
declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata)
|
|
declare i1 @llvm.experimental.constrained.fcmp.f64(double, double, metadata, metadata)
|
|
declare i1 @llvm.experimental.constrained.fcmp.f128(fp128, fp128, metadata, metadata)
|