Skip to content

[CIR] Upstream lowering of conditional operators to TernaryOp #138156

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

Merged
merged 6 commits into from
Jun 3, 2025
Merged
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
18 changes: 18 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,24 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createBinop(loc, lhs, cir::BinOpKind::Or, rhs);
}

mlir::Value createSelect(mlir::Location loc, mlir::Value condition,
mlir::Value trueValue, mlir::Value falseValue) {
assert(trueValue.getType() == falseValue.getType() &&
"trueValue and falseValue should have the same type");
return create<cir::SelectOp>(loc, trueValue.getType(), condition, trueValue,
falseValue);
}

mlir::Value createLogicalAnd(mlir::Location loc, mlir::Value lhs,
Copy link
Contributor

Choose a reason for hiding this comment

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

It appears these will only work if lhs and rhs are bool values. I guess the assert above will force that, but it initially surprised me.

mlir::Value rhs) {
return createSelect(loc, lhs, rhs, getBool(false, loc));
}

mlir::Value createLogicalOr(mlir::Location loc, mlir::Value lhs,
mlir::Value rhs) {
return createSelect(loc, lhs, getBool(true, loc), rhs);
}

mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
OverflowBehavior ob = OverflowBehavior::None) {
auto op =
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ struct MissingFeatures {
static bool deferredDecls() { return false; }
static bool setTargetAttributes() { return false; }
static bool coverageMapping() { return false; }
static bool peepholeProtection() { return false; }
static bool instrumentation() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand All @@ -232,8 +234,9 @@ struct MissingFeatures {
static bool ptrDiffOp() { return false; }
static bool ptrStrideOp() { return false; }
static bool switchOp() { return false; }
static bool ternaryOp() { return false; }
static bool throwOp() { return false; }
static bool tryOp() { return false; }
static bool vecTernaryOp() { return false; }
static bool zextOp() { return false; }

// Future CIR attributes
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
// A normal fixed sized variable becomes an alloca in the entry block,
mlir::Type allocaTy = convertTypeForMem(ty);
// Create the temp alloca and declare variable using it.
address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
/*insertIntoFnEntryBlock=*/false);
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);

emission.Addr = address;
Expand Down
117 changes: 104 additions & 13 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
#include "CIRGenModule.h"
#include "CIRGenValue.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Value.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"
#include <optional>

using namespace clang;
using namespace clang::CIRGen;
Expand Down Expand Up @@ -229,7 +231,7 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,

static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
const VarDecl *vd) {
QualType T = e->getType();
QualType t = e->getType();

// If it's thread_local, emit a call to its wrapper function instead.
assert(!cir::MissingFeatures::opGlobalThreadLocal());
Expand Down Expand Up @@ -259,7 +261,7 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
cgf.cgm.errorNYI(e->getSourceRange(),
"emitGlobalVarDeclLValue: reference type");
else
lv = cgf.makeAddrLValue(addr, T, AlignmentSource::Decl);
lv = cgf.makeAddrLValue(addr, t, AlignmentSource::Decl);
assert(!cir::MissingFeatures::setObjCGCLValueClass());
return lv;
}
Expand Down Expand Up @@ -1259,10 +1261,28 @@ mlir::Value CIRGenFunction::emitOpOnBoolExpr(mlir::Location loc,
// cir.ternary(!x, t, f) -> cir.ternary(x, f, t)
assert(!cir::MissingFeatures::shouldReverseUnaryCondOnBoolExpr());

if (isa<ConditionalOperator>(cond)) {
cgm.errorNYI(cond->getExprLoc(), "Ternary NYI");
assert(!cir::MissingFeatures::ternaryOp());
return createDummyValue(loc, cond->getType());
if (const ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(cond)) {
Expr *trueExpr = condOp->getTrueExpr();
Expr *falseExpr = condOp->getFalseExpr();
mlir::Value condV = emitOpOnBoolExpr(loc, condOp->getCond());

mlir::Value ternaryOpRes =
builder
.create<cir::TernaryOp>(
loc, condV, /*thenBuilder=*/
[this, trueExpr](mlir::OpBuilder &b, mlir::Location loc) {
mlir::Value lhs = emitScalarExpr(trueExpr);
b.create<cir::YieldOp>(loc, lhs);
},
/*elseBuilder=*/
[this, falseExpr](mlir::OpBuilder &b, mlir::Location loc) {
mlir::Value rhs = emitScalarExpr(falseExpr);
b.create<cir::YieldOp>(loc, rhs);
})
.getResult();

return emitScalarConversion(ternaryOpRes, condOp->getType(),
getContext().BoolTy, condOp->getExprLoc());
}

if (isa<CXXThrowExpr>(cond)) {
Expand Down Expand Up @@ -1394,13 +1414,84 @@ mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
return builder.createDummyValue(loc, t, alignment);
}

/// This creates an alloca and inserts it into the entry block if
/// \p insertIntoFnEntryBlock is true, otherwise it inserts it at the current
/// insertion point of the builder.
//===----------------------------------------------------------------------===//
// CIR builder helpers
//===----------------------------------------------------------------------===//

Address CIRGenFunction::createMemTemp(QualType ty, mlir::Location loc,
const Twine &name, Address *alloca,
mlir::OpBuilder::InsertPoint ip) {
// FIXME: Should we prefer the preferred type alignment here?
return createMemTemp(ty, getContext().getTypeAlignInChars(ty), loc, name,
alloca, ip);
}

Address CIRGenFunction::createMemTemp(QualType ty, CharUnits align,
mlir::Location loc, const Twine &name,
Address *alloca,
mlir::OpBuilder::InsertPoint ip) {
Address result = createTempAlloca(convertTypeForMem(ty), align, loc, name,
/*ArraySize=*/nullptr, alloca, ip);
if (ty->isConstantMatrixType()) {
assert(!cir::MissingFeatures::matrixType());
cgm.errorNYI(loc, "temporary matrix value");
}
return result;
}

/// This creates a alloca and inserts it into the entry block of the
/// current region.
Address CIRGenFunction::createTempAllocaWithoutCast(
mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name,
mlir::Value arraySize, mlir::OpBuilder::InsertPoint ip) {
cir::AllocaOp alloca = ip.isSet()
? createTempAlloca(ty, loc, name, ip, arraySize)
: createTempAlloca(ty, loc, name, arraySize);
alloca.setAlignmentAttr(cgm.getSize(align));
return Address(alloca, ty, align);
}

/// This creates a alloca and inserts it into the entry block. The alloca is
/// casted to default address space if necessary.
Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
mlir::Location loc, const Twine &name,
bool insertIntoFnEntryBlock) {
mlir::Value alloca =
emitAlloca(name.str(), ty, loc, align, insertIntoFnEntryBlock);
return Address(alloca, ty, align);
mlir::Value arraySize,
Address *allocaAddr,
mlir::OpBuilder::InsertPoint ip) {
Address alloca =
createTempAllocaWithoutCast(ty, align, loc, name, arraySize, ip);
if (allocaAddr)
*allocaAddr = alloca;
mlir::Value v = alloca.getPointer();
// Alloca always returns a pointer in alloca address space, which may
// be different from the type defined by the language. For example,
// in C++ the auto variables are in the default address space. Therefore
// cast alloca to the default address space when necessary.
assert(!cir::MissingFeatures::addressSpace());
return Address(v, ty, align);
}

/// This creates an alloca and inserts it into the entry block if \p ArraySize
/// is nullptr, otherwise inserts it at the current insertion point of the
/// builder.
cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty,
mlir::Location loc,
const Twine &name,
mlir::Value arraySize,
bool insertIntoFnEntryBlock) {
return cast<cir::AllocaOp>(emitAlloca(name.str(), ty, loc, CharUnits(),
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like this should be doing something to get the alignment based on the type. Looking at the equivalent classic codegen, if CreateTempAlloca is called without explicit alignment, it calls IRBuilder::CreateAlloca, which uses DataLayout::getPrefTypeAlign() to set the alignment.

insertIntoFnEntryBlock, arraySize)
.getDefiningOp());
}

/// This creates an alloca and inserts it into the provided insertion point
cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty,
mlir::Location loc,
const Twine &name,
mlir::OpBuilder::InsertPoint ip,
mlir::Value arraySize) {
assert(ip.isSet() && "Insertion point is not set");
return cast<cir::AllocaOp>(
emitAlloca(name.str(), ty, loc, CharUnits(), ip, arraySize)
.getDefiningOp());
}
11 changes: 9 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy,
// Allocate the temporary variable
// to store the pointer to first unitialized element
const Address tmpAddr = cgf.createTempAlloca(
cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp",
/*insertIntoFnEntryBlock=*/false);
cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp");
LValue tmpLV = cgf.makeAddrLValue(tmpAddr, elementPtrType);
cgf.emitStoreThroughLValue(RValue::get(element), tmpLV);

Expand Down Expand Up @@ -274,3 +273,11 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr(
void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) {
AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e));
}

LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) {
assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!");
Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange()));
LValue lv = makeAddrLValue(temp, e->getType());
emitAggExpr(e, AggValueSlot::forLValue(lv));
return lv;
}
Loading