Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2394,6 +2394,180 @@ LValue CIRGenFunction::emitPredefinedLValue(const PredefinedExpr *e) {
return emitStringLiteralLValue(sl, gvName);
}

LValue CIRGenFunction::emitOpaqueValueLValue(const OpaqueValueExpr *e) {
assert(OpaqueValueMappingData::shouldBindAsLValue(e));
return getOrCreateOpaqueLValueMapping(e);
}

namespace {
// 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.
std::optional<LValue> handleConditionalOperatorLValueSimpleCase(
CIRGenFunction &cgf, const AbstractConditionalOperator *e) {
const Expr *condExpr = e->getCond();
llvm::APSInt condExprVal;
if (!cgf.constantFoldsToSimpleInteger(condExpr, condExprVal))
return std::nullopt;

const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr();
if (!condExprVal.getBoolValue())
std::swap(live, dead);

if (cgf.containsLabel(dead))
return std::nullopt;

// If the true case is live, we need to track its region.
assert(!cir::MissingFeatures::incrementProfileCounter());
assert(!cir::MissingFeatures::pgoUse());
// If a throw expression we emit it and return an undefined lvalue
// because it can't be used.
if (auto *throwExpr = dyn_cast<CXXThrowExpr>(live->IgnoreParens())) {
cgf.emitCXXThrowExpr(throwExpr);
// Return an undefined lvalue - the throw terminates execution
// so this value will never actually be used
mlir::Type elemTy = cgf.convertType(dead->getType());
mlir::Value undefPtr =
cgf.getBuilder().getNullPtr(cgf.getBuilder().getPointerTo(elemTy),
cgf.getLoc(throwExpr->getSourceRange()));
return cgf.makeAddrLValue(Address(undefPtr, elemTy, CharUnits::One()),
dead->getType());
}
return cgf.emitLValue(live);
}

/// 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 (auto *throwExpr = dyn_cast<CXXThrowExpr>(operand->IgnoreParens())) {
cgf.emitCXXThrowExpr(throwExpr);
return std::nullopt;
}

return cgf.emitLValue(operand);
}
} // namespace

// 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;
ConditionalEvaluation eval(*this);
mlir::Location loc = getLoc(e->getSourceRange());
CIRGenBuilderTy &builder = getBuilder();

mlir::Value condV = emitOpOnBoolExpr(loc, e->getCond());
SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{};
mlir::Type yieldTy{};

auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc,
const Expr *expr, std::optional<LValue> &resultLV) {
CIRGenFunction::LexicalScope lexScope{*this, loc, b.getInsertionBlock()};
curLexScope->setAsTernary();

assert(!cir::MissingFeatures::incrementProfileCounter());
eval.beginEvaluation();
resultLV = branchGenFunc(*this, expr);
mlir::Value resultPtr = resultLV ? resultLV->getPointer() : mlir::Value();
eval.endEvaluation();

if (resultPtr) {
yieldTy = resultPtr.getType();
cir::YieldOp::create(b, loc, resultPtr);
} else {
// If LHS or RHS is a void expression we need
// to patch arms as to properly match yield types.
// If the current block's terminator is an UnreachableOp (from a throw),
// we don't need a yield
if (builder.getInsertionBlock()->mightHaveTerminator()) {
mlir::Operation *terminator =
builder.getInsertionBlock()->getTerminator();
if (isa_and_nonnull<cir::UnreachableOp>(terminator))
insertPoints.push_back(b.saveInsertionPoint());
}
}
};

