Skip to content

Commit 09763c1

Browse files
committed
[CIR] Upstream lowering of lvalue conditional operators to TernaryOp
This patch adds visitors for BinLAnd, BinLOr and AbstractConditionalOperator. Note that this patch still lacks visitation of OpaqueValueExpr which are needed for the GNU ?: operator.
1 parent c617466 commit 09763c1

File tree

12 files changed

+1243
-20
lines changed

12 files changed

+1243
-20
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
272272
return createCast(loc, cir::CastKind::bitcast, src, newTy);
273273
}
274274

275+
// TODO(cir): the following function was introduced to keep in sync with LLVM
276+
// codegen. CIR does not have "zext" operations. It should eventually be
277+
// renamed or removed. For now, we just add whatever cast is required here.
278+
mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src,
279+
mlir::Type newTy) {
280+
mlir::Type srcTy = src.getType();
281+
282+
if (srcTy == newTy)
283+
return src;
284+
285+
if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy))
286+
return createBoolToInt(src, newTy);
287+
288+
llvm_unreachable("unhandled extension cast");
289+
}
290+
275291
//===--------------------------------------------------------------------===//
276292
// Binary Operators
277293
//===--------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ struct MissingFeatures {
163163
static bool setDSOLocal() { return false; }
164164
static bool foldCaseStmt() { return false; }
165165
static bool constantFoldSwitchStatement() { return false; }
166+
static bool peepholeProtection() { return false; }
167+
static bool instrumenation() { return false; }
166168

167169
// Missing types
168170
static bool dataMemberType() { return false; }
@@ -188,8 +190,9 @@ struct MissingFeatures {
188190
static bool ptrStrideOp() { return false; }
189191
static bool selectOp() { return false; }
190192
static bool switchOp() { return false; }
191-
static bool ternaryOp() { return false; }
193+
static bool throwOp() { return false; }
192194
static bool tryOp() { return false; }
195+
static bool vecTernaryOp() { return false; }
193196
static bool zextOp() { return false; }
194197

195198
// Future CIR attributes

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
5050
// A normal fixed sized variable becomes an alloca in the entry block,
5151
mlir::Type allocaTy = convertTypeForMem(ty);
5252
// Create the temp alloca and declare variable using it.
53-
address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
54-
/*insertIntoFnEntryBlock=*/false);
53+
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
5554
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
5655

5756
emission.Addr = address;

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 263 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
#include "CIRGenModule.h"
1616
#include "CIRGenValue.h"
1717
#include "mlir/IR/BuiltinAttributes.h"
18+
#include "mlir/IR/Value.h"
1819
#include "clang/AST/Attr.h"
1920
#include "clang/AST/CharUnits.h"
2021
#include "clang/AST/Decl.h"
2122
#include "clang/AST/Expr.h"
2223
#include "clang/AST/ExprCXX.h"
2324
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2425
#include "clang/CIR/MissingFeatures.h"
26+
#include <optional>
2527

2628
using namespace clang;
2729
using namespace clang::CIRGen;
@@ -218,7 +220,7 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
218220

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

223225
// If it's thread_local, emit a call to its wrapper function instead.
224226
assert(!cir::MissingFeatures::opGlobalThreadLocal());
@@ -248,7 +250,7 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
248250
cgf.cgm.errorNYI(e->getSourceRange(),
249251
"emitGlobalVarDeclLValue: reference type");
250252
else
251-
lv = cgf.makeAddrLValue(addr, T, AlignmentSource::Decl);
253+
lv = cgf.makeAddrLValue(addr, t, AlignmentSource::Decl);
252254
assert(!cir::MissingFeatures::setObjCGCLValueClass());
253255
return lv;
254256
}
@@ -948,6 +950,165 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
948950
emitLValue(e);
949951
}
950952

