-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[CIR] Better handling of void
function return
#128089
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -16,15 +16,19 @@ | |||||
#include "clang/CIR/Dialect/IR/CIRDialect.h" | ||||||
#include "llvm/ADT/TypeSwitch.h" | ||||||
|
||||||
#include <cassert> | ||||||
|
||||||
//===----------------------------------------------------------------------===// | ||||||
// CIR Custom Parser/Printer Signatures | ||||||
//===----------------------------------------------------------------------===// | ||||||
|
||||||
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); | ||||||
static mlir::ParseResult parseFuncType(mlir::AsmParser &p, | ||||||
mlir::Type &optionalReturnTypes, | ||||||
llvm::SmallVector<mlir::Type> ¶ms, | ||||||
bool &isVarArg); | ||||||
|
||||||
static void printFuncType(mlir::AsmPrinter &p, mlir::Type optionalReturnTypes, | ||||||
mlir::ArrayRef<mlir::Type> params, bool isVarArg); | ||||||
|
||||||
//===----------------------------------------------------------------------===// | ||||||
// Get autogenerated stuff | ||||||
|
@@ -331,9 +335,38 @@ 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) { | ||||||
// A special parser is needed for function returning void to handle the missing | ||||||
// type. | ||||||
static mlir::ParseResult parseFuncTypeReturn(mlir::AsmParser &p, | ||||||
mlir::Type &optionalReturnType) { | ||||||
if (succeeded(p.parseOptionalLParen())) { | ||||||
// If we have already a '(', the function has no return type | ||||||
optionalReturnType = {}; | ||||||
return mlir::success(); | ||||||
} | ||||||
mlir::Type type; | ||||||
if (p.parseType(type)) | ||||||
return mlir::failure(); | ||||||
if (isa<cir::VoidType>(type)) { | ||||||
// An explicit !cir.void means also no return type. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically a comment is enough to cause us to not do the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could put the comment before the if otherwise. |
||||||
optionalReturnType = {}; | ||||||
} else { | ||||||
// Otherwise use the actual type. | ||||||
optionalReturnType = type; | ||||||
} | ||||||
return p.parseLParen(); | ||||||
} | ||||||
|
||||||
// A special pretty-printer for function returning or not a result. | ||||||
static void printFuncTypeReturn(mlir::AsmPrinter &p, | ||||||
mlir::Type optionalReturnType) { | ||||||
if (optionalReturnType) | ||||||
p << optionalReturnType << ' '; | ||||||
} | ||||||
|
||||||
static mlir::ParseResult | ||||||
parseFuncTypeArgs(mlir::AsmParser &p, llvm::SmallVector<mlir::Type> ¶ms, | ||||||
bool &isVarArg) { | ||||||
isVarArg = false; | ||||||
// `(` `)` | ||||||
if (succeeded(p.parseOptionalRParen())) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see what is happening here now, not sure I like it? IMO, the parse-args function should be responsible for 'taking' BOTH of the parens, everything else should do a 'look-ahead' type parsing if necessary. THOUGH I see the latter doesn't seem to exist in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not familiar with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lanza @bcardosolopes Anyone have any ideas here? This parsing is REALLY weird, but we still need a way to know that we're 'done'. The other alternative is to change how these are printed/read (so it is something like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is the way the parsers are implemented to chain things ahead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can technically do it the other way around as mlir function types are structured and have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only question is what the 'void' type looks like? Could/should we still use the
That way we do The 'trailing return type' syntax is nice as it is consistent with the C++ language. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, thanks for the update! I was trying to judge opinions here, but the incubator review is sufficient to make sure those opinions are heard. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
+1 |
||||||
|
@@ -363,8 +396,10 @@ mlir::ParseResult parseFuncTypeArgs(mlir::AsmParser &p, | |||||
return p.parseRParen(); | ||||||
} | ||||||
|
||||||
void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params, | ||||||
bool isVarArg) { | ||||||
static void printFuncTypeArgs(mlir::AsmPrinter &p, | ||||||
mlir::ArrayRef<mlir::Type> params, | ||||||
bool isVarArg) { | ||||||
p << '('; | ||||||
llvm::interleaveComma(params, p, | ||||||
[&p](mlir::Type type) { p.printType(type); }); | ||||||
if (isVarArg) { | ||||||
|
@@ -375,11 +410,48 @@ void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params, | |||||
p << ')'; | ||||||
} | ||||||
|
||||||
// Use a custom parser to handle the optional return and argument types without | ||||||
// an optional anchor. | ||||||
static mlir::ParseResult parseFuncType(mlir::AsmParser &p, | ||||||
mlir::Type &optionalReturnTypes, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
llvm::SmallVector<mlir::Type> ¶ms, | ||||||
bool &isVarArg) { | ||||||
if (failed(parseFuncTypeReturn(p, optionalReturnTypes))) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return failure(); | ||||||
return parseFuncTypeArgs(p, params, isVarArg); | ||||||
} | ||||||
|
||||||
static void printFuncType(mlir::AsmPrinter &p, mlir::Type optionalReturnTypes, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
mlir::ArrayRef<mlir::Type> params, bool isVarArg) { | ||||||
printFuncTypeReturn(p, optionalReturnTypes); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
printFuncTypeArgs(p, params, isVarArg); | ||||||
} | ||||||
|
||||||
// Return the actual return type or an explicit !cir.void if the function does | ||||||
// not return anything | ||||||
mlir::Type FuncType::getReturnType() const { | ||||||
if (isVoid()) | ||||||
return cir::VoidType::get(getContext()); | ||||||
return static_cast<detail::FuncTypeStorage *>(getImpl())->optionalReturnType; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
/// Returns the result type of the function as an ArrayRef, enabling better | ||||||
/// integration with generic MLIR utilities. | ||||||
llvm::ArrayRef<mlir::Type> FuncType::getReturnTypes() const { | ||||||
return static_cast<detail::FuncTypeStorage *>(getImpl())->returnType; | ||||||
if (isVoid()) | ||||||
return {}; | ||||||
return static_cast<detail::FuncTypeStorage *>(getImpl())->optionalReturnType; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also to mirror There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No because it returns the real return types (plural) that any real MLIR user is waiting for: the actual return types, which in the case of C/C++ is 0 or 1 type. |
||||||
} | ||||||
|
||||||
bool FuncType::isVoid() const { return mlir::isa<VoidType>(getReturnType()); } | ||||||
// Whether the function returns void | ||||||
bool FuncType::isVoid() const { | ||||||
auto rt = | ||||||
static_cast<detail::FuncTypeStorage *>(getImpl())->optionalReturnType; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
assert((!rt || !mlir::isa<cir::VoidType>(rt)) && | ||||||
"The return type for a function returning void should be empty " | ||||||
"instead of a real !cir.void"); | ||||||
return !rt; | ||||||
} | ||||||
|
||||||
//===----------------------------------------------------------------------===// | ||||||
// PointerType Definitions | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,12 @@ | |
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I presume There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's definitely unfortunate that we're adding this parsing code but don't have any tests for it. I'm working on a PR to bring cir-opt over so we can do that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
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: } | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm perhaps a little confused as to what the format here is we're trying to parse. Why is just a
(
meanempty return type
?Or is this supposed to be the 'rest' of the function type? In that case, perhaps we need a 'look-ahead' instead of a parse?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this is a kind of look-ahead which is required when I introduced the change to remove the creation of an artificial
!cir.void
which used to have the consequence of breaking an MLIR invariant,number(return-types) == number(return-values)
.A C type like
char(int)
is lowered and pretty-printed as!cir.func<!cir.int<s, 8>(!cir.int<s, 32>)
whilevoid(int)
is lowered and pretty-printed as!cir.func<(!cir.int<s, 32>)
.For the MLIR functions themselves, they are handled by the
func
MLIR standard dialect with a syntax like:with an empty type for returning "void".
An alternate design could be to have a new keyword like
void
understood by the parser to avoid the manual look ahead in the parser as the MLIR default parser knows how to discriminate with prefix keywords. Thenvoid(int)
would be lowered and pretty-printed as!cir.func<void (!cir.int<s, 32>)
.