Skip to content

Commit f59cbb2

Browse files
committed
[CIR] 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 a lot of 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 does not break the MLIR invariant about the same number of return types and returned values. This change does not keeps the same parsing/pretty-printed syntax as before for compatibility like in llvm#1203 because it requires some new features from the MLIR parser infrastructure itself, which is not great. For now use a list of return types.
1 parent 17cd670 commit f59cbb2

File tree

7 files changed

+58
-24
lines changed

7 files changed

+58
-24
lines changed

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3474,8 +3474,6 @@ def FuncOp : CIR_Op<"func", [
34743474
/// Returns the results types that the callable region produces when
34753475
/// executed.
34763476
llvm::ArrayRef<mlir::Type> getCallableResults() {
3477-
if (::llvm::isa<cir::VoidType>(getFunctionType().getReturnType()))
3478-
return {};
34793477
return getFunctionType().getReturnTypes();
34803478
}
34813479

@@ -3492,10 +3490,15 @@ def FuncOp : CIR_Op<"func", [
34923490
}
34933491

34943492
/// Returns the argument types of this function.
3495-
llvm::ArrayRef<mlir::Type> getArgumentTypes() { return getFunctionType().getInputs(); }
3493+
llvm::ArrayRef<mlir::Type> getArgumentTypes() {
3494+
return getFunctionType().getInputs();
3495+
}
34963496

3497-
/// Returns the result types of this function.
3498-
llvm::ArrayRef<mlir::Type> getResultTypes() { return getFunctionType().getReturnTypes(); }
3497+
/// Returns 0 or 1 result type of this function (0 in the case of a function
3498+
/// returing void)
3499+
llvm::ArrayRef<mlir::Type> getResultTypes() {
3500+
return getFunctionType().getReturnTypes();
3501+
}
34993502

35003503
/// Hook for OpTrait::FunctionOpInterfaceTrait, called after verifying that
35013504
/// the 'type' attribute is present and checks if it holds a function type.

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,22 +379,27 @@ def CIR_FuncType : CIR_Type<"Func", "func"> {
379379

380380
```mlir
381381
!cir.func<!bool ()>
382+
!cir.func<(!s8i, !s8i)>
382383
!cir.func<!s32i (!s8i, !s8i)>
383384
!cir.func<!s32i (!s32i, ...)>
384385
```
385386
}];
386387

