Skip to content

Commit 88e90df

Browse files
committed
[CIR] Better handling of void function return
Upstream llvm/clangir#1249 "Remove return !cir.void from IR and textual representation" C/C++ functions returning `void` had an explicit `!cir.void` return type while not having any returned value, which was breaking MLIR invariants when the CIR dialect is used in a greater context, for example with the inliner. Now a C/C++ function returning `void` has no return type and no return values, which satisfies the MLIR invariant about the same number of return types and returned values.
1 parent 6e7da07 commit 88e90df

File tree

6 files changed

+110
-23
lines changed

6 files changed

+110
-23
lines changed

clang/include/clang/CIR/Dialect/IR/CIRTypes.td

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,29 +273,36 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr",
273273
def CIR_FuncType : CIR_Type<"Func", "func"> {
274274
let summary = "CIR function type";
275275
let description = [{
276-
The `!cir.func` is a function type. It consists of a single return type, a
277-
list of parameter types and can optionally be variadic.
276+
The `!cir.func` is a function type. It consists of an optional return type,
277+
a list of parameter types and can optionally be variadic.
278278

279279
Example:
280280

281281
```mlir
282+
!cir.func<()>
282283
!cir.func<!bool ()>
284+
!cir.func<(!s8i, !s8i)>
283285
!cir.func<!s32i (!s8i, !s8i)>
284286
!cir.func<!s32i (!s32i, ...)>
285287
```
286288
}];
287289

288290
let parameters = (ins ArrayRefParameter<"mlir::Type">:$inputs,
289-
"mlir::Type":$returnType, "bool":$varArg);
291+
"mlir::Type":$optionalReturnType, "bool":$varArg);
292+
// Use a custom parser to handle the optional return and argument types
293+
// without an optional anchor.
290294
let assemblyFormat = [{
291-
`<` $returnType ` ` `(` custom<FuncTypeArgs>($inputs, $varArg) `>`
295+
`<` custom<FuncType>($optionalReturnType, $inputs, $varArg) `>`
292296
}];
293297

294298
let builders = [
299+
// Construct with an actual return type or explicit !cir.void
295300
TypeBuilderWithInferredContext<(ins
296301
"llvm::ArrayRef<mlir::Type>":$inputs, "mlir::Type":$returnType,
297302
CArg<"bool", "false">:$isVarArg), [{
298-
return $_get(returnType.getContext(), inputs, returnType, isVarArg);
303+
return $_get(returnType.getContext(), inputs,
304+
mlir::isa<cir::VoidType>(returnType) ? nullptr : returnType,
305+
isVarArg);
299306
}]>
300307
];
301308

