[clang] [CIR] Upstream lowering of conditional operators to TernaryOp (PR #138156)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Thu May 1 17:47:05 PDT 2025
================
@@ -948,6 +950,165 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
emitLValue(e);
}
+// Handle the case where the condition is a constant evaluatable simple integer,
+// which means we don't have to separately handle the true/false blocks.
+static std::optional<LValue> handleConditionalOperatorLValueSimpleCase(
+ CIRGenFunction &cgf, const AbstractConditionalOperator *e) {
+ const Expr *condExpr = e->getCond();
+ bool condExprBool = false;
+ if (cgf.constantFoldsToSimpleInteger(condExpr, condExprBool)) {
+ const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr();
+ if (!condExprBool)
+ std::swap(live, dead);
+
+ if (!cgf.containsLabel(dead)) {
+ // If the true case is live, we need to track its region.
+ if (condExprBool) {
+ assert(!cir::MissingFeatures::incrementProfileCounter());
+ }
+ // If a throw expression we emit it and return an undefined lvalue
+ // because it can't be used.
+ if (isa<CXXThrowExpr>(live->IgnoreParens())) {
+ assert(!cir::MissingFeatures::throwOp());
+ cgf.cgm.errorNYI(live->getSourceRange(),
+ "throw expressions in conditional operator");
+ return std::nullopt;
+ }
+ return cgf.emitLValue(live);
+ }
+ }
+ return std::nullopt;
+}
+
+/// Emit the operand of a glvalue conditional operator. This is either a glvalue
+/// or a (possibly-parenthesized) throw-expression. If this is a throw, no
+/// LValue is returned and the current block has been terminated.
+static std::optional<LValue> emitLValueOrThrowExpression(CIRGenFunction &cgf,
+ const Expr *operand) {
+ if (isa<CXXThrowExpr>(operand->IgnoreParens())) {
+ assert(!cir::MissingFeatures::throwOp());
+ cgf.cgm.errorNYI(operand->getSourceRange(),
+ "throw expressions in conditional operator");
+ return std::nullopt;
+ }
+
+ return cgf.emitLValue(operand);
+}
+
+// Create and generate the 3 blocks for a conditional operator.
+// Leaves the 'current block' in the continuation basic block.
+template <typename FuncTy>
+CIRGenFunction::ConditionalInfo
+CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e,
+ const FuncTy &branchGenFunc) {
+ ConditionalInfo info;
+ CIRGenFunction &cgf = *this;
+ ConditionalEvaluation eval(cgf);
+ mlir::Location loc = cgf.getLoc(e->getSourceRange());
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ Expr *trueExpr = e->getTrueExpr();
+ Expr *falseExpr = e->getFalseExpr();
+
+ mlir::Value condV = cgf.emitOpOnBoolExpr(loc, e->getCond());
+ SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{};
+ mlir::Type yieldTy{};
+
+ auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, Expr *expr,
+ std::optional<LValue> &branchInfo) {
+ CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()};
+ cgf.curLexScope->setAsTernary();
+
+ assert(!cir::MissingFeatures::incrementProfileCounter());
+ eval.begin(cgf);
+ branchInfo = branchGenFunc(cgf, expr);
+ mlir::Value branch = branchInfo->getPointer();
+ eval.end(cgf);
+
+ if (branch) {
+ yieldTy = branch.getType();
+ b.create<cir::YieldOp>(loc, branch);
+ } else {
+ // If LHS or RHS is a throw or void expression we need to patch
+ // arms as to properly match yield types.
+ insertPoints.push_back(b.saveInsertionPoint());
+ }
+ };
+
+ info.result = builder
+ .create<cir::TernaryOp>(
+ loc, condV, /*trueBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ emitBranch(b, loc, trueExpr, info.lhs);
+ },
+ /*falseBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ emitBranch(b, loc, falseExpr, info.rhs);
+ })
+ .getResult();
+
+ if (!insertPoints.empty()) {
+ // If both arms are void, so be it.
+ if (!yieldTy)
+ yieldTy = cgf.VoidTy;
+
+ // Insert required yields.
+ for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.restoreInsertionPoint(toInsert);
+
+ // Block does not return: build empty yield.
+ if (mlir::isa<cir::VoidType>(yieldTy)) {
+ builder.create<cir::YieldOp>(loc);
+ } else { // Block returns: set null yield value.
+ mlir::Value op0 = builder.getNullValue(yieldTy, loc);
----------------
andykaylor wrote:
I don't think this is right. If a block contains a throw, the incubator generates a `cir.throw` followed by a `cir.unreachable` and then inserts a null constant and a `cir.yield`. This causes verification to fail because `cir.unreachable` isn't the last operation in the block.
https://github.com/llvm/llvm-project/pull/138156
More information about the cfe-commits
mailing list