[clang] [CIR] Upstream simple function bodies (PR #127674)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 18 09:46:46 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: David Olsen (dkolsen-pgi)
<details>
<summary>Changes</summary>
Enable ClangIR generation for very simple functions. The functions have to return `void` or an integral type, contain only compound statements or `return` statements, and `return` statement expressions can only be integral literals of the correct type. The functions can have parameters, but those are currently ignored because there is no way to access them.
This change intentionally focuses on breadth (introducing scopes, statements, and expressions) rather than depth, because it enables people to work on upstreaming in parallel without interference.
The new ClangIR ops in this change are `ReturnOp`, `YieldOp`, `ScopeOp`, and `TrapOp`. These operations are complete (except for the `ParentOneOf` property) and shouldn't require further upstreaming changes. Significant additions were made to `FuncOp`, adding a type and a region, but that operation is still far from complete.
The classes `ScalarExprEmitter` and `CIRGenFunction`, along with the `emit*` functions in `CIRGenFunction` that generate ClangIR for statements, are new in this change. All of these are very incomplete and will be filled out in later upstreaming patches.
Existing test `hello.c` is removed and replaced by the new test `func-simple.cpp`. This tests all forms of functions that are currently supported.
---
Patch is 53.46 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/127674.diff
15 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+229-9)
- (added) clang/include/clang/CIR/TypeEvaluationKind.h (+21)
- (added) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+70)
- (added) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+203)
- (added) clang/lib/CIR/CodeGen/CIRGenFunction.h (+134)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+68-6)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+25)
- (added) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+128)
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+19-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+12-2)
- (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+3)
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+231-2)
- (added) clang/test/CIR/func-simple.cpp (+53)
- (modified) clang/test/CIR/global-var-simple.cpp (+1-1)
- (removed) clang/test/CIR/hello.c (-5)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index b15e0415360ea..45d39807b35c8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -11,8 +11,8 @@
///
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_CIR_DIALECT_IR_CIROPS
-#define LLVM_CLANG_CIR_DIALECT_IR_CIROPS
+#ifndef CLANG_CIR_DIALECT_IR_CIROPS_TD
+#define CLANG_CIR_DIALECT_IR_CIROPS_TD
include "clang/CIR/Dialect/IR/CIRDialect.td"
include "clang/CIR/Dialect/IR/CIRTypes.td"
@@ -115,6 +115,165 @@ def ConstantOp : CIR_Op<"const",
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// ReturnOp
+//===----------------------------------------------------------------------===//
+
+def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp"]>,
+ Terminator]> {
+ let summary = "Return from function";
+ let description = [{
+ The "return" operation represents a return operation within a function.
+ The operation takes an optional operand and produces no results.
+ The operand type must match the signature of the function that contains
+ the operation.
+
+ ```mlir
+ func @foo() -> i32 {
+ ...
+ cir.return %0 : i32
+ }
+ ```
+ }];
+
+ // The return operation takes an optional input operand to return. This
+ // value must match the return type of the enclosing function.
+ let arguments = (ins Variadic<CIR_AnyType>:$input);
+
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
+ // Allow building a ReturnOp with no return operand.
+ let builders = [
+ OpBuilder<(ins), [{ build($_builder, $_state, std::nullopt); }]>
+ ];
+
+ // Provide extra utility definitions on the c++ operation class definition.
+ let extraClassDeclaration = [{
+ bool hasOperand() { return getNumOperands() != 0; }
+ }];
+
+ let hasVerifier = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// YieldOp
+//===----------------------------------------------------------------------===//
+
+def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
+ ParentOneOf<["ScopeOp"]>]> {
+ let summary = "Represents the default branching behaviour of a region";
+ let description = [{
+ The `cir.yield` operation terminates regions on different CIR operations,
+ and it is used to represent the default branching behaviour of a region.
+ Said branching behaviour is determinted by the parent operation. For
+ example, a yield in a `switch-case` region implies a fallthrough, while
+ a yield in a `cir.if` region implies a branch to the exit block, and so
+ on.
+
+ In some cases, it might yield an SSA value and the semantics of how the
+ values are yielded is defined by the parent operation. For example, a
+ `cir.ternary` operation yields a value from one of its regions.
+
+ As a general rule, `cir.yield` must be explicitly used whenever a region has
+ more than one block and no terminator, or within `cir.switch` regions not
+ `cir.return` terminated.
+
+ Examples:
+ ```mlir
+ cir.if %4 {
+ ...
+ cir.yield
+ }
+
+ cir.switch (%5) [
+ case (equal, 3) {
+ ...
+ cir.yield
+ }, ...
+ ]
+
+ cir.scope {
+ ...
+ cir.yield
+ }
+
+ %x = cir.scope {
+ ...
+ cir.yield %val
+ }
+
+ %y = cir.ternary {
+ ...
+ cir.yield %val : i32
+ } : i32
+ ```
+ }];
+
+ let arguments = (ins Variadic<CIR_AnyType>:$args);
+ let assemblyFormat = "($args^ `:` type($args))? attr-dict";
+ let builders = [
+ OpBuilder<(ins), [{ /* nothing to do */ }]>,
+ ];
+}
+
+//===----------------------------------------------------------------------===//
+// ScopeOp
+//===----------------------------------------------------------------------===//
+
+def ScopeOp : CIR_Op<"scope", [
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]> {
+ let summary = "Represents a C/C++ scope";
+ let description = [{
+ `cir.scope` contains one region and defines a strict "scope" for all new
+ values produced within its blocks.
+
+ The region can contain an arbitrary number of blocks but usually defaults
+ to one and can optionally return a value (useful for representing values
+ coming out of C++ full-expressions) via `cir.yield`:
+
+
+ ```mlir
+ %rvalue = cir.scope {
+ ...
+ cir.yield %value
+ }
+ ```
+
+ The blocks can be terminated by `cir.yield`, `cir.return` or `cir.throw`.
+ If `cir.scope` yields no value, the `cir.yield` can be left out, and
+ will be inserted implicitly.
+ }];
+
+ let results = (outs Optional<CIR_AnyType>:$results);
+ let regions = (region AnyRegion:$scopeRegion);
+
+ let hasVerifier = 1;
+ let skipDefaultBuilders = 1;
+ let assemblyFormat = [{
+ custom<OmittedTerminatorRegion>($scopeRegion) (`:` type($results)^)? attr-dict
+ }];
+
+ let extraClassDeclaration = [{
+ /// Determine whether the scope is empty, meaning it contains a single block
+ /// terminated by a cir.yield.
+ bool isEmpty() {
+ auto &entry = getRegion().front();
+ return getRegion().hasOneBlock() &&
+ llvm::isa<YieldOp>(entry.front());
+ }
+ }];
+
+ let builders = [
+ // Scopes for yielding values.
+ OpBuilder<(ins
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Type &, mlir::Location)>":$scopeBuilder)>,
+ // Scopes without yielding values.
+ OpBuilder<(ins "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$scopeBuilder)>
+ ];
+}
+
//===----------------------------------------------------------------------===//
// GlobalOp
//===----------------------------------------------------------------------===//
@@ -158,25 +317,86 @@ def GlobalOp : CIR_Op<"global"> {
// FuncOp
//===----------------------------------------------------------------------===//
-// TODO(CIR): For starters, cir.func has only name, nothing else. The other
-// properties of a function will be added over time as more of ClangIR is
-// upstreamed.
+// TODO(CIR): FuncOp is still a tiny shell of what it will become. Many more
+// properties and attributes will be added as upstreaming continues.
-def FuncOp : CIR_Op<"func"> {
+def FuncOp : CIR_Op<"func", [
+ AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
+ IsolatedFromAbove
+]> {
let summary = "Declare or define a function";
let description = [{
The `cir.func` operation defines a function, similar to the `mlir::FuncOp`
built-in.
}];
- let arguments = (ins SymbolNameAttr:$sym_name);
+ let arguments = (ins SymbolNameAttr:$sym_name,
+ TypeAttrOf<CIR_FuncType>:$function_type,
+ OptionalAttr<DictArrayAttr>:$arg_attrs,
+ OptionalAttr<DictArrayAttr>:$res_attrs);
+
+ let regions = (region AnyRegion:$body);
let skipDefaultBuilders = 1;
- let builders = [OpBuilder<(ins "llvm::StringRef":$sym_name)>];
+ let builders = [OpBuilder<(ins "llvm::StringRef":$sym_name,
+ "FuncType":$type)>];
+
+ let extraClassDeclaration = [{
+ /// Returns the region on the current operation that is callable. This may
+ /// return null in the case of an external callable object, e.g. an external
+ /// function.
+ ::mlir::Region *getCallableRegion();
+
+ /// Returns the results types that the callable region produces when
+ /// executed.
+ llvm::ArrayRef<mlir::Type> getCallableResults() {
+ return getFunctionType().getReturnTypes();
+ }
+
+ /// Returns the argument types of this function.
+ llvm::ArrayRef<mlir::Type> getArgumentTypes() {
+ return getFunctionType().getInputs();
+ }
+
+ /// Returns 0 or 1 result type of this function (0 in the case of a function
+ /// returing void)
+ llvm::ArrayRef<mlir::Type> getResultTypes() {
+ return getFunctionType().getReturnTypes();
+ }
+
+ /// Hook for OpTrait::FunctionOpInterfaceTrait, called after verifying that
+ /// the 'type' attribute is present and checks if it holds a function type.
+ /// Ensures getType, getNumFuncArguments, and getNumFuncResults can be
+ /// called safely.
+ llvm::LogicalResult verifyType();
+
+ //===------------------------------------------------------------------===//
+ // SymbolOpInterface Methods
+ //===------------------------------------------------------------------===//
+
+ bool isDeclaration();
+ }];
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
}
-#endif // LLVM_CLANG_CIR_DIALECT_IR_CIROPS
+//===----------------------------------------------------------------------===//
+// TrapOp
+//===----------------------------------------------------------------------===//
+
+def TrapOp : CIR_Op<"trap", [Terminator]> {
+ let summary = "Exit the program abnormally";
+ let description = [{
+ The cir.trap operation causes the program to exit abnormally. The
+ implementations may implement this operation with different mechanisms. For
+ example, an implementation may implement this operation by calling abort,
+ while another implementation may implement this operation by executing an
+ illegal instruction.
+ }];
+
+ let assemblyFormat = "attr-dict";
+}
+
+#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/include/clang/CIR/TypeEvaluationKind.h b/clang/include/clang/CIR/TypeEvaluationKind.h
new file mode 100644
index 0000000000000..5d65eeb9d25b9
--- /dev/null
+++ b/clang/include/clang/CIR/TypeEvaluationKind.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_TYPEEVALUATIONKIND_H
+#define CLANG_CIR_TYPEEVALUATIONKIND_H
+
+namespace cir {
+
+// This is copied from clang/lib/CodeGen/CodeGenFunction.h. That file (1) is
+// not available as an include from ClangIR files, and (2) has lots of stuff
+// that we don't want in ClangIR.
+enum TypeEvaluationKind { TEK_Scalar, TEK_Complex, TEK_Aggregate };
+
+} // namespace cir
+
+#endif // CLANG_CIR_TYPEEVALUATIONKIND_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
new file mode 100644
index 0000000000000..b802705ca8fdc
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Emit Expr nodes with scalar CIR types as CIR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+
+#include "mlir/IR/Value.h"
+
+#include <cassert>
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+namespace {
+
+class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
+ CIRGenFunction &cgf;
+ CIRGenBuilderTy &builder;
+ bool ignoreResultAssign;
+
+public:
+ ScalarExprEmitter(CIRGenFunction &cgf, CIRGenBuilderTy &builder,
+ bool ira = false)
+ : cgf(cgf), builder(builder), ignoreResultAssign(ira) {}
+
+ //===--------------------------------------------------------------------===//
+ // Visitor Methods
+ //===--------------------------------------------------------------------===//
+
+ mlir::Value Visit(Expr *e) {
+ return StmtVisitor<ScalarExprEmitter, mlir::Value>::Visit(e);
+ }
+
+ mlir::Value VisitStmt(Stmt *s) {
+ llvm_unreachable("Statement passed to ScalarExprEmitter");
+ }
+
+ mlir::Value VisitExpr(Expr *e) {
+ cgf.getCIRGenModule().errorNYI(
+ e->getSourceRange(), "scalar expression kind: ", e->getStmtClassName());
+ return {};
+ }
+
+ mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
+ mlir::Type type = cgf.convertType(e->getType());
+ return builder.create<cir::ConstantOp>(
+ cgf.getLoc(e->getExprLoc()), type,
+ builder.getAttr<cir::IntAttr>(type, e->getValue()));
+ }
+};
+} // namespace
+
+/// Emit the computation of the specified expression of scalar type.
+mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) {
+ assert(e && hasScalarEvaluationKind(e->getType()) &&
+ "Invalid scalar expression to emit");
+
+ return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e));
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
new file mode 100644
index 0000000000000..a6d176166773d
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -0,0 +1,203 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Internal per-function state used for AST-to-ClangIR code gen
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+
+#include "clang/AST/GlobalDecl.h"
+
+#include <cassert>
+
+namespace clang::CIRGen {
+
+CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder,
+ bool suppressNewContext)
+ : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) {}
+
+CIRGenFunction::~CIRGenFunction() {}
+
+// This is copied from clang/lib/CodeGen/CodeGenFunction.cpp
+cir::TypeEvaluationKind CIRGenFunction::getEvaluationKind(QualType type) {
+ type = type.getCanonicalType();
+ while (true) {
+ switch (type->getTypeClass()) {
+#define TYPE(name, parent)
+#define ABSTRACT_TYPE(name, parent)
+#define NON_CANONICAL_TYPE(name, parent) case Type::name:
+#define DEPENDENT_TYPE(name, parent) case Type::name:
+#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(name, parent) case Type::name:
+#include "clang/AST/TypeNodes.inc"
+ llvm_unreachable("non-canonical or dependent type in IR-generation");
+
+ case Type::ArrayParameter:
+ case Type::HLSLAttributedResource:
+ llvm_unreachable("NYI");
+
+ case Type::Auto:
+ case Type::DeducedTemplateSpecialization:
+ llvm_unreachable("undeduced type in IR-generation");
+
+ // Various scalar types.
+ case Type::Builtin:
+ case Type::Pointer:
+ case Type::BlockPointer:
+ case Type::LValueReference:
+ case Type::RValueReference:
+ case Type::MemberPointer:
+ case Type::Vector:
+ case Type::ExtVector:
+ case Type::ConstantMatrix:
+ case Type::FunctionProto:
+ case Type::FunctionNoProto:
+ case Type::Enum:
+ case Type::ObjCObjectPointer:
+ case Type::Pipe:
+ case Type::BitInt:
+ return cir::TEK_Scalar;
+
+ // Complexes.
+ case Type::Complex:
+ return cir::TEK_Complex;
+
+ // Arrays, records, and Objective-C objects.
+ case Type::ConstantArray:
+ case Type::IncompleteArray:
+ case Type::VariableArray:
+ case Type::Record:
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ return cir::TEK_Aggregate;
+
+ // We operate on atomic values according to their underlying type.
+ case Type::Atomic:
+ type = cast<AtomicType>(type)->getValueType();
+ continue;
+ }
+ llvm_unreachable("unknown type kind!");
+ }
+}
+
+mlir::Type CIRGenFunction::convertTypeForMem(QualType t) {
+ return cgm.getTypes().convertTypeForMem(t);
+}
+
+mlir::Type CIRGenFunction::convertType(QualType t) {
+ return cgm.getTypes().convertType(t);
+}
+
+mlir::Location CIRGenFunction::getLoc(SourceLocation srcLoc) {
+ // Some AST nodes might contain invalid source locations (e.g.
+ // CXXDefaultArgExpr), workaround that to still get something out.
+ if (srcLoc.isValid()) {
+ const SourceManager &sm = getContext().getSourceManager();
+ PresumedLoc pLoc = sm.getPresumedLoc(srcLoc);
+ StringRef filename = pLoc.getFilename();
+ return mlir::FileLineColLoc::get(builder.getStringAttr(filename),
+ pLoc.getLine(), pLoc.getColumn());
+ } else {
+ // Do our best...
+ assert(currSrcLoc && "expected to inherit some source location");
+ return *currSrcLoc;
+ }
+}
+
+mlir::Location CIRGenFunction::getLoc(SourceRange srcLoc) {
+ // Some AST nodes might contain invalid source locations (e.g.
+ // CXXDefaultArgExpr), workaround that to still get something out.
+ if (srcLoc.isValid()) {
+ mlir::Location beg = getLoc(srcLoc.getBegin());
+ mlir::Location end = getLoc(srcLoc.getEnd());
+ SmallVector<mlir::Location, 2> locs = {beg, end};
+ mlir::Attribute metadata;
+ return mlir::FusedLoc::get(locs, metadata, &getMLIRContext());
+ } else if (currSrcLoc) {
+ return *currSrcLoc;
+ }
+
+ // We're brave, but time to give up.
+ return builder.getUnknownLoc();
+}
+
+mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
+ SmallVector<mlir::Location, 2> locs = {lhs, rhs};
+ mlir::Attribute metadata;
+ return mlir::FusedLoc::get(locs, metadata, &getMLIRContext());
+}
+
+void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
+ cir::FuncOp fn, cir::FuncType funcType,
+ SourceLocation loc,
+ SourceLocation startLoc) {
+ assert(!curFn &&
+ "CIRGenFunction can only be used for one function at a time");
+
+ fnRetTy = returnType;
+ curFn = fn;
+
+ mlir::Block *entryBB = &fn.getBlocks().front();
+ builder.setInsertionPointToStart(entryBB);
+}
+
+void CIRGenFunction::finishFunction(SourceLocation endLoc) {}
+
+mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
+ auto result = mlir::LogicalResult::success();
+ if (const CompoundStmt *block = dyn_cast<CompoundStmt>(body))
+ emitCompoundStmtWithoutScope(*block);
+ else
+ result = emitStmt(body, /*useCurrentScope=*/true);
+ return result;
+}
+
+cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
+ cir::FuncType funcType) {
+ const auto funcDecl = cast<FunctionDecl>(gd.getDecl());
+ SourceLocation loc = funcDecl->getLocation();
+ Stmt *body = funcDecl->getBody();
+ SourceRange bodyRange =
+ body ? body->getSourceRange() : funcDecl->getLocation();
+
+ SourceLocRAIIObject fnLoc{*this, loc.isValid() ? getLoc(loc)
+ : builder.getUnknownLoc()};
+
+ mlir::Block *entryBB = fn.addEntryBlock();
+ builder.setInsertionPointToStart(entryBB);
+
+ startFunction(gd, funcDecl->getReturnType(), fn, funcType, loc,
+ bodyRange.getBegin());
+ if (mlir::failed(emitFunctionBody(body))) {
+ fn.erase();
+ return nullptr;
+ }
+
+ // This code to insert a cir.return or cir.trap at the end of the function is
+ // temporary until the function return code, including
+ // CIRGenFunction::LexicalScope::emitImplicitReturn(), is upstreamed.
+ mlir::Block &lastBlock = fn.getRegion().back();
+ if (lastBlock.empty() || !lastBlock.mightHaveTerminator() ||
+ !lastBlock.getTerminator()->hasTrait<mlir::OpTrait::IsTerminator>()) {
+ builder.setInsertionPointToEnd(&lastBlock);
+ if (mlir::isa<cir::VoidType>(funcType.getReturnType())) {
+ builder.create<cir::ReturnOp>(getLoc(bodyRange.getEnd()));
+ } else {
+ builder.create<cir::TrapOp>(getLoc(bodyRange.getEnd()));
+ }
+ }
+
+ if (mlir::failed(fn.verifyBody()))
+ return nullptr;
+
+ f...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/127674
More information about the cfe-commits
mailing list