Author: David Olsen Date: 2025-02-26T20:58:49-08:00 New Revision: 556eb8244201a81fff7b246561a677a782b69fa0
URL: https://github.com/llvm/llvm-project/commit/556eb8244201a81fff7b246561a677a782b69fa0 DIFF: https://github.com/llvm/llvm-project/commit/556eb8244201a81fff7b246561a677a782b69fa0.diff LOG: [CIR] Function type return type improvements (#128787) When a C or C++ function has a return type of `void`, the function type is now represented in MLIR as having no return type rather than having a return type of `!cir.void`. This avoids breaking MLIR invariants that require the number of return types and the number of return values to match. Change the assembly format for `cir::FuncType` from having a leading return type to having a trailing return type. In other words, change ``` !cir.func<!returnType (!argTypes)> ``` to ``` !cir.func<(!argTypes) -> !returnType)> ``` Unless the function returns `void`, in which case change ``` !cir.func<!cir.void (!argTypes)> ``` to ``` !cir.func<(!argTypes)> ``` Added: Modified: clang/include/clang/CIR/Dialect/IR/CIROps.td clang/include/clang/CIR/Dialect/IR/CIRTypes.td clang/lib/CIR/CodeGen/CIRGenTypes.cpp clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/lib/CIR/Dialect/IR/CIRTypes.cpp clang/test/CIR/IR/func.cir clang/test/CIR/IR/global.cir clang/test/CIR/func-simple.cpp clang/test/CIR/global-var-simple.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 45d39807b35c8..f9ce38588e436 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -365,12 +365,6 @@ def FuncOp : CIR_Op<"func", [ 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 //===------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index fc8edbcf3e166..a78e5eae08e33 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -287,32 +287,44 @@ def CIR_BoolType : def CIR_FuncType : CIR_Type<"Func", "func"> { let summary = "CIR function type"; let description = [{ - The `!cir.func` is a function type. It consists of a single return type, a - list of parameter types and can optionally be variadic. + The `!cir.func` is a function type. It consists of an optional return type, + a list of parameter types and can optionally be variadic. Example: ```mlir - !cir.func<!bool ()> - !cir.func<!s32i (!s8i, !s8i)> - !cir.func<!s32i (!s32i, ...)> + !cir.func<()> + !cir.func<() -> bool> + !cir.func<(!s8i, !s8i)> + !cir.func<(!s8i, !s8i) -> !s32i> + !cir.func<(!s32i, ...) -> !s32i> ``` }]; let parameters = (ins ArrayRefParameter<"mlir::Type">:$inputs, - "mlir::Type":$returnType, "bool":$varArg); + OptionalParameter<"mlir::Type">:$optionalReturnType, + "bool":$varArg); + // Use a custom parser to handle argument types with variadic elipsis. let assemblyFormat = [{ - `<` $returnType ` ` `(` custom<FuncTypeArgs>($inputs, $varArg) `>` + `<` custom<FuncTypeParams>($inputs, $varArg) (`->` $optionalReturnType^)? `>` }]; let builders = [ + // Create a FuncType, converting the return type from C-style to + // MLIR-style. If the given return type is `cir::VoidType`, ignore it + // and create the FuncType with no return type, which is how MLIR + // represents function types. TypeBuilderWithInferredContext<(ins "llvm::ArrayRef<mlir::Type>":$inputs, "mlir::Type":$returnType, CArg<"bool", "false">:$isVarArg), [{ - return $_get(returnType.getContext(), inputs, returnType, isVarArg); + return $_get(returnType.getContext(), inputs, + mlir::isa<cir::VoidType>(returnType) ? nullptr : returnType, + isVarArg); }]> ]; + let genVerifyDecl = 1; + let extraClassDeclaration = [{ /// Returns whether the function is variadic. bool isVarArg() const { return getVarArg(); } @@ -323,12 +335,17 @@ def CIR_FuncType : CIR_Type<"Func", "func"> { /// Returns the number of arguments to the function. unsigned getNumInputs() const { return getInputs().size(); } - /// Returns the result type of the function as an ArrayRef, enabling better - /// integration with generic MLIR utilities. + /// Get the C-style return type of the function, which is !cir.void if the + /// function returns nothing and the actual return type otherwise. + mlir::Type getReturnType() const; + + /// Get the MLIR-style return type of the function, which is an empty + /// ArrayRef if the function returns nothing and a single-element ArrayRef + /// with the actual return type otherwise. llvm::ArrayRef<mlir::Type> getReturnTypes() const; - /// Returns whether the function is returns void. - bool isVoid() const; + /// Does the function type return nothing? + bool hasVoidReturn() const; /// Returns a clone of this function type with the given argument /// and result types. diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 16aec10fda81e..dcfaaedc2ef57 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -60,7 +60,7 @@ bool CIRGenTypes::isFuncTypeConvertible(const FunctionType *ft) { mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType qft) { assert(qft.isCanonical()); const FunctionType *ft = cast<FunctionType>(qft.getTypePtr()); - // First, check whether we can build the full fucntion type. If the function + // First, check whether we can build the full function type. If the function // type depends on an incomplete type (e.g. a struct or enum), we cannot lower // the function type. if (!isFuncTypeConvertible(ft)) { diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index bfc74d4373f34..3f1be930d71e5 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -416,17 +416,6 @@ void cir::FuncOp::print(OpAsmPrinter &p) { } } -// Hook for OpTrait::FunctionLike, called after verifying that the 'type' -// attribute is present. This can check for preconditions of the -// getNumArguments hook not failing. -LogicalResult cir::FuncOp::verifyType() { - auto type = getFunctionType(); - if (!isa<cir::FuncType>(type)) - return emitOpError("requires '" + getFunctionTypeAttrName().str() + - "' attribute of function type"); - return success(); -} - // TODO(CIR): The properties of functions that require verification haven't // been implemented yet. mlir::LogicalResult cir::FuncOp::verify() { return success(); } diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index d1b143efb955e..8bdde54ad41f6 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -21,10 +21,11 @@ //===----------------------------------------------------------------------===// static mlir::ParseResult -parseFuncTypeArgs(mlir::AsmParser &p, llvm::SmallVector<mlir::Type> ¶ms, - bool &isVarArg); -static void printFuncTypeArgs(mlir::AsmPrinter &p, - mlir::ArrayRef<mlir::Type> params, bool isVarArg); +parseFuncTypeParams(mlir::AsmParser &p, llvm::SmallVector<mlir::Type> ¶ms, + bool &isVarArg); +static void printFuncTypeParams(mlir::AsmPrinter &p, + mlir::ArrayRef<mlir::Type> params, + bool isVarArg); //===----------------------------------------------------------------------===// // Get autogenerated stuff @@ -282,40 +283,32 @@ FuncType FuncType::clone(TypeRange inputs, TypeRange results) const { return get(llvm::to_vector(inputs), results[0], isVarArg()); } -mlir::ParseResult parseFuncTypeArgs(mlir::AsmParser &p, - llvm::SmallVector<mlir::Type> ¶ms, - bool &isVarArg) { +// Custom parser that parses function parameters of form `(<type>*, ...)`. +static mlir::ParseResult +parseFuncTypeParams(mlir::AsmParser &p, llvm::SmallVector<mlir::Type> ¶ms, + bool &isVarArg) { isVarArg = false; - // `(` `)` - if (succeeded(p.parseOptionalRParen())) - return mlir::success(); - - // `(` `...` `)` - if (succeeded(p.parseOptionalEllipsis())) { - isVarArg = true; - return p.parseRParen(); - } - - // type (`,` type)* (`,` `...`)? - mlir::Type type; - if (p.parseType(type)) - return mlir::failure(); - params.push_back(type); - while (succeeded(p.parseOptionalComma())) { - if (succeeded(p.parseOptionalEllipsis())) { - isVarArg = true; - return p.parseRParen(); - } - if (p.parseType(type)) - return mlir::failure(); - params.push_back(type); - } - - return p.parseRParen(); -} - -void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params, - bool isVarArg) { + return p.parseCommaSeparatedList( + AsmParser::Delimiter::Paren, [&]() -> mlir::ParseResult { + if (isVarArg) + return p.emitError(p.getCurrentLocation(), + "variadic `...` must be the last parameter"); + if (succeeded(p.parseOptionalEllipsis())) { + isVarArg = true; + return success(); + } + mlir::Type type; + if (failed(p.parseType(type))) + return failure(); + params.push_back(type); + return success(); + }); +} + +static void printFuncTypeParams(mlir::AsmPrinter &p, + mlir::ArrayRef<mlir::Type> params, + bool isVarArg) { + p << '('; llvm::interleaveComma(params, p, [&p](mlir::Type type) { p.printType(type); }); if (isVarArg) { @@ -326,11 +319,39 @@ void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params, p << ')'; } +/// Get the C-style return type of the function, which is !cir.void if the +/// function returns nothing and the actual return type otherwise. +mlir::Type FuncType::getReturnType() const { + if (hasVoidReturn()) + return cir::VoidType::get(getContext()); + return getOptionalReturnType(); +} + +/// Get the MLIR-style return type of the function, which is an empty +/// ArrayRef if the function returns nothing and a single-element ArrayRef +/// with the actual return type otherwise. llvm::ArrayRef<mlir::Type> FuncType::getReturnTypes() const { - return static_cast<detail::FuncTypeStorage *>(getImpl())->returnType; + if (hasVoidReturn()) + return {}; + // Can't use getOptionalReturnType() here because llvm::ArrayRef hold a + // pointer to its elements and doesn't do lifetime extension. That would + // result in returning a pointer to a temporary that has gone out of scope. + return getImpl()->optionalReturnType; } -bool FuncType::isVoid() const { return mlir::isa<VoidType>(getReturnType()); } +// Does the fuction type return nothing? +bool FuncType::hasVoidReturn() const { return !getOptionalReturnType(); } + +mlir::LogicalResult +FuncType::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError, + llvm::ArrayRef<mlir::Type> argTypes, mlir::Type returnType, + bool isVarArg) { + if (returnType && mlir::isa<cir::VoidType>(returnType)) { + emitError() << "!cir.func cannot have an explicit 'void' return type"; + return mlir::failure(); + } + return mlir::success(); +} //===----------------------------------------------------------------------===// // BoolType diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir index a32c3e697ed25..4077bd33e0438 100644 --- a/clang/test/CIR/IR/func.cir +++ b/clang/test/CIR/IR/func.cir @@ -2,18 +2,18 @@ module { // void empty() { } -cir.func @empty() -> !cir.void { +cir.func @empty() { cir.return } -// CHECK: cir.func @empty() -> !cir.void { +// CHECK: cir.func @empty() { // CHECK: cir.return // CHECK: } // void voidret() { return; } -cir.func @voidret() -> !cir.void { +cir.func @voidret() { cir.return } -// CHECK: cir.func @voidret() -> !cir.void { +// CHECK: cir.func @voidret() { // CHECK: cir.return // CHECK: } diff --git a/clang/test/CIR/IR/global.cir b/clang/test/CIR/IR/global.cir index 6c68ab0a501ff..9d187686d996c 100644 --- a/clang/test/CIR/IR/global.cir +++ b/clang/test/CIR/IR/global.cir @@ -30,9 +30,9 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { cir.global @ip = #cir.ptr<null> : !cir.ptr<!cir.int<s, 32>> cir.global @dp : !cir.ptr<!cir.double> cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>> - cir.global @fp : !cir.ptr<!cir.func<!cir.void ()>> - cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<!cir.int<s, 32> (!cir.int<s, 32>)>> - cir.global @fpvar : !cir.ptr<!cir.func<!cir.void (!cir.int<s, 32>, ...)>> + cir.global @fp : !cir.ptr<!cir.func<()>> + cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!cir.int<s, 32>) -> !cir.int<s, 32>>> + cir.global @fpvar : !cir.ptr<!cir.func<(!cir.int<s, 32>, ...)>> } // CHECK: cir.global @c : !cir.int<s, 8> @@ -64,6 +64,6 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { // CHECK: cir.global @ip = #cir.ptr<null> : !cir.ptr<!cir.int<s, 32>> // CHECK: cir.global @dp : !cir.ptr<!cir.double> // CHECK: cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>> -// CHECK: cir.global @fp : !cir.ptr<!cir.func<!cir.void ()>> -// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<!cir.int<s, 32> (!cir.int<s, 32>)>> -// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<!cir.void (!cir.int<s, 32>, ...)>> +// CHECK: cir.global @fp : !cir.ptr<!cir.func<()>> +// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!cir.int<s, 32>) -> !cir.int<s, 32>>> +// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<(!cir.int<s, 32>, ...)>> diff --git a/clang/test/CIR/func-simple.cpp b/clang/test/CIR/func-simple.cpp index 22c120d3404d3..3947055e300a0 100644 --- a/clang/test/CIR/func-simple.cpp +++ b/clang/test/CIR/func-simple.cpp @@ -2,12 +2,12 @@ // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s void empty() { } -// CHECK: cir.func @empty() -> !cir.void { +// CHECK: cir.func @empty() { // CHECK: cir.return // CHECK: } void voidret() { return; } -// CHECK: cir.func @voidret() -> !cir.void { +// CHECK: cir.func @voidret() { // CHECK: cir.return // CHECK: } diff --git a/clang/test/CIR/global-var-simple.cpp b/clang/test/CIR/global-var-simple.cpp index dfe8371668e2c..f8e233cd5fe33 100644 --- a/clang/test/CIR/global-var-simple.cpp +++ b/clang/test/CIR/global-var-simple.cpp @@ -92,10 +92,10 @@ char **cpp; // CHECK: cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>> void (*fp)(); -// CHECK: cir.global @fp : !cir.ptr<!cir.func<!cir.void ()>> +// CHECK: cir.global @fp : !cir.ptr<!cir.func<()>> int (*fpii)(int) = 0; -// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<!cir.int<s, 32> (!cir.int<s, 32>)>> +// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!cir.int<s, 32>) -> !cir.int<s, 32>>> void (*fpvar)(int, ...); -// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<!cir.void (!cir.int<s, 32>, ...)>> +// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<(!cir.int<s, 32>, ...)>> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits