Skip to content
Open
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
61 changes: 61 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2293,6 +2293,67 @@ def CIR_BrCondOp : CIR_Op<"brcond", [
}];
}

//===----------------------------------------------------------------------===//
// IndirectBrOp
//===----------------------------------------------------------------------===//

def CIR_IndirectBrOp : CIR_Op<"indirectbr", [
DeclareOpInterfaceMethods<BranchOpInterface>
, SameVariadicOperandSize, Terminator, Pure]> {
let summary = "Indirect branch";
let description = [{
The `cir.indirectbr` operation represents an indirect branch to one of
several possible successor blocks. The target block is computed from
the value of the given address operand.

This operation is typically generated when handling constructs like
the GCC extension `&&label` combined with an indirect `goto *ptr;`.

The `poison` attribute is used to mark an `indirectbr` that was created
but is known to be invalid — for instance, when a label address was
taken but no indirect branch was ever emitted.

Example:

```mlir
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
%1 = cir.blockaddress <@A, "A"> -> !cir.ptr<!void>
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
%2 = cir.load align(8) %0 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
cir.br ^bb1(%2 : !cir.ptr<!void>)
^bb1(%3: !cir.ptr<!void>):
cir.indirectbr %3 : <!void>, [
^bb2
]
```
or with a poison:

```mlir
cir.indirectbr %0 poison : <!void>, [
^bb3,
^bb2
]
```
}];

let arguments = (ins
CIR_VoidPtrType:$addr,
UnitAttr:$poison,
VariadicOfVariadic<AnyType, "indbr_operand_segments">:$succOperands,
DenseI32ArrayAttr:$indbr_operand_segments
);

let successors = (successor VariadicSuccessor<AnySuccessor>:$successors);
let assemblyFormat = [{
$addr ( `poison` $poison^ )? `:` type($addr) `,`
custom<IndirectBrOpSucessors>(ref(type($addr)),
$successors,
$succOperands,
type($succOperands))
attr-dict
}];
}

//===----------------------------------------------------------------------===//
// While & DoWhileOp
//===----------------------------------------------------------------------===//
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ struct MissingFeatures {
static bool emitScalarRangeCheck() { return false; }
static bool stmtExprEvaluation() { return false; }
static bool setCallingConv() { return false; }
static bool indirectBranch() { return false; }
static bool escapedLocals() { return false; }
static bool deferredReplacements() { return false; }
static bool shouldInstrumentFunction() { return false; }
Expand Down
18 changes: 15 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,22 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
auto func = cast<cir::FuncOp>(CGF.CurFn);
auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
&CGF.getMLIRContext(), func.getSymName(), e->getLabel()->getName());
return cir::BlockAddressOp::create(Builder, CGF.getLoc(e->getSourceRange()),
CGF.convertType(e->getType()),
blockInfoAttr);
auto blockAddressOp = cir::BlockAddressOp::create(
Builder, CGF.getLoc(e->getSourceRange()), CGF.convertType(e->getType()),
blockInfoAttr);
cir::LabelOp resolvedLabel = CGF.CGM.lookupBlockAddressInfo(blockInfoAttr);
if (!resolvedLabel) {
CGF.CGM.mapUnresolvedBlockAddress(blockAddressOp);
// Still add the op to maintain insertion order it will be resolved in
// resolveBlockAddresses
CGF.CGM.mapResolvedBlockAddress(blockAddressOp, nullptr);
} else {
CGF.CGM.mapResolvedBlockAddress(blockAddressOp, resolvedLabel);
}
CGF.getIndirectGotoBlock(blockAddressOp);
return blockAddressOp;
}

mlir::Value VisitSizeOfPackExpr(SizeOfPackExpr *E) {
llvm_unreachable("NYI");
}
Expand Down
55 changes: 51 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,34 @@ cir::TryOp CIRGenFunction::LexicalScope::getClosestTryParent() {
return nullptr;
}

void CIRGenFunction::resolveBlockAddresses() {

for (auto &blockAddress : CGM.unresolvedBlockAddressToLabel) {
cir::LabelOp labelOp =
CGM.lookupBlockAddressInfo(blockAddress.getBlockAddrInfo());
assert(labelOp && "expected cir.labelOp to already be emitted");
CGM.updateResolvedBlockAddress(blockAddress, labelOp);
}
CGM.unresolvedBlockAddressToLabel.clear();
}

void CIRGenFunction::finishIndirectBranch() {
if (!indirectGotoBlock)
return;
llvm::SmallVector<mlir::Block *> succesors;
llvm::SmallVector<mlir::ValueRange> rangeOperands;
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(indirectGotoBlock);
for (auto &[blockAdd, labelOp] : CGM.blockAddressToLabel) {
succesors.push_back(labelOp->getBlock());
rangeOperands.push_back(labelOp->getBlock()->getArguments());
}
cir::IndirectBrOp::create(builder, builder.getUnknownLoc(),
indirectGotoBlock->getArgument(0), false,
rangeOperands, succesors);
CGM.blockAddressToLabel.clear();
}

void CIRGenFunction::finishFunction(SourceLocation endLoc) {
// CIRGen doesn't use a BreakContinueStack or evaluates OnlySimpleReturnStmts.

Expand Down Expand Up @@ -584,15 +612,23 @@ void CIRGenFunction::finishFunction(SourceLocation endLoc) {

// If someone did an indirect goto, emit the indirect goto block at the end of
// the function.
assert(!cir::MissingFeatures::indirectBranch() && "NYI");

// Resolve block address-to-label mappings, then emit the indirect branch
// with the corresponding targets.
resolveBlockAddresses();
finishIndirectBranch();
// If some of our locals escaped, insert a call to llvm.localescape in the
// entry block.
assert(!cir::MissingFeatures::escapedLocals() && "NYI");

// If someone took the address of a label but never did an indirect goto, we
// made a zero entry PHI node, which is illegal, zap it now.
assert(!cir::MissingFeatures::indirectBranch() && "NYI");
// If a label address was taken but no indirect goto was used, we can't remove
// the block argument here. Instead, we mark the 'indirectbr' op
// as poison so that the cleanup can be deferred to lowering, since the
// verifier doesn't allow the 'indirectbr' target address to be null.
if (indirectGotoBlock && indirectGotoBlock->hasNoPredecessors()) {
auto indrBr = cast<cir::IndirectBrOp>(indirectGotoBlock->front());
indrBr.setPoison(true);
}

// CIRGen doesn't need to emit EHResumeBlock, TerminateLandingPad,
// TerminateHandler, UnreachableBlock, TerminateFunclets, NormalCleanupDest
Expand Down Expand Up @@ -1965,6 +2001,17 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
return numElements;
}

mlir::Block *CIRGenFunction::getIndirectGotoBlock(cir::BlockAddressOp op) {
// If we already made the indirect branch for indirect goto, return its block.
if (indirectGotoBlock)
return indirectGotoBlock;

mlir::OpBuilder::InsertionGuard guard(builder);
indirectGotoBlock = builder.createBlock(builder.getBlock()->getParent(), {},
{op.getType()}, {op.getLoc()});
return indirectGotoBlock;
}

mlir::Value CIRGenFunction::emitAlignmentAssumption(
mlir::Value ptrValue, QualType ty, SourceLocation loc,
SourceLocation assumptionLoc, mlir::IntegerAttr alignment,
Expand Down
13 changes: 12 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ class CIRGenFunction : public CIRGenTypeCache {
llvm::DenseMap<const clang::LabelDecl *, JumpDest> LabelMap;
JumpDest &getJumpDestForLabel(const clang::LabelDecl *D);

/// IndirectBranch - The first time an indirect goto is seen we create a block
/// reserved for the indirect branch. Unlike before,the actual 'indirectbr'
/// is emitted at the end of the function, once all block destinations have
/// been resolved.
mlir::Block *indirectGotoBlock = nullptr;

void resolveBlockAddresses();
void finishIndirectBranch();

// ---------------------
// Opaque value handling
// ---------------------
Expand Down Expand Up @@ -688,6 +697,8 @@ class CIRGenFunction : public CIRGenTypeCache {

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

mlir::Block *getIndirectGotoBlock(cir::BlockAddressOp op);

void checkTargetFeatures(const CallExpr *E, const FunctionDecl *TargetDecl);
void checkTargetFeatures(SourceLocation Loc, const FunctionDecl *TargetDecl);

Expand Down Expand Up @@ -2165,7 +2176,7 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitFunctionBody(const clang::Stmt *Body);

mlir::LogicalResult emitGotoStmt(const clang::GotoStmt &S);

mlir::LogicalResult emitIndirectGotoStmt(const IndirectGotoStmt &s);
/// Emit an if on a boolean condition to the specified blocks.
/// FIXME: Based on the condition, this might try to simplify the codegen of
/// the conditional based on the branch. TrueCount should be the number of
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4404,3 +4404,41 @@ CIRGenModule::mergeTBAAInfoForMemoryTransfer(TBAAAccessInfo destInfo,
return TBAAAccessInfo();
return tbaa->mergeTBAAInfoForConditionalOperator(destInfo, srcInfo);
}

void CIRGenModule::mapBlockAddress(cir::BlockAddrInfoAttr blockInfo,
cir::LabelOp label) {
auto result = blockAddressInfoToLabel.try_emplace(blockInfo, label);
(void)result;
assert(result.second &&
"attempting to map a blockaddress info that is already mapped");
}

void CIRGenModule::mapUnresolvedBlockAddress(cir::BlockAddressOp op) {
auto result = unresolvedBlockAddressToLabel.insert(op);
(void)result;
assert(result.second &&
"attempting to map a blockaddress operation that is already mapped");
}

void CIRGenModule::mapResolvedBlockAddress(cir::BlockAddressOp op,
cir::LabelOp label) {
auto result = blockAddressToLabel.try_emplace(op, label);
(void)result;
assert(result.second &&
"attempting to map a blockaddress operation that is already mapped");
}

void CIRGenModule::updateResolvedBlockAddress(cir::BlockAddressOp op,
cir::LabelOp newLabel) {
auto *it = blockAddressToLabel.find(op);
assert(it != blockAddressToLabel.end() &&
"trying to update a blockaddress not previously mapped");
assert(!it->second && "blockaddress already has a resolved label");

it->second = newLabel;
}

cir::LabelOp
CIRGenModule::lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo) {
return blockAddressInfoToLabel.lookup(blockInfo);
}
18 changes: 18 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,24 @@ class CIRGenModule : public CIRGenTypeCache {
/// this if it ends up taking too much memory.
llvm::DenseMap<const clang::FieldDecl *, llvm::StringRef> LambdaFieldToName;

/// Map BlockAddrInfoAttr (function name, label name) to the corresponding CIR
/// LabelOp. This provides the main lookup table used to resolve block
/// addresses into their label operations.
llvm::DenseMap<cir::BlockAddrInfoAttr, cir::LabelOp> blockAddressInfoToLabel;
/// Map CIR BlockAddressOps directly to their resolved LabelOps.
/// Used once a block address has been successfully lowered to a label.
llvm::MapVector<cir::BlockAddressOp, cir::LabelOp> blockAddressToLabel;
/// Track CIR BlockAddressOps that cannot be resolved immediately
/// because their LabelOp has not yet been emitted. These entries
/// are solved later once the corresponding label is available.
llvm::DenseSet<cir::BlockAddressOp> unresolvedBlockAddressToLabel;
cir::LabelOp lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo);
void mapBlockAddress(cir::BlockAddrInfoAttr blockInfo, cir::LabelOp label);
void mapUnresolvedBlockAddress(cir::BlockAddressOp op);
void mapResolvedBlockAddress(cir::BlockAddressOp op, cir::LabelOp);
void updateResolvedBlockAddress(cir::BlockAddressOp op,
cir::LabelOp newLabel);

/// If the declaration has internal linkage but is inside an
/// extern "C" linkage specification, prepare to emit an alias for it
/// to the expected name.
Expand Down
23 changes: 20 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,9 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *S,
return emitOMPTaskyieldDirective(cast<OMPTaskyieldDirective>(*S));
case Stmt::OMPBarrierDirectiveClass:
return emitOMPBarrierDirective(cast<OMPBarrierDirective>(*S));
// Unsupported AST nodes:
case Stmt::IndirectGotoStmtClass:
return emitIndirectGotoStmt(cast<IndirectGotoStmt>(*S));
// Unsupported AST nodes:
case Stmt::CapturedStmtClass:
case Stmt::ObjCAtTryStmtClass:
case Stmt::ObjCAtThrowStmtClass:
Expand Down Expand Up @@ -652,13 +653,29 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const LabelDecl *D) {
}

builder.setInsertionPointToEnd(labelBlock);
builder.create<cir::LabelOp>(getLoc(D->getSourceRange()), D->getName());
auto label =
builder.create<cir::LabelOp>(getLoc(D->getSourceRange()), D->getName());
builder.setInsertionPointToEnd(labelBlock);

auto func = cast<cir::FuncOp>(CurFn);
CGM.mapBlockAddress(cir::BlockAddrInfoAttr::get(builder.getContext(),
func.getSymNameAttr(),
label.getLabelAttr()),
label);
// FIXME: emit debug info for labels, incrementProfileCounter
return mlir::success();
}

mlir::LogicalResult
CIRGenFunction::emitIndirectGotoStmt(const IndirectGotoStmt &s) {
auto val = emitScalarExpr(s.getTarget());
assert(indirectGotoBlock &&
"If you jumping to a indirect branch should be alareadye emitted");
cir::BrOp::create(builder, getLoc(s.getSourceRange()), indirectGotoBlock,
val);
builder.createBlock(builder.getBlock()->getParent());
return mlir::success();
}

mlir::LogicalResult
CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &S) {
builder.createContinue(getLoc(S.getContinueLoc()));
Expand Down
59 changes: 59 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,65 @@ Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
return nullptr;
}

