Skip to content

Commit 762a171

Browse files
[CIR] Upstream support for cir.indirectbr (#169967)
This PR upstreams support for the `cir.indirectBr` operation, which is used to implement GCC’s labels-as-values `indirect goto`. To ensure correct lowering, we introduce precise bookkeeping to associate each `block_address` operation with its corresponding `label` op. This is required because a `block_address` may be emitted before the `label` it refers to. In such cases, the reference is deferred and later resolved by `resolveBlockAddresses`, which guarantees that all `indirectBr` successors are emitted in the correct and fully resolved order.
1 parent ce2518f commit 762a171

File tree

12 files changed

+454
-22
lines changed

12 files changed

+454
-22
lines changed

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,55 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
14921492
}];
14931493
}
14941494

1495+
//===----------------------------------------------------------------------===//
1496+
// IndirectBrOp
1497+
//===----------------------------------------------------------------------===//
1498+
1499+
def CIR_IndirectBrOp : CIR_Op<"indirect_br", [
1500+
DeclareOpInterfaceMethods<BranchOpInterface>,
1501+
SameVariadicOperandSize, Terminator, Pure
1502+
]> {
1503+
let summary = "Indirect branch";
1504+
let description = [{
1505+
The `cir.indirectbr` operation represents an indirect branch to one of
1506+
several possible successor blocks. The target block is computed from
1507+
the value of the given address operand.
1508+
1509+
This operation is typically generated when handling constructs like
1510+
the GCC extension `&&label` combined with an indirect `goto *ptr;`.
1511+
1512+
The `poison` attribute is used to mark an `indirectbr` that was created
1513+
but is known to be invalid, for instance when a label address was
1514+
taken but no indirect branch was ever emitted.
1515+
1516+
Example:
1517+
1518+
```mlir
1519+
%0 = cir.block_address <@A, "A"> : !cir.ptr<!void>
1520+
cir.indirectbr %0 poison : <!void>, [
1521+
^bb1
1522+
]
1523+
```
1524+
}];
1525+
1526+
let arguments = (ins
1527+
CIR_VoidPtrType:$addr,
1528+
UnitAttr:$poison,
1529+
VariadicOfVariadic<AnyType, "operand_segments">:$succ_operands,
1530+
DenseI32ArrayAttr:$operand_segments
1531+
);
1532+
1533+
let successors = (successor VariadicSuccessor<AnySuccessor>:$successors);
1534+
let assemblyFormat = [{
1535+
$addr ( `poison` $poison^ )? `:` qualified(type($addr)) `,`
1536+
custom<IndirectBrOpSucessors>(ref(type($addr)),
1537+
$successors,
1538+
$succ_operands,
1539+
type($succ_operands))
1540+
attr-dict
1541+
}];
1542+
}
1543+
14951544
//===----------------------------------------------------------------------===//
14961545
// Common loop op definitions
14971546
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,22 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
174174

175175
mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
176176
auto func = cast<cir::FuncOp>(cgf.curFn);
177-
auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
177+
cir::BlockAddrInfoAttr blockInfoAttr = cir::BlockAddrInfoAttr::get(
178178
&cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
179-
return cir::BlockAddressOp::create(builder, cgf.getLoc(e->getSourceRange()),
180-
cgf.convertType(e->getType()),
181-
blockInfoAttr);
179+
cir::BlockAddressOp blockAddressOp = cir::BlockAddressOp::create(
180+
builder, cgf.getLoc(e->getSourceRange()), cgf.convertType(e->getType()),
181+
blockInfoAttr);
182+
cir::LabelOp resolvedLabel = cgf.cgm.lookupBlockAddressInfo(blockInfoAttr);
183+
if (!resolvedLabel) {
184+
cgf.cgm.mapUnresolvedBlockAddress(blockAddressOp);
185+
// Still add the op to maintain insertion order it will be resolved in
186+
// resolveBlockAddresses
187+
cgf.cgm.mapResolvedBlockAddress(blockAddressOp, nullptr);
188+
} else {
189+
cgf.cgm.mapResolvedBlockAddress(blockAddressOp, resolvedLabel);
190+
}
191+
cgf.instantiateIndirectGotoBlock();
192+
return blockAddressOp;
182193
}
183194

184195
mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,48 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
531531
}
532532
}
533533

534+
void CIRGenFunction::resolveBlockAddresses() {
535+
for (cir::BlockAddressOp &blockAddress : cgm.unresolvedBlockAddressToLabel) {
536+
cir::LabelOp labelOp =
537+
cgm.lookupBlockAddressInfo(blockAddress.getBlockAddrInfo());
538+
assert(labelOp && "expected cir.labelOp to already be emitted");
539+
cgm.updateResolvedBlockAddress(blockAddress, labelOp);
540+
}
541+
cgm.unresolvedBlockAddressToLabel.clear();
542+
}
543+
544+
void CIRGenFunction::finishIndirectBranch() {
545+
if (!indirectGotoBlock)
546+
return;
547+
llvm::SmallVector<mlir::Block *> succesors;
548+
llvm::SmallVector<mlir::ValueRange> rangeOperands;
549+
mlir::OpBuilder::InsertionGuard guard(builder);
550+
builder.setInsertionPointToEnd(indirectGotoBlock);
551+
for (auto &[blockAdd, labelOp] : cgm.blockAddressToLabel) {
552+
succesors.push_back(labelOp->getBlock());
553+
rangeOperands.push_back(labelOp->getBlock()->getArguments());
554+
}
555+
cir::IndirectBrOp::create(builder, builder.getUnknownLoc(),
556+
indirectGotoBlock->getArgument(0), false,
557+
rangeOperands, succesors);
558+
cgm.blockAddressToLabel.clear();
559+
}
560+
534561
void CIRGenFunction::finishFunction(SourceLocation endLoc) {
562+
// Resolve block address-to-label mappings, then emit the indirect branch
563+
// with the corresponding targets.
564+
resolveBlockAddresses();
565+
finishIndirectBranch();
566+
567+
// If a label address was taken but no indirect goto was used, we can't remove
568+
// the block argument here. Instead, we mark the 'indirectbr' op
569+
// as poison so that the cleanup can be deferred to lowering, since the
570+
// verifier doesn't allow the 'indirectbr' target address to be null.
571+
if (indirectGotoBlock && indirectGotoBlock->hasNoPredecessors()) {
572+
auto indrBr = cast<cir::IndirectBrOp>(indirectGotoBlock->front());
573+
indrBr.setPoison(true);
574+
}
575+
535576
// Pop any cleanups that might have been associated with the
536577
// parameters. Do this in whatever block we're currently in; it's
537578
// important to do this before we enter the return block or return
@@ -1086,6 +1127,17 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
10861127
return builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
10871128
}
10881129

