Skip to content

Commit a91edc3

Browse files
[CIR] Add if statement support
Upstream if statement support Formatted code and added test cases. added multiple RUN lines for the codegen test
1 parent 96f95c9 commit a91edc3

File tree

13 files changed

+926
-9
lines changed

13 files changed

+926
-9
lines changed

clang/include/clang/CIR/Dialect/IR/CIRDialect.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ class SameFirstOperandAndResultType
6060
using BuilderCallbackRef =
6161
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>;
6262

63+
namespace cir {
64+
void buildTerminatedBody(mlir::OpBuilder &builder, mlir::Location loc);
65+
} // namespace cir
66+
6367
// TableGen'erated files for MLIR dialects require that a macro be defined when
6468
// they are included. GET_OP_CLASSES tells the file to define the classes for
6569
// the operations of that dialect.

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -472,8 +472,8 @@ def StoreOp : CIR_Op<"store", [
472472
// ReturnOp
473473
//===----------------------------------------------------------------------===//
474474

475-
def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "DoWhileOp",
476-
"WhileOp", "ForOp"]>,
475+
def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "IfOp",
476+
"DoWhileOp", "WhileOp", "ForOp"]>,
477477
Terminator]> {
478478
let summary = "Return from function";
479479
let description = [{
@@ -510,6 +510,58 @@ def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "DoWhileOp",
510510
let hasVerifier = 1;
511511
}
512512

513+
//===----------------------------------------------------------------------===//
514+
// IfOp
515+
//===----------------------------------------------------------------------===//
516+
517+
def IfOp : CIR_Op<"if",
518+
[DeclareOpInterfaceMethods<RegionBranchOpInterface>,
519+
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]>{
520+
521+
let summary = "the if-then-else operation";
522+
let description = [{
523+
The `cir.if` operation represents an if-then-else construct for
524+
conditionally executing two regions of code. The operand is a `cir.bool`
525+
type.
526+
527+
Examples:
528+
529+
```mlir
530+
cir.if %b {
531+
...
532+
} else {
533+
...
534+
}
535+
536+
cir.if %c {
537+
...
538+
}
539+
540+
cir.if %c {
541+
...
542+
cir.br ^a
543+
^a:
544+
cir.yield
545+
}
546+
```
547+
548+
`cir.if` defines no values and the 'else' can be omitted. The if/else
549+
regions must be terminated. If the region has only one block, the terminator
550+
can be left out, and `cir.yield` terminator will be inserted implictly.
551+
Otherwise, the region must be explicitly terminated.
552+
}];
553+
let arguments = (ins CIR_BoolType:$condition);
554+
let regions = (region AnyRegion:$thenRegion, AnyRegion:$elseRegion);
555+
let hasCustomAssemblyFormat=1;
556+
let hasVerifier=1;
557+
let skipDefaultBuilders=1;
558+
let builders = [
559+
OpBuilder<(ins "mlir::Value":$cond, "bool":$withElseRegion,
560+
CArg<"BuilderCallbackRef", "buildTerminatedBody">:$thenBuilder,
561+
CArg<"BuilderCallbackRef", "nullptr">:$elseBuilder)>
562+
];
563+
}
564+
513565
//===----------------------------------------------------------------------===//
514566
// ConditionOp
515567
//===----------------------------------------------------------------------===//
@@ -560,8 +612,8 @@ def ConditionOp : CIR_Op<"condition", [
560612
//===----------------------------------------------------------------------===//
561613

562614
def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
563-
ParentOneOf<["ScopeOp", "WhileOp", "ForOp",
564-
"DoWhileOp"]>]> {
615+
ParentOneOf<["IfOp", "ScopeOp", "WhileOp",
616+
"ForOp", "DoWhileOp"]>]> {
565617
let summary = "Represents the default branching behaviour of a region";
566618
let description = [{
567619
The `cir.yield` operation terminates regions on different CIR operations,

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct MissingFeatures {
8080

8181
// Clang early optimizations or things defered to LLVM lowering.
8282
static bool mayHaveIntegerOverflow() { return false; }
83+
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
8384

8485
// Misc
8586
static bool cxxABI() { return false; }
@@ -110,6 +111,9 @@ struct MissingFeatures {
110111
static bool lvalueBaseInfo() { return false; }
111112
static bool alignCXXRecordDecl() { return false; }
112113
static bool setNonGC() { return false; }
114+
static bool constantFoldsToSimpleInteger() { return false; }
115+
static bool incrementProfileCounter() { return false; }
116+
static bool insertBuiltinUnpredictable() { return false; }
113117

114118
// Missing types
115119
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,106 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
474474
emitLValue(e);
475475
}
476476

477+
/// Emit an `if` on a boolean condition, filling `then` and `else` into
478+
/// appropriated regions.
479+
mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond,
480+
const Stmt *thenS,
481+
const Stmt *elseS) {
482+
// Attempt to be more accurate as possible with IfOp location, generate
483+
// one fused location that has either 2 or 4 total locations, depending
484+
// on else's availability.
485+
auto getStmtLoc = [this](const Stmt &s) {
486+
return mlir::FusedLoc::get(&getMLIRContext(),
487+
{getLoc(s.getSourceRange().getBegin()),
488+
getLoc(s.getSourceRange().getEnd())});
489+
};
490+
mlir::Location thenLoc = getStmtLoc(*thenS);
491+
std::optional<mlir::Location> elseLoc;
492+
if (elseS)
493+
elseLoc = getStmtLoc(*elseS);
494+
495+
mlir::LogicalResult resThen = mlir::success(), resElse = mlir::success();
496+
emitIfOnBoolExpr(
497+
cond, /*thenBuilder=*/
498+
[&](mlir::OpBuilder &, mlir::Location) {
499+
LexicalScope lexScope{*this, thenLoc, builder.getInsertionBlock()};
500+
resThen = emitStmt(thenS, /*useCurrentScope=*/true);
501+
},
502+
thenLoc,
503+
/*elseBuilder=*/
504+
[&](mlir::OpBuilder &, mlir::Location) {
505+
assert(elseLoc && "Invalid location for elseS.");
506+
LexicalScope lexScope{*this, *elseLoc, builder.getInsertionBlock()};
507+
resElse = emitStmt(elseS, /*useCurrentScope=*/true);
508+
},
509+
elseLoc);
510+
511+
return mlir::LogicalResult::success(resThen.succeeded() &&
512+
resElse.succeeded());
513+
}
514+
515+
/// Emit an `if` on a boolean condition, filling `then` and `else` into
516+
/// appropriated regions.
517+
cir::IfOp CIRGenFunction::emitIfOnBoolExpr(
518+
const clang::Expr *cond, BuilderCallbackRef thenBuilder,
519+
mlir::Location thenLoc, BuilderCallbackRef elseBuilder,
520+
std::optional<mlir::Location> elseLoc) {
521+
522+
SmallVector<mlir::Location, 2> ifLocs{thenLoc};
523+
if (elseLoc)
524+
ifLocs.push_back(*elseLoc);
525+
mlir::Location loc = mlir::FusedLoc::get(&getMLIRContext(), ifLocs);
526+
527+
// Emit the code with the fully general case.
528+
mlir::Value condV = emitOpOnBoolExpr(loc, cond);
529+
return builder.create<cir::IfOp>(loc, condV, elseLoc.has_value(),
530+
/*thenBuilder=*/thenBuilder,
531+
/*elseBuilder=*/elseBuilder);
532+
}
533+
534+
/// TODO(cir): PGO data
535+
/// TODO(cir): see EmitBranchOnBoolExpr for extra ideas).
536+
mlir::Value CIRGenFunction::emitOpOnBoolExpr(mlir::Location loc,
537+
const Expr *cond) {
538+
// TODO(CIR): scoped ApplyDebugLocation DL(*this, Cond);
539+
// TODO(CIR): __builtin_unpredictable and profile counts?
540+
cond = cond->IgnoreParens();
541+
542+
// if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(cond)) {
543+
// llvm_unreachable("binaryoperator ifstmt NYI");
544+
// }
545+
546+
if (const UnaryOperator *CondUOp = dyn_cast<UnaryOperator>(cond)) {
547+
// In LLVM the condition is reversed here for efficient codegen.
548+
// This should be done in CIR prior to LLVM lowering, if we do now
549+
// we can make CIR based diagnostics misleading.
550+
// cir.ternary(!x, t, f) -> cir.ternary(x, f, t)
551+
assert(!cir::MissingFeatures::shouldReverseUnaryCondOnBoolExpr());
552+
}
553+
554+
if (const ConditionalOperator *CondOp = dyn_cast<ConditionalOperator>(cond)) {
555+
556+
cgm.errorNYI(cond->getExprLoc(), "Ternary NYI");
557+
assert(!cir::MissingFeatures::ternaryOp());
558+
return createDummyValue(loc, cond->getType());
559+
}
560+
561+
// if (const CXXThrowExpr *Throw = dyn_cast<CXXThrowExpr>(cond)) {
562+
// llvm_unreachable("NYI");
563+
// }
564+
565+
// If the branch has a condition wrapped by __builtin_unpredictable,
566+
// create metadata that specifies that the branch is unpredictable.
567+
// Don't bother if not optimizing because that metadata would not be used.
568+
auto *Call = dyn_cast<CallExpr>(cond->IgnoreImpCasts());
569+
if (Call && cgm.getCodeGenOpts().OptimizationLevel != 0) {
570+
assert(!cir::MissingFeatures::insertBuiltinUnpredictable());
571+
}
572+
573+
// Emit the code with the fully general case.
574+
return evaluateExprAsBool(cond);
575+
}
576+
477577
mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
478578
mlir::Location loc, CharUnits alignment,
479579
bool insertIntoFnEntryBlock,

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,20 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *e) {
14841484
return {};
14851485
}
14861486

1487+
/// If the specified expression does not fold
1488+
/// to a constant, or if it does but contains a label, return false. If it
1489+
/// constant folds return true and set the boolean result in Result.
1490+
bool CIRGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
1491+
bool &ResultBool,
1492+
bool AllowLabels) {
1493+
llvm::APSInt ResultInt;
1494+
if (!ConstantFoldsToSimpleInteger(Cond, ResultInt, AllowLabels))
1495+
return false;
1496+
1497+
ResultBool = ResultInt.getBoolValue();
1498+
return true;
1499+
}
1500+
14871501
/// Return the size or alignment of the type of argument of the sizeof
14881502
/// expression as an integer.
14891503
mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,55 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
135135
return mlir::FusedLoc::get(locs, metadata, &getMLIRContext());
136136
}
137137

138+
bool CIRGenFunction::ContainsLabel(const Stmt *s, bool ignoreCaseStmts) {
139+
// Null statement, not a label!
140+
if (!s)
141+
return false;
142+
143+
// If this is a label, we have to emit the code, consider something like:
144+
// if (0) { ... foo: bar(); } goto foo;
145+
//
146+
// TODO: If anyone cared, we could track __label__'s, since we know that you
147+
// can't jump to one from outside their declared region.
148+
if (isa<LabelStmt>(s))
149+
return true;
150+
151+
// If this is a case/default statement, and we haven't seen a switch, we
152+
// have to emit the code.
153+
if (isa<SwitchCase>(s) && !ignoreCaseStmts)
154+
return true;
155+
156+
// If this is a switch statement, we want to ignore cases below it.
157+
if (isa<SwitchStmt>(s))
158+
ignoreCaseStmts = true;
159+
160+
// Scan subexpressions for verboten labels.
161+
return std::any_of(s->child_begin(), s->child_end(),
162+
[=](const Stmt *subStmt) {
163+
return ContainsLabel(subStmt, ignoreCaseStmts);
164+
});
165+
}
166+
167+
/// If the specified expression does not fold
168+
/// to a constant, or if it does but contains a label, return false. If it
169+
/// constant folds return true and set the folded value.
170+
bool CIRGenFunction::ConstantFoldsToSimpleInteger(const Expr *cond,
171+
llvm::APSInt &resultInt,
172+
bool allowLabels) {
173+
// FIXME: Rename and handle conversion of other evaluatable things
174+
// to bool.
175+
Expr::EvalResult result;
176+
if (!cond->EvaluateAsInt(result, getContext()))
177+
return false; // Not foldable, not integer or not fully evaluatable.
178+
179+
llvm::APSInt intValue = result.Val.getInt();
180+
if (!allowLabels && ContainsLabel(cond))
181+
return false; // Contains a label.
182+
183+
resultInt = intValue;
184+
return true;
185+
}
186+
138187
void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc,
139188
CharUnits alignment) {
140189
if (!type->isVoidType()) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "clang/AST/ASTContext.h"
2525
#include "clang/AST/CharUnits.h"
2626
#include "clang/AST/Decl.h"
27+
#include "clang/AST/Stmt.h"
2728
#include "clang/AST/Type.h"
2829
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2930
#include "clang/CIR/MissingFeatures.h"
@@ -166,6 +167,20 @@ class CIRGenFunction : public CIRGenTypeCache {
166167
/// that it requires no code to be generated.
167168
bool isTrivialInitializer(const Expr *init);
168169

170+
/// If the specified expression does not fold to a constant, or if it does but
171+
/// contains a label, return false. If it constant folds return true and set
172+
/// the boolean result in Result.
173+
bool ConstantFoldsToSimpleInteger(const clang::Expr *Cond, bool &ResultBool,
174+
bool AllowLabels = false);
175+
bool ConstantFoldsToSimpleInteger(const clang::Expr *Cond,
176+
llvm::APSInt &ResultInt,
177+
bool AllowLabels = false);
178+
179+
/// Return true if the statement contains a label in it. If
180+
/// this statement is not executed normally, it not containing a label means
181+
/// that we can just remove the code.
182+
bool ContainsLabel(const clang::Stmt *s, bool IgnoreCaseStmts = false);
183+
169184
struct AutoVarEmission {
170185
const clang::VarDecl *Variable;
171186
/// The address of the alloca for languages with explicit address space
@@ -459,6 +474,25 @@ class CIRGenFunction : public CIRGenTypeCache {
459474
mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
460475
LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
461476

477+
/// Emit an if on a boolean condition to the specified blocks.
478+
/// FIXME: Based on the condition, this might try to simplify the codegen of
479+
/// the conditional based on the branch. TrueCount should be the number of
480+
/// times we expect the condition to evaluate to true based on PGO data. We
481+
/// might decide to leave this as a separate pass (see EmitBranchOnBoolExpr
482+
/// for extra ideas).
483+
mlir::LogicalResult emitIfOnBoolExpr(const clang::Expr *cond,
484+
const clang::Stmt *thenS,
485+
const clang::Stmt *elseS);
486+
cir::IfOp emitIfOnBoolExpr(const clang::Expr *cond,
487+
BuilderCallbackRef thenBuilder,
488+
mlir::Location thenLoc,
489+
BuilderCallbackRef elseBuilder,
490+
std::optional<mlir::Location> elseLoc = {});
491+
492+
mlir::Value emitOpOnBoolExpr(mlir::Location loc, const clang::Expr *cond);
493+
494+
mlir::LogicalResult emitIfStmt(const clang::IfStmt &s);
495+
462496
/// Emit code to compute the specified expression,
463497
/// ignoring the result.
464498
void emitIgnoredExpr(const clang::Expr *e);

0 commit comments

Comments
 (0)