Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,68 @@ def CIR_ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> {
}];
}

//===----------------------------------------------------------------------===//
// CmpThreeWayInfoAttr
//===----------------------------------------------------------------------===//

def CIR_CmpOrdering : CIR_I32EnumAttr<
"CmpOrdering", "three-way comparison ordering kind", [
I32EnumAttrCase<"Strong", 0, "strong">,
I32EnumAttrCase<"Partial", 1, "partial">
]> {
let genSpecializedAttr = 0;
}

def CIR_CmpThreeWayInfoAttr : CIR_Attr<"CmpThreeWayInfo", "cmp3way_info"> {
let summary = "Holds information about a three-way comparison operation";
let description = [{
The `#cmp3way_info` attribute contains information about a three-way
comparison operation `cir.cmp3way`.

The `ordering` parameter gives the ordering kind of the three-way comparison
operation. It may be either strong ordering or partial ordering.

Given the two input operands of the three-way comparison operation `lhs` and
`rhs`, the `lt`, `eq`, `gt`, and `unordered` parameters gives the result
value that should be produced by the three-way comparison operation when the
ordering between `lhs` and `rhs` is `lhs < rhs`, `lhs == rhs`, `lhs > rhs`,
or neither, respectively.
}];
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 an example to show the expected format?


let parameters = (ins
EnumParameter<CIR_CmpOrdering>:$ordering,
"int64_t":$lt, "int64_t":$eq, "int64_t":$gt,
OptionalParameter<"std::optional<int64_t>">:$unordered
);

let builders = [
AttrBuilder<(ins "int64_t":$lt, "int64_t":$eq, "int64_t":$gt), [{
return $_get($_ctxt, CmpOrdering::Strong, lt, eq, gt, std::nullopt);
}]>,
AttrBuilder<(ins "int64_t":$lt, "int64_t":$eq, "int64_t":$gt,
"int64_t":$unordered), [{
return $_get($_ctxt, CmpOrdering::Partial, lt, eq, gt, unordered);
}]>,
];

let extraClassDeclaration = [{
/// Get attribute alias name for this attribute.
std::string getAlias() const;
}];

let assemblyFormat = [{
`<`
$ordering `,`
`lt` `=` $lt `,`
`eq` `=` $eq `,`
`gt` `=` $gt
(`,` `unordered` `=` $unordered^)?
`>`
}];

let genVerifyDecl = 1;
}

//===----------------------------------------------------------------------===//
// GlobalViewAttr
//===----------------------------------------------------------------------===//
Expand Down
65 changes: 65 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,71 @@ def CIR_ScopeOp : CIR_Op<"scope", [
let hasLLVMLowering = false;
}

//===----------------------------------------------------------------------===//
// CmpThreeWayOp
//===----------------------------------------------------------------------===//

def CIR_CmpThreeWayOp : CIR_Op<"cmp3way", [Pure, SameTypeOperands]> {
let summary = "Compare two values with C++ three-way comparison semantics";
let description = [{
The `cir.cmp3way` operation models the `<=>` operator in C++20. It takes two
operands with the same type and produces a result indicating the ordering
between the two input operands.

The result of the operation is a signed integer that indicates the ordering
between the two input operands.

There are two kinds of ordering: strong ordering and partial ordering.
Comparing different types of values yields different kinds of orderings.
The `info` parameter gives the ordering kind and other necessary information
about the comparison.

Example:

```mlir
!s32i = !cir.int<s, 32>

#cmp3way_strong = #cmp3way_info<strong, lt = -1, eq = 0, gt = 1>
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
#cmp3way_strong = #cmp3way_info<strong, lt = -1, eq = 0, gt = 1>
#cmp3way_info_strong_ltn1eq0gt1 = #cmp3way_info<strong, lt = -1, eq = 0, gt = 1>

#cmp3way_partial = #cmp3way_info<strong, lt = -1, eq = 0, gt = 1, unordered = 2>
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
#cmp3way_partial = #cmp3way_info<strong, lt = -1, eq = 0, gt = 1, unordered = 2>
#cmp3way_info_partial_ltn1eq0gt1un2 = #cmp3way_info<strong, lt = -1, eq = 0, gt = 1, unordered = 2>


%0 = cir.const #cir.int<0> : !s32i
%1 = cir.const #cir.int<1> : !s32i
%2 = cir.cmp3way(%0 : !s32i, %1, #cmp3way_strong) : !s8i
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
%2 = cir.cmp3way(%0 : !s32i, %1, #cmp3way_strong) : !s8i
%2 = cir.cmp3way(%0 : !s32i, %1, #cmp3way_info_strong_ltn1eq0gt1) : !s8i


%3 = cir.const #cir.fp<0.0> : !cir.float
%4 = cir.const #cir.fp<1.0> : !cir.float
%5 = cir.cmp3way(%3 : !cir.float, %4, #cmp3way_partial) : !s8i
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
%5 = cir.cmp3way(%3 : !cir.float, %4, #cmp3way_partial) : !s8i
%5 = cir.cmp3way(%3 : !cir.float, %4, #cmp3way_info_partial_ltn1eq0gt1un2) : !s8i

```
}];

let arguments = (ins
CIR_AnyType:$lhs,
CIR_AnyType:$rhs,
CIR_CmpThreeWayInfoAttr:$info
);

let results = (outs CIR_AnySIntType:$result);

let assemblyFormat = [{
`(` $lhs `:` type($lhs) `,` $rhs `,` qualified($info) `)`
`:` type($result) attr-dict
}];
Comment on lines +1006 to +1009
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
let assemblyFormat = [{
`(` $lhs `:` type($lhs) `,` $rhs `,` qualified($info) `)`
`:` type($result) attr-dict
}];
let assemblyFormat = [{
`(` $lhs `,` $rhs `,` qualified($info) `)`
`:` type($lhs) -> type($result) attr-dict
}];

I don't think this was consistent with our latest ASM style guidelines. I'm not 100% sure about my suggestion. @xlauko Can you help here?


let extraClassDeclaration = [{
/// Determine whether this three-way comparison produces a strong ordering.
bool isStrongOrdering() {
return getInfo().getOrdering() == cir::CmpOrdering::Strong;
}

/// Determine whether this three-way comparison compares integral operands.
bool isIntegralComparison() {
return mlir::isa<cir::IntType>(getLhs().getType());
}
}];

let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// SwitchOp
//===----------------------------------------------------------------------===//
Expand Down
33 changes: 33 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,39 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return cir::StackRestoreOp::create(*this, loc, v);
}

