Skip to content
Merged
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
16 changes: 12 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
11 changes: 1 addition & 10 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
52 changes: 23 additions & 29 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 0 additions & 23 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 0 additions & 11 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 4 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,13 +706,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());
Expand Down
104 changes: 104 additions & 0 deletions clang/test/CIR/CodeGen/goto-address-label-table.c
Original file line number Diff line number Diff line change
@@ -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 %{{.+}}]
47 changes: 9 additions & 38 deletions clang/test/CIR/CodeGen/label-values.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>>
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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
Expand Down