Skip to content

[CIR] Upstream builtin lowering emitter & FAbs op #151750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

AmrDeveloper
Copy link
Member

Upstreaming FAbsOp as a prerequisite for upstreaming ComplexDivOp

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Aug 1, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 1, 2025

@llvm/pr-subscribers-clang

Author: Amr Hesham (AmrDeveloper)

Changes

Upstreaming FAbsOp as a prerequisite for upstreaming ComplexDivOp


Full diff: https://github.com/llvm/llvm-project/pull/151750.diff

10 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+18)
  • (modified) clang/include/clang/CIR/Dialect/IR/CMakeLists.txt (+4)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+23)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+7)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+4)
  • (added) clang/test/CIR/CodeGen/builtins.cpp (+14)
  • (added) clang/utils/TableGen/CIRLoweringEmitter.cpp (+108)
  • (modified) clang/utils/TableGen/CMakeLists.txt (+1)
  • (modified) clang/utils/TableGen/TableGen.cpp (+7)
  • (modified) clang/utils/TableGen/TableGenBackends.h (+3)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 5ef5b60ed5a52..975a8d102d800 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3199,4 +3199,22 @@ def CIR_ExpectOp : CIR_Op<"expect", [
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// Floating Point Ops
+//===----------------------------------------------------------------------===//
+
+class CIR_UnaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
+    : CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]>
+{
+  let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src);
+  let results = (outs CIR_AnyFloatOrVecOfFloatType:$result);
+  let summary = "libc builtin equivalent ignoring "
+                "floating point exceptions and errno";
+  let assemblyFormat = "$src `:` type($src) attr-dict";
+
+  let llvmOp = llvmOpName;
+}
+
+def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp">;
+
 #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
index 6e7f3da4add3e..3a574b96340e7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
@@ -20,6 +20,10 @@ mlir_tablegen(CIROpsAttributes.h.inc -gen-attrdef-decls)
 mlir_tablegen(CIROpsAttributes.cpp.inc -gen-attrdef-defs)
 add_public_tablegen_target(MLIRCIREnumsGen)
 
+clang_tablegen(CIRBuiltinsLowering.inc -gen-cir-builtins-lowering
+               SOURCE CIROps.td
+               TARGET CIRBuiltinsLowering)
+
 set(LLVM_TARGET_DEFINITIONS CIRTypeConstraints.td)
 mlir_tablegen(CIRTypeConstraints.h.inc -gen-type-constraint-decls)
 mlir_tablegen(CIRTypeConstraints.cpp.inc -gen-type-constraint-defs)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 9049a016b2b9b..201c119c1747f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -71,6 +71,19 @@ RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
   return RValue::get(r);
 }
 
+template <class Operation>
+static RValue emitUnaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf,
+                                                 const CallExpr &e) {
+  mlir::Value arg = cgf.emitScalarExpr(e.getArg(0));
+
+  assert(!cir::MissingFeatures::cgFPOptionsRAII());
+  assert(!cir::MissingFeatures::fpConstraints());
+
+  auto call =
+      cgf.getBuilder().create<Operation>(arg.getLoc(), arg.getType(), arg);
+  return RValue::get(call->getResult(0));
+}
+
 RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
                                        const CallExpr *e,
                                        ReturnValueSlot returnValue) {
@@ -111,6 +124,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
   default:
     break;
 
+  case Builtin::BIfabs:
+  case Builtin::BIfabsf:
+  case Builtin::BIfabsl:
+  case Builtin::BI__builtin_fabs:
+  case Builtin::BI__builtin_fabsf:
+  case Builtin::BI__builtin_fabsf16:
+  case Builtin::BI__builtin_fabsl:
+  case Builtin::BI__builtin_fabsf128:
+    return emitUnaryMaybeConstrainedFPBuiltin<cir::FAbsOp>(*this, *e);
+
   case Builtin::BI__assume:
   case Builtin::BI__builtin_assume: {
     if (e->getArg(0)->HasSideEffects(getContext()))
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 957a51ab334aa..8d18176be8218 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2215,6 +2215,9 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMVecShuffleOpLowering,
                CIRToLLVMVecSplatOpLowering,
                CIRToLLVMVecTernaryOpLowering
+#define GET_BUILTIN_LOWERING_LIST
+#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
+#undef GET_BUILTIN_LOWERING_LIST
       // clang-format on
       >(converter, patterns.getContext());
 
@@ -2792,6 +2795,10 @@ mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+#define GET_BUILTIN_LOWERING_CLASSES_DEF
+#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
+#undef GET_BUILTIN_LOWERING_CLASSES_DEF
+
 std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
   return std::make_unique<ConvertCIRToLLVMPass>();
 }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index f339d4310ae0c..486a6b5daa563 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -628,6 +628,10 @@ class CIRToLLVMGetBitfieldOpLowering
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+#define GET_BUILTIN_LOWERING_CLASSES_DECLARE
+#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
+#undef GET_BUILTIN_LOWERING_CLASSES_DECLARE
+
 } // namespace direct
 } // namespace cir
 
