llvm / llvm/llvm-project

AMDGPU code size regression when using fdiv instead of rcp intrinsic

Open
#187,482 2 comments 0 reactions 0 assignees View on GitHub
backend:AMDGPU missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

In this example, when using an `fdiv` which can be substituted for the `llvm.amdgcn.rcp` intrinsic, there is a small code size regression. There is an fneg here which in the rcp case is optimally placed as the source of a v_fma_f32. This is free, since fma is always a VOP3 instruction. In the regressing case, the fneg source modifier is on a v_mul_f32, which forces using a VOP3 encoding costing an extra 8 bytes. The fneg in the original IR is in the optimal placement, so the DAG combiner is moving the fneg to a worse place.

https://godbolt.org/z/4M3ncjab7

```
; RUN: llc -mcpu=gfx900 < %s

target triple = "amdgcn-amd-amdhsa"

; fneg is placed on the FMA, saves 4 bytes. Should have only 1 v_mul_f32_e64
define float @good(float %arg) #0 {
bb:
%call = tail call float @llvm.fabs.f32(float %arg)
%fcmp = fcmp uge float %call, 0x3810000000000000
%fmul = fmul float %call, 0x4170000000000000
%select = select i1 %fcmp, float %call, float %fmul
%call1 = tail call float @llvm.amdgcn.log.f32(float %select)
%fmul2 = fmul float %call1, 0x3FD5555560000000
%call3 = tail call float @llvm.amdgcn.exp2.f32(float %fmul2)
%fmul4 = fmul float %call3, %call3
%call5 = tail call float @llvm.amdgcn.rcp.f32(float %fmul4)
%fneg = fneg float %select
%call6 = tail call float @llvm.fmuladd.f32(float %call5, float %fneg, float %call3)
%call7 = tail call float @llvm.fmuladd.f32(float %call6, float 0xBFD5555560000000, float %call3)
%fmul8 = fmul float %call7, 3.906250e-03
%select9 = select i1 %fcmp, float %call7, float %fmul8
%call10 = tail call i1 @llvm.is.fpclass.f32(float %arg, i32 612)
%call11 = tail call float @llvm.copysign.f32(float %select9, float %arg)
%select12 = select i1 %call10, float %arg, float %call11
ret float %select12
}

; fneg is placed on the fmul, requiring VOP3 encoding. Uses 2 v_mul_f32_e64, when the second could be v_mul_f32_e32
define float @bad(float %arg) #0 {
bb:
%call = tail call float @llvm.fabs.f32(float %arg)
%fcmp = fcmp uge float %call, 0x3810000000000000
%fmul = fmul float %call, 0x4170000000000000
%select = select i1 %fcmp, float %call, float %fmul
%call1 = tail call float @llvm.amdgcn.log.f32(float %select)
%fmul2 = fmul float %call1, 0x3FD5555560000000
%call3 = tail call float @llvm.amdgcn.exp2.f32(float %fmul2)
%fmul4 = fmul float %call3, %call3
%fdiv = fdiv afn float 1.000000e+00, %fmul4, !fpmath !0
%fneg = fneg float %select
%call5 = tail call float @llvm.fmuladd.f32(float %fdiv, float %fneg, float %call3)
%call6 = tail call float @llvm.fmuladd.f32(float %call5, float 0xBFD5555560000000, float %call3)
%fmul7 = fmul float %call6, 3.906250e-03
%select8 = select i1 %fcmp, float %call6, float %fmul7
%call9 = tail call i1 @llvm.is.fpclass.f32(float %arg, i32 612)
%call10 = tail call float @llvm.copysign.f32(float %select8, float %arg)
%select11 = select i1 %call9, float %arg, float %call10
ret float %select11
}

declare float @llvm.amdgcn.exp2.f32(float) #1
declare float @llvm.amdgcn.log.f32(float) #1
declare float @llvm.amdgcn.rcp.f32(float) #1
declare float @llvm.copysign.f32(float, float) #1
declare float @llvm.fabs.f32(float) #1
declare float @llvm.fmuladd.f32(float, float, float) #1
declare i1 @llvm.is.fpclass.f32(float, i32 immarg) #1

attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "amdgpu-no-cluster-id-x" "amdgpu-no-cluster-id-y" "amdgpu-no-cluster-id-z" "amdgpu-no-completion-action" "amdgpu-no-default-queue" "amdgpu-no-dispatch-id" "amdgpu-no-dispatch-ptr" "amdgpu-no-flat-scratch-init" "amdgpu-no-heap-ptr" "amdgpu-no-hostcall-ptr" "amdgpu-no-implicitarg-ptr" "amdgpu-no-lds-kernel-id" "amdgpu-no-multigrid-sync-arg" "amdgpu-no-queue-ptr" "amdgpu-no-workgroup-id-x" "amdgpu-no-workgroup-id-y" "amdgpu-no-workgroup-id-z" "amdgpu-no-workitem-id-x" "amdgpu-no-workitem-id-y" "amdgpu-no-workitem-id-z" }
attributes #1 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }

!0 = !{float 2.500000e+00}
```

fnegFoldsIntoOpcode and performFNegCombine probably need to handle ISD::FDIV better. hasSourceMods also reports false for fdiv, which isn't true most of the time

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.