|
15 | 15 | #include "CIRGenModule.h"
|
16 | 16 | #include "CIRGenValue.h"
|
17 | 17 | #include "mlir/IR/BuiltinAttributes.h"
|
| 18 | +#include "mlir/IR/Value.h" |
18 | 19 | #include "clang/AST/Attr.h"
|
19 | 20 | #include "clang/AST/CharUnits.h"
|
20 | 21 | #include "clang/AST/Decl.h"
|
21 | 22 | #include "clang/AST/Expr.h"
|
22 | 23 | #include "clang/AST/ExprCXX.h"
|
23 | 24 | #include "clang/CIR/Dialect/IR/CIRDialect.h"
|
24 | 25 | #include "clang/CIR/MissingFeatures.h"
|
| 26 | +#include <optional> |
25 | 27 |
|
26 | 28 | using namespace clang;
|
27 | 29 | using namespace clang::CIRGen;
|
@@ -218,7 +220,7 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
|
218 | 220 |
|
219 | 221 | static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
|
220 | 222 | const VarDecl *vd) {
|
221 |
| - QualType T = e->getType(); |
| 223 | + QualType t = e->getType(); |
222 | 224 |
|
223 | 225 | // If it's thread_local, emit a call to its wrapper function instead.
|
224 | 226 | assert(!cir::MissingFeatures::opGlobalThreadLocal());
|
@@ -248,7 +250,7 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e,
|
248 | 250 | cgf.cgm.errorNYI(e->getSourceRange(),
|
249 | 251 | "emitGlobalVarDeclLValue: reference type");
|
250 | 252 | else
|
251 |
| - lv = cgf.makeAddrLValue(addr, T, AlignmentSource::Decl); |
| 253 | + lv = cgf.makeAddrLValue(addr, t, AlignmentSource::Decl); |
252 | 254 | assert(!cir::MissingFeatures::setObjCGCLValueClass());
|
253 | 255 | return lv;
|
254 | 256 | }
|
@@ -948,6 +950,165 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
|
948 | 950 | emitLValue(e);
|
949 | 951 | }
|
950 | 952 |
|
| 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 | + |
951 | 1112 | /// Emit an `if` on a boolean condition, filling `then` and `else` into
|
952 | 1113 | /// appropriated regions.
|
953 | 1114 | mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond,
|
@@ -1012,10 +1173,28 @@ mlir::Value CIRGenFunction::emitOpOnBoolExpr(mlir::Location loc,
|
1012 | 1173 | // cir.ternary(!x, t, f) -> cir.ternary(x, f, t)
|
1013 | 1174 | assert(!cir::MissingFeatures::shouldReverseUnaryCondOnBoolExpr());
|
1014 | 1175 |
|
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()); |
1019 | 1198 | }
|
1020 | 1199 |
|
1021 | 1200 | if (isa<CXXThrowExpr>(cond)) {
|
@@ -1118,13 +1297,84 @@ mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
|
1118 | 1297 | return builder.createDummyValue(loc, t, alignment);
|
1119 | 1298 | }
|
1120 | 1299 |
|
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. |
1124 | 1339 | Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
|
1125 | 1340 | 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()); |
1130 | 1380 | }
|
0 commit comments