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 @@ -1433,6 +1433,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.block_address <@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
}];
}

//===----------------------------------------------------------------------===//
// Common loop op definitions
//===----------------------------------------------------------------------===//
Expand Down
19 changes: 15 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,22 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {

mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
auto func = cast<cir::FuncOp>(cgf.curFn);
auto blockInfoAttr = cir::BlockAddrInfoAttr::get(
cir::BlockAddrInfoAttr 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);
cir::BlockAddressOp 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();
return blockAddressOp;
}

mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
Expand Down
53 changes: 53 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,49 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
}
}

void CIRGenFunction::resolveBlockAddresses() {

for (cir::BlockAddressOp &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) {
// Resolve block address-to-label mappings, then emit the indirect branch
// with the corresponding targets.
resolveBlockAddresses();
finishIndirectBranch();

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

// Pop any cleanups that might have been associated with the
// parameters. Do this in whatever block we're currently in; it's
// important to do this before we enter the return block or return
Expand Down Expand Up @@ -1086,6 +1128,17 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
return builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
}

void CIRGenFunction::getIndirectGotoBlock() {
// If we already made the indirect branch for indirect goto, return its block.
if (indirectGotoBlock)
return;

mlir::OpBuilder::InsertionGuard guard(builder);
indirectGotoBlock =
builder.createBlock(builder.getBlock()->getParent(), {}, {voidPtrTy},
{builder.getUnknownLoc()});
}

mlir::Value CIRGenFunction::emitAlignmentAssumption(
mlir::Value ptrValue, QualType ty, SourceLocation loc,
SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue) {
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,14 @@ class CIRGenFunction : public CIRGenTypeCache {
return JumpDest(target, ehStack.getInnermostNormalCleanup(),
nextCleanupDestIndex++);
}
/// 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();

/// Perform the usual unary conversions on the specified expression and
/// compare the result against zero, returning an Int1Ty value.
Expand Down Expand Up @@ -1360,6 +1368,8 @@ class CIRGenFunction : public CIRGenTypeCache {

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

void getIndirectGotoBlock();

RValue emitCall(const CIRGenFunctionInfo &funcInfo,
const CIRGenCallee &callee, ReturnValueSlot returnValue,
const CallArgList &args, cir::CIRCallOpInterface *callOp,
Expand Down Expand Up @@ -1543,6 +1553,8 @@ class CIRGenFunction : public CIRGenTypeCache {

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

mlir::LogicalResult emitIndirectGotoStmt(const IndirectGotoStmt &s);

void emitImplicitAssignmentOperatorBody(FunctionArgList &args);

void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
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 @@ -2496,3 +2496,41 @@ DiagnosticBuilder CIRGenModule::errorNYI(SourceRange loc,
llvm::StringRef feature) {
return errorNYI(loc.getBegin(), feature) << loc;
}

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: 17 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,23 @@ class CIRGenModule : public CIRGenTypeCache {
/// the pointers are supposed to be uniqued, should be fine. Revisit 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);
/// Tell the consumer that this variable has been instantiated.
void handleCXXStaticMemberVarInstantiation(VarDecl *vd);

Expand Down
21 changes: 19 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
return emitCoroutineBody(cast<CoroutineBodyStmt>(*s));
case Stmt::CoreturnStmtClass:
case Stmt::IndirectGotoStmtClass:
return emitIndirectGotoStmt(cast<IndirectGotoStmt>(*s));
case Stmt::OMPParallelDirectiveClass:
case Stmt::OMPTaskwaitDirectiveClass:
case Stmt::OMPTaskyieldDirectiveClass:
Expand Down Expand Up @@ -555,6 +556,17 @@ mlir::LogicalResult CIRGenFunction::emitGotoStmt(const clang::GotoStmt &s) {
return mlir::success();
}

mlir::LogicalResult
CIRGenFunction::emitIndirectGotoStmt(const IndirectGotoStmt &s) {
mlir::Value 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.getKwLoc()));
Expand All @@ -581,9 +593,14 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const clang::LabelDecl &d) {
}

builder.setInsertionPointToEnd(labelBlock);
cir::LabelOp::create(builder, getLoc(d.getSourceRange()), d.getName());
cir::LabelOp label =
cir::LabelOp::create(builder, 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
assert(!cir::MissingFeatures::ehstackBranches());
assert(!cir::MissingFeatures::incrementProfileCounter());
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 @@ -1116,6 +1116,65 @@ Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
return getDest();
}

//===----------------------------------------------------------------------===//
// 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 << "]";
}

//===----------------------------------------------------------------------===//
// BrCondOp
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
Block *block = op.getOperation()->getBlock();
Block *dest = op.getDest();

if (isa<cir::LabelOp>(dest->front()))
if (isa<cir::LabelOp, cir::IndirectBrOp>(dest->front()))
return failure();
// Single edge between blocks: merge it.
if (block->getNumSuccessors() == 1 &&
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4057,6 +4057,12 @@ mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
return mlir::failure();
}

mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite(
cir::IndirectBrOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
return mlir::failure();
}

mlir::LogicalResult CIRToLLVMAwaitOpLowering::matchAndRewrite(
cir::AwaitOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down
Loading