diff --git a/clang/test/CIR/CodeGen/builtins.cpp b/clang/test/CIR/CodeGen/builtins.cpp
new file mode 100644
index 0000000000000..3d43821af4e51
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtins.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+double fabs(double x) {
+  return __builtin_fabs(x);
+}
+
+// CIR: {{.*}} = cir.fabs {{.*}} : !cir.double
+// LLVM: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
+// OGCG: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
diff --git a/clang/utils/TableGen/CIRLoweringEmitter.cpp b/clang/utils/TableGen/CIRLoweringEmitter.cpp
new file mode 100644
index 0000000000000..e494f38ef5b02
--- /dev/null
+++ b/clang/utils/TableGen/CIRLoweringEmitter.cpp
@@ -0,0 +1,108 @@
+//===- CIRLoweringEmitter.cpp - Generate lowering of builtins --=-*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TableGenBackends.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+using namespace llvm;
+
+namespace {
+std::string ClassDeclaration;
+std::string ClassDefinitions;
+std::string ClassList;
+
+// Adapted from mlir/lib/TableGen/Operator.cpp
+// Returns the C++ class name of the operation, which is the name of the
+// operation with the dialect prefix removed and the first underscore removed.
+// If the operation name starts with an underscore, the underscore is considered
+// part of the class name.
+std::string getCppClassName(const Record *Operation) {
+  StringRef Name = Operation->getName();
+  auto [prefix, cppClassName] = Name.split('_');
+  if (prefix.empty()) {
+    // Class name with a leading underscore and without dialect prefix
+    return Name.str();
+  }
+
+  if (cppClassName.empty()) {
+    // Class name without dialect prefix
+    return prefix.str();
+  }
+
+  return cppClassName.str();
+}
+
+void GenerateLowering(const Record *Operation) {
+  using namespace std::string_literals;
+  std::string Name = getCppClassName(Operation);
+  std::string LLVMOp = Operation->getValueAsString("llvmOp").str();
+
+  ClassDeclaration +=
+      "class CIR" + Name +
+      "Lowering : public mlir::OpConversionPattern<cir::" + Name +
+      R"C++(> {
+  public:
+    using OpConversionPattern<cir::)C++" +
+      Name + R"C++(>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::)C++" +
+      Name +
+      " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
+      "const "
+      "override;" +
+      R"C++(
+};
+)C++";
+
+  ClassDefinitions +=
+      R"C++(mlir::LogicalResult
+CIR)C++" +
+      Name + "Lowering::matchAndRewrite(cir::" + Name +
+      R"C++( op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const {)C++";
+
+  auto ResultCount = Operation->getValueAsDag("results")->getNumArgs();
+  if (ResultCount > 0)
+    ClassDefinitions += R"C++(
+  auto resTy = this->getTypeConverter()->convertType(op.getType());)C++";
+
+  ClassDefinitions += R"C++(
+  rewriter.replaceOpWithNewOp<mlir::LLVM::)C++" +
+                      LLVMOp + ">(op";
+
+  if (ResultCount > 0)
+    ClassDefinitions += ", resTy";
+
+  size_t ArgCount = Operation->getValueAsDag("arguments")->getNumArgs();
+  for (size_t i = 0; i != ArgCount; ++i)
+    ClassDefinitions += ", adaptor.getOperands()[" + std::to_string(i) + ']';
+
+  ClassDefinitions += R"C++();
+  return mlir::success();
+}
+)C++";
+
+  ClassList += ", CIR" + Name + "Lowering\n";
+}
+} // namespace
+
+void clang::EmitCIRBuiltinsLowering(const RecordKeeper &Records,
+                                    raw_ostream &OS) {
+  emitSourceFileHeader("Lowering of ClangIR builtins to LLVM IR builtins", OS);
+  for (const auto *Builtin :
+       Records.getAllDerivedDefinitions("LLVMLoweringInfo")) {
+    if (!Builtin->getValueAsString("llvmOp").empty())
+      GenerateLowering(Builtin);
+  }
+
+  OS << "#ifdef GET_BUILTIN_LOWERING_CLASSES_DECLARE\n"
+     << ClassDeclaration << "\n#endif\n";
+  OS << "#ifdef GET_BUILTIN_LOWERING_CLASSES_DEF\n"
+     << ClassDefinitions << "\n#endif\n";
+  OS << "#ifdef GET_BUILTIN_LOWERING_LIST\n" << ClassList << "\n#endif\n";
+}
diff --git a/clang/utils/TableGen/CMakeLists.txt b/clang/utils/TableGen/CMakeLists.txt
index ce759ec8548d9..14f13824e9575 100644
--- a/clang/utils/TableGen/CMakeLists.txt
+++ b/clang/utils/TableGen/CMakeLists.txt
@@ -4,6 +4,7 @@ add_tablegen(clang-tblgen CLANG
   DESTINATION "${CLANG_TOOLS_INSTALL_DIR}"
   EXPORT Clang
   ASTTableGen.cpp
+  CIRLoweringEmitter.cpp
   ClangASTNodesEmitter.cpp
   ClangASTPropertiesEmitter.cpp
   ClangAttrEmitter.cpp
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index 98c4a07a99c42..d4a383e801085 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -25,6 +25,7 @@ using namespace clang;
 enum ActionType {
   PrintRecords,
   DumpJSON,
+  GenCIRBuiltinsLowering,
   GenClangAttrClasses,
   GenClangAttrParserStringSwitches,
   GenClangAttrSubjectMatchRulesParserStringSwitches,
@@ -128,6 +129,9 @@ cl::opt<ActionType> Action(
                    "Print all records to stdout (default)"),
         clEnumValN(DumpJSON, "dump-json",
                    "Dump all records as machine-readable JSON"),
+        clEnumValN(GenCIRBuiltinsLowering, "gen-cir-builtins-lowering",
+                   "Generate lowering of ClangIR builtins to equivalent LLVM "
+                   "IR builtins"),
         clEnumValN(GenClangAttrClasses, "gen-clang-attr-classes",
                    "Generate clang attribute clases"),
         clEnumValN(GenClangAttrParserStringSwitches,
@@ -354,6 +358,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
   case DumpJSON:
     EmitJSON(Records, OS);
     break;
+  case GenCIRBuiltinsLowering:
+    EmitCIRBuiltinsLowering(Records, OS);
+    break;
   case GenClangAttrClasses:
     EmitClangAttrClass(Records, OS);
     break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index 79b1f66d0e49e..26d9e2c611898 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -24,6 +24,9 @@ class RecordKeeper;
 
 namespace clang {
 
+void EmitCIRBuiltinsLowering(const llvm::RecordKeeper &RK,
+                             llvm::raw_ostream &OS);
+
 void EmitClangDeclContext(const llvm::RecordKeeper &RK, llvm::raw_ostream &OS);
 /**
   @param PriorizeIfSubclassOf These classes should be prioritized in the output.

@llvmbot
Copy link
Member

llvmbot commented Aug 1, 2025

@llvm/pr-subscribers-clangir

Author: Amr Hesham (AmrDeveloper)

Changes

Upstreaming FAbsOp as a prerequisite for upstreaming ComplexDivOp


Full diff: https://github.com/llvm/llvm-project/pull/151750.diff

10 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+18)
  • (modified) clang/include/clang/CIR/Dialect/IR/CMakeLists.txt (+4)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+23)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+7)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+4)
  • (added) clang/test/CIR/CodeGen/builtins.cpp (+14)
  • (added) clang/utils/TableGen/CIRLoweringEmitter.cpp (+108)
  • (modified) clang/utils/TableGen/CMakeLists.txt (+1)
  • (modified) clang/utils/TableGen/TableGen.cpp (+7)
  • (modified) clang/utils/TableGen/TableGenBackends.h (+3)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 5ef5b60ed5a52..975a8d102d800 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3199,4 +3199,22 @@ def CIR_ExpectOp : CIR_Op<"expect", [
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// Floating Point Ops
+//===----------------------------------------------------------------------===//
+
+class CIR_UnaryFPToFPBuiltinOp<string mnemonic, string llvmOpName>
+    : CIR_Op<mnemonic, [Pure, SameOperandsAndResultType]>
+{
+  let arguments = (ins CIR_AnyFloatOrVecOfFloatType:$src);
+  let results = (outs CIR_AnyFloatOrVecOfFloatType:$result);
+  let summary = "libc builtin equivalent ignoring "
+                "floating point exceptions and errno";
+  let assemblyFormat = "$src `:` type($src) attr-dict";
+
+  let llvmOp = llvmOpName;
+}
+
+def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp">;
+
 #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
index 6e7f3da4add3e..3a574b96340e7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
@@ -20,6 +20,10 @@ mlir_tablegen(CIROpsAttributes.h.inc -gen-attrdef-decls)
 mlir_tablegen(CIROpsAttributes.cpp.inc -gen-attrdef-defs)
 add_public_tablegen_target(MLIRCIREnumsGen)
 
+clang_tablegen(CIRBuiltinsLowering.inc -gen-cir-builtins-lowering
+               SOURCE CIROps.td
+               TARGET CIRBuiltinsLowering)
+
 set(LLVM_TARGET_DEFINITIONS CIRTypeConstraints.td)
 mlir_tablegen(CIRTypeConstraints.h.inc -gen-type-constraint-decls)
 mlir_tablegen(CIRTypeConstraints.cpp.inc -gen-type-constraint-defs)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 9049a016b2b9b..201c119c1747f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -71,6 +71,19 @@ RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
   return RValue::get(r);
 }
 
+template <class Operation>
+static RValue emitUnaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf,
+                                                 const CallExpr &e) {
+  mlir::Value arg = cgf.emitScalarExpr(e.getArg(0));
+
+  assert(!cir::MissingFeatures::cgFPOptionsRAII());
+  assert(!cir::MissingFeatures::fpConstraints());
+
+  auto call =
+      cgf.getBuilder().create<Operation>(arg.getLoc(), arg.getType(), arg);
+  return RValue::get(call->getResult(0));
+}
+
 RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
                                        const CallExpr *e,
                                        ReturnValueSlot returnValue) {
@@ -111,6 +124,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
   default:
     break;
 
+  case Builtin::BIfabs:
+  case Builtin::BIfabsf:
+  case Builtin::BIfabsl:
+  case Builtin::BI__builtin_fabs:
+  case Builtin::BI__builtin_fabsf:
+  case Builtin::BI__builtin_fabsf16:
+  case Builtin::BI__builtin_fabsl:
+  case Builtin::BI__builtin_fabsf128:
+    return emitUnaryMaybeConstrainedFPBuiltin<cir::FAbsOp>(*this, *e);
+
   case Builtin::BI__assume:
   case Builtin::BI__builtin_assume: {
     if (e->getArg(0)->HasSideEffects(getContext()))
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 957a51ab334aa..8d18176be8218 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2215,6 +2215,9 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMVecShuffleOpLowering,
                CIRToLLVMVecSplatOpLowering,
                CIRToLLVMVecTernaryOpLowering
+#define GET_BUILTIN_LOWERING_LIST
+#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
+#undef GET_BUILTIN_LOWERING_LIST
       // clang-format on
       >(converter, patterns.getContext());
 
@@ -2792,6 +2795,10 @@ mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+#define GET_BUILTIN_LOWERING_CLASSES_DEF
+#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
+#undef GET_BUILTIN_LOWERING_CLASSES_DEF
+
 std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
   return std::make_unique<ConvertCIRToLLVMPass>();
 }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index f339d4310ae0c..486a6b5daa563 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -628,6 +628,10 @@ class CIRToLLVMGetBitfieldOpLowering
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+#define GET_BUILTIN_LOWERING_CLASSES_DECLARE
+#include "clang/CIR/Dialect/IR/CIRBuiltinsLowering.inc"
+#undef GET_BUILTIN_LOWERING_CLASSES_DECLARE
+
 } // namespace direct
 } // namespace cir
 
diff --git a/clang/test/CIR/CodeGen/builtins.cpp b/clang/test/CIR/CodeGen/builtins.cpp
new file mode 100644
index 0000000000000..3d43821af4e51
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtins.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+double fabs(double x) {
+  return __builtin_fabs(x);
+}
+
+// CIR: {{.*}} = cir.fabs {{.*}} : !cir.double
+// LLVM: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
+// OGCG: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
diff --git a/clang/utils/TableGen/CIRLoweringEmitter.cpp b/clang/utils/TableGen/CIRLoweringEmitter.cpp
new file mode 100644
index 0000000000000..e494f38ef5b02
--- /dev/null
+++ b/clang/utils/TableGen/CIRLoweringEmitter.cpp
@@ -0,0 +1,108 @@
+//===- CIRLoweringEmitter.cpp - Generate lowering of builtins --=-*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "TableGenBackends.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+using namespace llvm;
+
+namespace {
+std::string ClassDeclaration;
+std::string ClassDefinitions;
+std::string ClassList;
+
+// Adapted from mlir/lib/TableGen/Operator.cpp
+// Returns the C++ class name of the operation, which is the name of the
+// operation with the dialect prefix removed and the first underscore removed.
+// If the operation name starts with an underscore, the underscore is considered
+// part of the class name.
+std::string getCppClassName(const Record *Operation) {
+  StringRef Name = Operation->getName();
+  auto [prefix, cppClassName] = Name.split('_');
+  if (prefix.empty()) {
+    // Class name with a leading underscore and without dialect prefix
+    return Name.str();
+  }
+
+  if (cppClassName.empty()) {
+    // Class name without dialect prefix
+    return prefix.str();
+  }
+
+  return cppClassName.str();
+}
+
+void GenerateLowering(const Record *Operation) {
+  using namespace std::string_literals;
+  std::string Name = getCppClassName(Operation);
+  std::string LLVMOp = Operation->getValueAsString("llvmOp").str();
+
+  ClassDeclaration +=
+      "class CIR" + Name +
+      "Lowering : public mlir::OpConversionPattern<cir::" + Name +
+      R"C++(> {
+  public:
+    using OpConversionPattern<cir::)C++" +
+      Name + R"C++(>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::)C++" +
+      Name +
+      " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
+      "const "
+      "override;" +
+      R"C++(
+};
+)C++";
+
+  ClassDefinitions +=
+      R"C++(mlir::LogicalResult
+CIR)C++" +
+      Name + "Lowering::matchAndRewrite(cir::" + Name +
+      R"C++( op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const {)C++";
+
+  auto ResultCount = Operation->getValueAsDag("results")->getNumArgs();
+  if (ResultCount > 0)
+    ClassDefinitions += R"C++(
+  auto resTy = this->getTypeConverter()->convertType(op.getType());)C++";
+
+  ClassDefinitions += R"C++(
+  rewriter.replaceOpWithNewOp<mlir::LLVM::)C++" +
+                      LLVMOp + ">(op";
+
+  if (ResultCount > 0)
+    ClassDefinitions += ", resTy";
+
+  size_t ArgCount = Operation->getValueAsDag("arguments")->getNumArgs();
+  for (size_t i = 0; i != ArgCount; ++i)
+    ClassDefinitions += ", adaptor.getOperands()[" + std::to_string(i) + ']';
+
+  ClassDefinitions += R"C++();
+  return mlir::success();
+}
+)C++";
+
+  ClassList += ", CIR" + Name + "Lowering\n";
+}
+} // namespace
+
+void clang::EmitCIRBuiltinsLowering(const RecordKeeper &Records,
+                                    raw_ostream &OS) {
+  emitSourceFileHeader("Lowering of ClangIR builtins to LLVM IR builtins", OS);
+  for (const auto *Builtin :
+       Records.getAllDerivedDefinitions("LLVMLoweringInfo")) {
+    if (!Builtin->getValueAsString("llvmOp").empty())
+      GenerateLowering(Builtin);
+  }
+
+  OS << "#ifdef GET_BUILTIN_LOWERING_CLASSES_DECLARE\n"
+     << ClassDeclaration << "\n#endif\n";
+  OS << "#ifdef GET_BUILTIN_LOWERING_CLASSES_DEF\n"
+     << ClassDefinitions << "\n#endif\n";
+  OS << "#ifdef GET_BUILTIN_LOWERING_LIST\n" << ClassList << "\n#endif\n";
+}
diff --git a/clang/utils/TableGen/CMakeLists.txt b/clang/utils/TableGen/CMakeLists.txt
index ce759ec8548d9..14f13824e9575 100644
--- a/clang/utils/TableGen/CMakeLists.txt
+++ b/clang/utils/TableGen/CMakeLists.txt
@@ -4,6 +4,7 @@ add_tablegen(clang-tblgen CLANG
   DESTINATION "${CLANG_TOOLS_INSTALL_DIR}"
   EXPORT Clang
   ASTTableGen.cpp
+  CIRLoweringEmitter.cpp
   ClangASTNodesEmitter.cpp
   ClangASTPropertiesEmitter.cpp
   ClangAttrEmitter.cpp
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index 98c4a07a99c42..d4a383e801085 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -25,6 +25,7 @@ using namespace clang;
 enum ActionType {
   PrintRecords,
   DumpJSON,
+  GenCIRBuiltinsLowering,
   GenClangAttrClasses,
   GenClangAttrParserStringSwitches,
   GenClangAttrSubjectMatchRulesParserStringSwitches,
@@ -128,6 +129,9 @@ cl::opt<ActionType> Action(
                    "Print all records to stdout (default)"),
         clEnumValN(DumpJSON, "dump-json",
                    "Dump all records as machine-readable JSON"),
+        clEnumValN(GenCIRBuiltinsLowering, "gen-cir-builtins-lowering",
+                   "Generate lowering of ClangIR builtins to equivalent LLVM "
+                   "IR builtins"),
         clEnumValN(GenClangAttrClasses, "gen-clang-attr-classes",
                    "Generate clang attribute clases"),
         clEnumValN(GenClangAttrParserStringSwitches,
@@ -354,6 +358,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
   case DumpJSON:
     EmitJSON(Records, OS);
     break;
+  case GenCIRBuiltinsLowering:
+    EmitCIRBuiltinsLowering(Records, OS);
+    break;
   case GenClangAttrClasses:
     EmitClangAttrClass(Records, OS);
     break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index 79b1f66d0e49e..26d9e2c611898 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -24,6 +24,9 @@ class RecordKeeper;
 
 namespace clang {
 
+void EmitCIRBuiltinsLowering(const llvm::RecordKeeper &RK,
+                             llvm::raw_ostream &OS);
+
 void EmitClangDeclContext(const llvm::RecordKeeper &RK, llvm::raw_ostream &OS);
 /**
   @param PriorizeIfSubclassOf These classes should be prioritized in the output.

Comment on lines 3211 to 3212
let summary = "libc builtin equivalent ignoring "
"floating point exceptions and errno";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really make sense to say that this is libc equivalent, when one of its primary characteristics is a difference from the libc function. Do we need a summary here at all? I'd prefer to see individual summaries on the operations that derive from this, even knowing that they will be very similar.

let llvmOp = llvmOpName;
}

def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp">;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to see a summary and description here. Something like:

  let summary = "Computes the floating-point absolute value";
  let description = [{
    `cir.fabs` computes the absolute value of a floating-point operand
    and returns a result of the same type, ignoring floating-point
    exceptions. It does not set `errno`.
  }];

assert(!cir::MissingFeatures::fpConstraints());

auto call =
cgf.getBuilder().create<Operation>(arg.getLoc(), arg.getType(), arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cgf.getBuilder().create<Operation>(arg.getLoc(), arg.getType(), arg);
Operation::create(cgf.getBuilder(), (arg.getLoc(), arg.getType(), arg);

return cppClassName.str();
}

void GenerateLowering(const Record *Operation) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test for the tablegen handling in this file? There are some examples in clang/test/TableGen/ of clang-tablegen testing in general. What is the minimum .td file needed to make this work?

@@ -25,6 +25,7 @@ using namespace clang;
enum ActionType {
PrintRecords,
DumpJSON,
GenCIRBuiltinsLowering,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the changes in this directory need to be guarded by a CLANG_ENABLE_CIR preprocessor check, and we'll need approval from the regular clang maintainers.

std::string Name = getCppClassName(Operation);
std::string LLVMOp = Operation->getValueAsString("llvmOp").str();

ClassDeclaration +=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it even necessary to use tablegen for this? Could we not have a single CIRToLLVMUnaryFPToFPBuiltinOpLowering class that handled all the operations derived from UnaryFPToFPBuiltinOp in the same way using the llvmOp member?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possiable yes, i will try it :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can't create lowering for the table gen class CIR_ UnaryFPToFPBuiltinOp, I will try to create an interface or see how it can be handled without using emitter 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants