[clang] 916218c - [CIR] Upstream GotoOp (#153701)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 18 08:25:44 PDT 2025
Author: Andres-Salamanca
Date: 2025-08-18T10:25:40-05:00
New Revision: 916218ccbd72164071e74a0b145c17fd7db03667
URL: https://github.com/llvm/llvm-project/commit/916218ccbd72164071e74a0b145c17fd7db03667
DIFF: https://github.com/llvm/llvm-project/commit/916218ccbd72164071e74a0b145c17fd7db03667.diff
LOG: [CIR] Upstream GotoOp (#153701)
This PR upstreams `GotoOp`. It moves some tests from the `goto` test
file to the `label` test file, and adds verify logic to `FuncOp`. The
gotosSolver, required for lowering, will be implemented in a future PR.
Added:
clang/test/CIR/CodeGen/goto.cpp
clang/test/CIR/IR/invalid-goto.cir
Modified:
clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/lib/CIR/CodeGen/CIRGenStmt.cpp
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/test/CIR/CodeGen/label.c
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 0bf3cb26be850..6244d34300263 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -504,8 +504,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
auto last =
std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
- // TODO: Add LabelOp missing feature here
- return mlir::isa<cir::AllocaOp>(&op);
+ return mlir::isa<cir::AllocaOp, cir::LabelOp>(&op);
});
if (last != block->rend())
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 3bfa29b9c3472..129a6760c935a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1060,6 +1060,62 @@ def CIR_BrOp : CIR_Op<"br",[
}];
}
+//===----------------------------------------------------------------------===//
+// GotoOp
+//===----------------------------------------------------------------------===//
+
+def CIR_GotoOp : CIR_Op<"goto", [Terminator]> {
+ let description = [{
+
+ Transfers control to the specified `label`. This requires a corresponding
+ `cir.label` to exist and is used by to represent source level `goto`s
+ that jump across region boundaries. Alternatively, `cir.br` is used to
+ construct goto's that don't violate such boundaries.
+
+ `cir.goto` is completely symbolic (i.e. it "jumps" on a label that isn't
+ yet materialized) and should be taken into account by passes and analysis
+ when deciding if it's safe to make some assumptions about a given region
+ or basic block.
+
+ Example:
+ ```C++
+ int test(int x) {
+ if (x)
+ goto label;
+ {
+ x = 10;
+ label:
+ return x;
+ }
+ }
+ ```
+
+ ```mlir
+ cir.scope { // REGION #1
+ %2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+ %3 = cir.cast(int_to_bool, %2 : !s32i), !cir.bool
+ cir.if %3 {
+ cir.goto "label"
+ }
+ }
+ cir.scope { // REGION #2
+ %2 = cir.const #cir.int<10> : !s32i
+ cir.store %2, %0 : !s32i, !cir.ptr<!s32i>
+ cir.br ^bb1
+ ^bb1: // pred: ^bb0
+ cir.label "label"
+ %3 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+ cir.store %3, %1 : !s32i, !cir.ptr<!s32i>
+ %4 = cir.load %1 : !cir.ptr<!s32i>, !s32i
+ cir.return %4 : !s32i
+ }
+ cir.unreachable
+ ```
+ }];
+ let arguments = (ins StrAttr:$label);
+ let assemblyFormat = [{ $label attr-dict }];
+}
+
//===----------------------------------------------------------------------===//
// LabelOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 9a887ec047f86..554e46414c9a7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1120,6 +1120,8 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
+ mlir::LogicalResult emitGotoStmt(const clang::GotoStmt &s);
+
void emitImplicitAssignmentOperatorBody(FunctionArgList &args);
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index d1e4a14824011..d83018e513707 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -252,6 +252,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
else
emitCompoundStmt(cast<CompoundStmt>(*s));
break;
+ case Stmt::GotoStmtClass:
+ return emitGotoStmt(cast<GotoStmt>(*s));
case Stmt::ContinueStmtClass:
return emitContinueStmt(cast<ContinueStmt>(*s));
@@ -435,6 +437,24 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
return mlir::success();
}
+mlir::LogicalResult CIRGenFunction::emitGotoStmt(const clang::GotoStmt &s) {
+ // FIXME: LLVM codegen inserts emit a stop point here for debug info
+ // sake when the insertion point is available, but doesn't do
+ // anything special when there isn't. We haven't implemented debug
+ // info support just yet, look at this again once we have it.
+ assert(!cir::MissingFeatures::generateDebugInfo());
+
+ cir::GotoOp::create(builder, getLoc(s.getSourceRange()),
+ s.getLabel()->getName());
+
+ // A goto marks the end of a block, create a new one for codegen after
+ // emitGotoStmt can resume building in that block.
+ // Insert the new block to continue codegen after goto.
+ builder.createBlock(builder.getBlock()->getParent());
+
+ return mlir::success();
+}
+
mlir::LogicalResult
CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) {
builder.createContinue(getLoc(s.getContinueLoc()));
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 50246007b1072..220927601f74e 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -22,6 +22,8 @@
#include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc"
#include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc"
#include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/SetOperations.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/LogicalResult.h"
#include <numeric>
@@ -1647,9 +1649,28 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
}
}
-// TODO(CIR): The properties of functions that require verification haven't
-// been implemented yet.
-mlir::LogicalResult cir::FuncOp::verify() { return success(); }
+mlir::LogicalResult cir::FuncOp::verify() {
+
+ llvm::SmallSet<llvm::StringRef, 16> labels;
+ llvm::SmallSet<llvm::StringRef, 16> gotos;
+
+ 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());
+ }
+ });
+
+ if (!labels.empty() || !gotos.empty()) {
+ llvm::SmallSet<llvm::StringRef, 16> mismatched =
+ llvm::set_
diff erence(gotos, labels);
+
+ if (!mismatched.empty())
+ return emitOpError() << "goto/label mismatch";
+ }
+ return success();
+}
//===----------------------------------------------------------------------===//
// BinOp
diff --git a/clang/test/CIR/CodeGen/goto.cpp b/clang/test/CIR/CodeGen/goto.cpp
new file mode 100644
index 0000000000000..13ca65344a150
--- /dev/null
+++ b/clang/test/CIR/CodeGen/goto.cpp
@@ -0,0 +1,210 @@
+// 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 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+int shouldNotGenBranchRet(int x) {
+ if (x > 5)
+ goto err;
+ return 0;
+err:
+ return -1;
+}
+// CIR: cir.func dso_local @_Z21shouldNotGenBranchReti
+// CIR: cir.if {{.*}} {
+// CIR: cir.goto "err"
+// CIR: }
+// CIR: [[ZERO:%.*]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.store [[ZERO]], [[RETVAL:%.*]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.br ^bb1
+// CIR: ^bb1:
+// CIR: [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.return [[RET]] : !s32i
+// CIR: ^bb2:
+// CIR: cir.label "err"
+// CIR: [[ONE:%.*]] = cir.const #cir.int<1> : !s32i
+// CIR: [[MINUS:%.*]] = cir.unary(minus, [[ONE]]) nsw : !s32i, !s32i
+// CIR: cir.store [[MINUS]], [[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.br ^bb1
+
+// OGCG: define dso_local noundef i32 @_Z21shouldNotGenBranchReti
+// OGCG: if.then:
+// OGCG: br label %err
+// OGCG: if.end:
+// OGCG: br label %return
+// OGCG: err:
+// OGCG: br label %return
+// OGCG: return:
+
+int shouldGenBranch(int x) {
+ if (x > 5)
+ goto err;
+ x++;
+err:
+ return -1;
+}
+// CIR: cir.func dso_local @_Z15shouldGenBranchi
+// CIR: cir.if {{.*}} {
+// CIR: cir.goto "err"
+// CIR: }
+// CIR: cir.br ^bb1
+// CIR: ^bb1:
+// CIR: cir.label "err"
+
+// OGCG: define dso_local noundef i32 @_Z15shouldGenBranchi
+// OGCG: if.then:
+// OGCG: br label %err
+// OGCG: if.end:
+// OGCG: br label %err
+// OGCG: err:
+// OGCG: ret
+
+void severalLabelsInARow(int a) {
+ int b = a;
+ goto end1;
+ b = b + 1;
+ goto end2;
+end1:
+end2:
+ b = b + 2;
+}
+// CIR: cir.func dso_local @_Z19severalLabelsInARowi
+// CIR: cir.goto "end1"
+// CIR: ^bb[[#BLK1:]]
+// CIR: cir.goto "end2"
+// CIR: ^bb[[#BLK2:]]:
+// CIR: cir.label "end1"
+// CIR: cir.br ^bb[[#BLK3:]]
+// CIR: ^bb[[#BLK3]]:
+// CIR: cir.label "end2"
+
+// OGCG: define dso_local void @_Z19severalLabelsInARowi
+// OGCG: br label %end1
+// OGCG: end1:
+// OGCG: br label %end2
+// OGCG: end2:
+// OGCG: ret
+
+void severalGotosInARow(int a) {
+ int b = a;
+ goto end;
+ goto end;
+end:
+ b = b + 2;
+}
+// CIR: cir.func dso_local @_Z18severalGotosInARowi
+// CIR: cir.goto "end"
+// CIR: ^bb[[#BLK1:]]:
+// CIR: cir.goto "end"
+// CIR: ^bb[[#BLK2:]]:
+// CIR: cir.label "end"
+
+// OGCG: define dso_local void @_Z18severalGotosInARowi(i32 noundef %a) #0 {
+// OGCG: br label %end
+// OGCG: end:
+// OGCG: ret void
+
+extern "C" void action1();
+extern "C" void action2();
+extern "C" void multiple_non_case(int v) {
+ switch (v) {
+ default:
+ action1();
+ l2:
+ action2();
+ break;
+ }
+}
+
+// CIR: cir.func dso_local @multiple_non_case
+// CIR: cir.switch
+// CIR: cir.case(default, []) {
+// CIR: cir.call @action1()
+// CIR: cir.br ^[[BB1:[a-zA-Z0-9]+]]
+// CIR: ^[[BB1]]:
+// CIR: cir.label
+// CIR: cir.call @action2()
+// CIR: cir.break
+
+// OGCG: define dso_local void @multiple_non_case
+// OGCG: sw.default:
+// OGCG: call void @action1()
+// OGCG: br label %l2
+// OGCG: l2:
+// OGCG: call void @action2()
+// OGCG: br label [[BREAK:%.*]]
+
+extern "C" void case_follow_label(int v) {
+ switch (v) {
+ case 1:
+ label:
+ case 2:
+ action1();
+ break;
+ default:
+ action2();
+ goto label;
+ }
+}
+
+// CIR: cir.func dso_local @case_follow_label
+// CIR: cir.switch
+// CIR: cir.case(equal, [#cir.int<1> : !s32i]) {
+// CIR: cir.label "label"
+// CIR: cir.case(equal, [#cir.int<2> : !s32i]) {
+// CIR: cir.call @action1()
+// CIR: cir.break
+// CIR: cir.case(default, []) {
+// CIR: cir.call @action2()
+// CIR: cir.goto "label"
+
+// OGCG: define dso_local void @case_follow_label
+// OGCG: sw.bb:
+// OGCG: br label %label
+// OGCG: label:
+// OGCG: br label %sw.bb1
+// OGCG: sw.bb1:
+// OGCG: call void @action1()
+// OGCG: br label %sw.epilog
+// OGCG: sw.default:
+// OGCG: call void @action2()
+// OGCG: br label %label
+// OGCG: sw.epilog:
+// OGCG: ret void
+
+extern "C" void default_follow_label(int v) {
+ switch (v) {
+ case 1:
+ case 2:
+ action1();
+ break;
+ label:
+ default:
+ action2();
+ goto label;
+ }
+}
+
+// CIR: cir.func dso_local @default_follow_label
+// CIR: cir.switch
+// CIR: cir.case(equal, [#cir.int<1> : !s32i]) {
+// CIR: cir.yield
+// CIR: cir.case(equal, [#cir.int<2> : !s32i]) {
+// CIR: cir.call @action1()
+// CIR: cir.break
+// CIR: cir.label "label"
+// CIR: cir.case(default, []) {
+// CIR: cir.call @action2()
+// CIR: cir.goto "label"
+
+// OGCG: define dso_local void @default_follow_label
+// OGCG: sw.bb:
+// OGCG: call void @action1()
+// OGCG: br label %sw.epilog
+// OGCG: label:
+// OGCG: br label %sw.default
+// OGCG: sw.default:
+// OGCG: call void @action2()
+// OGCG: br label %label
+// OGCG: sw.epilog:
+// OGCG: ret void
diff --git a/clang/test/CIR/CodeGen/label.c b/clang/test/CIR/CodeGen/label.c
index 2a515fc4046e8..797c44475a621 100644
--- a/clang/test/CIR/CodeGen/label.c
+++ b/clang/test/CIR/CodeGen/label.c
@@ -101,3 +101,39 @@ void after_unreachable() {
// OGCG: unreachable
// OGCG: label:
// OGCG: ret void
+
+void labelWithoutMatch() {
+end:
+ return;
+}
+// CIR: cir.func no_proto dso_local @labelWithoutMatch
+// CIR: cir.label "end"
+// CIR: cir.return
+// CIR: }
+
+// OGCG: define dso_local void @labelWithoutMatch
+// OGCG: br label %end
+// OGCG: end:
+// OGCG: ret void
+
+struct S {};
+struct S get();
+void bar(struct S);
+
+void foo() {
+ {
+ label:
+ bar(get());
+ }
+}
+
+// CIR: cir.func no_proto dso_local @foo
+// CIR: cir.scope {
+// CIR: cir.label "label"
+// CIR: %0 = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["agg.tmp0"]
+
+// OGCG: define dso_local void @foo()
+// OGCG: %agg.tmp = alloca %struct.S, align 1
+// OGCG: %undef.agg.tmp = alloca %struct.S, align 1
+// OGCG: br label %label
+// OGCG: label:
diff --git a/clang/test/CIR/IR/invalid-goto.cir b/clang/test/CIR/IR/invalid-goto.cir
new file mode 100644
index 0000000000000..9f58bac92fa3f
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-goto.cir
@@ -0,0 +1,9 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// expected-error at +1 {{goto/label mismatch}}
+cir.func @bad_goto() -> () {
+ cir.goto "somewhere"
+^bb1:
+ cir.label "label"
+ cir.return
+}
More information about the cfe-commits
mailing list