953+
// Handle the case where the condition is a constant evaluatable simple integer,
954+
// which means we don't have to separately handle the true/false blocks.
955+
static std::optional<LValue> handleConditionalOperatorLValueSimpleCase(
956+
CIRGenFunction &cgf, const AbstractConditionalOperator *e) {
957+
const Expr *condExpr = e->getCond();
958+
bool condExprBool;
959+
if (cgf.constantFoldsToSimpleInteger(condExpr, condExprBool)) {
960+
const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr();
961+
if (!condExprBool)
962+
std::swap(live, dead);
963+
964+
if (!cgf.containsLabel(dead)) {
965+
// If the true case is live, we need to track its region.
966+
if (condExprBool) {
967+
assert(!cir::MissingFeatures::incrementProfileCounter());
968+
}
969+
// If a throw expression we emit it and return an undefined lvalue
970+
// because it can't be used.
971+
if (isa<CXXThrowExpr>(live->IgnoreParens())) {
972+
assert(!cir::MissingFeatures::throwOp());
973+
cgf.cgm.errorNYI(live->getSourceRange(),
974+
"throw expressions in conditional operator");
975+
return std::nullopt;
976+
}
977+
return cgf.emitLValue(live);
978+
}
979+
}
980+
return std::nullopt;
981+
}
982+
983+
/// Emit the operand of a glvalue conditional operator. This is either a glvalue
984+
/// or a (possibly-parenthesized) throw-expression. If this is a throw, no
985+
/// LValue is returned and the current block has been terminated.
986+
static std::optional<LValue> emitLValueOrThrowExpression(CIRGenFunction &cgf,
987+
const Expr *operand) {
988+
if (isa<CXXThrowExpr>(operand->IgnoreParens())) {
989+
assert(!cir::MissingFeatures::throwOp());
990+
cgf.cgm.errorNYI(operand->getSourceRange(),
991+
"throw expressions in conditional operator");
992+
return std::nullopt;
993+
}
994+
995+
return cgf.emitLValue(operand);
996+
}
997+
998+
// Create and generate the 3 blocks for a conditional operator.
999+
// Leaves the 'current block' in the continuation basic block.
1000+
template <typename FuncTy>
1001+
CIRGenFunction::ConditionalInfo
1002+
CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e,
1003+
const FuncTy &branchGenFunc) {
1004+
ConditionalInfo info;
1005+
CIRGenFunction &cgf = *this;
1006+
ConditionalEvaluation eval(cgf);
1007+
mlir::Location loc = cgf.getLoc(e->getSourceRange());
1008+
CIRGenBuilderTy &builder = cgf.getBuilder();
1009+
Expr *trueExpr = e->getTrueExpr();
1010+
Expr *falseExpr = e->getFalseExpr();
1011+
1012+
mlir::Value condV = cgf.emitOpOnBoolExpr(loc, e->getCond());
1013+
SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{};
1014+
mlir::Type yieldTy{};
1015+
1016+
auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, Expr *expr,
1017+
std::optional<LValue> &branchInfo) {
1018+
CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()};
1019+
cgf.curLexScope->setAsTernary();
1020+
1021+
assert(!cir::MissingFeatures::incrementProfileCounter());
1022+
eval.begin(cgf);
1023+
branchInfo = branchGenFunc(cgf, expr);
1024+
mlir::Value branch = branchInfo->getPointer();
1025+
eval.end(cgf);
1026+
1027+
if (branch) {
1028+
yieldTy = branch.getType();
1029+
b.create<cir::YieldOp>(loc, branch);
1030+
} else {
1031+
// If LHS or RHS is a throw or void expression we need to patch
1032+
// arms as to properly match yield types.
1033+
insertPoints.push_back(b.saveInsertionPoint());
1034+
}
1035+
};
1036+
1037+
info.result = builder
1038+
.create<cir::TernaryOp>(
1039+
loc, condV, /*trueBuilder=*/
1040+
[&](mlir::OpBuilder &b, mlir::Location loc) {
1041+
emitBranch(b, loc, trueExpr, info.lhs);
1042+
},
1043+
/*falseBuilder=*/
1044+
[&](mlir::OpBuilder &b, mlir::Location loc) {
1045+
emitBranch(b, loc, falseExpr, info.rhs);
1046+
})
1047+
.getResult();
1048+
1049+
if (!insertPoints.empty()) {
1050+
// If both arms are void, so be it.
1051+
if (!yieldTy)
1052+
yieldTy = cgf.VoidTy;
1053+
1054+
// Insert required yields.
1055+
for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) {
1056+
mlir::OpBuilder::InsertionGuard guard(builder);
1057+
builder.restoreInsertionPoint(toInsert);
1058+
1059+
// Block does not return: build empty yield.
1060+
if (mlir::isa<cir::VoidType>(yieldTy)) {
1061+
builder.create<cir::YieldOp>(loc);
1062+
} else { // Block returns: set null yield value.
1063+
mlir::Value op0 = builder.getNullValue(yieldTy, loc);
1064+
builder.create<cir::YieldOp>(loc, op0);
1065+
}
1066+
}
1067+
}
1068+
return info;
1069+
}
1070+
1071+
LValue CIRGenFunction::emitConditionalOperatorLValue(
1072+
const AbstractConditionalOperator *expr) {
1073+
if (!expr->isGLValue()) {
1074+
// ?: here should be an aggregate.
1075+
assert(hasAggregateEvaluationKind(expr->getType()) &&
1076+
"Unexpected conditional operator!");
1077+
return emitAggExprToLValue(expr);
1078+
}
1079+
1080+
OpaqueValueMapping binding(*this, expr);
1081+
if (std::optional<LValue> res =
1082+
handleConditionalOperatorLValueSimpleCase(*this, expr))
1083+
return *res;
1084+
1085+
ConditionalInfo info =
1086+
emitConditionalBlocks(expr, [](CIRGenFunction &cgf, const Expr *e) {
1087+
return emitLValueOrThrowExpression(cgf, e);
1088+
});
1089+
1090+
if ((info.lhs && !info.lhs->isSimple()) ||
1091+
(info.rhs && !info.rhs->isSimple())) {
1092+
cgm.errorNYI(expr->getSourceRange(), "unsupported conditional operator");
1093+
return {};
1094+
}
1095+
1096+
if (info.lhs && info.rhs) {
1097+
Address lhsAddr = info.lhs->getAddress();
1098+
Address rhsAddr = info.rhs->getAddress();
1099+
Address result(info.result, lhsAddr.getElementType(),
1100+
std::min(lhsAddr.getAlignment(), rhsAddr.getAlignment()));
1101+
AlignmentSource alignSource =
1102+
std::max(info.lhs->getBaseInfo().getAlignmentSource(),
1103+
info.rhs->getBaseInfo().getAlignmentSource());
1104+
assert(!cir::MissingFeatures::opTBAA());
1105+
return makeAddrLValue(result, expr->getType(), LValueBaseInfo(alignSource));
1106+
}
1107+
assert((info.lhs || info.rhs) &&
1108+
"both operands of glvalue conditional are throw-expressions?");
1109+
return info.lhs ? *info.lhs : *info.rhs;
1110+
}
1111+
9511112
/// Emit an `if` on a boolean condition, filling `then` and `else` into
9521113
/// appropriated regions.
9531114
mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond,
@@ -1012,10 +1173,28 @@ mlir::Value CIRGenFunction::emitOpOnBoolExpr(mlir::Location loc,
10121173
// cir.ternary(!x, t, f) -> cir.ternary(x, f, t)
10131174
assert(!cir::MissingFeatures::shouldReverseUnaryCondOnBoolExpr());
10141175

1015-
if (isa<ConditionalOperator>(cond)) {
1016-
cgm.errorNYI(cond->getExprLoc(), "Ternary NYI");
1017-
assert(!cir::MissingFeatures::ternaryOp());
1018-
return createDummyValue(loc, cond->getType());
1176+
if (const ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(cond)) {
1177+
Expr *trueExpr = condOp->getTrueExpr();
1178+
Expr *falseExpr = condOp->getFalseExpr();
1179+
mlir::Value condV = emitOpOnBoolExpr(loc, condOp->getCond());
1180+
1181+
mlir::Value ternaryOpRes =
1182+
builder
1183+
.create<cir::TernaryOp>(
1184+
loc, condV, /*thenBuilder=*/
1185+
[this, trueExpr](mlir::OpBuilder &b, mlir::Location loc) {
1186+
mlir::Value lhs = emitScalarExpr(trueExpr);
1187+
b.create<cir::YieldOp>(loc, lhs);
1188+
},
1189+
/*elseBuilder=*/
1190+
[this, falseExpr](mlir::OpBuilder &b, mlir::Location loc) {
1191+
mlir::Value rhs = emitScalarExpr(falseExpr);
1192+
b.create<cir::YieldOp>(loc, rhs);
1193+
})
1194+
.getResult();
1195+
1196+
return emitScalarConversion(ternaryOpRes, condOp->getType(),
1197+
getContext().BoolTy, condOp->getExprLoc());
10191198
}
10201199

10211200
if (isa<CXXThrowExpr>(cond)) {
@@ -1118,13 +1297,84 @@ mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
11181297
return builder.createDummyValue(loc, t, alignment);
11191298
}
11201299

1121-
/// This creates an alloca and inserts it into the entry block if
1122-
/// \p insertIntoFnEntryBlock is true, otherwise it inserts it at the current
1123-
/// insertion point of the builder.
1300+
//===----------------------------------------------------------------------===//
1301+
// CIR builder helpers
1302+
//===----------------------------------------------------------------------===//
1303+
1304+
Address CIRGenFunction::createMemTemp(QualType ty, mlir::Location loc,
1305+
const Twine &name, Address *alloca,
1306+
mlir::OpBuilder::InsertPoint ip) {
1307+
// FIXME: Should we prefer the preferred type alignment here?
1308+
return createMemTemp(ty, getContext().getTypeAlignInChars(ty), loc, name,
1309+
alloca, ip);
1310+
}
1311+
1312+
Address CIRGenFunction::createMemTemp(QualType ty, CharUnits align,
1313+
mlir::Location loc, const Twine &name,
1314+
Address *alloca,
1315+
mlir::OpBuilder::InsertPoint ip) {
1316+
Address result = createTempAlloca(convertTypeForMem(ty), align, loc, name,
1317+
/*ArraySize=*/nullptr, alloca, ip);
1318+
if (ty->isConstantMatrixType()) {
1319+
assert(!cir::MissingFeatures::matrixType());
1320+
cgm.errorNYI(loc, "temporary matrix value");
1321+
}
1322+
return result;
1323+
}
1324+
1325+
/// This creates a alloca and inserts it into the entry block of the
1326+
/// current region.
1327+
Address CIRGenFunction::createTempAllocaWithoutCast(
1328+
mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name,
1329+
mlir::Value arraySize, mlir::OpBuilder::InsertPoint ip) {
1330+
cir::AllocaOp alloca = ip.isSet()
1331+
? createTempAlloca(ty, loc, name, ip, arraySize)
1332+
: createTempAlloca(ty, loc, name, arraySize);
1333+
alloca.setAlignmentAttr(cgm.getSize(align));
1334+
return Address(alloca, ty, align);
1335+
}
1336+
1337+
/// This creates a alloca and inserts it into the entry block. The alloca is
1338+
/// casted to default address space if necessary.
11241339
Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
11251340
mlir::Location loc, const Twine &name,
1126-
bool insertIntoFnEntryBlock) {
1127-
mlir::Value alloca =
1128-
emitAlloca(name.str(), ty, loc, align, insertIntoFnEntryBlock);
1129-
return Address(alloca, ty, align);
1341+
mlir::Value arraySize,
1342+
Address *allocaAddr,
1343+
mlir::OpBuilder::InsertPoint ip) {
1344+
Address alloca =
1345+
createTempAllocaWithoutCast(ty, align, loc, name, arraySize, ip);
1346+
if (allocaAddr)
1347+
*allocaAddr = alloca;
1348+
mlir::Value v = alloca.getPointer();
1349+
// Alloca always returns a pointer in alloca address space, which may
1350+
// be different from the type defined by the language. For example,
1351+
// in C++ the auto variables are in the default address space. Therefore
1352+
// cast alloca to the default address space when necessary.
1353+
assert(!cir::MissingFeatures::addressSpace());
1354+
return Address(v, ty, align);
1355+
}
1356+
1357+
/// This creates an alloca and inserts it into the entry block if \p ArraySize
1358+
/// is nullptr, otherwise inserts it at the current insertion point of the
1359+
/// builder.
1360+
cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty,
1361+
mlir::Location loc,
1362+
const Twine &name,
1363+
mlir::Value arraySize,
1364+
bool insertIntoFnEntryBlock) {
1365+
return cast<cir::AllocaOp>(emitAlloca(name.str(), ty, loc, CharUnits(),
1366+
insertIntoFnEntryBlock, arraySize)
1367+
.getDefiningOp());
1368+
}
1369+
1370+
/// This creates an alloca and inserts it into the provided insertion point
1371+
cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty,
1372+
mlir::Location loc,
1373+
const Twine &name,
1374+
mlir::OpBuilder::InsertPoint ip,
1375+
mlir::Value arraySize) {
1376+
assert(ip.isSet() && "Insertion point is not set");
1377+
return cast<cir::AllocaOp>(
1378+
emitAlloca(name.str(), ty, loc, CharUnits(), ip, arraySize)
1379+
.getDefiningOp());
11301380
}

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy,
155155
// Allocate the temporary variable
156156
// to store the pointer to first unitialized element
157157
const Address tmpAddr = cgf.createTempAlloca(
158-
cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp",
159-
/*insertIntoFnEntryBlock=*/false);
158+
cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp");
160159
LValue tmpLV = cgf.makeAddrLValue(tmpAddr, elementPtrType);
161160
cgf.emitStoreThroughLValue(RValue::get(element), tmpLV);
162161

@@ -275,3 +274,11 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr(
275274
void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) {
276275
AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e));
277276
}
277+
278+
LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) {
279+
assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!");
280+
Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange()));
281+
LValue lv = makeAddrLValue(temp, e->getType());
282+
emitAggExpr(e, AggValueSlot::forLValue(lv));
283+
return lv;
284+
}

0 commit comments

Comments
 (0)