From 756f0656c3ad7788ed80ff9ccc091e19d07d63f8 Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Fri, 1 Aug 2025 20:48:02 +0200 Subject: [PATCH 1/3] [CIR] Upstream builtin fabs op --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 18 +++ .../clang/CIR/Dialect/IR/CMakeLists.txt | 4 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 23 ++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 7 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 4 + clang/test/CIR/CodeGen/builtins.cpp | 14 +++ clang/utils/TableGen/CIRLoweringEmitter.cpp | 108 ++++++++++++++++++ clang/utils/TableGen/CMakeLists.txt | 1 + clang/utils/TableGen/TableGen.cpp | 7 ++ clang/utils/TableGen/TableGenBackends.h | 3 + 10 files changed, 189 insertions(+) create mode 100644 clang/test/CIR/CodeGen/builtins.cpp create mode 100644 clang/utils/TableGen/CIRLoweringEmitter.cpp 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 + : CIR_Op +{ + 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 +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(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(*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 createConvertCIRToLLVMPass() { return std::make_unique(); } 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 { + public: + using OpConversionPattern::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(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 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. From 834eb5c09bb4cca1aea1697a7a54cfdf208b821e Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Sat, 2 Aug 2025 09:42:09 +0200 Subject: [PATCH 2/3] Address code review comments --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 12 ++++-- clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 +- .../TableGen/emit-cir-builtins-lowering.td | 42 +++++++++++++++++++ clang/utils/TableGen/TableGen.cpp | 9 +++- 4 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 clang/test/TableGen/emit-cir-builtins-lowering.td diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 975a8d102d800..efbe5d84df901 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3208,13 +3208,19 @@ class CIR_UnaryFPToFPBuiltinOp { 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">; +def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> { + 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`. + }]; +} #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 201c119c1747f..748fb9d79c102 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -80,7 +80,7 @@ static RValue emitUnaryMaybeConstrainedFPBuiltin(CIRGenFunction &cgf, assert(!cir::MissingFeatures::fpConstraints()); auto call = - cgf.getBuilder().create(arg.getLoc(), arg.getType(), arg); + Operation::create(cgf.getBuilder(), arg.getLoc(), arg.getType(), arg); return RValue::get(call->getResult(0)); } diff --git a/clang/test/TableGen/emit-cir-builtins-lowering.td b/clang/test/TableGen/emit-cir-builtins-lowering.td new file mode 100644 index 0000000000000..bcc0c7450f5d4 --- /dev/null +++ b/clang/test/TableGen/emit-cir-builtins-lowering.td @@ -0,0 +1,42 @@ +// RUN: clang-tblgen -gen-cir-builtins-lowering -I%S %s -o - 2>&1 | \ +// RUN: FileCheck --strict-whitespace %s + +def ins; +def outs; + +class OpBase { + dag arguments = (ins); + dag results = (outs); +} + +class LLVMLoweringInfo { + string llvmOp = ""; +} + +class CIR_Op : LLVMLoweringInfo, OpBase; + +def CIR_FAbsOp : CIR_Op { + let arguments = (ins); + let results = (outs); + + let llvmOp = "FAbsOp"; +} + +// CHECK: #ifdef GET_BUILTIN_LOWERING_CLASSES_DECLARE +// CHECK: class CIRFAbsOpLowering : public mlir::OpConversionPattern { +// CHECK: public: +// CHECK: using OpConversionPattern::OpConversionPattern; +// CHECK: mlir::LogicalResult +// CHECK: matchAndRewrite(cir::FAbsOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const override; +// CHECK: }; +// CHECK: #endif +// CHECK: #ifdef GET_BUILTIN_LOWERING_CLASSES_DEF +// CHECK: mlir::LogicalResult +// CHECK: CIRFAbsOpLowering::matchAndRewrite(cir::FAbsOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { +// CHECK: rewriter.replaceOpWithNewOp(op); +// CHECK: return mlir::success(); +// CHECK: } +// CHECK: #endif +// CHECK: #ifdef GET_BUILTIN_LOWERING_LIST +// CHECK: , CIRFAbsOpLowering +// CHECK: #endif diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index d4a383e801085..74f7b74952d19 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -12,6 +12,7 @@ #include "ASTTableGen.h" #include "TableGenBackends.h" // Declares all backends. +#include "clang/Config/config.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" @@ -25,7 +26,9 @@ using namespace clang; enum ActionType { PrintRecords, DumpJSON, +#if CLANG_ENABLE_CIR GenCIRBuiltinsLowering, +#endif // CLANG_ENABLE_CIR GenClangAttrClasses, GenClangAttrParserStringSwitches, GenClangAttrSubjectMatchRulesParserStringSwitches, @@ -129,9 +132,11 @@ cl::opt Action( "Print all records to stdout (default)"), clEnumValN(DumpJSON, "dump-json", "Dump all records as machine-readable JSON"), +#if CLANG_ENABLE_CIR clEnumValN(GenCIRBuiltinsLowering, "gen-cir-builtins-lowering", "Generate lowering of ClangIR builtins to equivalent LLVM " "IR builtins"), +#endif // CLANG_ENABLE_CIR clEnumValN(GenClangAttrClasses, "gen-clang-attr-classes", "Generate clang attribute clases"), clEnumValN(GenClangAttrParserStringSwitches, @@ -358,9 +363,11 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) { case DumpJSON: EmitJSON(Records, OS); break; +#if CLANG_ENABLE_CIR case GenCIRBuiltinsLowering: EmitCIRBuiltinsLowering(Records, OS); break; +#endif // CLANG_ENABLE_CIR case GenClangAttrClasses: EmitClangAttrClass(Records, OS); break; @@ -646,7 +653,7 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) { return false; } -} +} // namespace int main(int argc, char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); From a4b20779db4862be1f0570ff00137a86708d50da Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Sat, 2 Aug 2025 15:59:32 +0200 Subject: [PATCH 3/3] Add lit test features flag for clangir --- clang/test/TableGen/emit-cir-builtins-lowering.td | 1 + clang/test/lit.cfg.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/clang/test/TableGen/emit-cir-builtins-lowering.td b/clang/test/TableGen/emit-cir-builtins-lowering.td index bcc0c7450f5d4..9469dfbf25505 100644 --- a/clang/test/TableGen/emit-cir-builtins-lowering.td +++ b/clang/test/TableGen/emit-cir-builtins-lowering.td @@ -1,5 +1,6 @@ // RUN: clang-tblgen -gen-cir-builtins-lowering -I%S %s -o - 2>&1 | \ // RUN: FileCheck --strict-whitespace %s +// REQUIRES: clangir def ins; def outs; diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 1957bb1715eb6..1e2a3d061114a 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -410,3 +410,7 @@ def calculate_arch_features(arch_string): # possibly be present in system and user configuration files, so disable # default configs for the test runs. config.environment["CLANG_NO_DEFAULT_CONFIG"] = "1" + +# Check if clangir is enabled. +if config.clang_enable_cir: + config.available_features.add("clangir")