Skip to content

Commit 0d5b429

Browse files
committed
[CIR] cir.call with scalar return type
This patch introduces support for calling functions with a scalar return type to the upstream. This patch also includes an initial version of CIRGenTargetInfo and related definitions which are essential for the CIRGen of call ops.
1 parent fb00fa5 commit 0d5b429

22 files changed

+579
-40
lines changed

clang/include/clang/CIR/ABIArgInfo.h

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//==-- ABIArgInfo.h - Abstract info regarding ABI-specific arguments -------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Defines ABIArgInfo and associated types used by CIR to track information
10+
// regarding ABI-coerced types for function arguments and return values. This
11+
// was moved to the common library as it might be used by both CIRGen and
12+
// passes.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef CLANG_CIR_ABIARGINFO_H
17+
#define CLANG_CIR_ABIARGINFO_H
18+
19+
#include "mlir/IR/Types.h"
20+
#include "clang/CIR/MissingFeatures.h"
21+
22+
namespace cir {
23+
24+
class ABIArgInfo {
25+
public:
26+
enum Kind : uint8_t {
27+
/// Pass the argument directly using the normal converted CIR type,
28+
/// or by coercing to another specified type stored in 'CoerceToType'). If
29+
/// an offset is specified (in UIntData), then the argument passed is offset
30+
/// by some number of bytes in the memory representation. A dummy argument
31+
/// is emitted before the real argument if the specified type stored in
32+
/// "PaddingType" is not zero.
33+
Direct,
34+
35+
/// Ignore the argument (treat as void). Useful for void and empty
36+
/// structs.
37+
Ignore,
38+
39+
// TODO: more argument kinds will be added as the upstreaming proceeds.
40+
};
41+
42+
private:
43+
mlir::Type typeData;
44+
struct DirectAttrInfo {
45+
unsigned offset;
46+
unsigned align;
47+
};
48+
union {
49+
DirectAttrInfo directAttr;
50+
};
51+
Kind theKind;
52+
53+
public:
54+
ABIArgInfo(Kind k = Direct) : directAttr{0, 0}, theKind(k) {}
55+
56+
static ABIArgInfo getDirect(mlir::Type ty = nullptr) {
57+
ABIArgInfo info(Direct);
58+
info.setCoerceToType(ty);
59+
assert(!cir::MissingFeatures::abiArgInfo());
60+
return info;
61+
}
62+
63+
static ABIArgInfo getIgnore() { return ABIArgInfo(Ignore); }
64+
65+
Kind getKind() const { return theKind; }
66+
bool isDirect() const { return theKind == Direct; }
67+
bool isIgnore() const { return theKind == Ignore; }
68+
69+
bool canHaveCoerceToType() const {
70+
assert(!cir::MissingFeatures::abiArgInfo());
71+
return isDirect();
72+
}
73+
74+
unsigned getDirectOffset() const {
75+
assert(!cir::MissingFeatures::abiArgInfo());
76+
return directAttr.offset;
77+
}
78+
79+
mlir::Type getCoerceToType() const {
80+
assert(canHaveCoerceToType() && "invalid kind!");
81+
return typeData;
82+
}
83+
84+
void setCoerceToType(mlir::Type ty) {
85+
assert(canHaveCoerceToType() && "invalid kind!");
86+
typeData = ty;
87+
}
88+
};
89+
90+
} // namespace cir
91+
92+
#endif // CLANG_CIR_ABIARGINFO_H

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
207207
// Call operators
208208
//===--------------------------------------------------------------------===//
209209

210-
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee) {
211-
auto op = create<cir::CallOp>(loc, callee);
210+
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
211+
mlir::Type returnType) {
212+
auto op = create<cir::CallOp>(loc, callee, returnType);
212213
return op;
213214
}
214215

