llvm / llvm/llvm-project

[mlir][arith] convert-arith-to-llvm silently leaves arith.extf/truncf on fp8 and fp6 types, breaking translation to LLVM IR

Open
#221,950 1 comment 0 reactions 0 assignees View on GitHub
mlir
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

**Summary**
arith.extf and arith.truncf on the FP8 and FP6 types have no expansion in arith-expand and no conversion in convert-arith-to-llvm. Because convert-arith-to-llvm is a partial conversion, it reports success and leaves the ops in place, so a pipeline that is supposed to produce pure LLVM dialect silently does not, and the failure only appears later in mlir-translate. bf16, f8E8M0FNU and f4E2M1FN are expanded and work correctly; the other eight narrow formats are not.

**Environment**
LLVM 23.0.0git, upstream commit aaa2dbb8d014 (2026-05-20). Reproduced with unmodified mlir-opt and mlir-translate.

**Reproducer**

```mlir
// fp8.mlir
func.func @fp8_roundtrip(%arg0: f32) -> f32 {
%0 = arith.truncf %arg0 : f32 to f8E4M3FN
%1 = arith.extf %0 : f8E4M3FN to f32
return %1 : f32
}
```

Step 1 — arith-expand with every float option enabled is a no-op:

`$ mlir-opt fp8.mlir --arith-expand="include-bf16=true include-f8e8m0=true include-f4e2m1=true"`

```mlir
module {
func.func @fp8_roundtrip(%arg0: f32) -> f32 {
%0 = arith.truncf %arg0 : f32 to f8E4M3FN
%1 = arith.extf %0 : f8E4M3FN to f32
return %1 : f32
}
}
```

Step 2 — arith-emulate-unsupported-floats cannot help either, since it marks these two ops legal by construction:

`$ mlir-opt fp8.mlir --arith-emulate-unsupported-floats="source-types=f8E4M3FN target-type=f32"`

```mlir
%0 = arith.truncf %arg0 : f32 to f8E4M3FN
%1 = arith.extf %0 : f8E4M3FN to f32
```

Step 3 — the actual bug. Lowering to LLVM dialect exits 0, yet arith ops remain:

`$ mlir-opt fp8.mlir --pass-pipeline="builtin.module(convert-arith-to-llvm,convert-func-to-llvm,reconcile-unrealized-casts)"`

```mlir
...
%0 = arith.truncf %arg0 : f32 to f8E4M3FN
%1 = arith.extf %0 : f8E4M3FN to f32
```

```console
$ echo $?
0
```

Step 4 — translation then fails on that output:

```console
$ mlir-translate --mlir-to-llvmir lowered.mlir
error: Dialect `arith' not found for custom op 'arith.truncf'
$ echo $?
1
```

Contrast, same shape with bf16, which upstream does expand:

```console
$ mlir-opt bf16.mlir --arith-expand="include-bf16=true" # no arith.truncf/extf remain

**Scope**
| Type | arith-expand result |
|--------|--------|
| f8E4M3FN, f8E4M3, f8E5M2, f8E3M4 | not expanded |
| f8E5M2FNUZ, f8E4M3FNUZ | not expanded |
| f6E2M3FN, f6E3M2FN | not expanded |
| f8E8M0FNU | expanded |
| f4E2M1FN | expanded |
| bf16 | expanded |
```

**Root cause**
mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp has three float expansions — populateExpandBFloat16Patterns, populateExpandF8E8M0Patterns, populateExpandF4E2M1Patterns — and the legality predicates name only those types:

```c++
target.addDynamicallyLegalOp(
[=](arith::ExtFOp op) {
...
if (includeBf16) legalTypes &= !(inETy.isBF16() && outETy.isF32());
if (includeF8E8M0) legalTypes &= !llvm::isa(inETy);
if (includeF4E2M1) legalTypes &= !llvm::isa(inETy);
return legalTypes;
});
```

Separately, LLVMTypeConverter::convertFloatType maps every FP8/FP6/FP4 type to a same-width integer, and LLVM::isCompatibleFloatingPointType admits only bf16, f16, f32, f64, f80, f128, ppc_fp128. So the LLVM dialect can carry these values as storage but has no arithmetic for them — the conversion must happen while the format is still in the type system, i.e. at the arith level, which is where the three existing expansions live.

**Expected**
Either an include-f8-style option adding expansions for the remaining formats alongside the existing three, or — at minimum — convert-arith-to-llvm failing loudly instead of reporting success on a module it did not fully convert. The silent success is the more damaging half: a downstream pipeline has no way to detect it short of scanning the output for arith. ops.

Contributor guide

Open the contributing guide

Research direction

Start in mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp, reviewing the existing float expansion patterns and legality predicates for arith.extf and arith.truncf. Reproduce the f8E4M3FN pipeline with mlir-opt, then verify that the remaining narrow formats are either expanded before LLVM conversion or that conversion fails loudly; confirm the result with mlir-translate.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
56/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.