The idea behind this canonicalization is that it allows us to handle less patterns, because we know that some will be canonicalized away. This is indeed very useful to e.g. know that constants are always on the right. However, this is only useful if the canonicalization is actually reliable. This is the case for constants, but not for arguments: Moving these to the right makes it look like the "more complex" expression is guaranteed to be on the left, but this is not actually the case in practice. It fails as soon as you replace the argument with another instruction. The end result is that it looks like things correctly work in tests, while they actually don't. We use the "thwart complexity-based canonicalization" trick to handle this in tests, but it's often a challenge for new contributors to get this right, and based on the regressions this PR originally exposed, we clearly don't get this right in many cases. For this reason, I think that it's better to remove this complexity canonicalization. It will make it much easier to write tests for commuted cases and make sure that they are handled.
206 lines
7.2 KiB
LLVM
206 lines
7.2 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
|
|
|
|
define float @fadd_fpext_op0(float %x, double %y) {
|
|
; CHECK-LABEL: @fadd_fpext_op0(
|
|
; CHECK-NEXT: [[EXT:%.*]] = fpext float [[X:%.*]] to double
|
|
; CHECK-NEXT: [[BO:%.*]] = fadd reassoc double [[Y:%.*]], [[EXT]]
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc double [[BO]] to float
|
|
; CHECK-NEXT: ret float [[R]]
|
|
;
|
|
%ext = fpext float %x to double
|
|
%bo = fadd reassoc double %ext, %y
|
|
%r = fptrunc double %bo to float
|
|
ret float %r
|
|
}
|
|
|
|
define half @fsub_fpext_op1(half %x, double %y) {
|
|
; CHECK-LABEL: @fsub_fpext_op1(
|
|
; CHECK-NEXT: [[EXT:%.*]] = fpext half [[X:%.*]] to double
|
|
; CHECK-NEXT: [[BO:%.*]] = fsub reassoc double [[Y:%.*]], [[EXT]]
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc double [[BO]] to half
|
|
; CHECK-NEXT: ret half [[R]]
|
|
;
|
|
%ext = fpext half %x to double
|
|
%bo = fsub reassoc double %y, %ext
|
|
%r = fptrunc double %bo to half
|
|
ret half %r
|
|
}
|
|
|
|
define <2 x float> @fdiv_constant_op0(<2 x double> %x) {
|
|
; CHECK-LABEL: @fdiv_constant_op0(
|
|
; CHECK-NEXT: [[BO:%.*]] = fdiv reassoc <2 x double> <double 4.210000e+01, double -1.000000e-01>, [[X:%.*]]
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc <2 x double> [[BO]] to <2 x float>
|
|
; CHECK-NEXT: ret <2 x float> [[R]]
|
|
;
|
|
%bo = fdiv reassoc <2 x double> <double 42.1, double -0.1>, %x
|
|
%r = fptrunc <2 x double> %bo to <2 x float>
|
|
ret <2 x float> %r
|
|
}
|
|
|
|
define <2 x half> @fmul_constant_op1(<2 x float> %x) {
|
|
; CHECK-LABEL: @fmul_constant_op1(
|
|
; CHECK-NEXT: [[BO:%.*]] = fmul reassoc <2 x float> [[X:%.*]], <float 0x47EFFFFFE0000000, float 5.000000e-01>
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc <2 x float> [[BO]] to <2 x half>
|
|
; CHECK-NEXT: ret <2 x half> [[R]]
|
|
;
|
|
%bo = fmul reassoc <2 x float> %x, <float 0x47efffffe0000000, float 0.5>
|
|
%r = fptrunc <2 x float> %bo to <2 x half>
|
|
ret <2 x half> %r
|
|
}
|
|
|
|
define float @fptrunc_select_true_val(float %x, double %y, i1 %cond) {
|
|
; CHECK-LABEL: @fptrunc_select_true_val(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = fptrunc double [[Y:%.*]] to float
|
|
; CHECK-NEXT: [[NARROW_SEL:%.*]] = select fast i1 [[COND:%.*]], float [[TMP1]], float [[X:%.*]]
|
|
; CHECK-NEXT: ret float [[NARROW_SEL]]
|
|
;
|
|
%e = fpext float %x to double
|
|
%sel = select fast i1 %cond, double %y, double %e
|
|
%r = fptrunc double %sel to float
|
|
ret float %r
|
|
}
|
|
|
|
define <2 x float> @fptrunc_select_false_val(<2 x float> %x, <2 x double> %y, <2 x i1> %cond) {
|
|
; CHECK-LABEL: @fptrunc_select_false_val(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = fptrunc <2 x double> [[Y:%.*]] to <2 x float>
|
|
; CHECK-NEXT: [[NARROW_SEL:%.*]] = select nnan <2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x float> [[TMP1]]
|
|
; CHECK-NEXT: ret <2 x float> [[NARROW_SEL]]
|
|
;
|
|
%e = fpext <2 x float> %x to <2 x double>
|
|
%sel = select nnan <2 x i1> %cond, <2 x double> %e, <2 x double> %y
|
|
%r = fptrunc <2 x double> %sel to <2 x float>
|
|
ret <2 x float> %r
|
|
}
|
|
|
|
declare void @use(float)
|
|
|
|
define half @fptrunc_select_true_val_extra_use(half %x, float %y, i1 %cond) {
|
|
; CHECK-LABEL: @fptrunc_select_true_val_extra_use(
|
|
; CHECK-NEXT: [[E:%.*]] = fpext half [[X:%.*]] to float
|
|
; CHECK-NEXT: call void @use(float [[E]])
|
|
; CHECK-NEXT: [[TMP1:%.*]] = fptrunc float [[Y:%.*]] to half
|
|
; CHECK-NEXT: [[NARROW_SEL:%.*]] = select ninf i1 [[COND:%.*]], half [[TMP1]], half [[X]]
|
|
; CHECK-NEXT: ret half [[NARROW_SEL]]
|
|
;
|
|
%e = fpext half %x to float
|
|
call void @use(float %e)
|
|
%sel = select ninf i1 %cond, float %y, float %e
|
|
%r = fptrunc float %sel to half
|
|
ret half %r
|
|
}
|
|
|
|
; Negative test - this would require an extra instruction.
|
|
|
|
define half @fptrunc_select_true_val_extra_use_2(half %x, float %y, i1 %cond) {
|
|
; CHECK-LABEL: @fptrunc_select_true_val_extra_use_2(
|
|
; CHECK-NEXT: [[E:%.*]] = fpext half [[X:%.*]] to float
|
|
; CHECK-NEXT: [[SEL:%.*]] = select ninf i1 [[COND:%.*]], float [[Y:%.*]], float [[E]]
|
|
; CHECK-NEXT: call void @use(float [[SEL]])
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc float [[SEL]] to half
|
|
; CHECK-NEXT: ret half [[R]]
|
|
;
|
|
%e = fpext half %x to float
|
|
%sel = select ninf i1 %cond, float %y, float %e
|
|
call void @use(float %sel)
|
|
%r = fptrunc float %sel to half
|
|
ret half %r
|
|
}
|
|
|
|
; Negative test - the extend must be from the same source type as the result of the trunc.
|
|
|
|
define float @fptrunc_select_true_val_type_mismatch(half %x, double %y, i1 %cond) {
|
|
; CHECK-LABEL: @fptrunc_select_true_val_type_mismatch(
|
|
; CHECK-NEXT: [[E:%.*]] = fpext half [[X:%.*]] to double
|
|
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], double [[Y:%.*]], double [[E]]
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc double [[SEL]] to float
|
|
; CHECK-NEXT: ret float [[R]]
|
|
;
|
|
%e = fpext half %x to double
|
|
%sel = select i1 %cond, double %y, double %e
|
|
%r = fptrunc double %sel to float
|
|
ret float %r
|
|
}
|
|
|
|
; Negative test - but given enough FMF, should this be folded?
|
|
|
|
define float @fptrunc_select_true_val_type_mismatch_fast(half %x, double %y, i1 %cond) {
|
|
; CHECK-LABEL: @fptrunc_select_true_val_type_mismatch_fast(
|
|
; CHECK-NEXT: [[E:%.*]] = fpext half [[X:%.*]] to double
|
|
; CHECK-NEXT: [[SEL:%.*]] = select fast i1 [[COND:%.*]], double [[Y:%.*]], double [[E]]
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc double [[SEL]] to float
|
|
; CHECK-NEXT: ret float [[R]]
|
|
;
|
|
%e = fpext half %x to double
|
|
%sel = select fast i1 %cond, double %y, double %e
|
|
%r = fptrunc double %sel to float
|
|
ret float %r
|
|
}
|
|
|
|
; Convert from integer is exact, so convert directly to float.
|
|
|
|
define <2 x float> @ItoFtoF_s54_f64_f32(<2 x i54> %i) {
|
|
; CHECK-LABEL: @ItoFtoF_s54_f64_f32(
|
|
; CHECK-NEXT: [[R:%.*]] = sitofp <2 x i54> [[I:%.*]] to <2 x float>
|
|
; CHECK-NEXT: ret <2 x float> [[R]]
|
|
;
|
|
%x = sitofp <2 x i54> %i to <2 x double>
|
|
%r = fptrunc <2 x double> %x to <2 x float>
|
|
ret <2 x float> %r
|
|
}
|
|
|
|
; Convert from integer is exact, so convert directly to half.
|
|
; Extra use is ok.
|
|
|
|
define half @ItoFtoF_u24_f32_f16(i24 %i) {
|
|
; CHECK-LABEL: @ItoFtoF_u24_f32_f16(
|
|
; CHECK-NEXT: [[X:%.*]] = uitofp i24 [[I:%.*]] to float
|
|
; CHECK-NEXT: call void @use(float [[X]])
|
|
; CHECK-NEXT: [[R:%.*]] = uitofp i24 [[I]] to half
|
|
; CHECK-NEXT: ret half [[R]]
|
|
;
|
|
%x = uitofp i24 %i to float
|
|
call void @use(float %x)
|
|
%r = fptrunc float %x to half
|
|
ret half %r
|
|
}
|
|
|
|
; Negative test - intermediate rounding in float type.
|
|
|
|
define float @ItoFtoF_s55_f64_f32(i55 %i) {
|
|
; CHECK-LABEL: @ItoFtoF_s55_f64_f32(
|
|
; CHECK-NEXT: [[X:%.*]] = sitofp i55 [[I:%.*]] to double
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc double [[X]] to float
|
|
; CHECK-NEXT: ret float [[R]]
|
|
;
|
|
%x = sitofp i55 %i to double
|
|
%r = fptrunc double %x to float
|
|
ret float %r
|
|
}
|
|
|
|
; Negative test - intermediate rounding in float type.
|
|
|
|
define half @ItoFtoF_u25_f32_f16(i25 %i) {
|
|
; CHECK-LABEL: @ItoFtoF_u25_f32_f16(
|
|
; CHECK-NEXT: [[X:%.*]] = uitofp i25 [[I:%.*]] to float
|
|
; CHECK-NEXT: [[R:%.*]] = fptrunc float [[X]] to half
|
|
; CHECK-NEXT: ret half [[R]]
|
|
;
|
|
%x = uitofp i25 %i to float
|
|
%r = fptrunc float %x to half
|
|
ret half %r
|
|
}
|
|
|
|
; Negative test - bitcast bfloat to half is not optimized
|
|
|
|
define half @fptrunc_to_bfloat_bitcast_to_half(float %src) {
|
|
; CHECK-LABEL: @fptrunc_to_bfloat_bitcast_to_half(
|
|
; CHECK-NEXT: [[TRUNC:%.*]] = fptrunc float [[SRC:%.*]] to bfloat
|
|
; CHECK-NEXT: [[CAST:%.*]] = bitcast bfloat [[TRUNC]] to half
|
|
; CHECK-NEXT: ret half [[CAST]]
|
|
;
|
|
%trunc = fptrunc float %src to bfloat
|
|
%cast = bitcast bfloat %trunc to half
|
|
ret half %cast
|
|
}
|