387-
let parameters = (ins ArrayRefParameter<"mlir::Type">:$inputs, "mlir::Type":$returnType,
388+
let parameters = (ins ArrayRefParameter<"mlir::Type">:$inputs, ArrayRefParameter<"mlir::Type">:$returnTypes,
388389
"bool":$varArg);
389390
let assemblyFormat = [{
390-
`<` $returnType ` ` `(` custom<FuncTypeArgs>($inputs, $varArg) `>`
391+
`<` $returnTypes ` ` `(` custom<FuncTypeArgs>($inputs, $varArg) `>`
391392
}];
392393

393394
let builders = [
395+
// Construct with an actual return type or explicit !cir.void
394396
TypeBuilderWithInferredContext<(ins
395397
"llvm::ArrayRef<mlir::Type>":$inputs, "mlir::Type":$returnType,
396398
CArg<"bool", "false">:$isVarArg), [{
397-
return $_get(returnType.getContext(), inputs, returnType, isVarArg);
399+
return $_get(returnType.getContext(), inputs,
400+
::mlir::isa<::cir::VoidType>(returnType) ? llvm::ArrayRef<mlir::Type>{}
401+
: llvm::ArrayRef{returnType},
402+
isVarArg);
398403
}]>
399404
];
400405

@@ -408,11 +413,11 @@ def CIR_FuncType : CIR_Type<"Func", "func"> {
408413
/// Returns the number of arguments to the function.
409414
unsigned getNumInputs() const { return getInputs().size(); }
410415

411-
/// Returns the result type of the function as an ArrayRef, enabling better
412-
/// integration with generic MLIR utilities.
413-
llvm::ArrayRef<mlir::Type> getReturnTypes() const;
416+
/// Returns the result type of the function as an actual return type or
417+
/// explicit !cir.void
418+
mlir::Type getReturnType() const;
414419

415-
/// Returns whether the function is returns void.
420+
/// Returns whether the function returns void.
416421
bool isVoid() const;
417422

418423
/// 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
@@ -271,7 +271,7 @@ mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType QFT) {
271271
assert(QFT.isCanonical());
272272
const Type *Ty = QFT.getTypePtr();
273273
const FunctionType *FT = cast<FunctionType>(QFT.getTypePtr());
274-
// First, check whether we can build the full fucntion type. If the function
274+
// First, check whether we can build the full function type. If the function
275275
// type depends on an incomplete type (e.g. a struct or enum), we cannot lower
276276
// the function type.
277277
assert(isFuncTypeConvertible(FT) && "NYI");

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2490,13 +2490,8 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
24902490
p.printSymbolName(getSymName());
24912491
auto fnType = getFunctionType();
24922492
llvm::SmallVector<Type, 1> resultTypes;
2493-
if (!fnType.isVoid())
2494-
function_interface_impl::printFunctionSignature(
2495-
p, *this, fnType.getInputs(), fnType.isVarArg(),
2496-
fnType.getReturnTypes());
2497-
else
2498-
function_interface_impl::printFunctionSignature(
2499-
p, *this, fnType.getInputs(), fnType.isVarArg(), {});
2493+
function_interface_impl::printFunctionSignature(
2494+
p, *this, fnType.getInputs(), fnType.isVarArg(), fnType.getReturnTypes());
25002495

25012496
if (mlir::ArrayAttr annotations = getAnnotationsAttr()) {
25022497
p << ' ';
@@ -2565,6 +2560,11 @@ LogicalResult cir::FuncOp::verifyType() {
25652560
if (!getNoProto() && type.isVarArg() && type.getNumInputs() == 0)
25662561
return emitError()
25672562
<< "prototyped function must have at least one non-variadic input";
2563+
if (auto rt = type.getReturnTypes();
2564+
!rt.empty() && mlir::isa<cir::VoidType>(rt.front()))
2565+
return emitOpError("The return type for a function returning void should "
2566+
"be empty instead of an explicit !cir.void");
2567+
25682568
return success();
25692569
}
25702570

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/ADT/TypeSwitch.h"
3434
#include "llvm/Support/ErrorHandling.h"
3535
#include "llvm/Support/MathExtras.h"
36+
#include <cassert>
3637
#include <optional>
3738

3839
using cir::MissingFeatures;
@@ -957,11 +958,22 @@ void printFuncTypeArgs(mlir::AsmPrinter &p, mlir::ArrayRef<mlir::Type> params,
957958
p << ')';
958959
}
959960

960-
llvm::ArrayRef<mlir::Type> FuncType::getReturnTypes() const {
961-
return static_cast<detail::FuncTypeStorage *>(getImpl())->returnType;
961+
// Return the actual return type or an explicit !cir.void if the function does
962+
// not return anything
963+
mlir::Type FuncType::getReturnType() const {
964+
if (isVoid())
965+
return cir::VoidType::get(getContext());
966+
return static_cast<detail::FuncTypeStorage *>(getImpl())->returnTypes.front();
962967
}
963968

964-
bool FuncType::isVoid() const { return mlir::isa<VoidType>(getReturnType()); }
969+
bool FuncType::isVoid() const {
970+
auto rt = static_cast<detail::FuncTypeStorage *>(getImpl())->returnTypes;
971+
assert(rt.empty() ||
972+
!mlir::isa<cir::VoidType>(rt.front()) &&
973+
"The return type for a function returning void should be empty "
974+
"instead of a real !cir.void");
975+
return rt.empty();
976+
}
965977

966978
//===----------------------------------------------------------------------===//
967979
// MethodType Definitions

clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ FuncType LowerTypes::getFunctionType(const LowerFunctionInfo &FI) {
109109
}
110110
}
111111

112-
return FuncType::get(getMLIRContext(), ArgTypes, resultType, FI.isVariadic());
112+
return FuncType::get(ArgTypes, resultType, FI.isVariadic());
113113
}
114114

115115
/// Convert a CIR type to its ABI-specific default form.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: cir-opt %s | FileCheck %s
2+
// Exercise different ways to encode a function returning void
3+
!s32i = !cir.int<s, 32>
4+
!fnptr3 = !cir.ptr<!cir.func<(!s32i)>>
5+
module {
6+
cir.func @ind3(%fnptr: !fnptr3, %a : !s32i) {
7+
// CHECK: cir.func @ind3(%arg0: !cir.ptr<!cir.func<(!s32i)>>, %arg1: !s32i) {
8+
cir.return
9+
}
10+
cir.func @f3() {
11+
// CHECK: cir.func @f3() {
12+
cir.return
13+
}
14+
}

0 commit comments

Comments
 (0)