https://github.com/mmha created https://github.com/llvm/llvm-project/pull/138156

This patch adds visitors for BinLAnd, BinLOr and AbstractConditionalOperator. 
Note that this patch still lacks visitation of OpaqueValueExpr which is needed 
for the GNU ?: operator.

>From 09763c19421acdb9896bab13f29679dbb38b9043 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 1 May 2025 17:27:23 +0200
Subject: [PATCH] [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.
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |  16 +
 clang/include/clang/CIR/MissingFeatures.h     |   5 +-
 clang/lib/CIR/CodeGen/CIRGenDecl.cpp          |   3 +-
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          | 276 ++++++++++++++-
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |  11 +-
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    | 334 ++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  14 +
 clang/lib/CIR/CodeGen/CIRGenFunction.h        | 245 ++++++++++++-
 clang/lib/CIR/CodeGen/CIRGenValue.h           |   1 +
 clang/test/CIR/CodeGen/binop.c                |  13 +
 clang/test/CIR/CodeGen/binop.cpp              | 225 ++++++++++++
 clang/test/CIR/CodeGen/ternary.cpp            | 120 +++++++
 12 files changed, 1243 insertions(+), 20 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/binop.c
 create mode 100644 clang/test/CIR/CodeGen/ternary.cpp

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index d58ced6ec8bff..104cf9e3d3875 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -272,6 +272,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return createCast(loc, cir::CastKind::bitcast, src, newTy);
   }
 
+  // TODO(cir): the following function was introduced to keep in sync with LLVM
+  // codegen. CIR does not have "zext" operations. It should eventually be
+  // renamed or removed. For now, we just add whatever cast is required here.
+  mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src,
+                                  mlir::Type newTy) {
+    mlir::Type srcTy = src.getType();
+
+    if (srcTy == newTy)
+      return src;
+
+    if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy))
+      return createBoolToInt(src, newTy);
+
+    llvm_unreachable("unhandled extension cast");
+  }
+
   
//===--------------------------------------------------------------------===//
   // Binary Operators
   
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 4d4951aa0e126..7b345cf1e8c7f 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -163,6 +163,8 @@ struct MissingFeatures {
   static bool setDSOLocal() { return false; }
   static bool foldCaseStmt() { return false; }
   static bool constantFoldSwitchStatement() { return false; }
+  static bool peepholeProtection() { return false; }
+  static bool instrumenation() { return false; }
 
   // Missing types
   static bool dataMemberType() { return false; }
@@ -188,8 +190,9 @@ struct MissingFeatures {
   static bool ptrStrideOp() { return false; }
   static bool selectOp() { 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
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp 
b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 90498cd18f46b..6f1ed2959c1dc 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -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;
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index da5a0b97a395e..48102e8c56b3e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -15,6 +15,7 @@
 #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"
@@ -22,6 +23,7 @@
 #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;
@@ -218,7 +220,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());
@@ -248,7 +250,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;
 }
@@ -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;
+  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);
+        builder.create<cir::YieldOp>(loc, op0);
+      }
+    }
+  }
+  return info;
+}
+
+LValue CIRGenFunction::emitConditionalOperatorLValue(
+    const AbstractConditionalOperator *expr) {
+  if (!expr->isGLValue()) {
+    // ?: here should be an aggregate.
+    assert(hasAggregateEvaluationKind(expr->getType()) &&
+           "Unexpected conditional operator!");
+    return emitAggExprToLValue(expr);
+  }
+
+  OpaqueValueMapping binding(*this, expr);
+  if (std::optional<LValue> res =
+          handleConditionalOperatorLValueSimpleCase(*this, expr))
+    return *res;
+
+  ConditionalInfo info =
+      emitConditionalBlocks(expr, [](CIRGenFunction &cgf, const Expr *e) {
+        return emitLValueOrThrowExpression(cgf, e);
+      });
+
+  if ((info.lhs && !info.lhs->isSimple()) ||
+      (info.rhs && !info.rhs->isSimple())) {
+    cgm.errorNYI(expr->getSourceRange(), "unsupported conditional operator");
+    return {};
+  }
+
+  if (info.lhs && info.rhs) {
+    Address lhsAddr = info.lhs->getAddress();
+    Address rhsAddr = info.rhs->getAddress();
+    Address result(info.result, lhsAddr.getElementType(),
+                   std::min(lhsAddr.getAlignment(), rhsAddr.getAlignment()));
+    AlignmentSource alignSource =
+        std::max(info.lhs->getBaseInfo().getAlignmentSource(),
+                 info.rhs->getBaseInfo().getAlignmentSource());
+    assert(!cir::MissingFeatures::opTBAA());
+    return makeAddrLValue(result, expr->getType(), 
LValueBaseInfo(alignSource));
+  }
+  assert((info.lhs || info.rhs) &&
+         "both operands of glvalue conditional are throw-expressions?");
+  return info.lhs ? *info.lhs : *info.rhs;
+}
+
 /// Emit an `if` on a boolean condition, filling `then` and `else` into
 /// appropriated regions.
 mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond,
@@ -1012,10 +1173,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)) {
@@ -1118,13 +1297,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(),
+                                        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());
 }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index e006a77c6e7d6..928a5eb8fe3be 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -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);
 
@@ -275,3 +274,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;
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index aef5b125a2877..cd07a6cc59fc5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -302,6 +302,8 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
   }
 
   mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *e);
+  mlir::Value
+  VisitAbstractConditionalOperator(const AbstractConditionalOperator *e);
 
   // Unary Operators.
   mlir::Value VisitUnaryPostDec(const UnaryOperator *e) {
@@ -875,6 +877,174 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
     // NOTE: We don't need to EnsureInsertPoint() like LLVM codegen.
     return Visit(e->getRHS());
   }
+
+  mlir::Value VisitBinLAnd(const clang::BinaryOperator *e) {
+    if (e->getType()->isVectorType()) {
+      assert(!cir::MissingFeatures::vectorType());
+      return {};
+    }
+
+    bool instrumentRegions = cgf.cgm.getCodeGenOpts().hasProfileClangInstr();
+    mlir::Type resTy = cgf.convertType(e->getType());
+    mlir::Location loc = cgf.getLoc(e->getExprLoc());
+
+    // If we have 0 && RHS, see if we can elide RHS, if so, just return 0.
+    // If we have 1 && X, just emit X without inserting the control flow.
+    bool lhsCondVal;
+    if (cgf.constantFoldsToSimpleInteger(e->getLHS(), lhsCondVal)) {
+      if (lhsCondVal) { // If we have 1 && X, just emit X.
+
+        mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS());
+
+        if (instrumentRegions) {
+          assert(!cir::MissingFeatures::instrumenation());
+          cgf.cgm.errorNYI(e->getExprLoc(), "instrumenation");
+        }
+        // ZExt result to int or bool.
+        return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy);
+      }
+      // 0 && RHS: If it is safe, just elide the RHS, and return 0/false.
+      if (!cgf.containsLabel(e->getRHS()))
+        return builder.getNullValue(resTy, loc);
+    }
+
+    CIRGenFunction::ConditionalEvaluation eval(cgf);
+
+    mlir::Value lhsCondV = cgf.evaluateExprAsBool(e->getLHS());
+    auto resOp = builder.create<cir::TernaryOp>(
+        loc, lhsCondV, /*trueBuilder=*/
+        [&](mlir::OpBuilder &b, mlir::Location loc) {
+          CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                b.getInsertionBlock()};
+          cgf.curLexScope->setAsTernary();
+          mlir::Value rhsCondV = cgf.evaluateExprAsBool(e->getRHS());
+          auto res = b.create<cir::TernaryOp>(
+              loc, rhsCondV, /*trueBuilder*/
+              [&](mlir::OpBuilder &b, mlir::Location loc) {
+                CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                      b.getInsertionBlock()};
+                cgf.curLexScope->setAsTernary();
+                auto res =
+                    b.create<cir::ConstantOp>(loc, builder.getTrueAttr());
+                b.create<cir::YieldOp>(loc, res.getRes());
+              },
+              /*falseBuilder*/
+              [&](mlir::OpBuilder &b, mlir::Location loc) {
+                CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                      b.getInsertionBlock()};
+                cgf.curLexScope->setAsTernary();
+                auto res =
+                    b.create<cir::ConstantOp>(loc, builder.getFalseAttr());
+                b.create<cir::YieldOp>(loc, res.getRes());
+              });
+          b.create<cir::YieldOp>(loc, res.getResult());
+        },
+        /*falseBuilder*/
+        [&](mlir::OpBuilder &b, mlir::Location loc) {
+          CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                b.getInsertionBlock()};
+          cgf.curLexScope->setAsTernary();
+          auto res = b.create<cir::ConstantOp>(loc, builder.getFalseAttr());
+          b.create<cir::YieldOp>(loc, res.getRes());
+        });
+    return builder.createZExtOrBitCast(resOp.getLoc(), resOp.getResult(),
+                                       resTy);
+  }
+
+  mlir::Value VisitBinLOr(const clang::BinaryOperator *e) {
+    if (e->getType()->isVectorType()) {
+      assert(!cir::MissingFeatures::vectorType());
+      return {};
+    }
+
+    bool instrumentRegions = cgf.cgm.getCodeGenOpts().hasProfileClangInstr();
+    mlir::Type resTy = cgf.convertType(e->getType());
+    mlir::Location loc = cgf.getLoc(e->getExprLoc());
+
+    // If we have 1 || RHS, see if we can elide RHS, if so, just return 1.
+    // If we have 0 || X, just emit X without inserting the control flow.
+    bool lhsCondVal;
+    if (cgf.constantFoldsToSimpleInteger(e->getLHS(), lhsCondVal)) {
+      if (!lhsCondVal) { // If we have 0 || X, just emit X.
+
+        mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS());
+
+        if (instrumentRegions) {
+          assert(!cir::MissingFeatures::instrumenation());
+          cgf.cgm.errorNYI(e->getExprLoc(), "instrumenation");
+        }
+        // ZExt result to int or bool.
+        return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy);
+      }
+      // 1 || RHS: If it is safe, just elide the RHS, and return 1/true.
+      if (!cgf.containsLabel(e->getRHS())) {
+        if (auto intTy = mlir::dyn_cast<cir::IntType>(resTy))
+          return builder.getConstantInt(loc, intTy, 1);
+        return builder.getBool(true, loc);
+      }
+    }
+
+    CIRGenFunction::ConditionalEvaluation eval(cgf);
+
+    mlir::Value lhsCondV = cgf.evaluateExprAsBool(e->getLHS());
+    auto resOp = builder.create<cir::TernaryOp>(
+        loc, lhsCondV, /*trueBuilder=*/
+        [&](mlir::OpBuilder &b, mlir::Location loc) {
+          CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                b.getInsertionBlock()};
+          cgf.curLexScope->setAsTernary();
+          auto res = b.create<cir::ConstantOp>(loc, builder.getTrueAttr());
+          b.create<cir::YieldOp>(loc, res.getRes());
+        },
+        /*falseBuilder*/
+        [&](mlir::OpBuilder &b, mlir::Location loc) {
+          CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                b.getInsertionBlock()};
+          cgf.curLexScope->setAsTernary();
+          mlir::Value rhsCondV = cgf.evaluateExprAsBool(e->getRHS());
+          auto res = b.create<cir::TernaryOp>(
+              loc, rhsCondV, /*trueBuilder*/
+              [&](mlir::OpBuilder &b, mlir::Location loc) {
+                SmallVector<mlir::Location, 2> locs;
+                if (mlir::isa<mlir::FileLineColLoc>(loc)) {
+                  locs.push_back(loc);
+                  locs.push_back(loc);
+                } else if (mlir::isa<mlir::FusedLoc>(loc)) {
+                  auto fusedLoc = mlir::cast<mlir::FusedLoc>(loc);
+                  locs.push_back(fusedLoc.getLocations()[0]);
+                  locs.push_back(fusedLoc.getLocations()[1]);
+                }
+                CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                      b.getInsertionBlock()};
+                cgf.curLexScope->setAsTernary();
+                auto res =
+                    b.create<cir::ConstantOp>(loc, builder.getTrueAttr());
+                b.create<cir::YieldOp>(loc, res.getRes());
+              },
+              /*falseBuilder*/
+              [&](mlir::OpBuilder &b, mlir::Location loc) {
+                SmallVector<mlir::Location, 2> locs;
+                if (mlir::isa<mlir::FileLineColLoc>(loc)) {
+                  locs.push_back(loc);
+                  locs.push_back(loc);
+                } else if (mlir::isa<mlir::FusedLoc>(loc)) {
+                  auto fusedLoc = mlir::cast<mlir::FusedLoc>(loc);
+                  locs.push_back(fusedLoc.getLocations()[0]);
+                  locs.push_back(fusedLoc.getLocations()[1]);
+                }
+                CIRGenFunction::LexicalScope lexScope{cgf, loc,
+                                                      b.getInsertionBlock()};
+                cgf.curLexScope->setAsTernary();
+                auto res =
+                    b.create<cir::ConstantOp>(loc, builder.getFalseAttr());
+                b.create<cir::YieldOp>(loc, res.getRes());
+              });
+          b.create<cir::YieldOp>(loc, res.getResult());
+        });
+
+    return builder.createZExtOrBitCast(resOp.getLoc(), resOp.getResult(),
+                                       resTy);
+  }
 };
 
 LValue ScalarExprEmitter::emitCompoundAssignLValue(
@@ -1658,6 +1828,170 @@ mlir::Value 
ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
                cgf.cgm.UInt64Ty, e->EvaluateKnownConstInt(cgf.getContext())));
 }
 
+/// Return true if the specified expression is cheap enough and 
side-effect-free
+/// enough to evaluate unconditionally instead of conditionally.  This is used
+/// to convert control flow into selects in some cases.
+/// TODO(cir): can be shared with LLVM codegen.
+static bool isCheapEnoughToEvaluateUnconditionally(const Expr *e,
+                                                   CIRGenFunction &cgf) {
+  // Anything that is an integer or floating point constant is fine.
+  return e->IgnoreParens()->isEvaluatable(cgf.getContext());
+
+  // Even non-volatile automatic variables can't be evaluated unconditionally.
+  // Referencing a thread_local may cause non-trivial initialization work to
+  // occur. If we're inside a lambda and one of the variables is from the scope
+  // outside the lambda, that function may have returned already. Reading its
+  // locals is a bad idea. Also, these reads may introduce races there didn't
+  // exist in the source-level program.
+}
+
+mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator(
+    const AbstractConditionalOperator *e) {
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+  mlir::Location loc = cgf.getLoc(e->getSourceRange());
+  ignoreResultAssign = false;
+
+  // Bind the common expression if necessary.
+  CIRGenFunction::OpaqueValueMapping binding(cgf, e);
+
+  Expr *condExpr = e->getCond();
+  Expr *lhsExpr = e->getTrueExpr();
+  Expr *rhsExpr = e->getFalseExpr();
+
+  // If the condition constant folds and can be elided, try to avoid emitting
+  // the condition and the dead arm.
+  bool condExprBool;
+  if (cgf.constantFoldsToSimpleInteger(condExpr, condExprBool)) {
+    Expr *live = lhsExpr, *dead = rhsExpr;
+    if (!condExprBool)
+      std::swap(live, dead);
+
+    // If the dead side doesn't have labels we need, just emit the Live part.
+    if (!cgf.containsLabel(dead)) {
+      if (condExprBool)
+        assert(!cir::MissingFeatures::incrementProfileCounter());
+      mlir::Value result = Visit(live);
+
+      // If the live part is a throw expression, it acts like it has a void
+      // type, so evaluating it returns a null Value.  However, a conditional
+      // with non-void type must return a non-null Value.
+      if (!result && !e->getType()->isVoidType()) {
+        cgf.cgm.errorNYI(e->getSourceRange(),
+                         "throw expression in conditional operator");
+        result = {};
+      }
+
+      return result;
+    }
+  }
+
+  // OpenCL: If the condition is a vector, we can treat this condition like
+  // the select function.
+  if ((cgf.getLangOpts().OpenCL && condExpr->getType()->isVectorType()) ||
+      condExpr->getType()->isExtVectorType()) {
+    assert(!cir::MissingFeatures::vectorType());
+    cgf.cgm.errorNYI(e->getSourceRange(), "vector ternary op");
+  }
+
+  if (condExpr->getType()->isVectorType() ||
+      condExpr->getType()->isSveVLSBuiltinType()) {
+    assert(!cir::MissingFeatures::vecTernaryOp());
+    cgf.cgm.errorNYI(e->getSourceRange(), "vector ternary op");
+    return {};
+  }
+
+  // If this is a really simple expression (like x ? 4 : 5), emit this as a
+  // select instead of as control flow.  We can only do this if it is cheap and
+  // safe to evaluate the LHS and RHS unconditionally.
+  if (isCheapEnoughToEvaluateUnconditionally(lhsExpr, cgf) &&
+      isCheapEnoughToEvaluateUnconditionally(rhsExpr, cgf)) {
+    bool lhsIsVoid = false;
+    mlir::Value condV = cgf.evaluateExprAsBool(condExpr);
+    assert(!cir::MissingFeatures::incrementProfileCounter());
+
+    return builder
+        .create<cir::TernaryOp>(
+            loc, condV, /*thenBuilder=*/
+            [&](mlir::OpBuilder &b, mlir::Location loc) {
+              mlir::Value lhs = Visit(lhsExpr);
+              if (!lhs) {
+                lhs = builder.getNullValue(cgf.VoidTy, loc);
+                lhsIsVoid = true;
+              }
+              builder.create<cir::YieldOp>(loc, lhs);
+            },
+            /*elseBuilder=*/
+            [&](mlir::OpBuilder &b, mlir::Location loc) {
+              mlir::Value rhs = Visit(rhsExpr);
+              if (lhsIsVoid) {
+                assert(!rhs && "lhs and rhs types must match");
+                rhs = builder.getNullValue(cgf.VoidTy, loc);
+              }
+              builder.create<cir::YieldOp>(loc, rhs);
+            })
+        .getResult();
+  }
+
+  mlir::Value condV = cgf.emitOpOnBoolExpr(loc, condExpr);
+  CIRGenFunction::ConditionalEvaluation eval(cgf);
+  SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{};
+  mlir::Type yieldTy{};
+
+  auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, Expr *expr) {
+    CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()};
+    cgf.curLexScope->setAsTernary();
+
+    assert(!cir::MissingFeatures::incrementProfileCounter());
+    eval.begin(cgf);
+    mlir::Value branch = Visit(expr);
+    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());
+    }
+  };
+
+  mlir::Value result = builder
+                           .create<cir::TernaryOp>(
+                               loc, condV,
+                               /*trueBuilder=*/
+                               [&](mlir::OpBuilder &b, mlir::Location loc) {
+                                 emitBranch(b, loc, lhsExpr);
+                               },
+                               /*falseBuilder=*/
+                               [&](mlir::OpBuilder &b, mlir::Location loc) {
+                                 emitBranch(b, loc, rhsExpr);
+                               })
+                           .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);
+        builder.create<cir::YieldOp>(loc, op0);
+      }
+    }
+  }
+
+  return result;
+}
+
 mlir::Value CIRGenFunction::emitScalarPrePostIncDec(const UnaryOperator *e,
                                                     LValue lv, bool isInc,
                                                     bool isPre) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index fa86fce2f6e5c..9a69fe4ae4cf4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -200,6 +200,20 @@ bool CIRGenFunction::constantFoldsToSimpleInteger(const 
Expr *cond,
   return true;
 }
 
+/// If the specified expression does not fold
+/// to a constant, or if it does but contains a label, return false.  If it
+/// constant folds return true and set the boolean result in `resultBool`.
+bool CIRGenFunction::constantFoldsToSimpleInteger(const Expr *cond,
+                                                  bool &resultBool,
+                                                  bool allowLabels) {
+  llvm::APSInt resultInt;
+  if (!constantFoldsToSimpleInteger(cond, resultInt, allowLabels))
+    return false;
+
+  resultBool = resultInt.getBoolValue();
+  return true;
+}
+
 void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc,
                                             CharUnits alignment) {
   if (!type->isVoidType()) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index d50abfcfbc867..0daa29827c595 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -96,6 +96,10 @@ class CIRGenFunction : public CIRGenTypeCache {
     return getEvaluationKind(type) == cir::TEK_Scalar;
   }
 
+  static bool hasAggregateEvaluationKind(clang::QualType type) {
+    return getEvaluationKind(type) == cir::TEK_Aggregate;
+  }
+
   CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder,
                  bool suppressNewContext = false);
   ~CIRGenFunction();
@@ -104,6 +108,152 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
 
+  // ---------------------
+  // Opaque value handling
+  // ---------------------
+
+  /// Keeps track of the current set of opaque value expressions.
+  llvm::DenseMap<const OpaqueValueExpr *, LValue> opaqueLValues;
+  llvm::DenseMap<const OpaqueValueExpr *, RValue> opaqueRValues;
+
+  // This keeps track of the associated size for each VLA type.
+  // We track this by the size expression rather than the type itself because
+  // in certain situations, like a const qualifier applied to an VLA typedef,
+  // multiple VLA types can share the same size expression.
+  // FIXME: Maybe this could be a stack of maps that is pushed/popped as we
+  // enter/leave scopes.
+  llvm::DenseMap<const Expr *, mlir::Value> vlaSizeMap;
+
+public:
+  /// A non-RAII class containing all the information about a bound
+  /// opaque value.  OpaqueValueMapping, below, is a RAII wrapper for
+  /// this which makes individual mappings very simple; using this
+  /// class directly is useful when you have a variable number of
+  /// opaque values or don't want the RAII functionality for some
+  /// reason.
+  class OpaqueValueMappingData {
+    const OpaqueValueExpr *opaqueValue;
+    bool boundLValue;
+
+    OpaqueValueMappingData(const OpaqueValueExpr *ov, bool boundLValue)
+        : opaqueValue(ov), boundLValue(boundLValue) {}
+
+  public:
+    OpaqueValueMappingData() : opaqueValue(nullptr) {}
+
+    static bool shouldBindAsLValue(const Expr *expr) {
+      // gl-values should be bound as l-values for obvious reasons.
+      // Records should be bound as l-values because IR generation
+      // always keeps them in memory.  Expressions of function type
+      // act exactly like l-values but are formally required to be
+      // r-values in C.
+      return expr->isGLValue() || expr->getType()->isFunctionType() ||
+             hasAggregateEvaluationKind(expr->getType());
+    }
+
+    static OpaqueValueMappingData
+    bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const Expr *e) {
+      if (shouldBindAsLValue(ov))
+        return bind(cgf, ov, cgf.emitLValue(e));
+      return bind(cgf, ov, cgf.emitAnyExpr(e));
+    }
+
+    static OpaqueValueMappingData
+    bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const LValue &lv) {
+      assert(shouldBindAsLValue(ov));
+      cgf.opaqueLValues.insert(std::make_pair(ov, lv));
+      return OpaqueValueMappingData(ov, true);
+    }
+
+    static OpaqueValueMappingData
+    bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const RValue &rv) {
+      assert(!shouldBindAsLValue(ov));
+      cgf.opaqueRValues.insert(std::make_pair(ov, rv));
+
+      OpaqueValueMappingData data(ov, false);
+
+      // Work around an extremely aggressive peephole optimization in
+      // EmitScalarConversion which assumes that all other uses of a
+      // value are extant.
+      assert(!cir::MissingFeatures::peepholeProtection() && "NYI");
+      return data;
+    }
+
+    bool isValid() const { return opaqueValue != nullptr; }
+    void clear() { opaqueValue = nullptr; }
+
+    void unbind(CIRGenFunction &cgf) {
+      assert(opaqueValue && "no data to unbind!");
+
+      if (boundLValue) {
+        cgf.opaqueLValues.erase(opaqueValue);
+      } else {
+        cgf.opaqueRValues.erase(opaqueValue);
+        assert(!cir::MissingFeatures::peepholeProtection() && "NYI");
+      }
+    }
+  };
+
+  /// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr.
+  class OpaqueValueMapping {
+    CIRGenFunction &cgf;
+    OpaqueValueMappingData data;
+
+  public:
+    static bool shouldBindAsLValue(const Expr *expr) {
+      return OpaqueValueMappingData::shouldBindAsLValue(expr);
+    }
+
+    /// Build the opaque value mapping for the given conditional
+    /// operator if it's the GNU ?: extension.  This is a common
+    /// enough pattern that the convenience operator is really
+    /// helpful.
+    ///
+    OpaqueValueMapping(CIRGenFunction &cgf,
+                       const AbstractConditionalOperator *op)
+        : cgf(cgf) {
+      if (mlir::isa<ConditionalOperator>(op))
+        // Leave Data empty.
+        return;
+
+      const BinaryConditionalOperator *e =
+          mlir::cast<BinaryConditionalOperator>(op);
+      data = OpaqueValueMappingData::bind(cgf, e->getOpaqueValue(),
+                                          e->getCommon());
+    }
+
+    /// Build the opaque value mapping for an OpaqueValueExpr whose source
+    /// expression is set to the expression the OVE represents.
+    OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *ov)
+        : cgf(cgf) {
+      if (ov) {
+        assert(ov->getSourceExpr() && "wrong form of OpaqueValueMapping used "
+                                      "for OVE with no source expression");
+        data = OpaqueValueMappingData::bind(cgf, ov, ov->getSourceExpr());
+      }
+    }
+
+    OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *opaqueValue,
+                       LValue lvalue)
+        : cgf(cgf),
+          data(OpaqueValueMappingData::bind(cgf, opaqueValue, lvalue)) {}
+
+    OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *opaqueValue,
+                       RValue rvalue)
+        : cgf(cgf),
+          data(OpaqueValueMappingData::bind(cgf, opaqueValue, rvalue)) {}
+
+    void pop() {
+      data.unbind(cgf);
+      data.clear();
+    }
+
+    ~OpaqueValueMapping() {
+      if (data.isValid())
+        data.unbind(cgf);
+    }
+  };
+
 private:
   /// Declare a variable in the current scope, return success if the variable
   /// wasn't declared yet.
@@ -185,6 +335,8 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// the boolean result in Result.
   bool constantFoldsToBool(const clang::Expr *cond, bool &resultBool,
                            bool allowLabels = false);
+  bool constantFoldsToSimpleInteger(const clang::Expr *cond, bool &resultBool,
+                                    bool allowLabels = false);
   bool constantFoldsToSimpleInteger(const clang::Expr *cond,
                                     llvm::APSInt &resultInt,
                                     bool allowLabels = false);