//===----------------------------------------------------------------------===//
// IndirectBrCondOp
//===----------------------------------------------------------------------===//

mlir::SuccessorOperands
cir::IndirectBrOp::getSuccessorOperands(unsigned index) {
assert(index < getNumSuccessors() && "invalid successor index");
return mlir::SuccessorOperands(getSuccOperandsMutable()[index]);
}

ParseResult parseIndirectBrOpSucessors(
OpAsmParser &parser, Type &flagType,
SmallVectorImpl<Block *> &succOperandBlocks,
SmallVectorImpl<SmallVector<OpAsmParser::UnresolvedOperand>> &succOperands,
SmallVectorImpl<SmallVector<Type>> &succOperandsTypes) {
if (failed(parser.parseCommaSeparatedList(
OpAsmParser::Delimiter::Square,
[&]() {
Block *destination = nullptr;
SmallVector<OpAsmParser::UnresolvedOperand> operands;
SmallVector<Type> operandTypes;

if (parser.parseSuccessor(destination).failed())
return failure();

if (succeeded(parser.parseOptionalLParen())) {
if (failed(parser.parseOperandList(
operands, OpAsmParser::Delimiter::None)) ||
failed(parser.parseColonTypeList(operandTypes)) ||
failed(parser.parseRParen()))
return failure();
}
succOperandBlocks.push_back(destination);
succOperands.emplace_back(operands);
succOperandsTypes.emplace_back(operandTypes);
return success();
},
"successor blocks")))
return failure();
return success();
}

void printIndirectBrOpSucessors(OpAsmPrinter &p, cir::IndirectBrOp op,
Type flagType, SuccessorRange succs,
OperandRangeRange succOperands,
const TypeRangeRange &succOperandsTypes) {
p << "[";
llvm::interleave(
llvm::zip(succs, succOperands),
[&](auto i) {
p.printNewline();
p.printSuccessorAndUseList(std::get<0>(i), std::get<1>(i));
},
[&] { p << ','; });
if (!succOperands.empty())
p.printNewline();
p << "]";
}

//===----------------------------------------------------------------------===//
// CaseOp
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading