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
25 changes: 25 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1026,4 +1026,29 @@ def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> {
let storageType = [{ CatchUnwind }];
}

//===----------------------------------------------------------------------===//
// CIR_BlockAddrInfoAttr
//===----------------------------------------------------------------------===//

def CIR_BlockAddrInfoAttr : CIR_Attr<"BlockAddrInfo", "block_addr_info"> {
let summary = "Block Addres attribute";
let description = [{
This attribute is used to represent the address of a basic block
within a function. It combines the symbol reference to a function
with the name of a label inside that function.
}];
let parameters = (ins "mlir::FlatSymbolRefAttr":$func,
"mlir::StringAttr":$label);

let assemblyFormat = "`<` $func `,` $label `>`";
let builders = [
AttrBuilder<(ins "llvm::StringRef":$func_name,
"llvm::StringRef":$label_name
), [{
return $_get($_ctxt, mlir::FlatSymbolRefAttr::get($_ctxt, func_name),
mlir::StringAttr::get($_ctxt, label_name));
}]>
];
}

#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD
34 changes: 34 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -4800,4 +4800,38 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
}];
}

//===----------------------------------------------------------------------===//
// BlockAddressOp
//===----------------------------------------------------------------------===//

def CIR_BlockAddressOp : CIR_Op<"block_address", [Pure]> {
let summary = "Get the address of a cir.label within a function";
let description = [{
The `cir.blockaddress` operation takes a function name and a label and
produces a pointer value that represents the address of that cir.label
within the specified function.

This operation models GCC's "labels as values" extension (`&&label`), which
allows taking the address of a local label and using it as a computed
jump target (e.g., with `goto *addr;`).

Example:
```mlir
%1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
{alignment = 8 : i64}
%addr = cir.block_address <@c, "label1"> : !cir.ptr<!cir.void>
cir.store align(8) %addr, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
cir.br ^bb1
^bb1:
cir.label "label"
```
}];

let arguments = (ins CIR_BlockAddrInfoAttr:$block_addr_info);
let results = (outs CIR_VoidPtrType:$addr);
let assemblyFormat = [{
$block_addr_info `:` qualified(type($addr)) attr-dict
}];
}

#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
return emitLoadOfLValue(e);
}

mlir::Value VisitAddrLabelExpr(const AddrLabelExpr *e) {
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);
}

mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
mlir::Type type = cgf.convertType(e->getType());
return cir::ConstantOp::create(builder, cgf.getLoc(e->getExprLoc()),
Expand Down
29 changes: 26 additions & 3 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1902,22 +1902,45 @@ mlir::LogicalResult cir::FuncOp::verify() {

llvm::SmallSet<llvm::StringRef, 16> labels;
llvm::SmallSet<llvm::StringRef, 16> gotos;

llvm::SmallSet<llvm::StringRef, 16> blockAddresses;
bool invalidBlockAddress = false;
getOperation()->walk([&](mlir::Operation *op) {
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
labels.insert(lab.getLabel());
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
gotos.insert(goTo.getLabel());
} else if (auto blkAdd = dyn_cast<cir::BlockAddressOp>(op)) {
if (blkAdd.getBlockAddrInfoAttr().getFunc().getAttr() != getSymName()) {
// Stop the walk early, no need to continue
invalidBlockAddress = true;
return mlir::WalkResult::interrupt();
}
blockAddresses.insert(blkAdd.getBlockAddrInfoAttr().getLabel());
}
return mlir::WalkResult::advance();
});

if (invalidBlockAddress)
return emitOpError() << "blockaddress references a different function";

llvm::SmallSet<llvm::StringRef, 16> mismatched;
if (!labels.empty() || !gotos.empty()) {
llvm::SmallSet<llvm::StringRef, 16> mismatched =
llvm::set_difference(gotos, labels);
mismatched = llvm::set_difference(gotos, labels);

if (!mismatched.empty())
return emitOpError() << "goto/label mismatch";
}

mismatched.clear();

if (!labels.empty() || !blockAddresses.empty()) {
mismatched = llvm::set_difference(blockAddresses, labels);

if (!mismatched.empty())
return emitOpError()
<< "expects an existing label target in the referenced function";
}

return success();
}

Expand Down
17 changes: 15 additions & 2 deletions clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "PassDetail.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/Passes.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/TimeProfiler.h"
#include <memory>

Expand All @@ -30,17 +31,29 @@ static void process(cir::FuncOp func) {
mlir::OpBuilder rewriter(func.getContext());
llvm::StringMap<Block *> labels;
llvm::SmallVector<cir::GotoOp, 4> gotos;
llvm::SmallSet<StringRef, 4> blockAddrLabel;

func.getBody().walk([&](mlir::Operation *op) {
if (auto lab = dyn_cast<cir::LabelOp>(op)) {
// Will construct a string copy inplace. Safely erase the label
labels.try_emplace(lab.getLabel(), lab->getBlock());
lab.erase();
} else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
gotos.push_back(goTo);
} else if (auto blockAddr = dyn_cast<cir::BlockAddressOp>(op)) {
blockAddrLabel.insert(blockAddr.getBlockAddrInfo().getLabel());
}
});

for (auto &lab : labels) {
StringRef labelName = lab.getKey();
Block *block = lab.getValue();
if (!blockAddrLabel.contains(labelName)) {
// erase the LabelOp inside the block if safe
if (auto lab = dyn_cast<cir::LabelOp>(&block->front())) {
lab.erase();
}
}
}

for (auto goTo : gotos) {
mlir::OpBuilder::InsertionGuard guard(rewriter);
rewriter.setInsertionPoint(goTo);
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 @@ -3778,6 +3778,12 @@ mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
return mlir::success();
}

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

std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
return std::make_unique<ConvertCIRToLLVMPass>();
}
Expand Down
76 changes: 76 additions & 0 deletions clang/test/CIR/CodeGen/label-values.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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

void A(void) {
void *ptr = &&LABEL_A;
LABEL_A:
return;
}
// CIR: cir.func dso_local @A
// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
// CIR: [[BLOCK:%.*]] = cir.block_address <@A, "LABEL_A"> : !cir.ptr<!void>
// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.br ^bb1
// CIR: ^bb1: // pred: ^bb0
// CIR: cir.label "LABEL_A"
// CIR: cir.return

void B(void) {
LABEL_B:
void *ptr = &&LABEL_B;
}

// CIR: cir.func dso_local @B()
// CIR: [[PTR:%.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
// CIR: cir.br ^bb1
// CIR: ^bb1:
// CIR: cir.label "LABEL_B"
// CIR: [[BLOCK:%.*]] = cir.block_address <@B, "LABEL_B"> : !cir.ptr<!void>
// CIR: cir.store align(8) [[BLOCK]], [[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.return

void C(int x) {
void *ptr = (x == 0) ? &&LABEL_A : &&LABEL_B;
LABEL_A:
return;
LABEL_B:
return;
}

// CIR: cir.func dso_local @C
// CIR: [[BLOCK1:%.*]] = cir.block_address <@C, "LABEL_A"> : !cir.ptr<!void>
// CIR: [[BLOCK2:%.*]] = cir.block_address <@C, "LABEL_B"> : !cir.ptr<!void>
// CIR: [[COND:%.*]] = cir.select if [[CMP:%.*]] then [[BLOCK1]] else [[BLOCK2]] : (!cir.bool, !cir.ptr<!void>, !cir.ptr<!void>) -> !cir.ptr<!void>
// CIR: cir.store align(8) [[COND]], [[PTR:%.*]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.br ^bb1
// CIR: ^bb1: // pred: ^bb0
// CIR: cir.label "LABEL_A"
// CIR: cir.br ^bb2
// CIR: ^bb2: // 2 preds: ^bb1, ^bb3
// CIR: cir.return
// CIR: ^bb3: // no predecessors
// CIR: cir.label "LABEL_B"
// CIR: cir.br ^bb2

void D(void) {
void *ptr = &&LABEL_A;
void *ptr2 = &&LABEL_A;
LABEL_A:
void *ptr3 = &&LABEL_A;
return;
}

// CIR: cir.func dso_local @D
// CIR: %[[PTR:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init]
// CIR: %[[PTR2:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr2", init]
// CIR: %[[PTR3:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr3", init]
// CIR: %[[BLK1:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
// CIR: cir.store align(8) %[[BLK1]], %[[PTR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: %[[BLK2:.*]] = cir.block_address <@D, "LABEL_A"> : !cir.ptr<!void>
// CIR: cir.store align(8) %[[BLK2]], %[[PTR2]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.br ^bb1
// CIR: ^bb1: // pred: ^bb0
// 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>>
// CIR: cir.return
34 changes: 34 additions & 0 deletions clang/test/CIR/IR/block-adress.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s

!void = !cir.void

module {
cir.func @block_address(){
%0 = cir.block_address <@block_address, "label"> : !cir.ptr<!void>
cir.br ^bb1
^bb1:
cir.label "label"
cir.return
}
// CHECK: cir.func @block_address
// CHECK: %0 = cir.block_address <@block_address, "label"> : !cir.ptr<!void>
// CHECK: cir.br ^bb1
// CHECK: ^bb1:
// CHECK: cir.label "label"
// CHECK: cir.return

cir.func @block_address_inside_scope() -> () {
cir.scope{
%0 = cir.block_address <@block_address_inside_scope, "label"> : !cir.ptr<!void>
}
cir.br ^bb1
^bb1:
cir.label "label"
cir.return
}
// CHECK: cir.func @block_address_inside_scope
// CHECK: cir.scope
// CHECK: %0 = cir.block_address <@block_address_inside_scope, "label"> : !cir.ptr<!void>
// CHECK: cir.label "label"
// CHECK: cir.return
}
21 changes: 21 additions & 0 deletions clang/test/CIR/IR/invalid-block-address.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: cir-opt %s -verify-diagnostics -split-input-file

!void = !cir.void

// expected-error@+1 {{expects an existing label target in the referenced function}}
cir.func @bad_block_address() -> () {
%0 = cir.block_address <@bad_block_address, "label"> : !cir.ptr<!void>
cir.br ^bb1
^bb1:
cir.label "wrong_label"
cir.return
}

// expected-error@+1 {{blockaddress references a different function}}
cir.func @bad_block_func() -> () {
%0 = cir.block_address <@mismatch_func, "label"> : !cir.ptr<!void>
cir.br ^bb1
^bb1:
cir.label "label"
cir.return
}
62 changes: 62 additions & 0 deletions clang/test/CIR/Transforms/goto_solver.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// RUN: cir-opt %s -cir-goto-solver --verify-roundtrip -o - | FileCheck %s

!void = !cir.void

cir.func @a(){
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
%1 = cir.block_address <@a, "label1"> : !cir.ptr<!void>
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
cir.br ^bb1
^bb1:
cir.label "label1"
cir.br ^bb2
^bb2:
// This label is not referenced by any blockaddressOp, so it should be removed
cir.label "label2"
cir.return
}

// CHECK: cir.func @a()
// CHECK: %1 = cir.block_address <@a, "label1"> : !cir.ptr<!void>
// CHECK: ^bb1:
// CHECK: cir.label "label1"
// CHECK: cir.br ^bb2
// CHECK: ^bb2:
// CHECK-NOT: cir.label "label2"

cir.func @b(){
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
%1 = cir.block_address <@b, "label1"> : !cir.ptr<!void>
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
cir.goto "label2"
^bb1:
cir.label "label1"
cir.br ^bb2
^bb2:
// This label is not referenced by any blockaddressOp, so it should be removed
cir.label "label2"
cir.return
}

// CHECK: cir.func @b() {
// CHECK: %1 = cir.block_address <@b, "label1"> : !cir.ptr<!void>
// CHECK: cir.store align(8) %1, {{.*}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CHECK: cir.br ^bb2
// CHECK: ^bb1:
// CHECK: cir.label "label1"
// CHECK: cir.br ^bb2
// CHECK: ^bb2:
// CHECK-NOT: cir.label "label2"

cir.func @c() {
cir.label "label1"
%0 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["ptr", init] {alignment = 8 : i64}
%1 = cir.block_address <@c, "label1"> : !cir.ptr<!void>
cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
cir.return
}

// CHECK: cir.func @c
// CHECK: cir.label "label1"
// CHECK: %1 = cir.block_address <@c, "label1"> : !cir.ptr<!void>
// CHECK: cir.store align(8) %1, {{.*}} : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
4 changes: 4 additions & 0 deletions clang/tools/cir-opt/cir-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ int main(int argc, char **argv) {
return mlir::createHoistAllocasPass();
});

::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
return mlir::createGotoSolverPass();
});

mlir::registerTransformsPasses();

return mlir::asMainReturnCode(MlirOptMain(
Expand Down
Loading