cir::CmpThreeWayOp createThreeWayCmpStrong(mlir::Location loc,
mlir::Value lhs, mlir::Value rhs,
const llvm::APSInt &ltRes,
const llvm::APSInt &eqRes,
const llvm::APSInt &gtRes) {
assert(ltRes.getBitWidth() == eqRes.getBitWidth() &&
ltRes.getBitWidth() == gtRes.getBitWidth() &&
"the three comparison results must have the same bit width");
cir::IntType cmpResultTy = getSIntNTy(ltRes.getBitWidth());
auto infoAttr = cir::CmpThreeWayInfoAttr::get(
getContext(), ltRes.getSExtValue(), eqRes.getSExtValue(),
gtRes.getSExtValue());
return cir::CmpThreeWayOp::create(*this, loc, cmpResultTy, lhs, rhs,
infoAttr);
}

cir::CmpThreeWayOp
createThreeWayCmpPartial(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
const llvm::APSInt &ltRes, const llvm::APSInt &eqRes,
const llvm::APSInt &gtRes,
const llvm::APSInt &unorderedRes) {
assert(ltRes.getBitWidth() == eqRes.getBitWidth() &&
ltRes.getBitWidth() == gtRes.getBitWidth() &&
ltRes.getBitWidth() == unorderedRes.getBitWidth() &&
"the four comparison results must have the same bit width");
auto cmpResultTy = getSIntNTy(ltRes.getBitWidth());
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
auto cmpResultTy = getSIntNTy(ltRes.getBitWidth());
mlir::Type cmpResultTy = getSIntNTy(ltRes.getBitWidth());

auto infoAttr = cir::CmpThreeWayInfoAttr::get(
getContext(), ltRes.getSExtValue(), eqRes.getSExtValue(),
gtRes.getSExtValue(), unorderedRes.getSExtValue());
return cir::CmpThreeWayOp::create(*this, loc, cmpResultTy, lhs, rhs,
infoAttr);
}

mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
Address dstAddr, mlir::Type storageType,
mlir::Value src, const CIRGenBitFieldInfo &info,
Expand Down
58 changes: 57 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
#include "llvm/IR/Value.h"
#include <cstdint>

using namespace clang;
Expand Down Expand Up @@ -298,8 +299,63 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
Visit(e->getRHS());
}
void VisitBinCmp(const BinaryOperator *e) {
cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitBinCmp");
assert(cgf.getContext().hasSameType(e->getLHS()->getType(),
e->getRHS()->getType()));
const ComparisonCategoryInfo &cmpInfo =
cgf.getContext().CompCategories.getInfoForType(e->getType());
assert(cmpInfo.Record->isTriviallyCopyable() &&
"cannot copy non-trivially copyable aggregate");

QualType argTy = e->getLHS()->getType();

if (!argTy->isIntegralOrEnumerationType() && !argTy->isRealFloatingType() &&
!argTy->isNullPtrType() && !argTy->isPointerType() &&
!argTy->isMemberPointerType() && !argTy->isAnyComplexType())
llvm_unreachable("aggregate three-way comparison");
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
llvm_unreachable("aggregate three-way comparison");
cgf.cgm.errorNYI(e->getBeginLoc(), "aggregate three-way comparison");


mlir::Location loc = cgf.getLoc(e->getSourceRange());
CIRGenBuilderTy builder = cgf.getBuilder();

if (e->getType()->isAnyComplexType())
Copy link
Contributor

Choose a reason for hiding this comment

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

Should there also be a diagnostic issued for aggregate types here? I'm not sure we handle that case correctly. The unreachable on line 314 doesn't seem to cover it.

llvm_unreachable("NYI");
Copy link
Contributor

Choose a reason for hiding this comment

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

  cgf.cgm.errorNYI(e->getBeginLoc(), "VisitBinCmp: complex type");


mlir::Value lhs = cgf.emitAnyExpr(e->getLHS()).getValue();
mlir::Value rhs = cgf.emitAnyExpr(e->getRHS()).getValue();

mlir::Value resultScalar;
if (argTy->isNullPtrType()) {
resultScalar =
builder.getConstInt(loc, cmpInfo.getEqualOrEquiv()->getIntValue());
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this case is covered in your test.

} else {
llvm::APSInt ltRes = cmpInfo.getLess()->getIntValue();
llvm::APSInt eqRes = cmpInfo.getEqualOrEquiv()->getIntValue();
llvm::APSInt gtRes = cmpInfo.getGreater()->getIntValue();
if (!cmpInfo.isPartial()) {
// Strong ordering.
resultScalar =
builder.createThreeWayCmpStrong(loc, lhs, rhs, ltRes, eqRes, gtRes);
} else {
// Partial ordering.
llvm::APSInt unorderedRes = cmpInfo.getUnordered()->getIntValue();
resultScalar = builder.createThreeWayCmpPartial(
loc, lhs, rhs, ltRes, eqRes, gtRes, unorderedRes);
}
}

// Create the return value in the destination slot.
ensureDest(loc, e->getType());
LValue destLVal = cgf.makeAddrLValue(dest.getAddress(), e->getType());

// Emit the address of the first (and only) field in the comparison category
// type, and initialize it from the constant integer value produced above.
const FieldDecl *resultField = *cmpInfo.Record->field_begin();
LValue fieldLVal = cgf.emitLValueForFieldInitialization(
destLVal, resultField, resultField->getName());
cgf.emitStoreThroughLValue(RValue::get(resultScalar), fieldLVal);

// All done! The result is in the dest slot.
}

void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *e) {
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXRewrittenBinaryOperator");
Expand Down
52 changes: 52 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,58 @@ LogicalResult FPAttr::verify(function_ref<InFlightDiagnostic()> emitError,
return success();
}

//===----------------------------------------------------------------------===//
// CmpThreeWayInfoAttr definitions
//===----------------------------------------------------------------------===//

std::string CmpThreeWayInfoAttr::getAlias() const {
std::string alias = "cmp3way_info";
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
std::string alias = "cmp3way_info";
std::string alias = "cmpinfo";

Maybe this to make it more compact? The examples and tests would need to be updated accordingly.


if (getOrdering() == CmpOrdering::Strong)
alias.append("_strong_");
else
alias.append("_partial_");

auto appendInt = [&](int64_t value) {
if (value < 0) {
alias.push_back('n');
value = -value;
}
alias.append(std::to_string(value));
};

alias.append("lt");
appendInt(getLt());
alias.append("eq");
appendInt(getEq());
alias.append("gt");
appendInt(getGt());

if (std::optional<int> unordered = getUnordered()) {
alias.append("un");
appendInt(unordered.value());
}

return alias;
}

LogicalResult
CmpThreeWayInfoAttr::verify(function_ref<InFlightDiagnostic()> emitError,
CmpOrdering ordering, int64_t lt, int64_t eq,
int64_t gt, std::optional<int64_t> unordered) {
// The presense of unordered must match the value of ordering.
if (ordering == CmpOrdering::Strong && unordered) {
emitError() << "strong ordering does not include unordered ordering";
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 an invalid IR test that verifies these errors?

return failure();
}
if (ordering == CmpOrdering::Partial && !unordered) {
emitError() << "partial ordering lacks unordered ordering";
return failure();
}

return success();
}

//===----------------------------------------------------------------------===//
// ConstComplexAttr definitions
//===----------------------------------------------------------------------===//
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
os << dynCastInfoAttr.getAlias();
return AliasResult::FinalAlias;
}
if (auto cmpThreeWayInfoAttr =
mlir::dyn_cast<cir::CmpThreeWayInfoAttr>(attr)) {
os << cmpThreeWayInfoAttr.getAlias();
return AliasResult::FinalAlias;
}
return AliasResult::NoAlias;
}
};
Expand Down Expand Up @@ -1132,6 +1137,20 @@ Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
return nullptr;
}

//===----------------------------------------------------------------------===//
// CmpThreeWayOp
//===----------------------------------------------------------------------===//

mlir::LogicalResult CmpThreeWayOp::verify() {
// Type of the result must be a signed integer type.
if (!getType().isSigned()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need to verify this. The MLIR constraints should handle it automatically.

emitOpError() << "result type of cir.cmp3way must be a signed integer type";
return failure();
}

return success();
}

//===----------------------------------------------------------------------===//
// CaseOp
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading