Author: adams381 Date: 2026-06-25T17:51:19-05:00 New Revision: c5804067b7e622299d75ac420bdbac52c613c05c
URL: https://github.com/llvm/llvm-project/commit/c5804067b7e622299d75ac420bdbac52c613c05c DIFF: https://github.com/llvm/llvm-project/commit/c5804067b7e622299d75ac420bdbac52c613c05c.diff LOG: [CIR] Wire const goto labels into indirect branch (#201644) A computed goto through a constant dispatch table -- the GNU static dispatch-table idiom `static const void *tbl[] = {&&L1, &&L2}; goto *tbl[i];` -- reached `errorNYI("Indirect goto without a goto block")` in `emitIndirectGotoStmt`. #203644 emits the label-address constant (the value-like `#cir.block_addr_info`) into the table, but it takes a label's address in a constant context without registering the label as address-taken, so no indirect-goto block exists for the following `goto *tbl[i]` to branch to. (#203644 landed the constant attribute, its lowering, and the GotoSolver label retention; this is the remaining dispatch wiring.) `VisitAddrLabelExpr` in the constant emitter now records each label via `takeAddressOfConstantLabel`, which instantiates the indirect-goto block and tracks the label; `finishIndirectBranch` then adds those labels as `cir.indirect_br` successors alongside the existing op-form labels. A label named more than once in a table is kept as a distinct successor each time, to match classic codegen. Registering the label needs a non-const `CIRGenFunction`, so `VisitAddrLabelExpr` `const_cast`s `emitter.cgf` -- the same thing classic Clang does (`ConstantEmitter::CGF` is non-const), matching the existing cast at `CIRGenExprConstant.cpp:929`. New test `goto-address-label-table.c` covers CIR, the CIR-lowered LLVM, and classic OGCG: a plain table, a duplicate-label table, a label whose address is taken but never reached by a goto, and one reached through both a constant table and a runtime block-address. Added: clang/test/CIR/CodeGen/goto-address-label-table.c Modified: clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp clang/lib/CIR/CodeGen/CIRGenFunction.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/CodeGen/CIRGenModule.h clang/lib/CIR/CodeGen/CIRGenStmt.cpp clang/test/CIR/CodeGen/label-values.c Removed: ################################################################################ diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index c29b66ac2f8bc..eddb00c6fb2c1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -1521,10 +1521,18 @@ ConstantLValueEmitter::VisitPredefinedExpr(const PredefinedExpr *e) { ConstantLValue ConstantLValueEmitter::VisitAddrLabelExpr(const AddrLabelExpr *e) { - auto func = cast<cir::FuncOp>(emitter.cgf->curFn); - return cir::BlockAddrInfoAttr::get(cgm.getBuilder().getContext(), - func.getSymName(), - e->getLabel()->getName()); + // A label address taken in a constant context, e.g. a static computed-goto + // dispatch table `static const void *tbl[] = {&&L1, &&L2}`. Besides emitting + // the constant, register the label as address-taken so a following + // `goto *tbl[i]` lists it among the indirect branch's successors. A label is + // always function-local, so cgf is set here. + assert(emitter.cgf && "label address in a constant requires a function"); + CIRGenFunction &cgf = *const_cast<CIRGenFunction *>(emitter.cgf); + auto func = cast<cir::FuncOp>(cgf.curFn); + cir::BlockAddrInfoAttr info = cir::BlockAddrInfoAttr::get( + &cgf.getMLIRContext(), func.getSymName(), e->getLabel()->getName()); + cgf.indirectGotoTargets.push_back(info); + return info; } ConstantLValue ConstantLValueEmitter::VisitCallExpr(const CallExpr *e) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 5e8bb9df83ab3..774cfc8ef7ab6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -211,16 +211,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { 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.instantiateIndirectGotoBlock(); + cgf.indirectGotoTargets.push_back(blockInfoAttr); return blockAddressOp; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 6606cf74c7dea..d0aedc0689404 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -604,48 +604,42 @@ 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() { + // The block is created on the first `goto *expr`, so if it is absent the + // function has no indirect goto and nothing needs wiring -- a label whose + // address is merely taken still emits its address constant on its own. if (!indirectGotoBlock) return; - llvm::SmallVector<mlir::Block *> succesors; + + // Every label is emitted by now, so each address-taken label resolves to its + // LabelOp. A label may be named more than once (a dispatch table can list it + // twice), but a block only needs to appear once in the successor list, so + // duplicates are dropped. + llvm::SmallVector<mlir::Block *> successors; llvm::SmallVector<mlir::ValueRange> rangeOperands; + llvm::SmallPtrSet<mlir::Block *, 8> seen; + for (cir::BlockAddrInfoAttr info : indirectGotoTargets) { + cir::LabelOp labelOp = cgm.lookupBlockAddressInfo(info); + assert(labelOp && "expected cir.label to be emitted for block address"); + mlir::Block *dest = labelOp->getBlock(); + if (!seen.insert(dest).second) + continue; + successors.push_back(dest); + rangeOperands.push_back(dest->getArguments()); + } + 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(); + rangeOperands, successors); + indirectGotoTargets.clear(); } void CIRGenFunction::finishFunction(SourceLocation endLoc) { - // Resolve block address-to-label mappings, then emit the indirect branch - // with the corresponding targets. - resolveBlockAddresses(); + // Emit the indirect branch with all resolved label destinations. 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 diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 0ce6005ccac88..322355fde3957 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -725,12 +725,17 @@ class CIRGenFunction : public CIRGenTypeCache { }; /// 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. + /// reserved for the indirect branch. The actual `cir.indirect_br` is emitted + /// at the end of the function, once every label destination is known. mlir::Block *indirectGotoBlock = nullptr; - void resolveBlockAddresses(); + /// Labels whose address is taken in this function (via `&&label`, as either + /// an operation or a constant initializer). The indirect branch block is + /// created lazily on the first `goto *expr`; these targets are resolved to + /// their LabelOps and wired as `cir.indirect_br` successors in + /// finishIndirectBranch. + llvm::SmallVector<cir::BlockAddrInfoAttr> indirectGotoTargets; + void finishIndirectBranch(); /// Perform the usual unary conversions on the specified expression and diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 3f71b0013be62..69dfadf58aa49 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3881,29 +3881,6 @@ void CIRGenModule::mapBlockAddress(cir::BlockAddrInfoAttr blockInfo, "attempting to map a blockaddress info that is already mapped"); } -void CIRGenModule::mapUnresolvedBlockAddress(cir::BlockAddressOp op) { - [[maybe_unused]] auto result = unresolvedBlockAddressToLabel.insert(op); - assert(result.second && - "attempting to map a blockaddress operation that is already mapped"); -} - -void CIRGenModule::mapResolvedBlockAddress(cir::BlockAddressOp op, - cir::LabelOp label) { - [[maybe_unused]] auto result = blockAddressToLabel.try_emplace(op, label); - 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 c6fef5dde5fd9..144f8c7b9f3e7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -195,19 +195,8 @@ class CIRGenModule : public CIRGenTypeCache { /// 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); /// Add a global value to the llvmUsed list. void addUsedGlobal(cir::CIRGlobalValueInterface gv); diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index a2c999f584399..d3acac5801e74 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -707,13 +707,10 @@ mlir::LogicalResult CIRGenFunction::emitGotoStmt(const clang::GotoStmt &s) { mlir::LogicalResult CIRGenFunction::emitIndirectGotoStmt(const IndirectGotoStmt &s) { mlir::Value val = emitScalarExpr(s.getTarget()); - if (!indirectGotoBlock) { - // If the target labels were emitted as constants, we have more work to do. - // This diagnostic is here to flag the condition, but the changes may end - // up being implemented elsewhere. - cgm.errorNYI(s.getSourceRange(), "Indirect goto without a goto block"); - return mlir::failure(); - } + // Create the shared indirect-branch block on first use. Its successors are + // every address-taken label, wired in finishIndirectBranch once all labels + // are emitted. + instantiateIndirectGotoBlock(); cir::BrOp::create(builder, getLoc(s.getSourceRange()), indirectGotoBlock, val); builder.createBlock(builder.getBlock()->getParent()); diff --git a/clang/test/CIR/CodeGen/goto-address-label-table.c b/clang/test/CIR/CodeGen/goto-address-label-table.c new file mode 100644 index 0000000000000..2e273a7c392e9 --- /dev/null +++ b/clang/test/CIR/CodeGen/goto-address-label-table.c @@ -0,0 +1,104 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,LLVMCIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=LLVM,OGCG + +// CIR-DAG: cir.global "private" internal dso_local @f.tbl = #cir.const_array<[#cir.block_addr_info<@f, "L1"> : !cir.ptr<!void>, #cir.block_addr_info<@f, "L2"> : !cir.ptr<!void>]> : !cir.array<!cir.ptr<!void> x 2> +// CIR-DAG: cir.global "private" internal dso_local @g.tbl = #cir.const_array<[#cir.block_addr_info<@g, "A"> : !cir.ptr<!void>, #cir.block_addr_info<@g, "A"> : !cir.ptr<!void>, #cir.block_addr_info<@g, "B"> : !cir.ptr<!void>]> : !cir.array<!cir.ptr<!void> x 3> +// CIR-DAG: cir.global "private" internal dso_local @h.tbl = #cir.const_array<[#cir.block_addr_info<@h, "L1"> : !cir.ptr<!void>]> : !cir.array<!cir.ptr<!void> x 1> +// CIR-DAG: cir.global "private" internal dso_local @m.ctbl = #cir.const_array<[#cir.block_addr_info<@m, "A2"> : !cir.ptr<!void>]> : !cir.array<!cir.ptr<!void> x 1> + +// LLVM-DAG: @f.tbl = internal global [2 x ptr] [ptr blockaddress(@f, %[[FL1:[0-9a-zA-Z_.]+]]), ptr blockaddress(@f, %[[FL2:[0-9a-zA-Z_.]+]])], align 16 +// LLVM-DAG: @g.tbl = internal global [3 x ptr] [ptr blockaddress(@g, %[[GA:[0-9a-zA-Z_.]+]]), ptr blockaddress(@g, %[[GA]]), ptr blockaddress(@g, %[[GB:[0-9a-zA-Z_.]+]])], align 16 +// LLVM-DAG: @h.tbl = internal global [1 x ptr] [ptr blockaddress(@h, %{{[0-9a-zA-Z_.]+}})], align 8 +// LLVM-DAG: @m.ctbl = internal global [1 x ptr] [ptr blockaddress(@m, %[[MA2:[0-9a-zA-Z_.]+]])], align 8 + +int f(int x) { + static const void *tbl[] = {&&L1, &&L2}; + goto *tbl[x]; +L1: + return 1; +L2: + return 2; +} + +// CIR-LABEL: cir.func {{.*}} @f +// CIR: %[[TBL:.*]] = cir.get_global @f.tbl +// CIR: cir.indirect_br %{{.*}} : !cir.ptr<!void>, [ +// CIR-NEXT: ^[[L1BB:.*]], +// CIR-NEXT: ^[[L2BB:.*]] +// CIR: ] +// CIR: ^[[L1BB]]: +// CIR: cir.label "L1" +// CIR: ^[[L2BB]]: +// CIR: cir.label "L2" + +// LLVM-LABEL: define dso_local i32 @f( +// LLVM: indirectbr ptr %{{.*}}, [label %[[FL1]], label %[[FL2]]] + +// A appears twice in g's table, but a block only needs to be listed once as an +// indirect-branch successor, so CIR drops the duplicate (classic keeps it). +int g(int x) { + static const void *tbl[] = {&&A, &&A, &&B}; + goto *tbl[x]; +A: + return 1; +B: + return 2; +} + +// CIR-LABEL: cir.func {{.*}} @g +// CIR: cir.indirect_br %{{.*}} : !cir.ptr<!void>, [ +// CIR-NEXT: ^[[ABB:.*]], +// CIR-NEXT: ^[[BBB:.*]] +// CIR: ] +// CIR: ^[[ABB]]: +// CIR: cir.label "A" +// CIR: ^[[BBB]]: +// CIR: cir.label "B" + +// LLVM-LABEL: define dso_local i32 @g( +// LLVMCIR: indirectbr ptr %{{.*}}, [label %[[GA]], label %[[GB]]] +// OGCG: indirectbr ptr %{{.*}}, [label %[[GA]], label %[[GA]], label %[[GB]]] + +// h takes a label address but never executes a `goto *`, so CIR emits no +// indirect branch (classic still emits a dead poisoned indirectbr). +int h(int x) { + static const void *tbl[] = {&&L1}; + (void)tbl; + return x; +L1: + return 0; +} + +// CIR-LABEL: cir.func {{.*}} @h +// CIR-NOT: cir.indirect_br + +// LLVM-LABEL: define dso_local i32 @h( +// LLVMCIR-NOT: indirectbr +// OGCG: indirectbr ptr poison, [label %{{.+}}] + +// A2's address comes from a constant table, B2's from a runtime block-address +// op; both feed the same indirect branch. +int m(int sel) { + static const void *ctbl[] = {&&A2}; + void *p = &&B2; + void *t = (void *)ctbl[0]; + void *dest = sel ? t : p; + goto *dest; +A2: + return 1; +B2: + return 2; +} + +// CIR-LABEL: cir.func {{.*}} @m +// CIR: cir.block_address <@m, "B2"> +// CIR: cir.indirect_br +// CIR-DAG: cir.label "A2" +// CIR-DAG: cir.label "B2" + +// LLVM-LABEL: define dso_local i32 @m( +// LLVM: indirectbr ptr %{{.*}}, [label %[[MA2]], label %{{.+}}] diff --git a/clang/test/CIR/CodeGen/label-values.c b/clang/test/CIR/CodeGen/label-values.c index cd54d91a57e9a..f2bc5aa5a549f 100644 --- a/clang/test/CIR/CodeGen/label-values.c +++ b/clang/test/CIR/CodeGen/label-values.c @@ -165,11 +165,9 @@ void D(void) { // CIR: cir.br ^bb1 // CIR: ^bb1([[PHI:%*.]]: !cir.ptr<!void> {{.*}}): // pred: ^bb0 // CIR: cir.indirect_br [[PHI]] : !cir.ptr<!void>, [ -// CIR-DAG: ^bb2, -// CIR-DAG: ^bb2, -// CIR-DAG: ^bb2 +// CIR-NEXT: ^bb2 // CIR: ] -// CIR: ^bb2: // 3 preds: ^bb1, ^bb1, ^bb1 +// CIR: ^bb2: // pred: ^bb1 // CIR: cir.label "LABEL_A" // CIR: %[[BLK3:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void> // CIR: cir.store align(8) %[[BLK3]], %[[PTR3]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>> @@ -185,7 +183,7 @@ void D(void) { // LLVM: br label %[[indirectgoto:.*]] // LLVM: [[indirectgoto]]: // LLVM: [[PHI:%.*]] = phi ptr [ %[[BLOCKADD]], %[[ENTRY:.*]] ] -// LLVM: indirectbr ptr [[PHI]], [label %[[LABEL_A]], label %[[LABEL_A]], label %[[LABEL_A]]] +// LLVM: indirectbr ptr [[PHI]], [label %[[LABEL_A]]] // LLVM: [[LABEL_A]]: // LLVM: store ptr blockaddress(@D, %[[LABEL_A]]), ptr %[[PTR3]], align 8 // LLVM: ret void @@ -205,8 +203,8 @@ void D(void) { // OGCG: %indirect.goto.dest = phi ptr [ %[[BLOCKADD]], %entry ] // OGCG: indirectbr ptr %indirect.goto.dest, [label %LABEL_A, label %LABEL_A, label %LABEL_A] -// This test checks that CIR preserves insertion order of blockaddresses -// for indirectbr, even if some were resolved immediately and others later. +// E takes label addresses but never executes a `goto *`, so CIR emits no +// indirect branch (classic still emits a dead poisoned indirectbr, see OGCG). void E(void) { void *ptr = &&LABEL_D; void *ptr2 = &&LABEL_C; @@ -219,38 +217,11 @@ void E(void) { return; } -//CIR: cir.func {{.*}} @E() -//CIR: ^bb1({{.*}}: !cir.ptr<!void> {{.*}}): // no predecessors -//CIR: cir.indirect_br {{.*}} poison : !cir.ptr<!void>, [ -//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" +// CIR-LABEL: cir.func {{.*}} @E() +// CIR-NOT: cir.indirect_br -// LLVM: define dso_local void @E() -// LLVM: store ptr blockaddress(@E, %[[LABEL_D:.*]]) -// LLVM: store ptr blockaddress(@E, %[[LABEL_C:.*]]) -// LLVM: br label %[[LABEL_A:.*]] -// LLVM: [[indirectgoto:.*]]: ; No predecessors! -// LLVM: indirectbr ptr poison, [label %[[LABEL_D]], label %[[LABEL_C]], label %[[LABEL_B:.*]], label %[[LABEL_A]]] -// LLVM: [[LABEL_A]]: -// LLVM: br label %[[LABEL_B]] -// LLVM: [[LABEL_B]]: -// LLVM: store ptr blockaddress(@E, %[[LABEL_B]]) -// LLVM: store ptr blockaddress(@E, %[[LABEL_A]]) -// LLVM: br label %[[LABEL_C]] -// LLVM: [[LABEL_C]]: -// LLVM: br label %[[LABEL_D]] -// LLVM: [[LABEL_D]]: +// LLVM-LABEL: define dso_local void @E() +// LLVM-NOT: indirectbr // OGCG: define dso_local void @E() #0 { // OGCG: store ptr blockaddress(@E, %LABEL_D), ptr %ptr, align 8 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
