| ; 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 f0x7F7FFFFF, 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 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 float @fptrunc_fast_select_true_val(float %x, double %y, i1 %cond) { |
| ; CHECK-LABEL: @fptrunc_fast_select_true_val( |
| ; CHECK-NEXT: [[TMP1:%.*]] = fptrunc fast double [[Y:%.*]] to float |
| ; CHECK-NEXT: [[NARROW_SEL:%.*]] = select 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 fast 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 <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 |
| } |
| |
| define <2 x float> @fptrunc_nnan_select_false_val(<2 x float> %x, <2 x double> %y, <2 x i1> %cond) { |
| ; CHECK-LABEL: @fptrunc_nnan_select_false_val( |
| ; CHECK-NEXT: [[TMP1:%.*]] = fptrunc nnan <2 x double> [[Y:%.*]] to <2 x float> |
| ; CHECK-NEXT: [[NARROW_SEL:%.*]] = select <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 nnan <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 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 |
| } |
| |
| define half @fptrunc_max(half %arg) { |
| ; CHECK-LABEL: @fptrunc_max( |
| ; CHECK-NEXT: [[CMP:%.*]] = fcmp olt half [[ARG:%.*]], 0.000000e+00 |
| ; CHECK-NEXT: [[NARROW_SEL:%.*]] = select i1 [[CMP]], half 0.000000e+00, half [[ARG]] |
| ; CHECK-NEXT: ret half [[NARROW_SEL]] |
| ; |
| %ext = fpext half %arg to double |
| %cmp = fcmp olt double %ext, 0.000000e+00 |
| %max = select i1 %cmp, double 0.000000e+00, double %ext |
| %trunc = fptrunc double %max to half |
| ret half %trunc |
| } |
| |
| ; 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 |
| } |
| |
| ; Convert from integer is exact, so cast to float even if fpext is simplified. |
| |
| define float @fptrunc_narrow_sitofp_with_fpext(i64 %x) { |
| ; CHECK-LABEL: @fptrunc_narrow_sitofp_with_fpext( |
| ; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[X:%.*]], 74383 |
| ; CHECK-NEXT: [[TMP2:%.*]] = uitofp nneg i64 [[TMP1]] to float |
| ; CHECK-NEXT: [[CONV4:%.*]] = fdiv float [[TMP2]], 7.438300e+04 |
| ; CHECK-NEXT: ret float [[CONV4]] |
| ; |
| %1 = urem i64 %x, 74383 |
| %conv = sitofp i64 %1 to float |
| %conv2 = fpext float %conv to double |
| %div3 = fdiv double %conv2, 7.438300e+04 |
| %conv4 = fptrunc double %div3 to float |
| ret float %conv4 |
| } |
| |
| ; Convert from integer is exact, so cast to float. |
| |
| define float @fptrunc_narrow_urem_within_float_range(i64 %x) { |
| ; CHECK-LABEL: @fptrunc_narrow_urem_within_float_range( |
| ; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[X:%.*]], 74383 |
| ; CHECK-NEXT: [[TMP2:%.*]] = uitofp nneg i64 [[TMP1]] to float |
| ; CHECK-NEXT: [[CONV4:%.*]] = fdiv float [[TMP2]], 7.438300e+04 |
| ; CHECK-NEXT: ret float [[CONV4]] |
| ; |
| %1 = urem i64 %x,74383 |
| %conv = sitofp i64 %1 to double |
| %div3 = fdiv double %conv, 7.438300e+04 |
| %conv4 = fptrunc double %div3 to float |
| ret float %conv4 |
| } |
| |
| ; Negative test - input is not known, so cast cannot be narrowed to float. |
| |
| define float @fptrunc_narrow_outside_float_range(i64 %x) { |
| ; CHECK-LABEL: @fptrunc_narrow_outside_float_range( |
| ; CHECK-NEXT: [[CONV:%.*]] = sitofp i64 [[X:%.*]] to double |
| ; CHECK-NEXT: [[DIV3:%.*]] = fdiv double [[CONV]], 7.438300e+04 |
| ; CHECK-NEXT: [[CONV4:%.*]] = fptrunc double [[DIV3]] to float |
| ; CHECK-NEXT: ret float [[CONV4]] |
| ; |
| %conv = sitofp i64 %x to double |
| %div3 = fdiv double %conv, 7.438300e+04 |
| %conv4 = fptrunc double %div3 to float |
| ret float %conv4 |
| } |
| |
| ; Narrowing fptrunc(binop(fpext,fpext)) recomputes the binop in a smaller type, |
| ; which can overflow to inf where the wide op was finite, so the binop's ninf |
| ; alone must NOT be copied to the narrowed op (it would make that inf poison: |
| ; miscompile). nnan is value-based and stays sound, so it is preserved. ninf |
| ; survives only when both the binop and the fptrunc have it: the binop's ninf |
| ; covers the narrowed op's operands, the fptrunc's covers its result. |
| |
| define half @fmul_narrow_drop_ninf(half %x, half %y) { |
| ; CHECK-LABEL: @fmul_narrow_drop_ninf( |
| ; CHECK-NEXT: [[R:%.*]] = fmul half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fmul ninf float %wx, %wy |
| %r = fptrunc float %m to half |
| ret half %r |
| } |
| |
| define half @fadd_narrow_drop_ninf(half %x, half %y) { |
| ; CHECK-LABEL: @fadd_narrow_drop_ninf( |
| ; CHECK-NEXT: [[R:%.*]] = fadd half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fadd ninf float %wx, %wy |
| %r = fptrunc float %m to half |
| ret half %r |
| } |
| |
| define half @fsub_narrow_drop_ninf(half %x, half %y) { |
| ; CHECK-LABEL: @fsub_narrow_drop_ninf( |
| ; CHECK-NEXT: [[R:%.*]] = fsub half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fsub ninf float %wx, %wy |
| %r = fptrunc float %m to half |
| ret half %r |
| } |
| |
| define half @fdiv_narrow_drop_ninf(half %x, half %y) { |
| ; CHECK-LABEL: @fdiv_narrow_drop_ninf( |
| ; CHECK-NEXT: [[R:%.*]] = fdiv half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fdiv ninf float %wx, %wy |
| %r = fptrunc float %m to half |
| ret half %r |
| } |
| |
| ; nnan is sound to keep; ninf is still dropped. |
| |
| define half @fmul_narrow_keep_nnan_drop_ninf(half %x, half %y) { |
| ; CHECK-LABEL: @fmul_narrow_keep_nnan_drop_ninf( |
| ; CHECK-NEXT: [[R:%.*]] = fmul nnan half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fmul nnan ninf float %wx, %wy |
| %r = fptrunc float %m to half |
| ret half %r |
| } |
| |
| ; Other flags (reassoc) are preserved; only ninf is cleared. |
| |
| define half @fmul_narrow_keep_reassoc_drop_ninf(half %x, half %y) { |
| ; CHECK-LABEL: @fmul_narrow_keep_reassoc_drop_ninf( |
| ; CHECK-NEXT: [[R:%.*]] = fmul reassoc half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fmul reassoc ninf float %wx, %wy |
| %r = fptrunc float %m to half |
| ret half %r |
| } |
| |
| ; ninf on the fptrunc alone must NOT transfer to the narrowed binop either: |
| ; ninf also constrains the operands. With x = inf, y = 0.0, the wide fmul is |
| ; nan and fptrunc ninf of nan is well-defined, but fmul ninf half inf, 0.0 |
| ; would be poison. |
| |
| define half @fmul_narrow_ninf_only_on_fptrunc(half %x, half %y) { |
| ; CHECK-LABEL: @fmul_narrow_ninf_only_on_fptrunc( |
| ; CHECK-NEXT: [[R:%.*]] = fmul half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fmul float %wx, %wy |
| %r = fptrunc ninf float %m to half |
| ret half %r |
| } |
| |
| ; ninf on both the binop and the fptrunc is kept. |
| |
| define half @fmul_narrow_ninf_on_both(half %x, half %y) { |
| ; CHECK-LABEL: @fmul_narrow_ninf_on_both( |
| ; CHECK-NEXT: [[R:%.*]] = fmul ninf half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fmul ninf float %wx, %wy |
| %r = fptrunc ninf float %m to half |
| ret half %r |
| } |
| |
| ; Same for fdiv, where even a finite result shows it: with x = 1.0, y = inf, |
| ; the wide fdiv is 0.0 and the fptrunc ninf is fully defined, but |
| ; fdiv ninf half 1.0, inf would be poison. |
| |
| define half @fdiv_narrow_ninf_only_on_fptrunc(half %x, half %y) { |
| ; CHECK-LABEL: @fdiv_narrow_ninf_only_on_fptrunc( |
| ; CHECK-NEXT: [[R:%.*]] = fdiv half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fdiv float %wx, %wy |
| %r = fptrunc ninf float %m to half |
| ret half %r |
| } |
| |
| define half @fdiv_narrow_ninf_on_both(half %x, half %y) { |
| ; CHECK-LABEL: @fdiv_narrow_ninf_on_both( |
| ; CHECK-NEXT: [[R:%.*]] = fdiv ninf half [[X:%.*]], [[Y:%.*]] |
| ; CHECK-NEXT: ret half [[R]] |
| ; |
| %wx = fpext half %x to float |
| %wy = fpext half %y to float |
| %m = fdiv ninf float %wx, %wy |
| %r = fptrunc ninf float %m to half |
| ret half %r |
| } |