1130+
void CIRGenFunction::instantiateIndirectGotoBlock() {
1131+
// If we already made the indirect branch for indirect goto, return its block.
1132+
if (indirectGotoBlock)
1133+
return;
1134+
1135+
mlir::OpBuilder::InsertionGuard guard(builder);
1136+
indirectGotoBlock =
1137+
builder.createBlock(builder.getBlock()->getParent(), {}, {voidPtrTy},
1138+
{builder.getUnknownLoc()});
1139+
}
1140+
10891141
mlir::Value CIRGenFunction::emitAlignmentAssumption(
10901142
mlir::Value ptrValue, QualType ty, SourceLocation loc,
10911143
SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,14 @@ class CIRGenFunction : public CIRGenTypeCache {
658658
return JumpDest(target, ehStack.getInnermostNormalCleanup(),
659659
nextCleanupDestIndex++);
660660
}
661+
/// IndirectBranch - The first time an indirect goto is seen we create a block
662+
/// reserved for the indirect branch. Unlike before,the actual 'indirectbr'
663+
/// is emitted at the end of the function, once all block destinations have
664+
/// been resolved.
665+
mlir::Block *indirectGotoBlock = nullptr;
666+
667+
void resolveBlockAddresses();
668+
void finishIndirectBranch();
661669

662670
/// Perform the usual unary conversions on the specified expression and
663671
/// compare the result against zero, returning an Int1Ty value.
@@ -1392,6 +1400,8 @@ class CIRGenFunction : public CIRGenTypeCache {
13921400

13931401
int64_t getAccessedFieldNo(unsigned idx, mlir::ArrayAttr elts);
13941402

1403+
void instantiateIndirectGotoBlock();
1404+
13951405
RValue emitCall(const CIRGenFunctionInfo &funcInfo,
13961406
const CIRGenCallee &callee, ReturnValueSlot returnValue,
13971407
const CallArgList &args, cir::CIRCallOpInterface *callOp,
@@ -1575,6 +1585,8 @@ class CIRGenFunction : public CIRGenTypeCache {
15751585

15761586
mlir::LogicalResult emitGotoStmt(const clang::GotoStmt &s);
15771587

1588+
mlir::LogicalResult emitIndirectGotoStmt(const IndirectGotoStmt &s);
1589+
15781590
void emitImplicitAssignmentOperatorBody(FunctionArgList &args);
15791591

15801592
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,3 +2498,39 @@ DiagnosticBuilder CIRGenModule::errorNYI(SourceRange loc,
24982498
llvm::StringRef feature) {
24992499
return errorNYI(loc.getBegin(), feature) << loc;
25002500
}
2501+
2502+
void CIRGenModule::mapBlockAddress(cir::BlockAddrInfoAttr blockInfo,
2503+
cir::LabelOp label) {
2504+
[[maybe_unused]] auto result =
2505+
blockAddressInfoToLabel.try_emplace(blockInfo, label);
2506+
assert(result.second &&
2507+
"attempting to map a blockaddress info that is already mapped");
2508+
}
2509+
2510+
void CIRGenModule::mapUnresolvedBlockAddress(cir::BlockAddressOp op) {
2511+
[[maybe_unused]] auto result = unresolvedBlockAddressToLabel.insert(op);
2512+
assert(result.second &&
2513+
"attempting to map a blockaddress operation that is already mapped");
2514+
}
2515+
2516+
void CIRGenModule::mapResolvedBlockAddress(cir::BlockAddressOp op,
2517+
cir::LabelOp label) {
2518+
[[maybe_unused]] auto result = blockAddressToLabel.try_emplace(op, label);
2519+
assert(result.second &&
2520+
"attempting to map a blockaddress operation that is already mapped");
2521+
}
2522+
2523+
void CIRGenModule::updateResolvedBlockAddress(cir::BlockAddressOp op,
2524+
cir::LabelOp newLabel) {
2525+
auto *it = blockAddressToLabel.find(op);
2526+
assert(it != blockAddressToLabel.end() &&
2527+
"trying to update a blockaddress not previously mapped");
2528+
assert(!it->second && "blockaddress already has a resolved label");
2529+
2530+
it->second = newLabel;
2531+
}
2532+
2533+
cir::LabelOp
2534+
CIRGenModule::lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo) {
2535+
return blockAddressInfoToLabel.lookup(blockInfo);
2536+
}

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,23 @@ class CIRGenModule : public CIRGenTypeCache {
126126
/// the pointers are supposed to be uniqued, should be fine. Revisit this if
127127
/// it ends up taking too much memory.
128128
llvm::DenseMap<const clang::FieldDecl *, llvm::StringRef> lambdaFieldToName;
129-
129+
/// Map BlockAddrInfoAttr (function name, label name) to the corresponding CIR
130+
/// LabelOp. This provides the main lookup table used to resolve block
131+
/// addresses into their label operations.
132+
llvm::DenseMap<cir::BlockAddrInfoAttr, cir::LabelOp> blockAddressInfoToLabel;
133+
/// Map CIR BlockAddressOps directly to their resolved LabelOps.
134+
/// Used once a block address has been successfully lowered to a label.
135+
llvm::MapVector<cir::BlockAddressOp, cir::LabelOp> blockAddressToLabel;
136+
/// Track CIR BlockAddressOps that cannot be resolved immediately
137+
/// because their LabelOp has not yet been emitted. These entries
138+
/// are solved later once the corresponding label is available.
139+
llvm::DenseSet<cir::BlockAddressOp> unresolvedBlockAddressToLabel;
140+
cir::LabelOp lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo);
141+
void mapBlockAddress(cir::BlockAddrInfoAttr blockInfo, cir::LabelOp label);
142+
void mapUnresolvedBlockAddress(cir::BlockAddressOp op);
143+
void mapResolvedBlockAddress(cir::BlockAddressOp op, cir::LabelOp);
144+
void updateResolvedBlockAddress(cir::BlockAddressOp op,
145+
cir::LabelOp newLabel);
130146
/// Tell the consumer that this variable has been instantiated.
131147
void handleCXXStaticMemberVarInstantiation(VarDecl *vd);
132148

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
203203
return emitCoroutineBody(cast<CoroutineBodyStmt>(*s));
204204
case Stmt::CoreturnStmtClass:
205205
case Stmt::IndirectGotoStmtClass:
206+
return emitIndirectGotoStmt(cast<IndirectGotoStmt>(*s));
206207
case Stmt::OMPParallelDirectiveClass:
207208
case Stmt::OMPTaskwaitDirectiveClass:
208209
case Stmt::OMPTaskyieldDirectiveClass:
@@ -562,6 +563,17 @@ mlir::LogicalResult CIRGenFunction::emitGotoStmt(const clang::GotoStmt &s) {
562563
return mlir::success();
563564
}
564565

566+
mlir::LogicalResult
567+
CIRGenFunction::emitIndirectGotoStmt(const IndirectGotoStmt &s) {
568+
mlir::Value val = emitScalarExpr(s.getTarget());
569+
assert(indirectGotoBlock &&
570+
"If you jumping to a indirect branch should be alareadye emitted");
571+
cir::BrOp::create(builder, getLoc(s.getSourceRange()), indirectGotoBlock,
572+
val);
573+
builder.createBlock(builder.getBlock()->getParent());
574+
return mlir::success();
575+
}
576+
565577
mlir::LogicalResult
566578
CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) {
567579
builder.createContinue(getLoc(s.getKwLoc()));
@@ -588,9 +600,14 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const clang::LabelDecl &d) {
588600
}
589601

590602
builder.setInsertionPointToEnd(labelBlock);
591-
cir::LabelOp::create(builder, getLoc(d.getSourceRange()), d.getName());
603+
cir::LabelOp label =
604+
cir::LabelOp::create(builder, getLoc(d.getSourceRange()), d.getName());
592605
builder.setInsertionPointToEnd(labelBlock);
593-
606+
auto func = cast<cir::FuncOp>(curFn);
607+
cgm.mapBlockAddress(cir::BlockAddrInfoAttr::get(builder.getContext(),
608+
func.getSymNameAttr(),
609+
label.getLabelAttr()),
610+
label);
594611
// FIXME: emit debug info for labels, incrementProfileCounter
595612
assert(!cir::MissingFeatures::ehstackBranches());
596613
assert(!cir::MissingFeatures::incrementProfileCounter());

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,65 @@ Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
12471247
return getDest();
12481248
}
12491249

1250+
//===----------------------------------------------------------------------===//
1251+
// IndirectBrCondOp
1252+
//===----------------------------------------------------------------------===//
1253+
1254+
mlir::SuccessorOperands
1255+
cir::IndirectBrOp::getSuccessorOperands(unsigned index) {
1256+
assert(index < getNumSuccessors() && "invalid successor index");
1257+
return mlir::SuccessorOperands(getSuccOperandsMutable()[index]);
1258+
}
1259+
1260+
ParseResult parseIndirectBrOpSucessors(
1261+
OpAsmParser &parser, Type &flagType,
1262+
SmallVectorImpl<Block *> &succOperandBlocks,
1263+
SmallVectorImpl<SmallVector<OpAsmParser::UnresolvedOperand>> &succOperands,
1264+
SmallVectorImpl<SmallVector<Type>> &succOperandsTypes) {
1265+
if (failed(parser.parseCommaSeparatedList(
1266+
OpAsmParser::Delimiter::Square,
1267+
[&]() {
1268+
Block *destination = nullptr;
1269+
SmallVector<OpAsmParser::UnresolvedOperand> operands;
1270+
SmallVector<Type> operandTypes;
1271+
1272+
if (parser.parseSuccessor(destination).failed())
1273+
return failure();
1274+
1275+
if (succeeded(parser.parseOptionalLParen())) {
1276+
if (failed(parser.parseOperandList(
1277+
operands, OpAsmParser::Delimiter::None)) ||
1278+
failed(parser.parseColonTypeList(operandTypes)) ||
1279+
failed(parser.parseRParen()))
1280+
return failure();
1281+
}
1282+
succOperandBlocks.push_back(destination);
1283+
succOperands.emplace_back(operands);
1284+
succOperandsTypes.emplace_back(operandTypes);
1285+
return success();
1286+
},
1287+
"successor blocks")))
1288+
return failure();
1289+
return success();
1290+
}
1291+
1292+
void printIndirectBrOpSucessors(OpAsmPrinter &p, cir::IndirectBrOp op,
1293+
Type flagType, SuccessorRange succs,
1294+
OperandRangeRange succOperands,
1295+
const TypeRangeRange &succOperandsTypes) {
1296+
p << "[";
1297+
llvm::interleave(
1298+
llvm::zip(succs, succOperands),
1299+
[&](auto i) {
1300+
p.printNewline();
1301+
p.printSuccessorAndUseList(std::get<0>(i), std::get<1>(i));
1302+
},
1303+
[&] { p << ','; });
1304+
if (!succOperands.empty())
1305+
p.printNewline();
1306+
p << "]";
1307+
}
1308+
12501309
//===----------------------------------------------------------------------===//
12511310
// BrCondOp
12521311
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
5252
Block *block = op.getOperation()->getBlock();
5353
Block *dest = op.getDest();
5454

55-
if (isa<cir::LabelOp>(dest->front()))
55+
if (isa<cir::LabelOp, cir::IndirectBrOp>(dest->front()))
5656
return failure();
5757
// Single edge between blocks: merge it.
5858
if (block->getNumSuccessors() == 1 &&

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4125,6 +4125,12 @@ mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
41254125
return mlir::failure();
41264126
}
41274127

4128+
mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite(
4129+
cir::IndirectBrOp op, OpAdaptor adaptor,
4130+
mlir::ConversionPatternRewriter &rewriter) const {
4131+
return mlir::failure();
4132+
}
4133+
41284134
mlir::LogicalResult CIRToLLVMAwaitOpLowering::matchAndRewrite(
41294135
cir::AwaitOp op, OpAdaptor adaptor,
41304136
mlir::ConversionPatternRewriter &rewriter) const {

0 commit comments

Comments
 (0)