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@+1 {{goto/label mismatch}} +cir.func @bad_goto() -> () { + cir.goto "somewhere" +^bb1: + cir.label "label" + cir.return +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits