1; RUN: llc -mtriple=aarch64 %s -o - | FileCheck %s 2; RUN: llc -mtriple=aarch64 -global-isel=true -global-isel-abort=2 %s -o - | FileCheck %s 3 4 5; Div whose result is unused should be removed unless we have strict exceptions 6 7; CHECK-LABEL: unused_div: 8; CHECK-NOT: fdiv 9; CHECK: ret 10define void @unused_div(float %x, float %y) { 11entry: 12 %add = fdiv float %x, %y 13 ret void 14} 15 16; CHECK-LABEL: unused_div_fpexcept_strict: 17; CHECK: fdiv s0, s0, s1 18; CHECK-NEXT: ret 19define void @unused_div_fpexcept_strict(float %x, float %y) #0 { 20entry: 21 %add = call float @llvm.experimental.constrained.fdiv.f32(float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 22 ret void 23} 24 25; CHECK-LABEL: unused_div_round_dynamic: 26; CHECK-NOT: fdiv 27; CHECK: ret 28define void @unused_div_round_dynamic(float %x, float %y) #0 { 29entry: 30 %add = call float @llvm.experimental.constrained.fdiv.f32(float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 31 ret void 32} 33 34 35; Machine CSE should eliminate the second add unless we have strict exceptions 36 37; CHECK-LABEL: add_twice: 38; CHECK: fadd [[ADD:s[0-9]+]], s0, s1 39; CHECK-NEXT: cmp w0, #0 40; CHECK-NEXT: fmul [[MUL:s[0-9]+]], [[ADD]], [[ADD]] 41; CHECK-NEXT: fcsel s0, [[ADD]], [[MUL]], eq 42; CHECK-NEXT: ret 43define float @add_twice(float %x, float %y, i32 %n) { 44entry: 45 %add = fadd float %x, %y 46 %tobool.not = icmp eq i32 %n, 0 47 br i1 %tobool.not, label %if.end, label %if.then 48 49if.then: 50 %add1 = fadd float %x, %y 51 %mul = fmul float %add, %add1 52 br label %if.end 53 54if.end: 55 %a.0 = phi float [ %mul, %if.then ], [ %add, %entry ] 56 ret float %a.0 57} 58 59; CHECK-LABEL: add_twice_fpexcept_strict: 60; CHECK: fmov [[X:s[0-9]+]], s0 61; CHECK-NEXT: fadd s0, s0, s1 62; CHECK-NEXT: cbz w0, [[LABEL:.LBB[0-9_]+]] 63; CHECK: fadd [[ADD:s[0-9]+]], [[X]], s1 64; CHECK-NEXT: fmul s0, s0, [[ADD]] 65; CHECK: [[LABEL]]: 66; CHECK-NEXT: ret 67define float @add_twice_fpexcept_strict(float %x, float %y, i32 %n) #0 { 68entry: 69 %add = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 70 %tobool.not = icmp eq i32 %n, 0 71 br i1 %tobool.not, label %if.end, label %if.then 72 73if.then: 74 %add1 = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 75 %mul = call float @llvm.experimental.constrained.fmul.f32(float %add, float %add1, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 76 br label %if.end 77 78if.end: 79 %a.0 = phi float [ %mul, %if.then ], [ %add, %entry ] 80 ret float %a.0 81} 82 83; CHECK-LABEL: add_twice_round_dynamic: 84; CHECK: fadd s0, s0, s1 85; CHECK-NEXT: cbz w0, [[LABEL:.LBB[0-9_]+]] 86; CHECK-NOT: fadd 87; CHECK: fmul s0, s0, s0 88; CHECK: [[LABEL]]: 89; CHECK-NEXT: ret 90define float @add_twice_round_dynamic(float %x, float %y, i32 %n) #0 { 91entry: 92 %add = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 93 %tobool.not = icmp eq i32 %n, 0 94 br i1 %tobool.not, label %if.end, label %if.then 95 96if.then: 97 %add1 = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 98 %mul = call float @llvm.experimental.constrained.fmul.f32(float %add, float %add1, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 99 br label %if.end 100 101if.end: 102 %a.0 = phi float [ %mul, %if.then ], [ %add, %entry ] 103 ret float %a.0 104} 105 106 107; Two adds separated by llvm.set.rounding should be preserved when rounding is 108; dynamic (as they may give different results) or when we have strict exceptions 109; (the llvm.set.rounding is irrelevant, but both could trap). 110 111; CHECK-LABEL: set_rounding: 112; CHECK-DAG: fadd [[SREG:s[0-9]+]], s0, s1 113; CHECK-DAG: mrs [[XREG1:x[0-9]+]], FPCR 114; CHECK-DAG: orr [[XREG2:x[0-9]+]], [[XREG1]], #0xc00000 115; CHECK: msr FPCR, [[XREG2]] 116; CHECK-NEXT: mrs [[XREG3:x[0-9]+]], FPCR 117; CHECK-NEXT: and [[XREG4:x[0-9]+]], [[XREG3]], #0xffffffffff3fffff 118; CHECK-NEXT: msr FPCR, [[XREG4]] 119; CHECK-NEXT: fsub s0, [[SREG]], [[SREG]] 120; CHECK-NEXT: ret 121define float @set_rounding(float %x, float %y) { 122entry: 123 %add1 = fadd float %x, %y 124 call void @llvm.set.rounding(i32 0) 125 %add2 = fadd float %x, %y 126 call void @llvm.set.rounding(i32 1) 127 %sub = fsub float %add1, %add2 128 ret float %sub 129} 130 131; CHECK-LABEL: set_rounding_fpexcept_strict: 132; CHECK-DAG: fadd [[SREG1:s[0-9]+]], s0, s1 133; CHECK-DAG: mrs [[XREG1:x[0-9]+]], FPCR 134; CHECK-DAG: orr [[XREG2:x[0-9]+]], [[XREG1]], #0xc00000 135; CHECK: msr FPCR, [[XREG2]] 136; CHECK-DAG: fadd [[SREG2:s[0-9]+]], s0, s1 137; CHECK-DAG: mrs [[XREG3:x[0-9]+]], FPCR 138; CHECK-DAG: and [[XREG4:x[0-9]+]], [[XREG3]], #0xffffffffff3fffff 139; CHECK-NEXT: msr FPCR, [[XREG4]] 140; CHECK-NEXT: fsub s0, [[SREG1]], [[SREG2]] 141; CHECK-NEXT: ret 142define float @set_rounding_fpexcept_strict(float %x, float %y) #0 { 143entry: 144 %add1 = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 145 call void @llvm.set.rounding(i32 0) #0 146 %add2 = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 147 call void @llvm.set.rounding(i32 1) #0 148 %sub = call float @llvm.experimental.constrained.fsub.f32(float %add1, float %add2, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 149 ret float %sub 150} 151 152; CHECK-LABEL: set_rounding_round_dynamic: 153; CHECK-DAG: fadd [[SREG1:s[0-9]+]], s0, s1 154; CHECK-DAG: mrs [[XREG1:x[0-9]+]], FPCR 155; CHECK-DAG: orr [[XREG2:x[0-9]+]], [[XREG1]], #0xc00000 156; CHECK: msr FPCR, [[XREG2]] 157; CHECK-DAG: fadd [[SREG2:s[0-9]+]], s0, s1 158; CHECK-DAG: mrs [[XREG3:x[0-9]+]], FPCR 159; CHECK-DAG: and [[XREG4:x[0-9]+]], [[XREG3]], #0xffffffffff3fffff 160; CHECK-NEXT: msr FPCR, [[XREG4]] 161; CHECK-NEXT: fsub s0, [[SREG1]], [[SREG2]] 162; CHECK-NEXT: ret 163define float @set_rounding_round_dynamic(float %x, float %y) #0 { 164entry: 165 %add1 = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 166 call void @llvm.set.rounding(i32 0) #0 167 %add2 = call float @llvm.experimental.constrained.fadd.f32(float %x, float %y, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 168 call void @llvm.set.rounding(i32 1) #0 169 %sub = call float @llvm.experimental.constrained.fsub.f32(float %add1, float %add2, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 170 ret float %sub 171} 172 173declare float @llvm.experimental.constrained.fadd.f32(float, float, metadata, metadata) 174declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, metadata) 175declare float @llvm.experimental.constrained.fmul.f32(float, float, metadata, metadata) 176declare float @llvm.experimental.constrained.fdiv.f32(float, float, metadata, metadata) 177declare i32 @llvm.get.rounding() 178declare void @llvm.set.rounding(i32) 179 180attributes #0 = { strictfp } 181