Skip to content

Commit

Permalink
[CIR] Add support for GCC function attribute "const" and "pure"
Browse files Browse the repository at this point in the history
This patch adds support for the following GCC function attributes:

  - __attribute__((const))
  - __attribute__((pure))
  • Loading branch information
Lancern committed Jan 9, 2025
1 parent 04d7dcf commit caedb99
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 49 deletions.
25 changes: 16 additions & 9 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,10 +652,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
mlir::Type returnType = cir::VoidType(),
mlir::ValueRange operands = mlir::ValueRange(),
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All,
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {

cir::CallOp callOp =
create<cir::CallOp>(loc, callee, returnType, operands, callingConv);
cir::CallOp callOp = create<cir::CallOp>(loc, callee, returnType, operands,
callingConv, sideEffect);

if (extraFnAttr) {
callOp->setAttr("extra_attrs", extraFnAttr);
Expand All @@ -671,32 +672,35 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee,
mlir::ValueRange operands = mlir::ValueRange(),
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All,
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
return createCallOp(loc, mlir::SymbolRefAttr::get(callee),
callee.getFunctionType().getReturnType(), operands,
callingConv, extraFnAttr);
callingConv, sideEffect, extraFnAttr);
}

cir::CallOp
createIndirectCallOp(mlir::Location loc, mlir::Value ind_target,
cir::FuncType fn_type,
mlir::ValueRange operands = mlir::ValueRange(),
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All,
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {

llvm::SmallVector<mlir::Value, 4> resOperands({ind_target});
resOperands.append(operands.begin(), operands.end());

return createCallOp(loc, mlir::SymbolRefAttr(), fn_type.getReturnType(),
resOperands, callingConv, extraFnAttr);
resOperands, callingConv, sideEffect, extraFnAttr);
}

cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
mlir::ValueRange operands = mlir::ValueRange(),
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All,
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
return createCallOp(loc, callee, cir::VoidType(), operands, callingConv,
extraFnAttr);
sideEffect, extraFnAttr);
}

cir::CallOp
Expand All @@ -705,10 +709,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
mlir::Type returnType = cir::VoidType(),
mlir::ValueRange operands = mlir::ValueRange(),
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All,
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
cir::CallOp tryCallOp =
create<cir::CallOp>(loc, callee, returnType, operands, callingConv,
/*exception=*/getUnitAttr());
sideEffect, /*exception=*/getUnitAttr());
if (extraFnAttr) {
tryCallOp->setAttr("extra_attrs", extraFnAttr);
} else {
Expand All @@ -724,20 +729,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
createTryCallOp(mlir::Location loc, cir::FuncOp callee,
mlir::ValueRange operands,
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All,
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
return createTryCallOp(loc, mlir::SymbolRefAttr::get(callee),
callee.getFunctionType().getReturnType(), operands,
callingConv, extraFnAttr);
callingConv, sideEffect, extraFnAttr);
}

cir::CallOp
createIndirectTryCallOp(mlir::Location loc, mlir::Value ind_target,
cir::FuncType fn_type, mlir::ValueRange operands,
cir::CallingConv callingConv = cir::CallingConv::C) {
cir::CallingConv callingConv = cir::CallingConv::C,
cir::SideEffect sideEffect = cir::SideEffect::All) {
llvm::SmallVector<mlir::Value, 4> resOperands({ind_target});
resOperands.append(operands.begin(), operands.end());
return createTryCallOp(loc, mlir::SymbolRefAttr(), fn_type.getReturnType(),
resOperands, callingConv);
resOperands, callingConv, sideEffect);
}

struct GetMethodResults {
Expand Down
52 changes: 49 additions & 3 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -3574,6 +3574,39 @@ def DeleteArrayOp : CIR_Op<"delete.array">,
// CallOp and TryCallOp
//===----------------------------------------------------------------------===//

def SE_All : I32EnumAttrCase<"All", 1, "all">;
def SE_Pure : I32EnumAttrCase<"Pure", 2, "pure">;
def SE_Const : I32EnumAttrCase<"Const", 3, "const">;

def SideEffect : I32EnumAttr<
"SideEffect", "allowed side effects of a function",
[SE_All, SE_Pure, SE_Const]> {
let description = [{
The `#cir.SideEffect` attribute specifies the possible side effects of the
callee of a call operation. This is an enumeration attribute and all
possible enumerators are:

- all: The callee can have any side effects. This is the default if no side
effects are explicitly listed.
- pure: The callee may read data from memory, but it cannot write data to
memory. This has the same effect as the GNU C/C++ attribute
`__attribute__((pure))`.
- const: The callee may not read or write data from memory. This has the
same effect as the GNU C/C++ attribute `__attribute__((const))`.

Examples:

```mlir
%0 = cir.const #cir.int<0> : !s32i
%1 = cir.const #cir.int<1> : !s32i
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(all)
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(pure)
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(const)
```
}];
let cppNamespace = "::cir";
}

class CIR_CallOp<string mnemonic, list<Trait> extra_traits = []> :
Op<CIR_Dialect, mnemonic,
!listconcat(extra_traits,
Expand Down Expand Up @@ -3624,6 +3657,7 @@ class CIR_CallOp<string mnemonic, list<Trait> extra_traits = []> :
OptionalAttr<FlatSymbolRefAttr>:$callee,
Variadic<CIR_AnyType>:$arg_ops,
DefaultValuedAttr<CallingConv, "CallingConv::C">:$calling_conv,
DefaultValuedAttr<SideEffect, "SideEffect::All">:$side_effect,
ExtraFuncAttr:$extra_attrs,
OptionalAttr<ASTCallExprInterface>:$ast
);
Expand Down Expand Up @@ -3676,12 +3710,15 @@ def CallOp : CIR_CallOp<"call", [NoRegionArguments]> {
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
CArg<"mlir::ValueRange", "{}">:$operands,
CArg<"CallingConv", "CallingConv::C">:$callingConv,
CArg<"SideEffect", "SideEffect::All">:$sideEffect,
CArg<"mlir::UnitAttr", "{}">:$exception), [{
$_state.addOperands(operands);
if (callee)
$_state.addAttribute("callee", callee);
$_state.addAttribute("calling_conv",
CallingConvAttr::get($_builder.getContext(), callingConv));
$_state.addAttribute("side_effect",
SideEffectAttr::get($_builder.getContext(), sideEffect));
if (exception)
$_state.addAttribute("exception", exception);
if (resType && !isa<VoidType>(resType))
Expand All @@ -3693,13 +3730,16 @@ def CallOp : CIR_CallOp<"call", [NoRegionArguments]> {
"FuncType":$fn_type,
CArg<"mlir::ValueRange", "{}">:$operands,
CArg<"CallingConv", "CallingConv::C">:$callingConv,
CArg<"SideEffect", "SideEffect::All">:$sideEffect,
CArg<"mlir::UnitAttr", "{}">:$exception), [{
$_state.addOperands(ValueRange{ind_target});
$_state.addOperands(operands);
if (!fn_type.isVoid())
$_state.addTypes(fn_type.getReturnType());
$_state.addAttribute("calling_conv",
CallingConvAttr::get($_builder.getContext(), callingConv));
$_state.addAttribute("side_effect",
SideEffectAttr::get($_builder.getContext(), sideEffect));
if (exception)
$_state.addAttribute("exception", exception);
// Create region placeholder for potential cleanups.
Expand Down Expand Up @@ -3742,7 +3782,8 @@ def TryCallOp : CIR_CallOp<"try_call",
CArg<"mlir::ValueRange", "{}">:$operands,
CArg<"mlir::ValueRange", "{}">:$contOperands,
CArg<"mlir::ValueRange", "{}">:$landingPadOperands,
CArg<"CallingConv", "CallingConv::C">:$callingConv), [{
CArg<"CallingConv", "CallingConv::C">:$callingConv,
CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
$_state.addOperands(operands);
if (callee)
$_state.addAttribute("callee", callee);
Expand All @@ -3751,6 +3792,8 @@ def TryCallOp : CIR_CallOp<"try_call",

$_state.addAttribute("calling_conv",
CallingConvAttr::get($_builder.getContext(), callingConv));
$_state.addAttribute("side_effect",
SideEffectAttr::get($_builder.getContext(), sideEffect));

// Handle branches
$_state.addOperands(contOperands);
Expand All @@ -3771,7 +3814,8 @@ def TryCallOp : CIR_CallOp<"try_call",
CArg<"mlir::ValueRange", "{}">:$operands,
CArg<"mlir::ValueRange", "{}">:$contOperands,
CArg<"mlir::ValueRange", "{}">:$landingPadOperands,
CArg<"CallingConv", "CallingConv::C">:$callingConv), [{
CArg<"CallingConv", "CallingConv::C">:$callingConv,
CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
::llvm::SmallVector<mlir::Value, 4> finalCallOperands({ind_target});
finalCallOperands.append(operands.begin(), operands.end());
$_state.addOperands(finalCallOperands);
Expand All @@ -3781,6 +3825,8 @@ def TryCallOp : CIR_CallOp<"try_call",

$_state.addAttribute("calling_conv",
CallingConvAttr::get($_builder.getContext(), callingConv));
$_state.addAttribute("side_effect",
SideEffectAttr::get($_builder.getContext(), sideEffect));

// Handle branches
$_state.addOperands(contOperands);
Expand Down Expand Up @@ -4187,7 +4233,7 @@ def MemCpyInlineOp : CIR_MemOp<"memcpy_inline"> {
Given two CIR pointers, `src` and `dst`, `memcpy_inline` will copy `len`
bytes from the memory pointed by `src` to the memory pointed by `dst`.

Unlike `cir.libc.memcpy`, this Op guarantees that no external functions
Unlike `cir.libc.memcpy`, this Op guarantees that no external functions
are called, and length of copied bytes is a constant.

Examples:
Expand Down
19 changes: 11 additions & 8 deletions clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ let cppNamespace = "::cir" in {
InterfaceMethod<
"Return the calling convention of the call operation",
"cir::CallingConv", "getCallingConv", (ins)>,
InterfaceMethod<
"Return the side effects of the call operation",
"cir::SideEffect", "getSideEffect", (ins)>,
];
}

Expand All @@ -50,20 +53,20 @@ let cppNamespace = "::cir" in {
>,
InterfaceMethod<"",
"bool", "hasLocalLinkage", (ins), [{}],
/*defaultImplementation=*/[{
return cir::isLocalLinkage($_op.getLinkage());
/*defaultImplementation=*/[{
return cir::isLocalLinkage($_op.getLinkage());
}]
>,
InterfaceMethod<"",
"bool", "hasExternalWeakLinkage", (ins), [{}],
/*defaultImplementation=*/[{
return cir::isExternalWeakLinkage($_op.getLinkage());
/*defaultImplementation=*/[{
return cir::isExternalWeakLinkage($_op.getLinkage());
}]
>,
InterfaceMethod<"",
"bool", "isExternalLinkage", (ins), [{}],
/*defaultImplementation=*/[{
return cir::isExternalLinkage($_op.getLinkage());
/*defaultImplementation=*/[{
return cir::isExternalLinkage($_op.getLinkage());
}]
>,
InterfaceMethod<"",
Expand All @@ -87,8 +90,8 @@ let cppNamespace = "::cir" in {
}]
>,
];
let extraClassDeclaration = [{
bool hasDefaultVisibility();
let extraClassDeclaration = [{
bool hasDefaultVisibility();
bool canBenefitFromLocalAlias();
}];
}
Expand Down
45 changes: 25 additions & 20 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,10 @@ static void AddAttributesFromFunctionProtoType(CIRGenBuilderTy &builder,
/// attributes that restrict how the frontend generates code must be
/// added here rather than getDefaultFunctionAttributes.
///
void CIRGenModule::constructAttributeList(StringRef Name,
const CIRGenFunctionInfo &FI,
CIRGenCalleeInfo CalleeInfo,
mlir::NamedAttrList &funcAttrs,
cir::CallingConv &callingConv,
bool AttrOnCallSite, bool IsThunk) {
void CIRGenModule::constructAttributeList(
StringRef Name, const CIRGenFunctionInfo &FI, CIRGenCalleeInfo CalleeInfo,
mlir::NamedAttrList &funcAttrs, cir::CallingConv &callingConv,
cir::SideEffect &sideEffect, bool AttrOnCallSite, bool IsThunk) {
// Implementation Disclaimer
//
// UnimplementedFeature and asserts are used throughout the code to track
Expand All @@ -364,6 +362,7 @@ void CIRGenModule::constructAttributeList(StringRef Name,

// Collect function CIR attributes from the CC lowering.
callingConv = FI.getEffectiveCallingConvention();
sideEffect = cir::SideEffect::All;
// TODO: NoReturn, cmse_nonsecure_call

// Collect function CIR attributes from the callee prototype if we have one.
Expand Down Expand Up @@ -421,8 +420,10 @@ void CIRGenModule::constructAttributeList(StringRef Name,
if (TargetDecl->hasAttr<ConstAttr>()) {
// gcc specifies that 'const' functions have greater restrictions than
// 'pure' functions, so they also cannot have infinite loops.
sideEffect = cir::SideEffect::Const;
} else if (TargetDecl->hasAttr<PureAttr>()) {
// gcc specifies that 'pure' functions cannot have infinite loops.
sideEffect = cir::SideEffect::Pure;
} else if (TargetDecl->hasAttr<NoAliasAttr>()) {
}

Expand Down Expand Up @@ -466,11 +467,13 @@ void CIRGenModule::constructAttributeList(StringRef Name,
getDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite, funcAttrs);
}

static cir::CIRCallOpInterface emitCallLikeOp(
CIRGenFunction &CGF, mlir::Location callLoc, cir::FuncType indirectFuncTy,
mlir::Value indirectFuncVal, cir::FuncOp directFuncOp,
SmallVectorImpl<mlir::Value> &CIRCallArgs, bool isInvoke,
cir::CallingConv callingConv, cir::ExtraFuncAttributesAttr extraFnAttrs) {
static cir::CIRCallOpInterface
emitCallLikeOp(CIRGenFunction &CGF, mlir::Location callLoc,
cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,
cir::FuncOp directFuncOp,
SmallVectorImpl<mlir::Value> &CIRCallArgs, bool isInvoke,
cir::CallingConv callingConv, cir::SideEffect sideEffect,
cir::ExtraFuncAttributesAttr extraFnAttrs) {
auto &builder = CGF.getBuilder();
auto getOrCreateSurroundingTryOp = [&]() {
// In OG, we build the landing pad for this scope. In CIR, we emit a
Expand Down Expand Up @@ -521,10 +524,11 @@ static cir::CIRCallOpInterface emitCallLikeOp(
assert(callingConv == cir::CallingConv::C && "NYI");
if (indirectFuncTy) {
callOpWithExceptions = builder.createIndirectTryCallOp(
callLoc, indirectFuncVal, indirectFuncTy, CIRCallArgs);
callLoc, indirectFuncVal, indirectFuncTy, CIRCallArgs, callingConv,
sideEffect);
} else {
callOpWithExceptions =
builder.createTryCallOp(callLoc, directFuncOp, CIRCallArgs);
callOpWithExceptions = builder.createTryCallOp(
callLoc, directFuncOp, CIRCallArgs, callingConv, sideEffect);
}
callOpWithExceptions->setAttr("extra_attrs", extraFnAttrs);

Expand All @@ -544,12 +548,12 @@ static cir::CIRCallOpInterface emitCallLikeOp(
if (indirectFuncTy) {
// TODO(cir): Set calling convention for indirect calls.
assert(callingConv == cir::CallingConv::C && "NYI");
return builder.createIndirectCallOp(callLoc, indirectFuncVal,
indirectFuncTy, CIRCallArgs,
cir::CallingConv::C, extraFnAttrs);
return builder.createIndirectCallOp(
callLoc, indirectFuncVal, indirectFuncTy, CIRCallArgs,
cir::CallingConv::C, sideEffect, extraFnAttrs);
}
return builder.createCallOp(callLoc, directFuncOp, CIRCallArgs, callingConv,
extraFnAttrs);
sideEffect, extraFnAttrs);
}

static RValue getRValueThroughMemory(mlir::Location loc,
Expand Down Expand Up @@ -755,8 +759,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &CallInfo,
FnName = calleeFnOp.getName();

cir::CallingConv callingConv;
cir::SideEffect sideEffect;
CGM.constructAttributeList(FnName, CallInfo, Callee.getAbstractInfo(), Attrs,
callingConv,
callingConv, sideEffect,
/*AttrOnCallSite=*/true,
/*IsThunk=*/false);

Expand Down Expand Up @@ -837,7 +842,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &CallInfo,

cir::CIRCallOpInterface callLikeOp = emitCallLikeOp(
*this, callLoc, indirectFuncTy, indirectFuncVal, directFuncOp,
CIRCallArgs, isInvoke, callingConv, extraFnAttrs);
CIRCallArgs, isInvoke, callingConv, sideEffect, extraFnAttrs);

if (E)
callLikeOp->setAttr("ast",
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2682,10 +2682,11 @@ void CIRGenModule::setCIRFunctionAttributes(GlobalDecl GD,
cir::FuncOp func, bool isThunk) {
// TODO(cir): More logic of constructAttributeList is needed.
cir::CallingConv callingConv;
cir::SideEffect sideEffect;

// Initialize PAL with existing attributes to merge attributes.
mlir::NamedAttrList PAL{func.getExtraAttrs().getElements().getValue()};
constructAttributeList(func.getName(), info, GD, PAL, callingConv,
constructAttributeList(func.getName(), info, GD, PAL, callingConv, sideEffect,
/*AttrOnCallSite=*/false, isThunk);
func.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get(
&getMLIRContext(), PAL.getDictionary(&getMLIRContext())));
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ class CIRGenModule : public CIRGenTypeCache {
CIRGenCalleeInfo CalleeInfo,
mlir::NamedAttrList &Attrs,
cir::CallingConv &callingConv,
bool AttrOnCallSite, bool IsThunk);
cir::SideEffect &sideEffect, bool AttrOnCallSite,
bool IsThunk);

/// Helper function for getDefaultFunctionAttributes. Builds a set of function
/// attributes which can be simply added to a function.
Expand Down
Loading

0 comments on commit caedb99

Please sign in to comment.