Skip to content

Commit ccf7371

Browse files
committed
[CIR] Add side effect attribute to call operations
1 parent a361a3d commit ccf7371

File tree

12 files changed

+271
-38
lines changed

12 files changed

+271
-38
lines changed

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,22 +213,26 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
213213
//===--------------------------------------------------------------------===//
214214

215215
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
216-
mlir::Type returnType, mlir::ValueRange operands) {
217-
return create<cir::CallOp>(loc, callee, returnType, operands);
216+
mlir::Type returnType, mlir::ValueRange operands,
217+
cir::SideEffect sideEffect = cir::SideEffect::All) {
218+
return create<cir::CallOp>(loc, callee, returnType, operands, sideEffect);
218219
}
219220

220221
cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee,
221-
mlir::ValueRange operands) {
222+
mlir::ValueRange operands,
223+
cir::SideEffect sideEffect = cir::SideEffect::All) {
222224
return createCallOp(loc, mlir::SymbolRefAttr::get(callee),
223-
callee.getFunctionType().getReturnType(), operands);
225+
callee.getFunctionType().getReturnType(), operands,
226+
sideEffect);
224227
}
225228

226229
cir::CallOp createIndirectCallOp(mlir::Location loc,
227230
mlir::Value indirectTarget,
228231
cir::FuncType funcType,
229-
mlir::ValueRange operands) {
232+
mlir::ValueRange operands,
233+
cir::SideEffect sideEffect) {
230234
return create<cir::CallOp>(loc, indirectTarget, funcType.getReturnType(),
231-
operands);
235+
operands, sideEffect);
232236
}
233237

234238
//===--------------------------------------------------------------------===//

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

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,40 @@ def FuncOp : CIR_Op<"func", [
18581858
// CallOp
18591859
//===----------------------------------------------------------------------===//
18601860

1861+
def SE_All : I32EnumAttrCase<"All", 1, "all">;
1862+
def SE_Pure : I32EnumAttrCase<"Pure", 2, "pure">;
1863+
def SE_Const : I32EnumAttrCase<"Const", 3, "const">;
1864+
1865+
def SideEffect
1866+
: I32EnumAttr<
1867+
"SideEffect",
1868+
"allowed side effects of a function", [SE_All, SE_Pure, SE_Const]> {
1869+
let description = [{
1870+
The side effect attribute specifies the possible side effects of the callee
1871+
of a call operation. This is an enumeration attribute and all possible
1872+
enumerators are:
1873+
1874+
- all: The callee can have any side effects. This is the default if no side
1875+
effects are explicitly listed.
1876+
- pure: The callee may read data from memory, but it cannot write data to
1877+
memory. This has the same effect as the GNU C/C++ attribute
1878+
`__attribute__((pure))`.
1879+
- const: The callee may not read or write data from memory. This has the
1880+
same effect as the GNU C/C++ attribute `__attribute__((const))`.
1881+
1882+
Examples:
1883+
1884+
```mlir
1885+
%0 = cir.const #cir.int<0> : !s32i
1886+
%1 = cir.const #cir.int<1> : !s32i
1887+
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(all)
1888+
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(pure)
1889+
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(const)
1890+
```
1891+
}];
1892+
let cppNamespace = "::cir";
1893+
}
1894+
18611895
class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
18621896
: Op<CIR_Dialect, mnemonic,
18631897
!listconcat(extra_traits,
@@ -1911,7 +1945,8 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
19111945
// will add in the future.
19121946

19131947
dag commonArgs = (ins OptionalAttr<FlatSymbolRefAttr>:$callee,
1914-
Variadic<CIR_AnyType>:$args);
1948+
Variadic<CIR_AnyType>:$args,
1949+
DefaultValuedAttr<SideEffect, "SideEffect::All">:$side_effect);
19151950
}
19161951

19171952
def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
@@ -1940,22 +1975,30 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
19401975
let arguments = commonArgs;
19411976

19421977
let builders = [
1943-
// Build a call op for a direct call
1944-
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
1945-
"mlir::ValueRange":$operands), [{
1978+
// Build a call op for a direct call
1979+
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
1980+
"mlir::ValueRange":$operands,
1981+
CArg<"SideEffect", "SideEffect::All">:$sideEffect),
1982+
[{
19461983
assert(callee && "callee attribute is required for direct call");
19471984
$_state.addOperands(operands);
19481985
$_state.addAttribute("callee", callee);
1986+
$_state.addAttribute("side_effect",
1987+
SideEffectAttr::get($_builder.getContext(), sideEffect));
19491988
if (resType && !isa<VoidType>(resType))
19501989
$_state.addTypes(resType);
19511990
}]>,
1952-
// Build a call op for an indirect call
1953-
OpBuilder<(ins "mlir::Value":$calleePtr, "mlir::Type":$resType,
1954-
"mlir::ValueRange":$operands), [{
1991+
// Build a call op for an indirect call
1992+
OpBuilder<(ins "mlir::Value":$calleePtr, "mlir::Type":$resType,
1993+
"mlir::ValueRange":$operands,
1994+
CArg<"SideEffect", "SideEffect::All">:$sideEffect),
1995+
[{
19551996
$_state.addOperands(calleePtr);
19561997
$_state.addOperands(operands);
19571998
if (resType && !isa<VoidType>(resType))
19581999
$_state.addTypes(resType);
2000+
$_state.addAttribute("side_effect",
2001+
SideEffectAttr::get($_builder.getContext(), sideEffect));
19592002
}]>,
19602003
];
19612004
}

clang/include/clang/CIR/Interfaces/CIROpInterfaces.td

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@ let cppNamespace = "::cir" in {
2424
def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", [CallOpInterface]> {
2525
// Currently we don't have any methods defined in CIRCallOpInterface. We'll
2626
// add more methods as the upstreaming proceeds.
27-
let methods = [
28-
InterfaceMethod<
29-
"Return the operand at index 'i', accounts for indirect call or "
30-
"exception info",
31-
"mlir::Value", "getArgOperand",
32-
(ins "unsigned":$i)>,
33-
InterfaceMethod<
34-
"Return the number of operands, accounts for indirect call or "
35-
"exception info",
36-
"unsigned", "getNumArgOperands", (ins)>,
27+
let methods =
28+
[InterfaceMethod<
29+
"Return the operand at index 'i', accounts for indirect call or "
30+
"exception info",
31+
"mlir::Value", "getArgOperand", (ins "unsigned":$i)>,
32+
InterfaceMethod<
33+
"Return the number of operands, accounts for indirect call or "
34+
"exception info",
35+
"unsigned", "getNumArgOperands", (ins)>,
36+
InterfaceMethod<"Return the side effects of the call operation",
37+
"cir::SideEffect", "getSideEffect", (ins)>,
3738
];
3839
}
3940

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ struct MissingFeatures {
9595
static bool opCallReturn() { return false; }
9696
static bool opCallArgEvaluationOrder() { return false; }
9797
static bool opCallCallConv() { return false; }
98-
static bool opCallSideEffect() { return false; }
9998
static bool opCallNoPrototypeFunc() { return false; }
10099
static bool opCallMustTail() { return false; }
101100
static bool opCallVirtual() { return false; }

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,35 @@ void CIRGenFunction::emitAggregateStore(mlir::Value value, Address dest) {
7777
builder.createStore(*currSrcLoc, value, dest);
7878
}
7979

80+
/// Construct the CIR attribute list of a function or call.
81+
void CIRGenModule::constructAttributeList(CIRGenCalleeInfo calleeInfo,
82+
cir::SideEffect &sideEffect) {
83+
assert(!cir::MissingFeatures::opCallCallConv());
84+
sideEffect = cir::SideEffect::All;
85+
86+
assert(!cir::MissingFeatures::opCallAttrs());
87+
88+
const Decl *targetDecl = calleeInfo.getCalleeDecl().getDecl();
89+
90+
if (targetDecl) {
91+
assert(!cir::MissingFeatures::opCallAttrs());
92+
93+
// 'const', 'pure' and 'noalias' attributed functions are also nounwind.
94+
if (targetDecl->hasAttr<ConstAttr>()) {
95+
// gcc specifies that 'const' functions have greater restrictions than
96+
// 'pure' functions, so they also cannot have infinite loops.
97+
sideEffect = cir::SideEffect::Const;
98+
} else if (targetDecl->hasAttr<PureAttr>()) {
99+
// gcc specifies that 'pure' functions cannot have infinite loops.
100+
sideEffect = cir::SideEffect::Pure;
101+
}
102+
103+
assert(!cir::MissingFeatures::opCallAttrs());
104+
}
105+
106+
assert(!cir::MissingFeatures::opCallAttrs());
107+
}
108+
80109
/// Returns the canonical formal type of the given C++ method.
81110
static CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *md) {
82111
return md->getType()
@@ -386,7 +415,8 @@ static cir::CIRCallOpInterface
386415
emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
387416
cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,
388417
cir::FuncOp directFuncOp,
389-
const SmallVectorImpl<mlir::Value> &cirCallArgs) {
418+
const SmallVectorImpl<mlir::Value> &cirCallArgs,
419+
cir::SideEffect sideEffect) {
390420
CIRGenBuilderTy &builder = cgf.getBuilder();
391421

392422
assert(!cir::MissingFeatures::opCallSurroundingTry());
@@ -397,11 +427,11 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
397427
if (indirectFuncTy) {
398428
// TODO(cir): Set calling convention for indirect calls.
399429
assert(!cir::MissingFeatures::opCallCallConv());
400-
return builder.createIndirectCallOp(callLoc, indirectFuncVal,
401-
indirectFuncTy, cirCallArgs);
430+
return builder.createIndirectCallOp(
431+
callLoc, indirectFuncVal, indirectFuncTy, cirCallArgs, sideEffect);
402432
}
403433

404-
return builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
434+
return builder.createCallOp(callLoc, directFuncOp, cirCallArgs, sideEffect);
405435
}
406436

407437
const CIRGenFunctionInfo &
@@ -513,8 +543,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
513543
funcName = calleeFuncOp.getName();
514544

515545
assert(!cir::MissingFeatures::opCallCallConv());
516-
assert(!cir::MissingFeatures::opCallSideEffect());
517546
assert(!cir::MissingFeatures::opCallAttrs());
547+
cir::SideEffect sideEffect;
548+
cgm.constructAttributeList(callee.getAbstractInfo(), sideEffect);
518549

519550
assert(!cir::MissingFeatures::invokeOp());
520551

@@ -538,8 +569,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
538569
assert(!cir::MissingFeatures::opCallAttrs());
539570

540571
mlir::Location callLoc = loc;
541-
cir::CIRCallOpInterface theCall = emitCallLikeOp(
542-
*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, cirCallArgs);
572+
cir::CIRCallOpInterface theCall =
573+
emitCallLikeOp(*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp,
574+
cirCallArgs, sideEffect);
543575

544576
if (callOp)
545577
*callOp = theCall;

clang/lib/CIR/CodeGen/CIRGenCall.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ class CIRGenCallee {
105105
/// callee
106106
CIRGenCallee prepareConcreteCallee(CIRGenFunction &cgf) const;
107107

108+
CIRGenCalleeInfo getAbstractInfo() const {
109+
assert(!cir::MissingFeatures::opCallVirtual());
110+
assert(isOrdinary());
111+
return abstractInfo;
112+
}
113+
108114
mlir::Operation *getFunctionPointer() const {
109115
assert(isOrdinary());
110116
return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr);

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H
1515

1616
#include "CIRGenBuilder.h"
17+
#include "CIRGenCall.h"
1718
#include "CIRGenTypeCache.h"
1819
#include "CIRGenTypes.h"
1920
#include "CIRGenValue.h"
@@ -145,6 +146,15 @@ class CIRGenModule : public CIRGenTypeCache {
145146
const CXXRecordDecl *derivedClass,
146147
llvm::iterator_range<CastExpr::path_const_iterator> path);
147148

149+
/// Get the CIR attributes and calling convention to use for a particular
150+
/// function type.
151+
///
152+
/// \param calleeInfo - The callee information these attributes are being
153+
/// constructed for. If valid, the attributes applied to this decl may
154+
/// contribute to the function attributes and calling convention.
155+
void constructAttributeList(CIRGenCalleeInfo calleeInfo,
156+
cir::SideEffect &sideEffect);
157+
148158
/// Return a constant array for the given string.
149159
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
150160

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

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,46 @@ Operation *cir::CIRDialect::materializeConstant(mlir::OpBuilder &builder,
9292
// Helpers
9393
//===----------------------------------------------------------------------===//
9494

95+
// Parses one of the keywords provided in the list `keywords` and returns the
96+
// position of the parsed keyword in the list. If none of the keywords from the
97+
// list is parsed, returns -1.
98+
static int parseOptionalKeywordAlternative(AsmParser &parser,
99+
ArrayRef<llvm::StringRef> keywords) {
100+
for (auto en : llvm::enumerate(keywords)) {
101+
if (succeeded(parser.parseOptionalKeyword(en.value())))
102+
return en.index();
103+
}
104+
return -1;
105+
}
106+
107+
namespace {
108+
template <typename Ty> struct EnumTraits {};
109+
110+
#define REGISTER_ENUM_TYPE(Ty) \
111+
template <> struct EnumTraits<cir::Ty> { \
112+
static llvm::StringRef stringify(cir::Ty value) { \
113+
return stringify##Ty(value); \
114+
} \
115+
static unsigned getMaxEnumVal() { return cir::getMaxEnumValFor##Ty(); } \
116+
}
117+
118+
REGISTER_ENUM_TYPE(SideEffect);
119+
} // namespace
120+
121+
/// Parse an enum from the keyword, return failure if the keyword is not found.
122+
template <typename EnumTy, typename RetTy = EnumTy>
123+
static ParseResult parseCIRKeyword(AsmParser &parser, RetTy &result) {
124+
llvm::SmallVector<llvm::StringRef, 10> names;
125+
for (unsigned i = 0, e = EnumTraits<EnumTy>::getMaxEnumVal(); i <= e; ++i)
126+
names.push_back(EnumTraits<EnumTy>::stringify(static_cast<EnumTy>(i)));
127+
128+
int index = parseOptionalKeywordAlternative(parser, names);
129+
if (index == -1)
130+
return failure();
131+
result = static_cast<RetTy>(index);
132+
return success();
133+
}
134+
95135
// Check if a region's termination omission is valid and, if so, creates and
96136
// inserts the omitted terminator into the region.
97137
static LogicalResult ensureRegionTerm(OpAsmParser &parser, Region &region,
@@ -534,6 +574,18 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
534574
if (parser.parseRParen())
535575
return mlir::failure();
536576

577+
if (parser.parseOptionalKeyword("side_effect").succeeded()) {
578+
if (parser.parseLParen().failed())
579+
return failure();
580+
cir::SideEffect sideEffect;
581+
if (parseCIRKeyword<cir::SideEffect>(parser, sideEffect).failed())
582+
return failure();
583+
if (parser.parseRParen().failed())
584+
return failure();
585+
auto attr = cir::SideEffectAttr::get(parser.getContext(), sideEffect);
586+
result.addAttribute("side_effect", attr);
587+
}
588+
537589
if (parser.parseOptionalAttrDict(result.attributes))
538590
return ::mlir::failure();
539591

@@ -556,7 +608,8 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
556608
static void printCallCommon(mlir::Operation *op,
557609
mlir::FlatSymbolRefAttr calleeSym,
558610
mlir::Value indirectCallee,
559-
mlir::OpAsmPrinter &printer) {
611+
mlir::OpAsmPrinter &printer,
612+
cir::SideEffect sideEffect) {
560613
printer << ' ';
561614

562615
auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
@@ -572,7 +625,13 @@ static void printCallCommon(mlir::Operation *op,
572625
}
573626
printer << "(" << ops << ")";
574627

575-
printer.printOptionalAttrDict(op->getAttrs(), {"callee"});
628+
if (sideEffect != cir::SideEffect::All) {
629+
printer << " side_effect(";
630+
printer << stringifySideEffect(sideEffect);
631+
printer << ")";
632+
}
633+
634+
printer.printOptionalAttrDict(op->getAttrs(), {"callee", "side_effect"});
576635

577636
printer << " : ";
578637
printer.printFunctionalType(op->getOperands().getTypes(),
@@ -586,7 +645,8 @@ mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
586645

587646
void cir::CallOp::print(mlir::OpAsmPrinter &p) {
588647
mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
589-
printCallCommon(*this, getCalleeAttr(), indirectCallee, p);
648+
cir::SideEffect sideEffect = getSideEffect();
649+
printCallCommon(*this, getCalleeAttr(), indirectCallee, p, sideEffect);
590650
}
591651

592652
static LogicalResult

0 commit comments

Comments
 (0)