@@ -309,11 +316,15 @@ def CIR_FuncType : CIR_Type<"Func", "func"> {
309316
/// Returns the number of arguments to the function.
310317
unsigned getNumInputs() const { return getInputs().size(); }
311318

319+
/// Returns the result type of the function as an actual return type or
320+
/// explicit !cir.void
321+
mlir::Type getReturnType() const;
322+
312323
/// Returns the result type of the function as an ArrayRef, enabling better
313324
/// integration with generic MLIR utilities.
314325
llvm::ArrayRef<mlir::Type> getReturnTypes() const;
315326

316-
/// Returns whether the function is returns void.
327+
/// Returns whether the function returns void.
317328
bool isVoid() const;
318329

319330
/// Returns a clone of this function type with the given argument

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ bool CIRGenTypes::isFuncTypeConvertible(const FunctionType *ft) {
6060
mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType qft) {
6161
assert(qft.isCanonical());
6262
const FunctionType *ft = cast<FunctionType>(qft.getTypePtr());
63-
// First, check whether we can build the full fucntion type. If the function
63+
// First, check whether we can build the full function type. If the function
6464
// type depends on an incomplete type (e.g. a struct or enum), we cannot lower
6565
// the function type.
6666
if (!isFuncTypeConvertible(ft)) {

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,11 @@ LogicalResult cir::FuncOp::verifyType() {
399399
if (!isa<cir::FuncType>(type))
400400
return emitOpError("requires '" + getFunctionTypeAttrName().str() +
401401
"' attribute of function type");
402+
if (auto rt = type.getReturnTypes();
403+
!rt.empty() && mlir::isa<cir::VoidType>(rt.front()))
404+
return emitOpError("The return type for a function returning void should "
405+
"be empty instead of an explicit !cir.void");
406+
402407
return success();
403408
}
404409

clang/lib/CIR/Dialect/IR/CIRTypes.cpp

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@
1616
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1717
#include "llvm/ADT/TypeSwitch.h"
1818

19+
#include <cassert>
20+
1921
//===----------------------------------------------------------------------===//
2022
// CIR Custom Parser/Printer Signatures
2123
//===----------------------------------------------------------------------===//
2224

23-
static mlir::ParseResult
24-
parseFuncTypeArgs(mlir::AsmParser &p, llvm::SmallVector<mlir::Type> &params,
25-
bool &isVarArg);
26-
static void printFuncTypeArgs(mlir::AsmPrinter &p,
27-
mlir::ArrayRef<mlir::Type> params, bool isVarArg);
25+
static mlir::ParseResult parseFuncType(mlir::AsmParser &p,
26+
mlir::Type &optionalReturnTypes,
27+
llvm::SmallVector<mlir::Type> &params,
28+
bool &isVarArg);
29+
30+
static void printFuncType(mlir::AsmPrinter &p, mlir::Type optionalReturnTypes,
31+
mlir::ArrayRef<mlir::Type> params, bool isVarArg);
2832

2933
//===----------------------------------------------------------------------===//
3034
// Get autogenerated stuff
@@ -331,9 +335,38 @@ FuncType FuncType::clone(TypeRange inputs, TypeRange results) const {
331335
return get(llvm::to_vector(inputs), results[0], isVarArg());
332336
}
333337

334-
mlir::ParseResult parseFuncTypeArgs(mlir::AsmParser &p,
335-
llvm::SmallVector<mlir::Type> &params,
336-
bool &isVarArg) {
338+
// A special parser is needed for function returning void to handle the missing
339+
// type.
340+
static mlir::ParseResult parseFuncTypeReturn(mlir::AsmParser &p,
341+
mlir::Type &optionalReturnType) {
342+
if (succeeded(p.parseOptionalLParen())) {
343+
// If we have already a '(', the function has no return type
344+
optionalReturnType = {};
345+
return mlir::success();
346+
}
347+
mlir::Type type;
348+
if (p.parseType(type))
349+
return mlir::failure();
350+
if (isa<cir::VoidType>(type))
351+
// An explicit !cir.void means also no return type.
352+
optionalReturnType = {};
353+
else
354+
// Otherwise use the actual type.
355+
optionalReturnType = type;
356+
return p.parseLParen();
357+
}
358+
359+
// A special pretty-printer for function returning or not a result.
360+
static void printFuncTypeReturn(mlir::AsmPrinter &p,
361+
mlir::Type optionalReturnType) {
362+
if (optionalReturnType)
363+
p << optionalReturnType << ' ';
364+
p << '(';
365+
}
366+
367+
static mlir::ParseResult
368+
parseFuncTypeArgs(mlir::AsmParser &p, llvm::SmallVector<mlir::Type> &params,
369+
bool &isVarArg) {
337370
isVarArg = false;
338371
// `(` `)`
339372
if (succeeded(p.parseOptionalRParen()))
@@ -363,8 +396,9 @@ mlir::ParseResult parseFuncTypeArgs(mlir::AsmParser &p,
363396
return p.parseRParen();
364397
}
365398

366-
void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params,
367-
bool isVarArg) {
399+
static void printFuncTypeArgs(mlir::AsmPrinter &p,
400+
mlir::ArrayRef<mlir::Type> params,
401+
bool isVarArg) {
368402
llvm::interleaveComma(params, p,
369403
[&p](mlir::Type type) { p.printType(type); });
370404
if (isVarArg) {
@@ -375,11 +409,48 @@ void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params,
375409
p << ')';
376410
}
377411

412+
// Use a custom parser to handle the optional return and argument types without
413+
// an optional anchor.
414+
static mlir::ParseResult parseFuncType(mlir::AsmParser &p,
415+
mlir::Type &optionalReturnTypes,
416+
llvm::SmallVector<mlir::Type> &params,
417+
bool &isVarArg) {
418+
if (failed(parseFuncTypeReturn(p, optionalReturnTypes)))
419+
return failure();
420+
return parseFuncTypeArgs(p, params, isVarArg);
421+
}
422+
423+
static void printFuncType(mlir::AsmPrinter &p, mlir::Type optionalReturnTypes,
424+
mlir::ArrayRef<mlir::Type> params, bool isVarArg) {
425+
printFuncTypeReturn(p, optionalReturnTypes);
426+
printFuncTypeArgs(p, params, isVarArg);
427+
}
428+
429+
// Return the actual return type or an explicit !cir.void if the function does
430+
// not return anything
431+
mlir::Type FuncType::getReturnType() const {
432+
if (isVoid())
433+
return cir::VoidType::get(getContext());
434+
return static_cast<detail::FuncTypeStorage *>(getImpl())->optionalReturnType;
435+
}
436+
437+
/// Returns the result type of the function as an ArrayRef, enabling better
438+
/// integration with generic MLIR utilities.
378439
llvm::ArrayRef<mlir::Type> FuncType::getReturnTypes() const {
379-
return static_cast<detail::FuncTypeStorage *>(getImpl())->returnType;
440+
if (isVoid())
441+
return {};
442+
return static_cast<detail::FuncTypeStorage *>(getImpl())->optionalReturnType;
380443
}
381444

382-
bool FuncType::isVoid() const { return mlir::isa<VoidType>(getReturnType()); }
445+
// Whether the function returns void
446+
bool FuncType::isVoid() const {
447+
auto rt =
448+
static_cast<detail::FuncTypeStorage *>(getImpl())->optionalReturnType;
449+
assert((!rt || !mlir::isa<cir::VoidType>(rt)) &&
450+
"The return type for a function returning void should be empty "
451+
"instead of a real !cir.void");
452+
return !rt;
453+
}
383454

384455
//===----------------------------------------------------------------------===//
385456
// PointerType Definitions

clang/test/CIR/func-simple.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s
33

44
void empty() { }
5-
// CHECK: cir.func @empty() -> !cir.void {
5+
// CHECK: cir.func @empty() {
66
// CHECK: cir.return
77
// CHECK: }
88

99
void voidret() { return; }
10-
// CHECK: cir.func @voidret() -> !cir.void {
10+
// CHECK: cir.func @voidret() {
1111
// CHECK: cir.return
1212
// CHECK: }
1313

clang/test/CIR/global-var-simple.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ char **cpp;
8989
// CHECK: cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>>
9090

9191
void (*fp)();
92-
// CHECK: cir.global @fp : !cir.ptr<!cir.func<!cir.void ()>>
92+
// CHECK: cir.global @fp : !cir.ptr<!cir.func<()>>
9393

9494
int (*fpii)(int) = 0;
9595
// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<!cir.int<s, 32> (!cir.int<s, 32>)>>
9696

9797
void (*fpvar)(int, ...);
98-
// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<!cir.void (!cir.int<s, 32>, ...)>>
98+
// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<(!cir.int<s, 32>, ...)>>

0 commit comments

Comments
 (0)