[Mlir-commits] [mlir] [mlir][arith] Add LUT-based lowering of arith.extf for FP8 to F32. (PR #196321)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu May 7 07:11:31 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Javed Absar (javedabsar1)
<details>
<summary>Changes</summary>
FP8 formats are for efficient storage and for the actual computation the data format is often
converted to FP32. MLIR's existing arith.extf lowers FP8 to a sequence of arithmetic operations.
This PR adds an alternative pass, `--convert-arith-fp8-extf-to-lut`, that replaces
`arith.extf %v : f8X to f32` with a table lookup into a 256-entry global constant.
One table is emitted per distinct FP8 format; values are precomputed at compile
time via APFloat to match exact IEEE semantics.
Supported formats:
f8E4M3FN, f8E5M2, f8E4M3FNUZ, f8E5M2FNUZ, f8E4M3B11FNUZ, f8E3M4, f8E4M3.
Includes FileCheck tests, an integration test (JIT), and unit test that verifies all 256 bit
patterns for each format against APFloat reference values.
---
Patch is 24.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/196321.diff
12 Files Affected:
- (added) mlir/include/mlir/Conversion/ArithToLUT/ArithToLUT.h (+23)
- (modified) mlir/include/mlir/Conversion/Passes.h (+1)
- (modified) mlir/include/mlir/Conversion/Passes.td (+26)
- (added) mlir/lib/Conversion/ArithToLUT/ArithToLUT.cpp (+197)
- (added) mlir/lib/Conversion/ArithToLUT/CMakeLists.txt (+18)
- (modified) mlir/lib/Conversion/CMakeLists.txt (+1)
- (added) mlir/test/Conversion/ArithToLUT/extf-to-lut.mlir (+36)
- (added) mlir/test/Conversion/ArithToLUT/linalg-generic-fp8-addf.mlir (+33)
- (added) mlir/test/Integration/Dialect/Arith/CPU/test-lut-extf-f8.mlir (+59)
- (added) mlir/unittests/Conversion/ArithToLUT/ArithToLUTTest.cpp (+177)
- (added) mlir/unittests/Conversion/ArithToLUT/CMakeLists.txt (+27)
- (modified) mlir/unittests/Conversion/CMakeLists.txt (+1)
``````````diff
diff --git a/mlir/include/mlir/Conversion/ArithToLUT/ArithToLUT.h b/mlir/include/mlir/Conversion/ArithToLUT/ArithToLUT.h
new file mode 100644
index 0000000000000..09545ebf3f7ac
--- /dev/null
+++ b/mlir/include/mlir/Conversion/ArithToLUT/ArithToLUT.h
@@ -0,0 +1,23 @@
+//===- ArithToLUT.h - Arith FP8 extf to LUT conversion -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_CONVERSION_ARITHTOLUT_ARITHTOLUT_H
+#define MLIR_CONVERSION_ARITHTOLUT_ARITHTOLUT_H
+
+#include "mlir/Pass/Pass.h"
+#include <memory>
+
+namespace mlir {
+class Pass;
+
+#define GEN_PASS_DECL_CONVERTARITHFP8EXTFTOLUT
+#include "mlir/Conversion/Passes.h.inc"
+
+} // namespace mlir
+
+#endif // MLIR_CONVERSION_ARITHTOLUT_ARITHTOLUT_H
diff --git a/mlir/include/mlir/Conversion/Passes.h b/mlir/include/mlir/Conversion/Passes.h
index a54b98004c3b6..51965e5df8f84 100644
--- a/mlir/include/mlir/Conversion/Passes.h
+++ b/mlir/include/mlir/Conversion/Passes.h
@@ -17,6 +17,7 @@
#include "mlir/Conversion/ArithToArmSME/ArithToArmSME.h"
#include "mlir/Conversion/ArithToEmitC/ArithToEmitCPass.h"
#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
+#include "mlir/Conversion/ArithToLUT/ArithToLUT.h"
#include "mlir/Conversion/ArithToSPIRV/ArithToSPIRV.h"
#include "mlir/Conversion/ArmNeon2dToIntr/ArmNeon2dToIntr.h"
#include "mlir/Conversion/ArmSMEToLLVM/ArmSMEToLLVM.h"
diff --git a/mlir/include/mlir/Conversion/Passes.td b/mlir/include/mlir/Conversion/Passes.td
index d401b56c7602d..2d3214f670a08 100644
--- a/mlir/include/mlir/Conversion/Passes.td
+++ b/mlir/include/mlir/Conversion/Passes.td
@@ -186,6 +186,32 @@ def ArithToLLVMConversionPass : Pass<"convert-arith-to-llvm"> {
];
}
+//===----------------------------------------------------------------------===//
+// ArithToLUT
+//===----------------------------------------------------------------------===//
+
+def ConvertArithFP8ExtFToLUT : Pass<"convert-arith-fp8-extf-to-lut", "ModuleOp"> {
+ let summary = "Replace arith.extf from f8 types to f32 with a memref LUT lookup";
+ let description = [{
+ Replaces each `arith.extf %v : f8X to f32` with a load from a 256-entry
+ f32 global constant table indexed by the f8 bit pattern (0–255). One table
+ is emitted per distinct f8 source format; the values are precomputed at
+ compile time via APFloat so they match the format's exact IEEE semantics.
+ This LUT approach is alternative to direct arith operations to convert to f32.
+
+ The expansion sequence is:
+ %tbl = memref.get_global @__extf_lut_<fmt> : memref<256xf32>
+ %i8 = arith.bitcast %v : f8X -> i8
+ %ui32 = arith.extui %i8 : i8 -> i32
+ %idx = arith.index_cast %ui32 : i32 -> index
+ %res = memref.load %tbl[%idx] : memref<256xf32>
+
+ Supported source types: f8E4M3FN, f8E5M2, f8E4M3FNUZ, f8E5M2FNUZ,
+ f8E4M3B11FNUZ, f8E3M4, f8E4M3.
+ }];
+ let dependentDialects = ["arith::ArithDialect", "memref::MemRefDialect"];
+}
+
//===----------------------------------------------------------------------===//
// ArithToAPFloat
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Conversion/ArithToLUT/ArithToLUT.cpp b/mlir/lib/Conversion/ArithToLUT/ArithToLUT.cpp
new file mode 100644
index 0000000000000..ad03c70149fdb
--- /dev/null
+++ b/mlir/lib/Conversion/ArithToLUT/ArithToLUT.cpp
@@ -0,0 +1,197 @@
+//===- ArithToLUT.cpp - Replace arith.extf f8→f32 with LUT lookup ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// For each arith.extf %v : f8X to f32, this pass emits a 256-entry f32 global
+// constant (one per distinct f8 format) and replaces the op with LUT:
+// e.g.
+// ```
+// ...
+// %f32_x = arith.extf %f8_x : f8E4M3FN to f32
+// ...
+// ```
+// results in this sequence:
+// ```
+// memref.global "private" constant @__extf_lut_f8E4M3FN : memref<256xf32>
+// = dense<"0x000000000000003B0000803B...">
+// ...
+// func.func @foo (...) {
+// ...
+// %tbl = memref.get_global @__extf_lut_f8E4M3FN : memref<256xf32>
+// %i8 = arith.bitcast %v : f8X -> i8
+// %ui32 = arith.extui %i8 : i8 -> i32
+// %idx = arith.index_cast %ui32 : i32 -> index
+// %res = memref.load %tbl[%idx] : memref<256xf32>
+// ```
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Conversion/ArithToLUT/ArithToLUT.h"
+
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/MemRef/IR/MemRef.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/PatternMatch.h"
+#include "mlir/IR/SymbolTable.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace mlir {
+#define GEN_PASS_DEF_CONVERTARITHFP8EXTFTOLUT
+#include "mlir/Conversion/Passes.h.inc"
+} // namespace mlir
+
+using namespace mlir;
+
+//===----------------------------------------------------------------------===//
+// Helpers
+//===----------------------------------------------------------------------===//
+
+// Returns true for the f8 float types that need LUT-based extf lowering.
+static bool isSupportedF8Type(Type t) {
+ return isa<Float8E4M3FNType, Float8E5M2Type, Float8E4M3FNUZType,
+ Float8E5M2FNUZType, Float8E4M3B11FNUZType, Float8E3M4Type,
+ Float8E4M3Type>(t);
+}
+
+// Returns a stable, symbol-safe name for the global LUT of the given f8 type.
+
+// Returns a name for the global LUT by appending the MLIR textual
+// // representation of the given f8 type to a fixed prefix.
+static std::string lutSymbolName(FloatType srcType) {
+ std::string name = "__extf_lut_";
+ llvm::raw_string_ostream os(name);
+ srcType.print(os);
+ return name;
+}
+
+// Precomputes 256 f32 values by enumerating every 8-bit pattern for srcType.
+static SmallVector<float, 256> buildExtFLUT(FloatType srcType) {
+ const llvm::fltSemantics &sem = srcType.getFloatSemantics();
+ SmallVector<float, 256> table;
+ table.reserve(256);
+ for (unsigned i = 0; i < 256; ++i) {
+ APFloat val(sem, APInt(8, i));
+ bool losesInfo = false;
+ val.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven,
+ &losesInfo);
+ table.push_back(val.convertToFloat());
+ }
+ return table;
+}
+
+// Inserts (or returns existing) memref.global constant for the given f8 type.
+static memref::GlobalOp
+getOrCreateLUT(ModuleOp module, FloatType srcType,
+ llvm::DenseMap<Type, memref::GlobalOp> &cache) {
+ auto it = cache.find(srcType);
+ if (it != cache.end())
+ return it->second;
+
+ std::string symName = lutSymbolName(srcType);
+ if (auto existing = module.lookupSymbol<memref::GlobalOp>(symName))
+ return existing;
+
+ OpBuilder builder(module.getContext());
+ builder.setInsertionPointToStart(module.getBody());
+ auto f32Ty = builder.getF32Type();
+ auto memrefTy = MemRefType::get({256}, f32Ty);
+ auto tensorTy = RankedTensorType::get({256}, f32Ty);
+
+ SmallVector<float, 256> values = buildExtFLUT(srcType);
+ auto denseAttr = DenseElementsAttr::get(tensorTy, ArrayRef<float>(values));
+
+ auto global = memref::GlobalOp::create(
+ builder, module.getLoc(),
+ /*sym_name=*/symName,
+ /*sym_visibility=*/builder.getStringAttr("private"),
+ /*type=*/memrefTy,
+ /*initial_value=*/denseAttr,
+ /*constant=*/true,
+ /*alignment=*/builder.getI64IntegerAttr(64));
+ cache[srcType] = global;
+ return global;
+}
+
+//===----------------------------------------------------------------------===//
+// Rewrite pattern
+//===----------------------------------------------------------------------===//
+
+struct ExtFToLUTPattern : public OpRewritePattern<arith::ExtFOp> {
+ ExtFToLUTPattern(MLIRContext *ctx,
+ llvm::DenseMap<Type, memref::GlobalOp> &lutCache)
+ : OpRewritePattern(ctx), lutCache(lutCache) {}
+
+ LogicalResult matchAndRewrite(arith::ExtFOp op,
+ PatternRewriter &rewriter) const override {
+ Type srcTy = op.getIn().getType();
+ Type dstTy = op.getType();
+
+ if (!isSupportedF8Type(srcTy) || !isa<Float32Type>(dstTy))
+ return failure();
+
+ auto srcFloatTy = cast<FloatType>(srcTy);
+ auto module = op->getParentOfType<ModuleOp>();
+ memref::GlobalOp global = getOrCreateLUT(module, srcFloatTy, lutCache);
+
+ Location loc = op.getLoc();
+ auto memrefTy = cast<MemRefType>(global.getType());
+
+ // %tbl = memref.get_global @__extf_lut_<fmt>
+ Value tbl = memref::GetGlobalOp::create(rewriter, loc, memrefTy,
+ global.getSymName());
+ // %i8 = arith.bitcast %in : f8X -> i8
+ Value i8val = arith::BitcastOp::create(rewriter, loc,
+ rewriter.getIntegerType(8),
+ op.getIn());
+
+ // %ui32 = arith.extui %i8 : i8 -> i32
+ Value ui32val =
+ arith::ExtUIOp::create(rewriter, loc, rewriter.getI32Type(), i8val);
+
+ // %idx = arith.index_cast %ui32 : i32 -> index
+ Value idx = arith::IndexCastOp::create(rewriter, loc,
+ rewriter.getIndexType(), ui32val);
+
+ // %res = memref.load %tbl[%idx]
+ Value result = memref::LoadOp::create(rewriter, loc, tbl, idx);
+
+ rewriter.replaceOp(op, result);
+ return success();
+ }
+
+private:
+ llvm::DenseMap<Type, memref::GlobalOp> &lutCache;
+};
+
+//===----------------------------------------------------------------------===//
+// Pass
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct ConvertArithFP8ExtFToLUTPass
+ : public impl::ConvertArithFP8ExtFToLUTBase<ConvertArithFP8ExtFToLUTPass> {
+
+ void runOnOperation() override {
+ ModuleOp module = getOperation();
+ MLIRContext *ctx = &getContext();
+
+ // Cache so each format gets exactly one global, inserted once.
+ llvm::DenseMap<Type, memref::GlobalOp> lutCache;
+
+ RewritePatternSet patterns(ctx);
+ patterns.add<ExtFToLUTPattern>(ctx, lutCache);
+
+ if (failed(applyPatternsGreedily(module, std::move(patterns))))
+ signalPassFailure();
+ }
+};
+} // namespace
diff --git a/mlir/lib/Conversion/ArithToLUT/CMakeLists.txt b/mlir/lib/Conversion/ArithToLUT/CMakeLists.txt
new file mode 100644
index 0000000000000..e94e4a1835e11
--- /dev/null
+++ b/mlir/lib/Conversion/ArithToLUT/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_mlir_conversion_library(MLIRArithToLUT
+ ArithToLUT.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/ArithToLUT
+
+ DEPENDS
+ MLIRConversionPassIncGen
+
+ LINK_COMPONENTS
+ Core
+
+ LINK_LIBS PUBLIC
+ MLIRArithDialect
+ MLIRMemRefDialect
+ MLIRPass
+ MLIRTransforms
+)
diff --git a/mlir/lib/Conversion/CMakeLists.txt b/mlir/lib/Conversion/CMakeLists.txt
index e17988b12cade..b5f298a777d5d 100644
--- a/mlir/lib/Conversion/CMakeLists.txt
+++ b/mlir/lib/Conversion/CMakeLists.txt
@@ -6,6 +6,7 @@ add_subdirectory(ArithAndMathToAPFloat)
add_subdirectory(ArithToArmSME)
add_subdirectory(ArithToEmitC)
add_subdirectory(ArithToLLVM)
+add_subdirectory(ArithToLUT)
add_subdirectory(ArithToSPIRV)
add_subdirectory(ArmNeon2dToIntr)
add_subdirectory(ArmSMEToSCF)
diff --git a/mlir/test/Conversion/ArithToLUT/extf-to-lut.mlir b/mlir/test/Conversion/ArithToLUT/extf-to-lut.mlir
new file mode 100644
index 0000000000000..2fce8572df790
--- /dev/null
+++ b/mlir/test/Conversion/ArithToLUT/extf-to-lut.mlir
@@ -0,0 +1,36 @@
+// RUN: mlir-opt %s --convert-arith-fp8-extf-to-lut | FileCheck %s
+
+// CHECK-DAG: memref.global "private" constant @__extf_lut_f8E4M3FN : memref<256xf32>
+// CHECK-DAG: memref.global "private" constant @__extf_lut_f8E5M2 : memref<256xf32>
+
+// Single f8E4M3FN input — table should appear once, op replaced by LUT sequence.
+func.func @extf_f8E4M3FN(%a: f8E4M3FN) -> f32 {
+ // CHECK-LABEL: @extf_f8E4M3FN
+ // CHECK: memref.get_global @__extf_lut_f8E4M3FN : memref<256xf32>
+ // CHECK: arith.bitcast {{.*}} : f8E4M3FN to i8
+ // CHECK: arith.extui {{.*}} : i8 to i32
+ // CHECK: arith.index_cast {{.*}} : i32 to index
+ // CHECK: memref.load {{.*}}[{{.*}}] : memref<256xf32>
+ // CHECK-NOT: arith.extf
+ %r = arith.extf %a : f8E4M3FN to f32
+ return %r : f32
+}
+
+// Two f8E4M3FN inputs — same table reused, not duplicated.
+func.func @extf_f8E4M3FN_twice(%a: f8E4M3FN, %b: f8E4M3FN) -> f32 {
+ // CHECK-LABEL: @extf_f8E4M3FN_twice
+ // CHECK-COUNT-2: memref.get_global @__extf_lut_f8E4M3FN
+ %ra = arith.extf %a : f8E4M3FN to f32
+ %rb = arith.extf %b : f8E4M3FN to f32
+ %sum = arith.addf %ra, %rb : f32
+ return %sum : f32
+}
+
+// Second f8 format — separate table emitted.
+func.func @extf_f8E5M2(%a: f8E5M2) -> f32 {
+ // CHECK-LABEL: @extf_f8E5M2
+ // CHECK: memref.get_global @__extf_lut_f8E5M2 : memref<256xf32>
+ // CHECK-NOT: arith.extf
+ %r = arith.extf %a : f8E5M2 to f32
+ return %r : f32
+}
diff --git a/mlir/test/Conversion/ArithToLUT/linalg-generic-fp8-addf.mlir b/mlir/test/Conversion/ArithToLUT/linalg-generic-fp8-addf.mlir
new file mode 100644
index 0000000000000..200a8262a9669
--- /dev/null
+++ b/mlir/test/Conversion/ArithToLUT/linalg-generic-fp8-addf.mlir
@@ -0,0 +1,33 @@
+// RUN: mlir-opt %s --convert-arith-fp8-extf-to-lut | FileCheck %s
+
+// linalg.generic over tensor<1024xf8E4M3FN>: both extf ops inside the body
+// should be replaced by LUT-lookup sequences; one shared table is emitted.
+
+// CHECK-DAG: memref.global "private" constant @__extf_lut_f8E4M3FN : memref<256xf32>
+
+func.func @linalg_addf_fp8(
+ %a: tensor<1024xf8E4M3FN>,
+ %b: tensor<1024xf8E4M3FN>) -> tensor<1024xf32> {
+ // CHECK-LABEL: @linalg_addf_fp8
+ // CHECK: linalg.generic
+ // CHECK-COUNT-2: memref.get_global @__extf_lut_f8E4M3FN : memref<256xf32>
+ // CHECK: arith.addf {{.*}} : f32
+ // CHECK-NOT: arith.extf
+ %init = tensor.empty() : tensor<1024xf32>
+ %result = linalg.generic {
+ indexing_maps = [
+ affine_map<(d0) -> (d0)>,
+ affine_map<(d0) -> (d0)>,
+ affine_map<(d0) -> (d0)>
+ ],
+ iterator_types = ["parallel"]
+ } ins(%a, %b : tensor<1024xf8E4M3FN>, tensor<1024xf8E4M3FN>)
+ outs(%init : tensor<1024xf32>) {
+ ^bb0(%in_a: f8E4M3FN, %in_b: f8E4M3FN, %out: f32):
+ %ra = arith.extf %in_a : f8E4M3FN to f32
+ %rb = arith.extf %in_b : f8E4M3FN to f32
+ %sum = arith.addf %ra, %rb : f32
+ linalg.yield %sum : f32
+ } -> tensor<1024xf32>
+ return %result : tensor<1024xf32>
+}
diff --git a/mlir/test/Integration/Dialect/Arith/CPU/test-lut-extf-f8.mlir b/mlir/test/Integration/Dialect/Arith/CPU/test-lut-extf-f8.mlir
new file mode 100644
index 0000000000000..2b89887493114
--- /dev/null
+++ b/mlir/test/Integration/Dialect/Arith/CPU/test-lut-extf-f8.mlir
@@ -0,0 +1,59 @@
+// Verify that the LUT-based f8E4M3FN → f32 lowering produces the correct f32
+// value for a selection of well-known bit patterns.
+
+// RUN: mlir-opt %s \
+// RUN: --convert-arith-fp8-extf-to-lut \
+// RUN: --finalize-memref-to-llvm \
+// RUN: --convert-arith-to-llvm \
+// RUN: --convert-vector-to-llvm \
+// RUN: --convert-func-to-llvm \
+// RUN: --reconcile-unrealized-casts \
+// RUN: | mlir-runner -e entry --entry-point-result=void \
+// RUN: --shared-libs=%mlir_c_runner_utils \
+// RUN: | FileCheck %s --match-full-lines
+
+func.func @check(%bits: i8) {
+ %f8 = arith.bitcast %bits : i8 to f8E4M3FN
+ %f32 = arith.extf %f8 : f8E4M3FN to f32
+ vector.print %f32 : f32
+ return
+}
+
+func.func @entry() {
+ // +0.0 (bit pattern 0x00)
+ %b0 = arith.constant 0 : i8
+ // CHECK: 0
+ func.call @check(%b0) : (i8) -> ()
+
+ // -0.0 (bit pattern 0x80)
+ %b1 = arith.constant -128 : i8
+ // CHECK: -0
+ func.call @check(%b1) : (i8) -> ()
+
+ // 1.0 (bit pattern 0x38: exp=7, mant=0)
+ %b2 = arith.constant 56 : i8
+ // CHECK: 1
+ func.call @check(%b2) : (i8) -> ()
+
+ // -1.0 (bit pattern 0xB8)
+ %b3 = arith.constant -72 : i8
+ // CHECK: -1
+ func.call @check(%b3) : (i8) -> ()
+
+ // 2.0 (bit pattern 0x40: exp=8, mant=0)
+ %b4 = arith.constant 64 : i8
+ // CHECK: 2
+ func.call @check(%b4) : (i8) -> ()
+
+ // 0.5 (bit pattern 0x30: exp=6, mant=0)
+ %b5 = arith.constant 48 : i8
+ // CHECK: 0.5
+ func.call @check(%b5) : (i8) -> ()
+
+ // max finite: 448.0 (bit pattern 0x7E: exp=15, mant=0b110)
+ %b6 = arith.constant 126 : i8
+ // CHECK: 448
+ func.call @check(%b6) : (i8) -> ()
+
+ return
+}
diff --git a/mlir/unittests/Conversion/ArithToLUT/ArithToLUTTest.cpp b/mlir/unittests/Conversion/ArithToLUT/ArithToLUTTest.cpp
new file mode 100644
index 0000000000000..884cc5dc2497b
--- /dev/null
+++ b/mlir/unittests/Conversion/ArithToLUT/ArithToLUTTest.cpp
@@ -0,0 +1,177 @@
+//===- ArithToLUTTest.cpp - Exhaustive correctness test for the LUT pass --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// For every supported f8 format, iterate all 256 bit patterns, run each
+// through the LUT-lowered extf function via the MLIR JIT, and compare the
+// result bit-for-bit against the APFloat reference value computed the same
+// way buildExtFLUT does at compile time.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Conversion/ArithToLUT/ArithToLUT.h"
+#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
+#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h"
+#include "mlir/Conversion/MemRefToLLVM/MemRefToLLVM.h"
+#include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/ExecutionEngine/ExecutionEngine.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/MLIRContext.h"
+#include "mlir/InitAllDialects.h"
+#include "mlir/Parser/Parser.h"
+#include "mlir/Pass/PassManager.h"
+#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
+#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "gmock/gmock.h"
+
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <string>
+
+// JIT is unavailable on some platforms.
+#ifdef __sparc__
+#define SKIP_WITHOUT_JIT(x) DISABLED_##x
+#else
+#define SKIP_WITHOUT_JIT(x) x
+#endif
+
+using namespace mlir;
+
+#if !defined(_WIN32) && !defined(_AIX)
+
+static struct LLVMInitializer {
+ LLVMInitializer() {
+ llvm::InitializeNativeTarget();
+ llvm::InitializeNativeTargetAsmPrinter();
+ }
+} initializer;
+
+static LogicalResult lowerToLLVM(ModuleOp module) {
+ PassManager pm(module->getName());
+ pm.addPass(createConvertArithFP8ExtFToLUT());
+ pm.addPass(createFinalizeMemRefToLLVMConversionPass());
+ pm.addNestedPass<func::FuncOp>(createArithToLLVMConversionPass());
+ pm.addPass(createConvertFuncToLLVMPass());
+ pm.addPass(createReconcileUnrealizedCastsPass());
+ return pm.run(module);
+}
+
+// Builds a module with function:
+// func @test(%arg0: i32) -> f32 { trunci i32 to i8; bitcast i8 to f8; extf f8 to f32 }
+static std::string makeModule(const char *f8TypeName) {
+ std::string s;
+ s += "func.func @test(%arg0: i32) -> f32 "
+ "attributes { llvm.emit_c_interface } {\n";
+ s += " %i8 = arith.trunci %arg0 : i32 to i8\n";
+ s += std::string(" %f8 = arith.bitcast %i8 : i8 to ") + f8TypeName + "\n";
+ s += std::string(" %f32 = arith.extf %f8 : ") + f8TypeName + " to f32\n";
+ s += " return %f32 : f32\n...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/196321
More information about the Mlir-commits
mailing list