215216
cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) {
216-
return createCallOp(loc, mlir::SymbolRefAttr::get(callee));
217+
return createCallOp(loc, mlir::SymbolRefAttr::get(callee),
218+
callee.getFunctionType().getReturnType());
217219
}
218220

219221
//===--------------------------------------------------------------------===//

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -1408,10 +1408,14 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
14081408
```
14091409
}];
14101410

1411+
let results = (outs Optional<CIR_AnyType>:$result);
14111412
let arguments = commonArgs;
14121413

1413-
let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee), [{
1414+
let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee,
1415+
"mlir::Type":$resType), [{
14141416
$_state.addAttribute("callee", callee);
1417+
if (resType && !isa<VoidType>(resType))
1418+
$_state.addTypes(resType);
14151419
}]>];
14161420
}
14171421

clang/include/clang/CIR/MissingFeatures.h

+5
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ struct MissingFeatures {
114114

115115
// Misc
116116
static bool cxxABI() { return false; }
117+
static bool cirgenABIInfo() { return false; }
118+
static bool cirgenTargetInfo() { return false; }
119+
static bool abiArgInfo() { return false; }
117120
static bool tryEmitAsConstant() { return false; }
118121
static bool constructABIArgDirectExtend() { return false; }
119122
static bool opGlobalViewAttr() { return false; }
@@ -132,6 +135,8 @@ struct MissingFeatures {
132135
static bool fpConstraints() { return false; }
133136
static bool sanitizers() { return false; }
134137
static bool addHeapAllocSiteMetadata() { return false; }
138+
static bool targetCIRGenInfoArch() { return false; }
139+
static bool targetCIRGenInfoOS() { return false; }
135140
static bool targetCodeGenInfoGetNullPointer() { return false; }
136141
static bool loopInfoStack() { return false; }
137142
static bool requiresCleanups() { return false; }

clang/lib/CIR/CodeGen/ABIInfo.h

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===----- ABIInfo.h - ABI information access & encapsulation ---*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_LIB_CIR_ABIINFO_H
10+
#define LLVM_CLANG_LIB_CIR_ABIINFO_H
11+
12+
namespace clang::CIRGen {
13+
14+
class CIRGenFunctionInfo;
15+
class CIRGenTypes;
16+
17+
class ABIInfo {
18+
ABIInfo() = delete;
19+
20+
public:
21+
CIRGenTypes &cgt;
22+
23+
ABIInfo(CIRGenTypes &cgt) : cgt(cgt) {}
24+
25+
virtual ~ABIInfo();
26+
27+
virtual void computeInfo(CIRGenFunctionInfo &funcInfo) const = 0;
28+
};
29+
30+
} // namespace clang::CIRGen
31+
32+
#endif // LLVM_CLANG_LIB_CIR_ABIINFO_H

clang/lib/CIR/CodeGen/CIRGenCall.cpp

+69-10
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
using namespace clang;
1919
using namespace clang::CIRGen;
2020

21-
CIRGenFunctionInfo *CIRGenFunctionInfo::create() {
22-
// For now we just create an empty CIRGenFunctionInfo.
23-
CIRGenFunctionInfo *fi = new CIRGenFunctionInfo();
21+
CIRGenFunctionInfo *CIRGenFunctionInfo::create(CanQualType resultType) {
22+
void *buffer = operator new(totalSizeToAlloc<ArgInfo>(1));
23+
24+
CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo();
25+
fi->getArgsBuffer()[0].type = resultType;
26+
2427
return fi;
2528
}
2629

@@ -29,13 +32,29 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
2932
return *this;
3033
}
3134

32-
static const CIRGenFunctionInfo &arrangeFreeFunctionLikeCall(CIRGenTypes &cgt) {
35+
static const CIRGenFunctionInfo &
36+
arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
37+
const FunctionType *fnType) {
38+
if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) {
39+
if (proto->isVariadic())
40+
cgm.errorNYI("call to variadic function");
41+
if (proto->hasExtParameterInfos())
42+
cgm.errorNYI("call to functions with extra parameter info");
43+
} else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
44+
cast<FunctionNoProtoType>(fnType)))
45+
cgm.errorNYI("call to function without a prototype");
46+
3347
assert(!cir::MissingFeatures::opCallArgs());
34-
return cgt.arrangeCIRFunctionInfo();
48+
49+
CanQualType retType = fnType->getReturnType()
50+
->getCanonicalTypeUnqualified()
51+
.getUnqualifiedType();
52+
return cgt.arrangeCIRFunctionInfo(retType);
3553
}
3654

37-
const CIRGenFunctionInfo &CIRGenTypes::arrangeFreeFunctionCall() {
38-
return arrangeFreeFunctionLikeCall(*this);
55+
const CIRGenFunctionInfo &
56+
CIRGenTypes::arrangeFreeFunctionCall(const FunctionType *fnType) {
57+
return arrangeFreeFunctionLikeCall(*this, cgm, fnType);
3958
}
4059

4160
static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf,
@@ -54,8 +73,12 @@ static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf,
5473

5574
RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
5675
const CIRGenCallee &callee,
76+
ReturnValueSlot returnValue,
5777
cir::CIRCallOpInterface *callOp,
5878
mlir::Location loc) {
79+
QualType retTy = funcInfo.getReturnType();
80+
const cir::ABIArgInfo &retInfo = funcInfo.getReturnInfo();
81+
5982
assert(!cir::MissingFeatures::opCallArgs());
6083
assert(!cir::MissingFeatures::emitLifetimeMarkers());
6184

@@ -87,9 +110,45 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
87110
assert(!cir::MissingFeatures::opCallMustTail());
88111
assert(!cir::MissingFeatures::opCallReturn());
89112

90-
// For now we just return nothing because we don't have support for return
91-
// values yet.
92-
RValue ret = RValue::get(nullptr);
113+
RValue ret;
114+
switch (retInfo.getKind()) {
115+
case cir::ABIArgInfo::Direct: {
116+
mlir::Type retCIRTy = convertType(retTy);
117+
if (retInfo.getCoerceToType() == retCIRTy &&
118+
retInfo.getDirectOffset() == 0) {
119+
switch (getEvaluationKind(retTy)) {
120+
case cir::TEK_Scalar: {
121+
mlir::ResultRange results = theCall->getOpResults();
122+
assert(results.size() == 1 && "unexpected number of returns");
123+
124+
// If the argument doesn't match, perform a bitcast to coerce it. This
125+
// can happen due to trivial type mismatches.
126+
if (results[0].getType() != retCIRTy)
127+
cgm.errorNYI(loc, "bitcast on function return value");
128+
129+
mlir::Region *region = builder.getBlock()->getParent();
130+
if (region != theCall->getParentRegion())
131+
cgm.errorNYI(loc, "function calls with cleanup");
132+
133+
return RValue::get(results[0]);
134+
}
135+
default:
136+
cgm.errorNYI(loc,
137+
"unsupported evaluation kind of function call result");
138+
}
139+
} else
140+
cgm.errorNYI(loc, "unsupported function call form");
141+
142+
break;
143+
}
144+
case cir::ABIArgInfo::Ignore:
145+
// If we are ignoring an argument that had a result, make sure to construct
146+
// the appropriate return value for our caller.
147+
ret = getUndefRValue(retTy);
148+
break;
149+
default:
150+
cgm.errorNYI(loc, "unsupported return value information");
151+
}
93152

94153
return ret;
95154
}

clang/lib/CIR/CodeGen/CIRGenCall.h

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ struct CallArg {};
8181

8282
class CallArgList : public llvm::SmallVector<CallArg, 8> {};
8383

84+
/// Contains the address where the return value of a function can be stored, and
85+
/// whether the address is volatile or not.
86+
class ReturnValueSlot {};
87+
8488
} // namespace clang::CIRGen
8589

8690
#endif // CLANG_LIB_CODEGEN_CIRGENCALL_H

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

+19-5
Original file line numberDiff line numberDiff line change
@@ -695,23 +695,36 @@ static CIRGenCallee emitDirectCallee(CIRGenModule &cgm, GlobalDecl gd) {
695695
return CIRGenCallee::forDirect(callee, gd);
696696
}
697697

698+
RValue CIRGenFunction::getUndefRValue(QualType ty) {
699+
if (ty->isVoidType())
700+
return RValue::get(nullptr);
701+
702+
cgm.errorNYI("unsupported type for undef rvalue");
703+
return RValue::get(nullptr);
704+
}
705+
698706
RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
699707
const CIRGenCallee &callee,
700-
const clang::CallExpr *e) {
708+
const clang::CallExpr *e,
709+
ReturnValueSlot returnValue) {
701710
// Get the actual function type. The callee type will always be a pointer to
702711
// function type or a block pointer type.
703712
assert(calleeTy->isFunctionPointerType() &&
704713
"Callee must have function pointer type!");
705714

706715
calleeTy = getContext().getCanonicalType(calleeTy);
716+
auto pointeeTy = cast<PointerType>(calleeTy)->getPointeeType();
707717

708718
if (getLangOpts().CPlusPlus)
709719
assert(!cir::MissingFeatures::sanitizers());
710720

721+
const auto *fnType = cast<FunctionType>(pointeeTy);
722+
711723
assert(!cir::MissingFeatures::sanitizers());
712724
assert(!cir::MissingFeatures::opCallArgs());
713725

714-
const CIRGenFunctionInfo &funcInfo = cgm.getTypes().arrangeFreeFunctionCall();
726+
const CIRGenFunctionInfo &funcInfo =
727+
cgm.getTypes().arrangeFreeFunctionCall(fnType);
715728

716729
assert(!cir::MissingFeatures::opCallNoPrototypeFunc());
717730
assert(!cir::MissingFeatures::opCallChainCall());
@@ -720,7 +733,7 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
720733

721734
cir::CIRCallOpInterface callOp;
722735
RValue callResult =
723-
emitCall(funcInfo, callee, &callOp, getLoc(e->getExprLoc()));
736+
emitCall(funcInfo, callee, returnValue, &callOp, getLoc(e->getExprLoc()));
724737

725738
assert(!cir::MissingFeatures::generateDebugInfo());
726739

@@ -746,7 +759,8 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *e) {
746759
return {};
747760
}
748761

749-
RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e) {
762+
RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e,
763+
ReturnValueSlot returnValue) {
750764
assert(!cir::MissingFeatures::objCBlocks());
751765

752766
if (isa<CXXMemberCallExpr>(e)) {
@@ -778,7 +792,7 @@ RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e) {
778792
}
779793
assert(!cir::MissingFeatures::opCallPseudoDtor());
780794

781-
return emitCall(e->getCallee()->getType(), callee, e);
795+
return emitCall(e->getCallee()->getType(), callee, e, returnValue);
782796
}
783797

784798
/// Emit code to compute the specified expression, ignoring the result.

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -1519,11 +1519,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
15191519
}
15201520

15211521
mlir::Value ScalarExprEmitter::VisitCallExpr(const CallExpr *e) {
1522-
if (e->getCallReturnType(cgf.getContext())->isReferenceType()) {
1523-
cgf.getCIRGenModule().errorNYI(
1524-
e->getSourceRange(), "call to function with non-void return type");
1525-
return {};
1526-
}
1522+
if (e->getCallReturnType(cgf.getContext())->isReferenceType())
1523+
return emitLoadOfLValue(e);
15271524

15281525
auto v = cgf.emitCallExpr(e).getScalarVal();
15291526
assert(!cir::MissingFeatures::emitLValueAlignmentAssumption());

0 commit comments

Comments
 (0)