-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[CIR][X86] Implement lowering for sqrt builtins #169310
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
base: main
Are you sure you want to change the base?
Conversation
|
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
|
@llvm/pr-subscribers-clangir Author: Priyanshu Kumar (Priyanshu3820) ChangesSummaryImplements CIR IR generation for X86 sqrt builtin functions, addressing issue #167765. DetailsThis PR adds support for lowering the following X86
Changes MadeNew Files
Modified Files
Test Coverage
Implementation DetailsThe implementation generates proper
TestingAll test cases pass locally with FileCheck validation. The implementation properly generates FixesFixes #167765 Patch is 28.92 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169310.diff 13 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 47ff9389e8028..12bc9cf7b5b04 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -822,6 +822,119 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
}];
}
+//===----------------------------------------------------------------------===//
+// CXX SpecialMemberAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [
+ I32EnumAttrCase<"Custom", 0, "custom">,
+ I32EnumAttrCase<"Default", 1, "default">,
+ I32EnumAttrCase<"Copy", 2, "copy">,
+ I32EnumAttrCase<"Move", 3, "move">,
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> {
+ let summary = "Marks a function as a C++ constructor";
+ let description = [{
+ This attribute identifies a C++ constructor and classifies its kind:
+
+ - `custom`: a user-defined constructor
+ - `default`: a default constructor
+ - `copy`: a copy constructor
+ - `move`: a move constructor
+
+ Example:
+ ```mlir
+ #cir.cxx_ctor<!rec_a, copy>
+ #cir.cxx_ctor<!rec_b, default, trivial>
+ ```
+ }];
+
+ let parameters = (ins
+ "mlir::Type":$type,
+ EnumParameter<CIR_CtorKind>:$ctor_kind,
+ DefaultValuedParameter<"bool", "false">:$is_trivial
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind,
+ CArg<"bool", "false">:$isTrivial), [{
+ return $_get(type.getContext(), type, ctorKind, isTrivial);
+ }]>,
+ ];
+
+ let assemblyFormat = [{
+ `<` $type `,` $ctor_kind (`,` `trivial` $is_trivial^)? `>`
+ }];
+}
+
+def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> {
+ let summary = "Marks a function as a CXX destructor";
+ let description = [{
+ This attribute identifies a C++ destructor.
+ }];
+
+ let parameters = (ins
+ "mlir::Type":$type,
+ DefaultValuedParameter<"bool", "false">:$is_trivial
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ CArg<"bool", "false">:$isTrivial), [{
+ return $_get(type.getContext(), type, isTrivial);
+ }]>
+ ];
+
+ let assemblyFormat = [{
+ `<` $type (`,` `trivial` $is_trivial^)? `>`
+ }];
+}
+
+def CIR_AssignKind : CIR_I32EnumAttr<"AssignKind", "CXX Assignment Operator Kind", [
+ I32EnumAttrCase<"Copy", 0, "copy">,
+ I32EnumAttrCase<"Move", 1, "move">,
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", "cxx_assign"> {
+ let summary = "Marks a function as a CXX assignment operator";
+ let description = [{
+ This attribute identifies a C++ assignment operator and classifies its kind:
+
+ - `copy`: a copy assignment
+ - `move`: a move assignment
+ }];
+
+ let parameters = (ins
+ "mlir::Type":$type,
+ EnumParameter<CIR_AssignKind>:$assign_kind,
+ DefaultValuedParameter<"bool", "false">:$is_trivial
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ CArg<"AssignKind">:$assignKind,
+ CArg<"bool", "false">:$isTrivial), [{
+ return $_get(type.getContext(), type, assignKind, isTrivial);
+ }]>
+ ];
+
+ let assemblyFormat = [{
+ `<` $type `,` $assign_kind (`,` `trivial` $is_trivial^)? `>`
+ }];
+}
+
+def CIR_CXXSpecialMemberAttr : AnyAttrOf<[
+ CIR_CXXCtorAttr,
+ CIR_CXXDtorAttr,
+ CIR_CXXAssignAttr
+]>;
+
//===----------------------------------------------------------------------===//
// BitfieldInfoAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.h b/clang/include/clang/CIR/Dialect/IR/CIROps.h
new file mode 100644
index 0000000000000..41da044b683a9
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.h
@@ -0,0 +1,27 @@
+//===- CIROps.h - CIR dialect operations ------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the operations in the CIR dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_DIALECT_IR_CIROPS_H
+#define CLANG_CIR_DIALECT_IR_CIROPS_H
+
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/OpDefinition.h"
+#include "mlir/Interfaces/InferTypeOpInterface.h"
+
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+
+#include "clang/CIR/Dialect/IR/CIROps.h.inc"
+
+#endif // CLANG_CIR_DIALECT_IR_CIROPS_H
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e612d6a0ba886..3c59a0b2a3144 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -802,8 +802,8 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
defvar CIR_YieldableScopes = [
- "ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp",
- "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
+ "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp",
+ "GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
];
def CIR_YieldOp : CIR_Op<"yield", [
@@ -2533,7 +2533,9 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr<DictArrayAttr>:$res_attrs,
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
CIR_OptionalPriorityAttr:$global_ctor_priority,
- CIR_OptionalPriorityAttr:$global_dtor_priority);
+ CIR_OptionalPriorityAttr:$global_dtor_priority,
+ OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member
+ );
let regions = (region AnyRegion:$body);
@@ -2572,7 +2574,32 @@ def CIR_FuncOp : CIR_Op<"func", [
//===------------------------------------------------------------------===//
bool isDeclaration();
- }];
+
+ //===------------------------------------------------------------------===//
+ // C++ Special Member Functions
+ //===------------------------------------------------------------------===//
+
+ /// Returns true if this function is a C++ special member function.
+ bool isCXXSpecialMemberFunction();
+
+ bool isCxxConstructor();
+ bool isCxxDestructor();
+
+ /// Returns true if this function is a copy or move assignment operator.
+ bool isCxxSpecialAssignment();
+
+ /// Returns the kind of constructor this function represents, if any.
+ std::optional<CtorKind> getCxxConstructorKind();
+
+ /// Returns the kind of assignment operator (move, copy) this function
+ /// represents, if any.
+ std::optional<AssignKind> getCxxSpecialAssignKind();
+
+ /// Returns true if the function is a trivial C++ member functions such as
+ /// trivial default constructor, copy/move constructor, copy/move assignment,
+ /// or destructor.
+ bool isCxxTrivialMemberFunction();
+}];
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
@@ -2752,6 +2779,100 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
];
}
+//===----------------------------------------------------------------------===//
+// AwaitOp
+//===----------------------------------------------------------------------===//
+
+def CIR_AwaitKind : CIR_I32EnumAttr<"AwaitKind", "await kind", [
+ I32EnumAttrCase<"Init", 0, "init">,
+ I32EnumAttrCase<"User", 1, "user">,
+ I32EnumAttrCase<"Yield", 2, "yield">,
+ I32EnumAttrCase<"Final", 3, "final">
+]>;
+
+def CIR_AwaitOp : CIR_Op<"await",[
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursivelySpeculatable, NoRegionArguments
+]> {
+ let summary = "Wraps C++ co_await implicit logic";
+ let description = [{
+ The under the hood effect of using C++ `co_await expr` roughly
+ translates to:
+
+ ```c++
+ // co_await expr;
+
+ auto &&x = CommonExpr();
+ if (!x.await_ready()) {
+ ...
+ x.await_suspend(...);
+ ...
+ }
+ x.await_resume();
+ ```
+
+ `cir.await` represents this logic by using 3 regions:
+ - ready: covers veto power from x.await_ready()
+ - suspend: wraps actual x.await_suspend() logic
+ - resume: handles x.await_resume()
+
+ Breaking this up in regions allows individual scrutiny of conditions
+ which might lead to folding some of them out. Lowerings coming out
+ of CIR, e.g. LLVM, should use the `suspend` region to track more
+ lower level codegen (e.g. intrinsic emission for coro.save/coro.suspend).
+
+ There are also 4 flavors of `cir.await` available:
+ - `init`: compiler generated initial suspend via implicit `co_await`.
+ - `user`: also known as normal, representing a user written `co_await`.
+ - `yield`: user written `co_yield` expressions.
+ - `final`: compiler generated final suspend via implicit `co_await`.
+
+ ```mlir
+ cir.scope {
+ ... // auto &&x = CommonExpr();
+ cir.await(user, ready : {
+ ... // x.await_ready()
+ }, suspend : {
+ ... // x.await_suspend()
+ }, resume : {
+ ... // x.await_resume()
+ })
+ }
+ ```
+
+ Note that resulution of the common expression is assumed to happen
+ as part of the enclosing await scope.
+ }];
+
+ let arguments = (ins CIR_AwaitKind:$kind);
+ let regions = (region SizedRegion<1>:$ready,
+ SizedRegion<1>:$suspend,
+ SizedRegion<1>:$resume);
+ let assemblyFormat = [{
+ `(` $kind `,`
+ `ready` `:` $ready `,`
+ `suspend` `:` $suspend `,`
+ `resume` `:` $resume `,`
+ `)`
+ attr-dict
+ }];
+
+ let skipDefaultBuilders = 1;
+ let builders = [
+ OpBuilder<(ins
+ "cir::AwaitKind":$kind,
+ CArg<"BuilderCallbackRef",
+ "nullptr">:$readyBuilder,
+ CArg<"BuilderCallbackRef",
+ "nullptr">:$suspendBuilder,
+ CArg<"BuilderCallbackRef",
+ "nullptr">:$resumeBuilder
+ )>
+ ];
+
+ let hasVerifier = 1;
+}
+
//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
@@ -4018,6 +4139,72 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// FPClass Test Flags
+//===----------------------------------------------------------------------===//
+
+def FPClassTestEnum : CIR_I32EnumAttr<"FPClassTest", "floating-point class test flags", [
+ // Basic flags
+ I32EnumAttrCase<"SignalingNaN", 1, "fcSNan">,
+ I32EnumAttrCase<"QuietNaN", 2, "fcQNan">,
+ I32EnumAttrCase<"NegativeInfinity", 4, "fcNegInf">,
+ I32EnumAttrCase<"NegativeNormal", 8, "fcNegNormal">,
+ I32EnumAttrCase<"NegativeSubnormal", 16, "fcNegSubnormal">,
+ I32EnumAttrCase<"NegativeZero", 32, "fcNegZero">,
+ I32EnumAttrCase<"PositiveZero", 64, "fcPosZero">,
+ I32EnumAttrCase<"PositiveSubnormal", 128, "fcPosSubnormal">,
+ I32EnumAttrCase<"PositiveNormal", 256, "fcPosNormal">,
+ I32EnumAttrCase<"PositiveInfinity", 512, "fcPosInf">,
+
+ // Composite flags
+ I32EnumAttrCase<"Nan", 3, "fcNan">, // fcSNan | fcQNan
+ I32EnumAttrCase<"Infinity", 516, "fcInf">, // fcPosInf | fcNegInf
+ I32EnumAttrCase<"Normal", 264, "fcNormal">, // fcPosNormal | fcNegNormal
+ I32EnumAttrCase<"Subnormal", 144, "fcSubnormal">, // fcPosSubnormal | fcNegSubnormal
+ I32EnumAttrCase<"Zero", 96, "fcZero">, // fcPosZero | fcNegZero
+ I32EnumAttrCase<"PositiveFinite", 448, "fcPosFinite">,// fcPosNormal | fcPosSubnormal | fcPosZero
+ I32EnumAttrCase<"NegativeFinite", 56, "fcNegFinite">, // fcNegNormal | fcNegSubnormal | fcNegZero
+ I32EnumAttrCase<"Finite", 504, "fcFinite">, // fcPosFinite | fcNegFinite
+ I32EnumAttrCase<"Positive", 960, "fcPositive">, // fcPosFinite | fcPosInf
+ I32EnumAttrCase<"Negative", 60, "fcNegative">, // fcNegFinite | fcNegInf
+ I32EnumAttrCase<"All", 1023, "fcAllFlags">, // fcNan | fcInf | fcFinite
+]> {
+ let cppNamespace = "::cir";
+}
+
+def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
+ let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang";
+
+ let description = [{
+ The `cir.is_fp_class` operation takes a floating-point value as its first
+ argument and a bitfield of flags as its second argument. The operation
+ returns a boolean value indicating whether the floating-point value
+ satisfies the given flags.
+
+ The flags must be a compile time constant and the values are:
+
+ | Bit # | floating-point class |
+ | ----- | -------------------- |
+ | 0 | Signaling NaN |
+ | 1 | Quiet NaN |
+ | 2 | Negative infinity |
+ | 3 | Negative normal |
+ | 4 | Negative subnormal |
+ | 5 | Negative zero |
+ | 6 | Positive zero |
+ | 7 | Positive subnormal |
+ | 8 | Positive normal |
+ | 9 | Positive infinity |
+ }];
+
+ let arguments = (ins CIR_AnyFloatType:$src,
+ FPClassTestEnum:$flags);
+ let results = (outs CIR_BoolType:$result);
+ let assemblyFormat = [{
+ $src `,` $flags `:` functional-type($src, $result) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Assume Operations
//===----------------------------------------------------------------------===//
@@ -4202,7 +4389,7 @@ def CIR_ObjSizeOp : CIR_Op<"objsize", [Pure]> {
When the `min` attribute is present, the operation returns the minimum
guaranteed accessible size. When absent (max mode), it returns the maximum
possible object size. Corresponds to `llvm.objectsize`'s `min` argument.
-
+
The `dynamic` attribute determines if the value should be evaluated at
runtime. Corresponds to `llvm.objectsize`'s `dynamic` argument.
@@ -4658,6 +4845,44 @@ def CIR_TryOp : CIR_Op<"try",[
let hasLLVMLowering = false;
}
+//===----------------------------------------------------------------------===//
+// Exception related: EhInflightOp
+//===----------------------------------------------------------------------===//
+
+def CIR_EhInflightOp : CIR_Op<"eh.inflight_exception"> {
+ let summary = "Materialize the catch clause formal parameter";
+ let description = [{
+ `cir.eh.inflight_exception` returns two values:
+ - `exception_ptr`: The exception pointer for the inflight exception
+ - `type_id`: the type info index for the exception type
+ This operation is expected to be the first operation in the unwind
+ destination basic blocks of a `cir.try_call` operation.
+
+ The `cleanup` attribute indicates that clean up code must be run before the
+ values produced by this operation are used to dispatch the exception. This
+ cleanup code must be executed even if the exception is not caught.
+ This helps CIR to pass down more accurate information for LLVM lowering
+ to landingpads.
+
+ Example:
+
+ ```mlir
+ %exception_ptr, %type_id = cir.eh.inflight_exception
+ %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
+ %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
+ ``
+ }];
+
+ let arguments = (ins UnitAttr:$cleanup,
+ OptionalAttr<FlatSymbolRefArrayAttr>:$catch_type_list);
+ let results = (outs CIR_VoidPtrType:$exception_ptr, CIR_UInt32:$type_id);
+ let assemblyFormat = [{
+ (`cleanup` $cleanup^)?
+ ($catch_type_list^)?
+ attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Atomic operations
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 313184764f536..3e062add6633a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -657,6 +657,9 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
}
llvm_unreachable("Invalid value for RecordType::getKind()");
}
+ mlir::Type getElementType(size_t idx) {
+ return getMembers()[idx];
+ }
std::string getPrefixedName() {
return getKindAsStr() + "." + getName().getValue().str();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
index ee6900141647f..4864069e8d24b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
@@ -684,7 +684,19 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
case X86::BI__builtin_ia32_sqrtpd256:
case X86::BI__builtin_ia32_sqrtpd:
case X86::BI__builtin_ia32_sqrtps256:
- case X86::BI__builtin_ia32_sqrtps:
+ case X86::BI__builtin_ia32_sqrtps: {
+ auto loc = getLoc(E->getExprLoc());
+ assert(E->getNumArgs() == 1 && "__builtin_ia32_sqrtps takes one argument");
+ mlir::Value arg = emitScalarExpr(E->getArg(0));
+ mlir::Type argTy = arg.getType();
+ if (auto vecTy = argTy.dyn_cast<mlir::VectorType>()) {
+ assert(vecTy.getNumElements() == 4 &&
+ vecTy.getElementType().isa<mlir::FloatType>() &&
+ "__builtin_ia32_sqrtps expects <4 x float> / __m128");
+ }
+ auto sqrt = builder.create<cir::SqrtOp>(loc, argTy, arg);
+ return sqrt.getResult();
+ }
case X86::BI__builtin_ia32_sqrtph256:
case X86::BI__builtin_ia32_sqrtph:
case X86::BI__builtin_ia32_sqrtph512:
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 22aada882defc..d1b73e56dee83 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -910,6 +910,28 @@ static mlir::LogicalResult checkReturnAndFunction(cir::ReturnOp op,
return mlir::success();
}
+mlir::LogicalResult cir::SqrtOp::verify() {
+ auto inTy = getInput().getType();
+ auto outTy = getResult().getType();
+
+ if (inTy != outTy)
+ return emitOpError("input and result types must match");
+
+ // Accept scalar CIR/MLIR floating types.
+ if (inTy.isa<mlir::FloatType>())
+ return mlir::success();
+
+ // Accept CIR vector of floats.
+ if (auto vecTy = inTy.dyn_cast<cir::VectorType>()) {
+ if (vecTy.getElementType().isa<mlir::FloatType>())
+ return mlir::success();
+ }
+
+ return emitOpError(
+ "requires a floating-point scalar or vector-of-floating-point element type");
+}
+
+
mlir::LogicalResult cir::ReturnOp::verify() {
// Returns can be present in multiple different scopes, get the
// wrapping function and start from there.
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index d43a462a25092..526e23a927c09 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1,4 +1,4 @@
-//====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===//
+//====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -18,6 +18,7 @@
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
#include "mlir/Dialect/DLTI/DLTI.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/LLVMIR/IR/LLVMOps.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
#include "mlir/IR/BuiltinAttributes.h"
@@ -30,6 +31,7 @@
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Export.h"
#include "mlir/Transforms/DialectCon...
[truncated]
|
|
@llvm/pr-subscribers-clang Author: Priyanshu Kumar (Priyanshu3820) ChangesSummaryImplements CIR IR generation for X86 sqrt builtin functions, addressing issue #167765. DetailsThis PR adds support for lowering the following X86
Changes MadeNew Files
Modified Files
Test Coverage
Implementation DetailsThe implementation generates proper
TestingAll test cases pass locally with FileCheck validation. The implementation properly generates FixesFixes #167765 Patch is 28.92 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169310.diff 13 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 47ff9389e8028..12bc9cf7b5b04 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -822,6 +822,119 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
}];
}
+//===----------------------------------------------------------------------===//
+// CXX SpecialMemberAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [
+ I32EnumAttrCase<"Custom", 0, "custom">,
+ I32EnumAttrCase<"Default", 1, "default">,
+ I32EnumAttrCase<"Copy", 2, "copy">,
+ I32EnumAttrCase<"Move", 3, "move">,
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> {
+ let summary = "Marks a function as a C++ constructor";
+ let description = [{
+ This attribute identifies a C++ constructor and classifies its kind:
+
+ - `custom`: a user-defined constructor
+ - `default`: a default constructor
+ - `copy`: a copy constructor
+ - `move`: a move constructor
+
+ Example:
+ ```mlir
+ #cir.cxx_ctor<!rec_a, copy>
+ #cir.cxx_ctor<!rec_b, default, trivial>
+ ```
+ }];
+
+ let parameters = (ins
+ "mlir::Type":$type,
+ EnumParameter<CIR_CtorKind>:$ctor_kind,
+ DefaultValuedParameter<"bool", "false">:$is_trivial
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind,
+ CArg<"bool", "false">:$isTrivial), [{
+ return $_get(type.getContext(), type, ctorKind, isTrivial);
+ }]>,
+ ];
+
+ let assemblyFormat = [{
+ `<` $type `,` $ctor_kind (`,` `trivial` $is_trivial^)? `>`
+ }];
+}
+
+def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> {
+ let summary = "Marks a function as a CXX destructor";
+ let description = [{
+ This attribute identifies a C++ destructor.
+ }];
+
+ let parameters = (ins
+ "mlir::Type":$type,
+ DefaultValuedParameter<"bool", "false">:$is_trivial
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ CArg<"bool", "false">:$isTrivial), [{
+ return $_get(type.getContext(), type, isTrivial);
+ }]>
+ ];
+
+ let assemblyFormat = [{
+ `<` $type (`,` `trivial` $is_trivial^)? `>`
+ }];
+}
+
+def CIR_AssignKind : CIR_I32EnumAttr<"AssignKind", "CXX Assignment Operator Kind", [
+ I32EnumAttrCase<"Copy", 0, "copy">,
+ I32EnumAttrCase<"Move", 1, "move">,
+]> {
+ let genSpecializedAttr = 0;
+}
+
+def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", "cxx_assign"> {
+ let summary = "Marks a function as a CXX assignment operator";
+ let description = [{
+ This attribute identifies a C++ assignment operator and classifies its kind:
+
+ - `copy`: a copy assignment
+ - `move`: a move assignment
+ }];
+
+ let parameters = (ins
+ "mlir::Type":$type,
+ EnumParameter<CIR_AssignKind>:$assign_kind,
+ DefaultValuedParameter<"bool", "false">:$is_trivial
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ CArg<"AssignKind">:$assignKind,
+ CArg<"bool", "false">:$isTrivial), [{
+ return $_get(type.getContext(), type, assignKind, isTrivial);
+ }]>
+ ];
+
+ let assemblyFormat = [{
+ `<` $type `,` $assign_kind (`,` `trivial` $is_trivial^)? `>`
+ }];
+}
+
+def CIR_CXXSpecialMemberAttr : AnyAttrOf<[
+ CIR_CXXCtorAttr,
+ CIR_CXXDtorAttr,
+ CIR_CXXAssignAttr
+]>;
+
//===----------------------------------------------------------------------===//
// BitfieldInfoAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.h b/clang/include/clang/CIR/Dialect/IR/CIROps.h
new file mode 100644
index 0000000000000..41da044b683a9
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.h
@@ -0,0 +1,27 @@
+//===- CIROps.h - CIR dialect operations ------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the operations in the CIR dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_DIALECT_IR_CIROPS_H
+#define CLANG_CIR_DIALECT_IR_CIROPS_H
+
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/OpDefinition.h"
+#include "mlir/Interfaces/InferTypeOpInterface.h"
+
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+
+#include "clang/CIR/Dialect/IR/CIROps.h.inc"
+
+#endif // CLANG_CIR_DIALECT_IR_CIROPS_H
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e612d6a0ba886..3c59a0b2a3144 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -802,8 +802,8 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
defvar CIR_YieldableScopes = [
- "ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp",
- "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
+ "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp",
+ "GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
];
def CIR_YieldOp : CIR_Op<"yield", [
@@ -2533,7 +2533,9 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr<DictArrayAttr>:$res_attrs,
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
CIR_OptionalPriorityAttr:$global_ctor_priority,
- CIR_OptionalPriorityAttr:$global_dtor_priority);
+ CIR_OptionalPriorityAttr:$global_dtor_priority,
+ OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member
+ );
let regions = (region AnyRegion:$body);
@@ -2572,7 +2574,32 @@ def CIR_FuncOp : CIR_Op<"func", [
//===------------------------------------------------------------------===//
bool isDeclaration();
- }];
+
+ //===------------------------------------------------------------------===//
+ // C++ Special Member Functions
+ //===------------------------------------------------------------------===//
+
+ /// Returns true if this function is a C++ special member function.
+ bool isCXXSpecialMemberFunction();
+
+ bool isCxxConstructor();
+ bool isCxxDestructor();
+
+ /// Returns true if this function is a copy or move assignment operator.
+ bool isCxxSpecialAssignment();
+
+ /// Returns the kind of constructor this function represents, if any.
+ std::optional<CtorKind> getCxxConstructorKind();
+
+ /// Returns the kind of assignment operator (move, copy) this function
+ /// represents, if any.
+ std::optional<AssignKind> getCxxSpecialAssignKind();
+
+ /// Returns true if the function is a trivial C++ member functions such as
+ /// trivial default constructor, copy/move constructor, copy/move assignment,
+ /// or destructor.
+ bool isCxxTrivialMemberFunction();
+}];
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
@@ -2752,6 +2779,100 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
];
}
+//===----------------------------------------------------------------------===//
+// AwaitOp
+//===----------------------------------------------------------------------===//
+
+def CIR_AwaitKind : CIR_I32EnumAttr<"AwaitKind", "await kind", [
+ I32EnumAttrCase<"Init", 0, "init">,
+ I32EnumAttrCase<"User", 1, "user">,
+ I32EnumAttrCase<"Yield", 2, "yield">,
+ I32EnumAttrCase<"Final", 3, "final">
+]>;
+
+def CIR_AwaitOp : CIR_Op<"await",[
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursivelySpeculatable, NoRegionArguments
+]> {
+ let summary = "Wraps C++ co_await implicit logic";
+ let description = [{
+ The under the hood effect of using C++ `co_await expr` roughly
+ translates to:
+
+ ```c++
+ // co_await expr;
+
+ auto &&x = CommonExpr();
+ if (!x.await_ready()) {
+ ...
+ x.await_suspend(...);
+ ...
+ }
+ x.await_resume();
+ ```
+
+ `cir.await` represents this logic by using 3 regions:
+ - ready: covers veto power from x.await_ready()
+ - suspend: wraps actual x.await_suspend() logic
+ - resume: handles x.await_resume()
+
+ Breaking this up in regions allows individual scrutiny of conditions
+ which might lead to folding some of them out. Lowerings coming out
+ of CIR, e.g. LLVM, should use the `suspend` region to track more
+ lower level codegen (e.g. intrinsic emission for coro.save/coro.suspend).
+
+ There are also 4 flavors of `cir.await` available:
+ - `init`: compiler generated initial suspend via implicit `co_await`.
+ - `user`: also known as normal, representing a user written `co_await`.
+ - `yield`: user written `co_yield` expressions.
+ - `final`: compiler generated final suspend via implicit `co_await`.
+
+ ```mlir
+ cir.scope {
+ ... // auto &&x = CommonExpr();
+ cir.await(user, ready : {
+ ... // x.await_ready()
+ }, suspend : {
+ ... // x.await_suspend()
+ }, resume : {
+ ... // x.await_resume()
+ })
+ }
+ ```
+
+ Note that resulution of the common expression is assumed to happen
+ as part of the enclosing await scope.
+ }];
+
+ let arguments = (ins CIR_AwaitKind:$kind);
+ let regions = (region SizedRegion<1>:$ready,
+ SizedRegion<1>:$suspend,
+ SizedRegion<1>:$resume);
+ let assemblyFormat = [{
+ `(` $kind `,`
+ `ready` `:` $ready `,`
+ `suspend` `:` $suspend `,`
+ `resume` `:` $resume `,`
+ `)`
+ attr-dict
+ }];
+
+ let skipDefaultBuilders = 1;
+ let builders = [
+ OpBuilder<(ins
+ "cir::AwaitKind":$kind,
+ CArg<"BuilderCallbackRef",
+ "nullptr">:$readyBuilder,
+ CArg<"BuilderCallbackRef",
+ "nullptr">:$suspendBuilder,
+ CArg<"BuilderCallbackRef",
+ "nullptr">:$resumeBuilder
+ )>
+ ];
+
+ let hasVerifier = 1;
+}
+
//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
@@ -4018,6 +4139,72 @@ def CIR_RotateOp : CIR_Op<"rotate", [Pure, SameOperandsAndResultType]> {
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// FPClass Test Flags
+//===----------------------------------------------------------------------===//
+
+def FPClassTestEnum : CIR_I32EnumAttr<"FPClassTest", "floating-point class test flags", [
+ // Basic flags
+ I32EnumAttrCase<"SignalingNaN", 1, "fcSNan">,
+ I32EnumAttrCase<"QuietNaN", 2, "fcQNan">,
+ I32EnumAttrCase<"NegativeInfinity", 4, "fcNegInf">,
+ I32EnumAttrCase<"NegativeNormal", 8, "fcNegNormal">,
+ I32EnumAttrCase<"NegativeSubnormal", 16, "fcNegSubnormal">,
+ I32EnumAttrCase<"NegativeZero", 32, "fcNegZero">,
+ I32EnumAttrCase<"PositiveZero", 64, "fcPosZero">,
+ I32EnumAttrCase<"PositiveSubnormal", 128, "fcPosSubnormal">,
+ I32EnumAttrCase<"PositiveNormal", 256, "fcPosNormal">,
+ I32EnumAttrCase<"PositiveInfinity", 512, "fcPosInf">,
+
+ // Composite flags
+ I32EnumAttrCase<"Nan", 3, "fcNan">, // fcSNan | fcQNan
+ I32EnumAttrCase<"Infinity", 516, "fcInf">, // fcPosInf | fcNegInf
+ I32EnumAttrCase<"Normal", 264, "fcNormal">, // fcPosNormal | fcNegNormal
+ I32EnumAttrCase<"Subnormal", 144, "fcSubnormal">, // fcPosSubnormal | fcNegSubnormal
+ I32EnumAttrCase<"Zero", 96, "fcZero">, // fcPosZero | fcNegZero
+ I32EnumAttrCase<"PositiveFinite", 448, "fcPosFinite">,// fcPosNormal | fcPosSubnormal | fcPosZero
+ I32EnumAttrCase<"NegativeFinite", 56, "fcNegFinite">, // fcNegNormal | fcNegSubnormal | fcNegZero
+ I32EnumAttrCase<"Finite", 504, "fcFinite">, // fcPosFinite | fcNegFinite
+ I32EnumAttrCase<"Positive", 960, "fcPositive">, // fcPosFinite | fcPosInf
+ I32EnumAttrCase<"Negative", 60, "fcNegative">, // fcNegFinite | fcNegInf
+ I32EnumAttrCase<"All", 1023, "fcAllFlags">, // fcNan | fcInf | fcFinite
+]> {
+ let cppNamespace = "::cir";
+}
+
+def CIR_IsFPClassOp : CIR_Op<"is_fp_class"> {
+ let summary = "Corresponding to the `__builtin_fpclassify` builtin function in clang";
+
+ let description = [{
+ The `cir.is_fp_class` operation takes a floating-point value as its first
+ argument and a bitfield of flags as its second argument. The operation
+ returns a boolean value indicating whether the floating-point value
+ satisfies the given flags.
+
+ The flags must be a compile time constant and the values are:
+
+ | Bit # | floating-point class |
+ | ----- | -------------------- |
+ | 0 | Signaling NaN |
+ | 1 | Quiet NaN |
+ | 2 | Negative infinity |
+ | 3 | Negative normal |
+ | 4 | Negative subnormal |
+ | 5 | Negative zero |
+ | 6 | Positive zero |
+ | 7 | Positive subnormal |
+ | 8 | Positive normal |
+ | 9 | Positive infinity |
+ }];
+
+ let arguments = (ins CIR_AnyFloatType:$src,
+ FPClassTestEnum:$flags);
+ let results = (outs CIR_BoolType:$result);
+ let assemblyFormat = [{
+ $src `,` $flags `:` functional-type($src, $result) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Assume Operations
//===----------------------------------------------------------------------===//
@@ -4202,7 +4389,7 @@ def CIR_ObjSizeOp : CIR_Op<"objsize", [Pure]> {
When the `min` attribute is present, the operation returns the minimum
guaranteed accessible size. When absent (max mode), it returns the maximum
possible object size. Corresponds to `llvm.objectsize`'s `min` argument.
-
+
The `dynamic` attribute determines if the value should be evaluated at
runtime. Corresponds to `llvm.objectsize`'s `dynamic` argument.
@@ -4658,6 +4845,44 @@ def CIR_TryOp : CIR_Op<"try",[
let hasLLVMLowering = false;
}
+//===----------------------------------------------------------------------===//
+// Exception related: EhInflightOp
+//===----------------------------------------------------------------------===//
+
+def CIR_EhInflightOp : CIR_Op<"eh.inflight_exception"> {
+ let summary = "Materialize the catch clause formal parameter";
+ let description = [{
+ `cir.eh.inflight_exception` returns two values:
+ - `exception_ptr`: The exception pointer for the inflight exception
+ - `type_id`: the type info index for the exception type
+ This operation is expected to be the first operation in the unwind
+ destination basic blocks of a `cir.try_call` operation.
+
+ The `cleanup` attribute indicates that clean up code must be run before the
+ values produced by this operation are used to dispatch the exception. This
+ cleanup code must be executed even if the exception is not caught.
+ This helps CIR to pass down more accurate information for LLVM lowering
+ to landingpads.
+
+ Example:
+
+ ```mlir
+ %exception_ptr, %type_id = cir.eh.inflight_exception
+ %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
+ %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
+ ``
+ }];
+
+ let arguments = (ins UnitAttr:$cleanup,
+ OptionalAttr<FlatSymbolRefArrayAttr>:$catch_type_list);
+ let results = (outs CIR_VoidPtrType:$exception_ptr, CIR_UInt32:$type_id);
+ let assemblyFormat = [{
+ (`cleanup` $cleanup^)?
+ ($catch_type_list^)?
+ attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Atomic operations
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 313184764f536..3e062add6633a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -657,6 +657,9 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
}
llvm_unreachable("Invalid value for RecordType::getKind()");
}
+ mlir::Type getElementType(size_t idx) {
+ return getMembers()[idx];
+ }
std::string getPrefixedName() {
return getKindAsStr() + "." + getName().getValue().str();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
index ee6900141647f..4864069e8d24b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
@@ -684,7 +684,19 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
case X86::BI__builtin_ia32_sqrtpd256:
case X86::BI__builtin_ia32_sqrtpd:
case X86::BI__builtin_ia32_sqrtps256:
- case X86::BI__builtin_ia32_sqrtps:
+ case X86::BI__builtin_ia32_sqrtps: {
+ auto loc = getLoc(E->getExprLoc());
+ assert(E->getNumArgs() == 1 && "__builtin_ia32_sqrtps takes one argument");
+ mlir::Value arg = emitScalarExpr(E->getArg(0));
+ mlir::Type argTy = arg.getType();
+ if (auto vecTy = argTy.dyn_cast<mlir::VectorType>()) {
+ assert(vecTy.getNumElements() == 4 &&
+ vecTy.getElementType().isa<mlir::FloatType>() &&
+ "__builtin_ia32_sqrtps expects <4 x float> / __m128");
+ }
+ auto sqrt = builder.create<cir::SqrtOp>(loc, argTy, arg);
+ return sqrt.getResult();
+ }
case X86::BI__builtin_ia32_sqrtph256:
case X86::BI__builtin_ia32_sqrtph:
case X86::BI__builtin_ia32_sqrtph512:
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 22aada882defc..d1b73e56dee83 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -910,6 +910,28 @@ static mlir::LogicalResult checkReturnAndFunction(cir::ReturnOp op,
return mlir::success();
}
+mlir::LogicalResult cir::SqrtOp::verify() {
+ auto inTy = getInput().getType();
+ auto outTy = getResult().getType();
+
+ if (inTy != outTy)
+ return emitOpError("input and result types must match");
+
+ // Accept scalar CIR/MLIR floating types.
+ if (inTy.isa<mlir::FloatType>())
+ return mlir::success();
+
+ // Accept CIR vector of floats.
+ if (auto vecTy = inTy.dyn_cast<cir::VectorType>()) {
+ if (vecTy.getElementType().isa<mlir::FloatType>())
+ return mlir::success();
+ }
+
+ return emitOpError(
+ "requires a floating-point scalar or vector-of-floating-point element type");
+}
+
+
mlir::LogicalResult cir::ReturnOp::verify() {
// Returns can be present in multiple different scopes, get the
// wrapping function and start from there.
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index d43a462a25092..526e23a927c09 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1,4 +1,4 @@
-//====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===//
+//====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -18,6 +18,7 @@
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
#include "mlir/Dialect/DLTI/DLTI.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/LLVMIR/IR/LLVMOps.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
#include "mlir/IR/BuiltinAttributes.h"
@@ -30,6 +31,7 @@
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Export.h"
#include "mlir/Transforms/DialectCon...
[truncated]
|
990cbc4 to
a76b756
Compare
| @@ -0,0 +1,27 @@ | |||
| //===- CIROps.h - CIR dialect operations ------------------------*- C++ -*-===// | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? Aren't all of these things included via CIRDialect.h?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I had added CIROps.h because I was getting a compilation error so I thought a wrapper header would come in handy to access the TableGen-generated operations.
Should I remove CIROps.h and update the include to use CIRDialect.h.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| case X86::BI__builtin_ia32_kunpckhi: | ||
| case X86::BI__builtin_ia32_sqrtsh_round_mask: | ||
| case X86::BI__builtin_ia32_sqrtsd_round_mask: | ||
| case X86::BI__builtin_ia32_sqrtss_round_mask: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to insert a call to errorNYI here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I just move the sqrt implementation to the top? That way, the builtins that are currently above them will just fall through to the NYI error at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. We are attempting to keep builtins in the same relative order as they appear in classic codegen and the incubator in order to make the code easier to navigate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood!
| if (auto vecTy = argTy.dyn_cast<mlir::VectorType>()) { | ||
| assert(vecTy.getNumElements() == 4 && | ||
| vecTy.getElementType().isa<mlir::FloatType>() && | ||
| "__builtin_ia32_sqrtps expects <4 x float> / __m128"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assertion is too narrow because multiple variations of the sqrt builtin fall through to this code. I suggest just removing the assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I'll remove the assertion since sqrtpd256, sqrtps512, and other variants also go through this path.
Thanks.
| @@ -1,4 +1,4 @@ | |||
| //====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===// | |||
| //====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===// | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What changed on this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like a phantom diff caused due to line ending differences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The line endings may be automatically updated to match the repository settings when the changes are committed, but it's a good idea to avoid changing line ending styles. There are a few lit tests where the line ending kind is actually important.
| #include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" | ||
| #include "mlir/Target/LLVMIR/Export.h" | ||
| #include "mlir/Transforms/DialectConversion.h" | ||
| #include "clang/Basic/LLVM.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed. Thanks for pointing this out. The file only uses MLIR types which are already included via the other headers. I'll remove it.
| #include "mlir/Conversion/LLVMCommon/TypeConverter.h" | ||
| #include "mlir/Dialect/DLTI/DLTI.h" | ||
| #include "mlir/Dialect/Func/IR/FuncOps.h" | ||
| #include "mlir/Dialect/LLVMIR/IR/LLVMOps.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This include is needed for LLVM::LLVMFuncOp which is used in the getOrInsertFunction() helper method to create LLVM function declarations for the sqrt intrinsics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That operation should already be included via LLVMDialect.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right. I removed it now.
|
|
||
| using namespace cir; | ||
| using namespace llvm; | ||
| using namespace mlir; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We prefer to have the mlir namespace explicit when used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had used explicit mlir:: prefixes throughout but added using namespace mlir; just to prevent any discrepancies. I have removed it now.
| using namespace llvm; | ||
| using namespace mlir; | ||
|
|
||
| static std::string getLLVMIntrinsicNameForType(Type llvmTy) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had initially planned to use this helper function but then used getIntrinsicSuffix() function. I will remove this now.
a60dd64 to
a4c8b5a
Compare
|
I think #165682 is removing all the builtins implemented here. |
@philnik777 Thanks for letting me know. Just to clarify, my PR is implementing CIR lowering for these builtins, not modifying Clang's builtin definitions themselves. But you raised a good point that if #165682 removes these x86 builtins from Clang upstream, CIR would need to adapt. Btw, what's the current status of your PR? |
Yes, exactly. AFAICT you're implementing functionality which will be removed. More generally, you might want to check whether any builtins generate platform-specific IR instructions. If no, there is (or should) probably be a generic version, which the x86 builtins can use instead. That way, instead of adding code, you might actually remove code to implement new features.
I expect it will land within the next few days. |
|
hi @andykaylor, @philnik777's PR #165682 is removing the x86 sqrt builtins from Clang and replacing them with generic __builtin_elementwise_sqrt versions. I just wanted to clarify whether removing these builtins from clang also mean that we will be going to remove them from CIR too or are we going to maintain these supported builtins independently? |
Thanks, that's what I wanted to clarify. That also means i'll have to have a new implementation targeting __builtin_elementwise_sqrt instead.
glad to hear that, all the best. |
|
just saw PR #165682 has been merged, which removes these x86 sqrt builtins from clang. I'm waiting for guidance on whether CIR should still implement these or follow upstream's direction with __builtin_elementwise_sqrt. |
Summary
Implements CIR IR generation for X86 sqrt builtin functions, addressing issue #167765.
Details
This PR adds support for lowering the following X86 sqrt builtins to CIR operations:
__builtin_ia32_sqrtps(128-bit float vector)__builtin_ia32_sqrtps256(256-bit float vector)__builtin_ia32_sqrtpd(128-bit double vector)__builtin_ia32_sqrtpd256(256-bit double vector)Additional sqrt variants (512-bit, half-precision, bfloat16, masked) can be added in future PRs following the same pattern.
Changes Made
New Files
clang/include/clang/CIR/Dialect/IR/CIROps.h- Wrapper header declaring CIR operationsModified Files
clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp- Added builtin cases for sqrt operationsclang/include/clang/CIR/Dialect/IR/CIROps.td- DefinedCIR_SqrtOpoperationclang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td- Added float type constraintsclang/lib/CIR/Dialect/IR/CIRDialect.cpp- ImplementsSqrtOp::verify()methodclang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h- DeclaredCIRToLLVMSqrtOpLoweringconversion pattern classclang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp- ImplementedCIRToLLVMSqrtOpLowering::matchAndRewrite()methodTest Coverage
clang/test/CIR/CodeGen/X86/cir-sqrtps-builtins.c- LIT test cases covering all sqrt builtin variants with FileCheck validationImplementation Details
The implementation generates proper
cir.sqrtoperations for:<4xf32>,<8xf32>,<16xf32>,<2xf64>,<4xf64>,<8xf64>etc.f32,f64,f16,bf16TypeConstraintmechanismTesting
All test cases pass locally with FileCheck validation. The implementation properly generates
cir.sqrtoperations for all sqrt builtin variants.Related to
issue #167765
Edit
It now implements sqrt builtins for all vector sizes.