diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index a19c4f951fff9..76d44c29b7da8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1433,6 +1433,67 @@ def CIR_BrCondOp : CIR_Op<"brcond", [ }]; } +//===----------------------------------------------------------------------===// +// IndirectBrOp +//===----------------------------------------------------------------------===// + +def CIR_IndirectBrOp : CIR_Op<"indirectbr", [ + DeclareOpInterfaceMethods + , 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, !cir.ptr>, ["ptr", init] + %1 = cir.block_address <@A, "A"> : !cir.ptr + cir.store align(8) %1, %0 : !cir.ptr, !cir.ptr> + %2 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr + cir.br ^bb1(%2 : !cir.ptr) + ^bb1(%3: !cir.ptr): + cir.indirectbr %3 : , [ + ^bb2 + ] + ``` + or with a poison: + + ```mlir + cir.indirectbr %0 poison : , [ + ^bb3, + ^bb2 + ] + ``` + }]; + + let arguments = (ins + CIR_VoidPtrType:$addr, + UnitAttr:$poison, + VariadicOfVariadic:$succOperands, + DenseI32ArrayAttr:$indbr_operand_segments + ); + + let successors = (successor VariadicSuccessor:$successors); + let assemblyFormat = [{ + $addr ( `poison` $poison^ )? `:` type($addr) `,` + custom(ref(type($addr)), + $successors, + $succOperands, + type($succOperands)) + attr-dict + }]; +} + //===----------------------------------------------------------------------===// // Common loop op definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 7c94743d5ffc6..d5bcafc9c81f4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -174,11 +174,22 @@ class ScalarExprEmitter : public StmtVisitor { mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) { auto func = cast(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) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 33bdfa315a9ea..ed8663d51aa10 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -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 succesors; + llvm::SmallVector 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(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 @@ -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) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 9adac089ea28b..f4b7cceef5f85 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -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. @@ -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, @@ -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, diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 809c24f8aa670..a9a1b300a79dd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -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); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 6600d086f8f61..fb1993c933cf2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -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 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 blockAddressInfoToLabel; + /// Map CIR BlockAddressOps directly to their resolved LabelOps. + /// Used once a block address has been successfully lowered to a label. + llvm::MapVector 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 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); diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 7bb8c2153056a..09801bd7f1888 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -203,6 +203,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s, return emitCoroutineBody(cast(*s)); case Stmt::CoreturnStmtClass: case Stmt::IndirectGotoStmtClass: + return emitIndirectGotoStmt(cast(*s)); case Stmt::OMPParallelDirectiveClass: case Stmt::OMPTaskwaitDirectiveClass: case Stmt::OMPTaskyieldDirectiveClass: @@ -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())); @@ -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(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()); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 6bf543cf794b7..b7d1a97185879 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1116,6 +1116,65 @@ Block *cir::BrOp::getSuccessorForOperands(ArrayRef) { 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 &succOperandBlocks, + SmallVectorImpl> &succOperands, + SmallVectorImpl> &succOperandsTypes) { + if (failed(parser.parseCommaSeparatedList( + OpAsmParser::Delimiter::Square, + [&]() { + Block *destination = nullptr; + SmallVector operands; + SmallVector 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 //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp index 2ef09b74dc968..0749c5e79e3ff 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -52,7 +52,7 @@ struct RemoveRedundantBranches : public OpRewritePattern { Block *block = op.getOperation()->getBlock(); Block *dest = op.getDest(); - if (isa(dest->front())) + if (isa(dest->front())) return failure(); // Single edge between blocks: merge it. if (block->getNumSuccessors() == 1 && diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 6136d48204e0c..76ddc360206b5 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -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 { diff --git a/clang/test/CIR/CodeGen/label-values.c b/clang/test/CIR/CodeGen/label-values.c index 41178e3f62f20..2d773770910fe 100644 --- a/clang/test/CIR/CodeGen/label-values.c +++ b/clang/test/CIR/CodeGen/label-values.c @@ -3,38 +3,52 @@ void A(void) { void *ptr = &&LABEL_A; + goto *ptr; LABEL_A: return; } + // CIR: cir.func dso_local @A // CIR: [[PTR:%.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init] {alignment = 8 : i64} // CIR: [[BLOCK:%.*]] = cir.block_address <@A, "LABEL_A"> : !cir.ptr // CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr, !cir.ptr> -// CIR: cir.br ^bb1 -// CIR: ^bb1: // pred: ^bb0 +// CIR: [[BLOCKADD:%.*]] = cir.load align(8) [[PTR]] : !cir.ptr>, !cir.ptr +// CIR: cir.br ^bb1([[BLOCKADD]] : !cir.ptr) +// CIR: ^bb1([[PHI:%.*]]: !cir.ptr {{.*}}): // pred: ^bb0 +// CIR: cir.indirectbr [[PHI]] : , [ +// CIR: ^bb2 +// CIR: ] +// CIR: ^bb2: // pred: ^bb1 // CIR: cir.label "LABEL_A" // CIR: cir.return void B(void) { LABEL_B: void *ptr = &&LABEL_B; + goto *ptr; } // CIR: cir.func dso_local @B() // CIR: [[PTR:%.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init] {alignment = 8 : i64} // CIR: cir.br ^bb1 -// CIR: ^bb1: +// CIR: ^bb1: // 2 preds: ^bb0, ^bb2 // CIR: cir.label "LABEL_B" // CIR: [[BLOCK:%.*]] = cir.block_address <@B, "LABEL_B"> : !cir.ptr // CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr, !cir.ptr> -// CIR: cir.return +// CIR: [[BLOCKADD:%.*]] = cir.load align(8) [[PTR]] : !cir.ptr>, !cir.ptr +// CIR: cir.br ^bb2([[BLOCKADD]] : !cir.ptr) +// CIR: ^bb2([[PHI:%.*]]: !cir.ptr {{.*}}): // pred: ^bb1 +// CIR: cir.indirectbr [[PHI]] : , [ +// CIR-NEXT: ^bb1 +// CIR: ] void C(int x) { - void *ptr = (x == 0) ? &&LABEL_A : &&LABEL_B; + void *ptr = (x == 0) ? &&LABEL_A : &&LABEL_B; + goto *ptr; LABEL_A: - return; + return; LABEL_B: - return; + return; } // CIR: cir.func dso_local @C @@ -42,19 +56,26 @@ void C(int x) { // CIR: [[BLOCK2:%.*]] = cir.block_address <@C, "LABEL_B"> : !cir.ptr // CIR: [[COND:%.*]] = cir.select if [[CMP:%.*]] then [[BLOCK1]] else [[BLOCK2]] : (!cir.bool, !cir.ptr, !cir.ptr) -> !cir.ptr // CIR: cir.store align(8) [[COND]], [[PTR:%.*]] : !cir.ptr, !cir.ptr> -// CIR: cir.br ^bb1 -// CIR: ^bb1: // pred: ^bb0 +// CIR: [[BLOCKADD:%.*]] = cir.load align(8) [[PTR]] : !cir.ptr>, !cir.ptr +// CIR: cir.br ^bb1([[BLOCKADD]] : !cir.ptr) +// CIR: ^bb1([[PHI:%.*]]: !cir.ptr {{.*}}): // pred: ^bb0 +// CIR: cir.indirectbr [[PHI]] : , [ +// CIR-NEXT: ^bb2, +// CIR-NEXT: ^bb4 +// CIR: ] +// CIR: ^bb2: // pred: ^bb1 // CIR: cir.label "LABEL_A" -// CIR: cir.br ^bb2 -// CIR: ^bb2: // 2 preds: ^bb1, ^bb3 +// CIR: cir.br ^bb3 +// CIR: ^bb3: // 2 preds: ^bb2, ^bb4 // CIR: cir.return -// CIR: ^bb3: // no predecessors +// CIR: ^bb4: // pred: ^bb1 // CIR: cir.label "LABEL_B" -// CIR: cir.br ^bb2 +// CIR: cir.br ^bb3 void D(void) { void *ptr = &&LABEL_A; void *ptr2 = &&LABEL_A; + goto *ptr2; LABEL_A: void *ptr3 = &&LABEL_A; return; @@ -69,8 +90,46 @@ void D(void) { // CIR: %[[BLK2:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr // CIR: cir.store align(8) %[[BLK2]], %[[PTR2]] : !cir.ptr, !cir.ptr> // CIR: cir.br ^bb1 -// CIR: ^bb1: // pred: ^bb0 +// CIR: ^bb1([[PHI:%*.]]: !cir.ptr {{.*}}): // pred: ^bb0 +// CIR: cir.indirectbr [[PHI]] : , [ +// CIR-DAG: ^bb2, +// CIR-DAG: ^bb2, +// CIR-DAG: ^bb2 +// CIR: ] +// CIR: ^bb2: // 3 preds: ^bb1, ^bb1, ^bb1 // CIR: cir.label "LABEL_A" // CIR: %[[BLK3:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr // CIR: cir.store align(8) %[[BLK3]], %[[PTR3]] : !cir.ptr, !cir.ptr> // CIR: cir.return + + +// This test checks that CIR preserves insertion order of blockaddresses +// for indirectbr, even if some were resolved immediately and others later. +void E(void) { + void *ptr = &&LABEL_D; + void *ptr2 = &&LABEL_C; +LABEL_A: +LABEL_B: + void *ptr3 = &&LABEL_B; + void *ptr4 = &&LABEL_A; +LABEL_C: +LABEL_D: + return; +} + +//CIR: cir.func dso_local @E() +//CIR: ^bb1({{.*}}: !cir.ptr {{.*}}): // no predecessors +//CIR: cir.indirectbr {{.*}} poison : , [ +//CIR-NEXT: ^bb5, +//CIR-NEXT: ^bb4, +//CIR-NEXT: ^bb3, +//CIR-NEXT: ^bb2 +//CIR: ] +//CIR: ^bb2: // 2 preds: ^bb0, ^bb1 +//CIR: cir.label "LABEL_A" +//CIR: ^bb3: // 2 preds: ^bb1, ^bb2 +//CIR: cir.label "LABEL_B" +//CIR: ^bb4: // 2 preds: ^bb1, ^bb3 +//CIR: cir.label "LABEL_C" +//CIR: ^bb5: // 2 preds: ^bb1, ^bb4 +//CIR: cir.label "LABEL_D" diff --git a/clang/test/CIR/IR/indirect-br.cir b/clang/test/CIR/IR/indirect-br.cir new file mode 100644 index 0000000000000..e091e31dc3f5f --- /dev/null +++ b/clang/test/CIR/IR/indirect-br.cir @@ -0,0 +1,46 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!void = !cir.void + +cir.func @E() { + %0 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr", init] {alignment = 8 : i64} + %1 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr2", init] {alignment = 8 : i64} + %2 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr3", init] {alignment = 8 : i64} + %3 = cir.alloca !cir.ptr, !cir.ptr>, ["ptr4", init] {alignment = 8 : i64} + %4 = cir.block_address <@E, "D"> : !cir.ptr + cir.store align(8) %4, %0 : !cir.ptr, !cir.ptr> + %5 = cir.block_address <@E, "C"> : !cir.ptr + cir.store align(8) %5, %1 : !cir.ptr, !cir.ptr> + %6 = cir.load align(8) %0 : !cir.ptr>, !cir.ptr + cir.br ^bb1(%6 : !cir.ptr) +^bb1(%7: !cir.ptr): // pred: ^bb0 + cir.indirectbr %7 : , [ + ^bb5, + ^bb4, + ^bb3, + ^bb2 + ] +^bb2: // pred: ^bb1 + cir.label "A" + cir.br ^bb3 +^bb3: // 2 preds: ^bb1, ^bb2 + cir.label "B" + %8 = cir.block_address <@E, "B"> : !cir.ptr + cir.store align(8) %8, %2 : !cir.ptr, !cir.ptr> + %9 = cir.block_address <@E, "A"> : !cir.ptr + cir.store align(8) %9, %3 : !cir.ptr, !cir.ptr> + cir.br ^bb4 +^bb4: // 2 preds: ^bb1, ^bb3 + cir.label "C" + cir.br ^bb5 +^bb5: // 2 preds: ^bb1, ^bb4 + cir.label "D" + cir.return +} + +// CHECK: cir.indirectbr %7 : , [ +// CHECK: ^bb5, +// CHECK: ^bb4, +// CHECK: ^bb3, +// CHECK: ^bb2 +// CHECK: ]