info.result = cir::TernaryOp::create(
builder, loc, condV,
/*trueBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
emitBranch(b, loc, e->getTrueExpr(), info.lhs);
},
/*falseBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
emitBranch(b, loc, e->getFalseExpr(), info.rhs);
})
.getResult();

// If both arms are void, so be it.
if (!yieldTy)
yieldTy = 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 (!yieldTy) {
cir::YieldOp::create(builder, loc);
} else { // Block returns: set null yield value.
mlir::Value op0 = builder.getNullValue(yieldTy, loc);
cir::YieldOp::create(builder, 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 with non-simple lvalue");
return LValue();
}

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;
}

/// An LValue is a candidate for having its loads and stores be made atomic if
/// we are operating under /volatile:ms *and* the LValue itself is volatile and
/// performing such an operation can be performed without a libcall.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,10 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
std::string("l-value not implemented for '") +
e->getStmtClassName() + "'");
return LValue();
case Expr::ConditionalOperatorClass:
return emitConditionalOperatorLValue(cast<ConditionalOperator>(e));
case Expr::BinaryConditionalOperatorClass:
return emitConditionalOperatorLValue(cast<BinaryConditionalOperator>(e));
case Expr::ArraySubscriptExprClass:
return emitArraySubscriptExpr(cast<ArraySubscriptExpr>(e));
case Expr::UnaryOperatorClass:
Expand Down Expand Up @@ -866,6 +870,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
return emitCastLValue(cast<CastExpr>(e));
case Expr::MaterializeTemporaryExprClass:
return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
case Expr::OpaqueValueExprClass:
return emitOpaqueValueLValue(cast<OpaqueValueExpr>(e));
case Expr::ChooseExprClass:
return emitLValue(cast<ChooseExpr>(e)->getChosenSubExpr());
}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,10 @@ class CIRGenFunction : public CIRGenTypeCache {

LValue emitMemberExpr(const MemberExpr *e);

LValue emitOpaqueValueLValue(const OpaqueValueExpr *e);

LValue emitConditionalOperatorLValue(const AbstractConditionalOperator *expr);

/// Given an expression with a pointer type, emit the value and compute our
/// best estimate of the alignment of the pointee.
///
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1978,13 +1978,19 @@ void cir::TernaryOp::build(
result.addOperands(cond);
OpBuilder::InsertionGuard guard(builder);
Region *trueRegion = result.addRegion();
Block *block = builder.createBlock(trueRegion);
builder.createBlock(trueRegion);
trueBuilder(builder, result.location);
Region *falseRegion = result.addRegion();
builder.createBlock(falseRegion);
falseBuilder(builder, result.location);

auto yield = dyn_cast<YieldOp>(block->getTerminator());
// Get result type from whichever branch has a yield (the other may have
// unreachable from a throw expression)
auto yield =
dyn_cast_or_null<cir::YieldOp>(trueRegion->back().getTerminator());
if (!yield)
yield = dyn_cast_or_null<cir::YieldOp>(falseRegion->back().getTerminator());

assert((yield && yield.getNumOperands() <= 1) &&
"expected zero or one result type");
if (yield.getNumOperands() == 1)
Expand Down
31 changes: 25 additions & 6 deletions clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,19 @@ class CIRTernaryOpFlattening : public mlir::OpRewritePattern<cir::TernaryOp> {
Block *trueBlock = &trueRegion.front();
mlir::Operation *trueTerminator = trueRegion.back().getTerminator();
rewriter.setInsertionPointToEnd(&trueRegion.back());
auto trueYieldOp = dyn_cast<cir::YieldOp>(trueTerminator);

rewriter.replaceOpWithNewOp<cir::BrOp>(trueYieldOp, trueYieldOp.getArgs(),
continueBlock);
// Handle both yield and unreachable terminators (throw expressions)
if (auto trueYieldOp = dyn_cast<cir::YieldOp>(trueTerminator)) {
rewriter.replaceOpWithNewOp<cir::BrOp>(trueYieldOp, trueYieldOp.getArgs(),
continueBlock);
} else if (isa<cir::UnreachableOp>(trueTerminator)) {
// Terminator is unreachable (e.g., from throw), just keep it
} else {
trueTerminator->emitError("unexpected terminator in ternary true region, "
"expected yield or unreachable, got: ")
<< trueTerminator->getName();
return mlir::failure();
}
rewriter.inlineRegionBefore(trueRegion, continueBlock);

Block *falseBlock = continueBlock;
Expand All @@ -517,9 +526,19 @@ class CIRTernaryOpFlattening : public mlir::OpRewritePattern<cir::TernaryOp> {
falseBlock = &falseRegion.front();
mlir::Operation *falseTerminator = falseRegion.back().getTerminator();
rewriter.setInsertionPointToEnd(&falseRegion.back());
auto falseYieldOp = dyn_cast<cir::YieldOp>(falseTerminator);
rewriter.replaceOpWithNewOp<cir::BrOp>(falseYieldOp, falseYieldOp.getArgs(),
continueBlock);

// Handle both yield and unreachable terminators (throw expressions)
if (auto falseYieldOp = dyn_cast<cir::YieldOp>(falseTerminator)) {
rewriter.replaceOpWithNewOp<cir::BrOp>(
falseYieldOp, falseYieldOp.getArgs(), continueBlock);
} else if (isa<cir::UnreachableOp>(falseTerminator)) {
// Terminator is unreachable (e.g., from throw), just keep it
} else {
falseTerminator->emitError("unexpected terminator in ternary false "
"region, expected yield or unreachable, got: ")
<< falseTerminator->getName();
return mlir::failure();
}
rewriter.inlineRegionBefore(falseRegion, continueBlock);

rewriter.setInsertionPointToEnd(condBlock);
Expand Down
Loading
Loading