@@ -230,7 +382,7 @@ class CIRGenFunction : public CIRGenTypeCache {
     /// Returns the address of the object within this declaration.
     /// Note that this does not chase the forwarding pointer for
     /// __block decls.
-    Address getObjectAddress(CIRGenFunction &CGF) const {
+    Address getObjectAddress(CIRGenFunction &cgf) const {
       if (!IsEscapingByRef)
         return Addr;
 
@@ -441,6 +593,8 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   void emitAggExpr(const clang::Expr *e, AggValueSlot slot);
 
+  LValue emitAggExprToLValue(const Expr *e);
+
   /// Emit code to compute the specified expression which can have any type. 
The
   /// result is returned as an RValue struct. If this is an aggregate
   /// expression, the aggloc/agglocvolatile arguments indicate where the result
@@ -528,6 +682,8 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
 
+  LValue emitConditionalOperatorLValue(const AbstractConditionalOperator 
*expr);
+
   void emitDecl(const clang::Decl &d);
   mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
   LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
@@ -642,12 +798,97 @@ class CIRGenFunction : public CIRGenTypeCache {
   void emitNullabilityCheck(LValue lhs, mlir::Value rhs,
                             clang::SourceLocation loc);
 
+  /// An object to manage conditionally-evaluated expressions.
+  class ConditionalEvaluation {
+    mlir::OpBuilder::InsertPoint insertPt;
+
+  public:
+    ConditionalEvaluation(CIRGenFunction &cgf)
+        : insertPt(cgf.builder.saveInsertionPoint()) {}
+    ConditionalEvaluation(mlir::OpBuilder::InsertPoint ip) : insertPt(ip) {}
+
+    void begin(CIRGenFunction &cgf) {
+      assert(cgf.outermostConditional != this);
+      if (!cgf.outermostConditional)
+        cgf.outermostConditional = this;
+    }
+
+    void end(CIRGenFunction &cgf) {
+      assert(cgf.outermostConditional != nullptr);
+      if (cgf.outermostConditional == this)
+        cgf.outermostConditional = nullptr;
+    }
+
+    /// Returns the insertion point which will be executed prior to each
+    /// evaluation of the conditional code. In LLVM OG, this method
+    /// is called getStartingBlock.
+    mlir::OpBuilder::InsertPoint getInsertPoint() const { return insertPt; }
+  };
+
+  struct ConditionalInfo {
+    std::optional<LValue> lhs{}, rhs{};
+    mlir::Value result{};
+  };
+
+  // Return true if we're currently emitting one branch or the other of a
+  // conditional expression.
+  bool isInConditionalBranch() const { return outermostConditional != nullptr; 
}
+
+  void setBeforeOutermostConditional(mlir::Value value, Address addr) {
+    assert(isInConditionalBranch());
+    {
+      mlir::OpBuilder::InsertionGuard guard(builder);
+      builder.restoreInsertionPoint(outermostConditional->getInsertPoint());
+      assert(!cir::MissingFeatures::opLoadStoreAlignment());
+      // TODO(cir): This store needs to use the alignment of addr
+      builder.createStore(value.getLoc(), value, addr.getPointer());
+    }
+  }
+
+  // Points to the outermost active conditional control. This is used so that
+  // we know if a temporary should be destroyed conditionally.
+  ConditionalEvaluation *outermostConditional = nullptr;
+
+  template <typename FuncTy>
+  ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e,
+                                        const FuncTy &branchGenFunc);
+
+  mlir::Value emitTernaryOnBoolExpr(const clang::Expr *cond, mlir::Location 
loc,
+                                    const clang::Stmt *thenS,
+                                    const clang::Stmt *elseS);
+
   /// ----------------------
   /// CIR build helpers
   /// -----------------
 public:
+  cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc,
+                                 const Twine &name = "tmp",
+                                 mlir::Value arraySize = nullptr,
+                                 bool insertIntoFnEntryBlock = false);
+  cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc,
+                                 const Twine &name = "tmp",
+                                 mlir::OpBuilder::InsertPoint ip = {},
+                                 mlir::Value arraySize = nullptr);
   Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc,
-                           const Twine &name, bool insertIntoFnEntryBlock);
+                           const Twine &name = "tmp",
+                           mlir::Value arraySize = nullptr,
+                           Address *alloca = nullptr,
+                           mlir::OpBuilder::InsertPoint ip = {});
+  Address createTempAllocaWithoutCast(mlir::Type ty, CharUnits align,
+                                      mlir::Location loc,
+                                      const Twine &name = "tmp",
+                                      mlir::Value arraySize = nullptr,
+                                      mlir::OpBuilder::InsertPoint ip = {});
+
+  /// Create a temporary memory object of the given type, with
+  /// appropriate alignmen and cast it to the default address space. Returns
+  /// the original alloca instruction by \p Alloca if it is not nullptr.
+  Address createMemTemp(QualType t, mlir::Location loc,
+                        const Twine &name = "tmp", Address *alloca = nullptr,
+                        mlir::OpBuilder::InsertPoint ip = {});
+  Address createMemTemp(QualType t, CharUnits align, mlir::Location loc,
+                        const Twine &name = "tmp", Address *alloca = nullptr,
+                        mlir::OpBuilder::InsertPoint ip = {});
 
   
//===--------------------------------------------------------------------===//
   //                         OpenACC Emission
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h 
b/clang/lib/CIR/CodeGen/CIRGenValue.h
index 1c453dc9c86b5..d30d5ab5b6a9e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -161,6 +161,7 @@ class LValue {
   clang::Qualifiers &getQuals() { return quals; }
 
   LValueBaseInfo getBaseInfo() const { return baseInfo; }
+  void setBaseInfo(LValueBaseInfo info) { baseInfo = info; }
 
   static LValue makeAddr(Address address, clang::QualType t,
                          LValueBaseInfo baseInfo) {
diff --git a/clang/test/CIR/CodeGen/binop.c b/clang/test/CIR/CodeGen/binop.c
new file mode 100644
index 0000000000000..280fd29b067f9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/binop.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+void conditionalResultIimplicitCast(int a, int b, float f) {
+  // Should implicit cast back to int.
+  int x = a && b;
+  // CHECK: %[[#INT:]] = cir.ternary
+  // CHECK: %{{.+}} = cir.cast(bool_to_int, %[[#INT]] : !cir.bool), !s32i
+  float y = f && f;
+  // CHECK: %[[#BOOL:]] = cir.ternary
+  // CHECK: %[[#INT:]] = cir.cast(bool_to_int, %[[#BOOL]] : !cir.bool), !s32i
+  // CHECK: %{{.+}} = cir.cast(int_to_float, %[[#INT]] : !s32i), !cir.float
+}
diff --git a/clang/test/CIR/CodeGen/binop.cpp b/clang/test/CIR/CodeGen/binop.cpp
index 086be8a2bba21..1df1e182f61cf 100644
--- a/clang/test/CIR/CodeGen/binop.cpp
+++ b/clang/test/CIR/CodeGen/binop.cpp
@@ -540,3 +540,228 @@ void long_shift_example(long long a, short b) {
 // OGCG:         store i64 %[[SHL]], ptr %[[X]]
 
 // OGCG:         ret void
+
+void b1(bool a, bool b) {
+  bool x = a && b;
+  x = x || b;
+}
+
+// CIR-LABEL: cir.func @_Z2b1bb(
+// CIR-SAME: %[[ARG0:.*]]: !cir.bool {{.*}}, %[[ARG1:.*]]: !cir.bool {{.*}})
+// CIR: [[A:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["a", init]
+// CIR: [[B:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b", init]
+// CIR: [[X:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init]
+// CIR: cir.store %[[ARG0]], [[A]] : !cir.bool, !cir.ptr<!cir.bool>
+// CIR: cir.store %[[ARG1]], [[B]] : !cir.bool, !cir.ptr<!cir.bool>
+// CIR: [[AVAL:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: [[RES1:%[0-9]+]] = cir.ternary([[AVAL]], true {
+// CIR: [[BVAL:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: [[INNER1:%[0-9]+]] = cir.ternary([[BVAL]], true {
+// CIR: {{%[0-9]+}} = cir.const #true
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }, false {
+// CIR: {{%[0-9]+}} = cir.const #false
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.yield [[INNER1]] : !cir.bool
+// CIR: }, false {
+// CIR: {{%[0-9]+}} = cir.const #false
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.store [[RES1]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
+// CIR: [[XVAL:%[0-9]+]] = cir.load [[X]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: [[RES2:%[0-9]+]] = cir.ternary([[XVAL]], true {
+// CIR: {{%[0-9]+}} = cir.const #true
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }, false {
+// CIR: [[BVAL2:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: [[INNER2:%[0-9]+]] = cir.ternary([[BVAL2]], true {
+// CIR: {{%[0-9]+}} = cir.const #true
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }, false {
+// CIR: {{%[0-9]+}} = cir.const #false
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.yield [[INNER2]] : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.store [[RES2]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
+// CIR: cir.return
+
+// LLVM-LABEL: define void @_Z2b1bb(
+// LLVM-SAME: i1 %[[ARG0:.*]], i1 %[[ARG1:.*]])
+// LLVM: %[[A_ADDR:.*]] = alloca i8
+// LLVM: %[[B_ADDR:.*]] = alloca i8
+// LLVM: %[[X:.*]] = alloca i8
+// LLVM: %[[ZEXT0:.*]] = zext i1 %[[ARG0]] to i8
+// LLVM: store i8 %[[ZEXT0]], ptr %[[A_ADDR]]
+// LLVM: %[[ZEXT1:.*]] = zext i1 %[[ARG1]] to i8
+// LLVM: store i8 %[[ZEXT1]], ptr %[[B_ADDR]]
+// LLVM: %[[A_VAL:.*]] = load i8, ptr %[[A_ADDR]]
+// LLVM: %[[A_BOOL:.*]] = trunc i8 %[[A_VAL]] to i1
+// LLVM: br i1 %[[A_BOOL]], label %[[AND_LHS_TRUE:.*]], label %[[AND_END:.*]]
+// LLVM: [[AND_LHS_TRUE]]:
+// LLVM: %[[B_VAL:.*]] = load i8, ptr %[[B_ADDR]]
+// LLVM: %[[B_BOOL:.*]] = trunc i8 %[[B_VAL]] to i1
+// LLVM: br i1 %[[B_BOOL]], label %[[B_TRUE:.*]], label %[[B_FALSE:.*]]
+// LLVM: [[B_TRUE]]:
+// LLVM: br label %[[AND_RHS_END:.*]]
+// LLVM: [[B_FALSE]]:
+// LLVM: br label %[[AND_RHS_END]]
+// LLVM: [[AND_RHS_END]]:
+// LLVM: %[[RHS_PHI:.*]] = phi i1 [ false, %[[B_FALSE]] ], [ true, %[[B_TRUE]] 
]
+// LLVM: br label %[[AND_DONE:.*]]
+// LLVM: [[AND_DONE]]:
+// LLVM: br label %[[MERGE_BB:.*]]
+// LLVM: [[AND_END]]:
+// LLVM: %[[AND_RESULT:.*]] = phi i1 [ false, %[[PRED1:.*]] ], [ %[[RHS_PHI]], 
%[[PRED2:.*]] ]
+// LLVM: br label %[[STORE_X:.*]]
+// LLVM: [[STORE_X]]:
+// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_RESULT]] to i8
+// LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]]
+// LLVM: %[[X_VAL:.*]] = load i8, ptr %[[X]]
+// LLVM: %[[X_BOOL:.*]] = trunc i8 %[[X_VAL]] to i1
+// LLVM: br i1 %[[X_BOOL]], label %[[OR_TRUE:.*]], label %[[OR_RHS:.*]]
+// LLVM: [[OR_TRUE]]:
+// LLVM: br label %[[OR_END:.*]]
+// LLVM: [[OR_RHS]]:
+// LLVM: %[[B_VAL2:.*]] = load i8, ptr %[[B_ADDR]]
+// LLVM: %[[B_BOOL2:.*]] = trunc i8 %[[B_VAL2]] to i1
+// LLVM: br i1 %[[B_BOOL2]], label %[[B_TRUE2:.*]], label %[[B_FALSE2:.*]]
+// LLVM: [[B_TRUE2]]:
+// LLVM: br label %[[OR_RHS_END:.*]]
+// LLVM: [[B_FALSE2]]:
+// LLVM: br label %[[OR_RHS_END]]
+// LLVM: [[OR_RHS_END]]:
+// LLVM: %[[RHS_PHI2:.*]] = phi i1 [ false, %[[B_FALSE2]] ], [ true, 
%[[B_TRUE2]] ]
+// LLVM: br label %[[OR_RHS_DONE:.*]]
+// LLVM: [[OR_RHS_DONE]]:
+// LLVM: br label %[[OR_END]]
+// LLVM: [[OR_END]]:
+// LLVM: %[[OR_RESULT:.*]] = phi i1 [ %[[RHS_PHI2]], %[[OR_RHS_DONE]] ], [ 
true, %[[OR_TRUE]] ]
+// LLVM: br label %[[FINAL_STORE:.*]]
+// LLVM: [[FINAL_STORE]]:
+// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_RESULT]] to i8
+// LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]]
+// LLVM: ret void
+
+
+void b3(int a, int b, int c, int d) {
+  bool x = (a == b) && (c == d);
+  x = (a == b) || (c == d);
+}
+
+// CIR-LABEL: cir.func @_Z2b3iiii(
+// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}, 
%[[ARG2:.*]]: !s32i {{.*}}, %[[ARG3:.*]]: !s32i {{.*}})
+// CIR: [[A:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
+// CIR: [[B:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
+// CIR: [[C:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["c", init]
+// CIR: [[D:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["d", init]
+// CIR: [[X:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init]
+// CIR: cir.store %[[ARG0]], [[A]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.store %[[ARG1]], [[B]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.store %[[ARG2]], [[C]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.store %[[ARG3]], [[D]] : !s32i, !cir.ptr<!s32i>
+// CIR: [[AVAL1:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[BVAL1:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[CMP1:%[0-9]+]] = cir.cmp(eq, [[AVAL1]], [[BVAL1]]) : !s32i, !cir.bool
+// CIR: [[AND_RESULT:%[0-9]+]] = cir.ternary([[CMP1]], true {
+// CIR: [[CVAL1:%[0-9]+]] = cir.load [[C]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[DVAL1:%[0-9]+]] = cir.load [[D]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[CMP2:%[0-9]+]] = cir.cmp(eq, [[CVAL1]], [[DVAL1]]) : !s32i, !cir.bool
+// CIR: [[INNER1:%[0-9]+]] = cir.ternary([[CMP2]], true {
+// CIR: {{%[0-9]+}} = cir.const #true
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }, false {
+// CIR: {{%[0-9]+}} = cir.const #false
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.yield [[INNER1]] : !cir.bool
+// CIR: }, false {
+// CIR: {{%[0-9]+}} = cir.const #false
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.store [[AND_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
+// CIR: [[AVAL2:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[BVAL2:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[CMP3:%[0-9]+]] = cir.cmp(eq, [[AVAL2]], [[BVAL2]]) : !s32i, !cir.bool
+// CIR: [[OR_RESULT:%[0-9]+]] = cir.ternary([[CMP3]], true {
+// CIR: {{%[0-9]+}} = cir.const #true
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }, false {
+// CIR: [[CVAL2:%[0-9]+]] = cir.load [[C]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[DVAL2:%[0-9]+]] = cir.load [[D]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[CMP4:%[0-9]+]] = cir.cmp(eq, [[CVAL2]], [[DVAL2]]) : !s32i, !cir.bool
+// CIR: [[INNER2:%[0-9]+]] = cir.ternary([[CMP4]], true {
+// CIR: {{%[0-9]+}} = cir.const #true
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }, false {
+// CIR: {{%[0-9]+}} = cir.const #false
+// CIR: cir.yield {{%[0-9]+}} : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.yield [[INNER2]] : !cir.bool
+// CIR: }) : (!cir.bool) -> !cir.bool
+// CIR: cir.store [[OR_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
+// CIR: cir.return
+
+// LLVM-LABEL: define void @_Z2b3iiii(
+// LLVM-SAME: i32 %[[ARG0:.*]], i32 %[[ARG1:.*]], i32 %[[ARG2:.*]], i32 
%[[ARG3:.*]])
+// LLVM: %[[A_ADDR:.*]] = alloca i32
+// LLVM: %[[B_ADDR:.*]] = alloca i32
+// LLVM: %[[C_ADDR:.*]] = alloca i32
+// LLVM: %[[D_ADDR:.*]] = alloca i32
+// LLVM: %[[X:.*]] = alloca i8
+// LLVM: store i32 %[[ARG0]], ptr %[[A_ADDR]]
+// LLVM: store i32 %[[ARG1]], ptr %[[B_ADDR]]
+// LLVM: store i32 %[[ARG2]], ptr %[[C_ADDR]]
+// LLVM: store i32 %[[ARG3]], ptr %[[D_ADDR]]
+// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]]
+// LLVM: %[[B_VAL:.*]] = load i32, ptr %[[B_ADDR]]
+// LLVM: %[[CMP1:.*]] = icmp eq i32 %[[A_VAL]], %[[B_VAL]]
+// LLVM: br i1 %[[CMP1]], label %[[AND_LHS_TRUE:.*]], label %[[AND_END:.*]]
+// LLVM: [[AND_LHS_TRUE]]:
+// LLVM: %[[C_VAL:.*]] = load i32, ptr %[[C_ADDR]]
+// LLVM: %[[D_VAL:.*]] = load i32, ptr %[[D_ADDR]]
+// LLVM: %[[CMP2:.*]] = icmp eq i32 %[[C_VAL]], %[[D_VAL]]
+// LLVM: br i1 %[[CMP2]], label %[[CD_TRUE:.*]], label %[[CD_FALSE:.*]]
+// LLVM: [[CD_TRUE]]:
+// LLVM: br label %[[AND_RHS_END:.*]]
+// LLVM: [[CD_FALSE]]:
+// LLVM: br label %[[AND_RHS_END]]
+// LLVM: [[AND_RHS_END]]:
+// LLVM: %[[RHS_PHI:.*]] = phi i1 [ false, %[[CD_FALSE]] ], [ true, 
%[[CD_TRUE]] ]
+// LLVM: br label %[[AND_DONE:.*]]
+// LLVM: [[AND_DONE]]:
+// LLVM: br label %[[MERGE_BB2:.*]]
+// LLVM: [[AND_END]]:
+// LLVM: %[[AND_RESULT:.*]] = phi i1 [ false, %[[AND_END]] ], [ %[[RHS_PHI]], 
%[[AND_DONE]] ]
+// LLVM: br label %[[STORE_X:.*]]
+// LLVM: [[STORE_X]]:
+// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_RESULT]] to i8
+// LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]]
+// LLVM: %[[A_VAL2:.*]] = load i32, ptr %[[A_ADDR]]
+// LLVM: %[[B_VAL2:.*]] = load i32, ptr %[[B_ADDR]]
+// LLVM: %[[CMP3:.*]] = icmp eq i32 %[[A_VAL2]], %[[B_VAL2]]
+// LLVM: br i1 %[[CMP3]], label %[[OR_TRUE:.*]], label %[[OR_RHS:.*]]
+// LLVM: [[OR_TRUE]]:
+// LLVM: br label %[[OR_END:.*]]
+// LLVM: [[OR_RHS]]:
+// LLVM: %[[C_VAL2:.*]] = load i32, ptr %[[C_ADDR]]
+// LLVM: %[[D_VAL2:.*]] = load i32, ptr %[[D_ADDR]]
+// LLVM: %[[CMP4:.*]] = icmp eq i32 %[[C_VAL2]], %[[D_VAL2]]
+// LLVM: br i1 %[[CMP4]], label %[[CD_TRUE2:.*]], label %[[CD_FALSE2:.*]]
+// LLVM: [[CD_TRUE2]]:
+// LLVM: br label %[[OR_RHS_END:.*]]
+// LLVM: [[CD_FALSE2]]:
+// LLVM: br label %[[OR_RHS_END]]
+// LLVM: [[OR_RHS_END]]:
+// LLVM: %[[RHS_PHI2:.*]] = phi i1 [ false, %[[CD_FALSE2]] ], [ true, 
%[[CD_TRUE2]] ]
+// LLVM: br label %[[OR_RHS_DONE:.*]]
+// LLVM: [[OR_RHS_DONE]]:
+// LLVM: br label %[[OR_END]]
+// LLVM: [[OR_END]]:
+// LLVM: %[[OR_RESULT:.*]] = phi i1 [ %[[RHS_PHI2]], %[[OR_RHS_DONE]] ], [ 
true, %[[OR_TRUE]] ]
+// LLVM: br label %[[FINAL_STORE:.*]]
+// LLVM: [[FINAL_STORE]]:
+// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_RESULT]] to i8
+// LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]]
+// LLVM: ret void
diff --git a/clang/test/CIR/CodeGen/ternary.cpp 
b/clang/test/CIR/CodeGen/ternary.cpp
new file mode 100644
index 0000000000000..28aaa2d4a85d1
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ternary.cpp
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+
+int x(int y) {
+  return y > 0 ? 3 : 5;
+}
+
+// CIR-LABEL: cir.func @_Z1xi(
+// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}) -> !s32i {
+// CIR: [[Y:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["y", init]
+// CIR: [[RETVAL:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR: cir.store %[[ARG0]], [[Y]] : !s32i, !cir.ptr<!s32i>
+// CIR: [[YVAL:%[0-9]+]] = cir.load [[Y]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[ZERO:%[0-9]+]] = cir.const #cir.int<0> : !s32i
+// CIR: [[CMP:%[0-9]+]] = cir.cmp(gt, [[YVAL]], [[ZERO]]) : !s32i, !cir.bool
+// CIR: [[TERNARY_RES:%[0-9]+]] = cir.ternary([[CMP]], true {
+// CIR: [[THREE:%[0-9]+]] = cir.const #cir.int<3> : !s32i
+// CIR: cir.yield [[THREE]] : !s32i
+// CIR: }, false {
+// CIR: [[FIVE:%[0-9]+]] = cir.const #cir.int<5> : !s32i
+// CIR: cir.yield [[FIVE]] : !s32i
+// CIR: }) : (!cir.bool) -> !s32i
+// CIR: cir.store [[TERNARY_RES]], [[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR: [[RETVAL_VAL:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.return [[RETVAL_VAL]] : !s32i
+
+// LLVM-LABEL: define i32 @_Z1xi(
+// LLVM-SAME: i32 %[[ARG0:.*]])
+// LLVM: %[[Y:.*]] = alloca i32
+// LLVM: %[[RETVAL:.*]] = alloca i32
+// LLVM: store i32 %[[ARG0]], ptr %[[Y]]
+// LLVM: %[[YVAL:.*]] = load i32, ptr %[[Y]]
+// LLVM: %[[CMP:.*]] = icmp sgt i32 %[[YVAL]], 0
+// LLVM: br i1 %[[CMP]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
+// LLVM: [[TRUE_BB]]:
+// LLVM: br label %[[MERGE_BB:.*]]
+// LLVM: [[FALSE_BB]]:
+// LLVM: br label %[[MERGE_BB]]
+// LLVM: [[MERGE_BB]]:
+// LLVM: %[[PHI:.*]] = phi i32 [ 5, %[[FALSE_BB]] ], [ 3, %[[TRUE_BB]] ]
+// LLVM: br label %[[FINAL_BB:.*]]
+// LLVM: [[FINAL_BB]]:
+// LLVM: store i32 %[[PHI]], ptr %[[RETVAL]]
+// LLVM: %[[RESULT:.*]] = load i32, ptr %[[RETVAL]]
+// LLVM: ret i32 %[[RESULT]]
+
+int foo(int a, int b) {
+  if (a < b ? 0 : a)
+    return -1;
+  return 0;
+}
+
+// CIR-LABEL: cir.func @_Z3fooii(
+// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}) -> !s32i {
+// CIR: [[A:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
+// CIR: [[B:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
+// CIR: [[RETVAL:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR: cir.store %[[ARG0]], [[A]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.store %[[ARG1]], [[B]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.scope {
+// CIR: [[ALOAD:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[BLOAD:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i
+// CIR: [[CMP:%[0-9]+]] = cir.cmp(lt, [[ALOAD]], [[BLOAD]]) : !s32i, !cir.bool
+// CIR: [[TERNARY_RES:%[0-9]+]] = cir.ternary([[CMP]], true {
+// CIR: [[ZERO:%[0-9]+]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.yield [[ZERO]] : !s32i
+// CIR: }, false {
+// CIR: [[ALOAD2:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.yield [[ALOAD2]] : !s32i
+// CIR: }) : (!cir.bool) -> !s32i
+// CIR: [[CAST:%[0-9]+]] = cir.cast(int_to_bool, [[TERNARY_RES]] : !s32i), 
!cir.bool
+// CIR: cir.if [[CAST]] {
+// CIR: [[ONE:%[0-9]+]] = cir.const #cir.int<1> : !s32i
+// CIR: [[MINUS_ONE:%[0-9]+]] = cir.unary(minus, [[ONE]]) nsw : !s32i, !s32i
+// CIR: cir.store [[MINUS_ONE]], [[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR: [[RETVAL_VAL:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.return [[RETVAL_VAL]] : !s32i
+// CIR: }
+// CIR: }
+// CIR: [[ZERO2:%[0-9]+]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.store [[ZERO2]], [[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR: [[RETVAL_VAL2:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.return [[RETVAL_VAL2]] : !s32i
+
+// LLVM-LABEL: define i32 @_Z3fooii(
+// LLVM-SAME: i32 %[[ARG0:.*]], i32 %[[ARG1:.*]])
+// LLVM: %[[A:.*]] = alloca i32
+// LLVM: %[[B:.*]] = alloca i32
+// LLVM: %[[RETVAL:.*]] = alloca i32
+// LLVM: store i32 %[[ARG0]], ptr %[[A]]
+// LLVM: store i32 %[[ARG1]], ptr %[[B]]
+// LLVM: br label %[[ENTRY_BB:.*]]
+// LLVM: [[ENTRY_BB]]:
+// LLVM: %[[AVAL:.*]] = load i32, ptr %[[A]]
+// LLVM: %[[BVAL:.*]] = load i32, ptr %[[B]]
+// LLVM: %[[CMP:.*]] = icmp slt i32 %[[AVAL]], %[[BVAL]]
+// LLVM: br i1 %[[CMP]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
+// LLVM: [[TRUE_BB]]:
+// LLVM: br label %[[MERGE_BB:.*]]
+// LLVM: [[FALSE_BB]]:
+// LLVM: %[[AVAL2:.*]] = load i32, ptr %[[A]]
+// LLVM: br label %[[MERGE_BB]]
+// LLVM: [[MERGE_BB]]:
+// LLVM: %[[PHI:.*]] = phi i32 [ %[[AVAL2]], %[[FALSE_BB]] ], [ 0, 
%[[TRUE_BB]] ]
+// LLVM: br label %[[CHECK_BB:.*]]
+// LLVM: [[CHECK_BB]]:
+// LLVM: %[[COND:.*]] = icmp ne i32 %[[PHI]], 0
+// LLVM: br i1 %[[COND]], label %[[RETURN_MINUS_ONE:.*]], label %[[CONT_BB:.*]]
+// LLVM: [[RETURN_MINUS_ONE]]:
+// LLVM: store i32 -1, ptr %[[RETVAL]]
+// LLVM: %[[RET1:.*]] = load i32, ptr %[[RETVAL]]
+// LLVM: ret i32 %[[RET1]]
+// LLVM: [[CONT_BB]]:
+// LLVM: br label %[[RETURN_ZERO:.*]]
+// LLVM: [[RETURN_ZERO]]:
+// LLVM: store i32 0, ptr %[[RETVAL]]
+// LLVM: %[[RET2:.*]] = load i32, ptr %[[RETVAL]]
+// LLVM: ret i32 %[[RET2]]

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to