[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