Skip to content

[CIR] Add support for GCC function attribute "const" and "pure" #1262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 side effect 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
Loading