Skip to content

Commit 4a27d49

Browse files
authored
[CIR] Add support for GCC function attribute "const" and "pure" (#1262)
This patch adds support for the following GCC function attributes: - `__attribute__((const))` - `__attribute__((pure))` The side effect information is attached to the call operations during CIRGen. During LLVM lowering, these information is consumed to further emit appropriate LLVM metadata on LLVM call instructions.
1 parent 3e898d9 commit 4a27d49

File tree

12 files changed

+255
-49
lines changed

12 files changed

+255
-49
lines changed

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -652,10 +652,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
652652
mlir::Type returnType = cir::VoidType(),
653653
mlir::ValueRange operands = mlir::ValueRange(),
654654
cir::CallingConv callingConv = cir::CallingConv::C,
655+
cir::SideEffect sideEffect = cir::SideEffect::All,
655656
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
656657

657-
cir::CallOp callOp =
658-
create<cir::CallOp>(loc, callee, returnType, operands, callingConv);
658+
cir::CallOp callOp = create<cir::CallOp>(loc, callee, returnType, operands,
659+
callingConv, sideEffect);
659660

660661
if (extraFnAttr) {
661662
callOp->setAttr("extra_attrs", extraFnAttr);
@@ -671,32 +672,35 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
671672
cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee,
672673
mlir::ValueRange operands = mlir::ValueRange(),
673674
cir::CallingConv callingConv = cir::CallingConv::C,
675+
cir::SideEffect sideEffect = cir::SideEffect::All,
674676
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
675677
return createCallOp(loc, mlir::SymbolRefAttr::get(callee),
676678
callee.getFunctionType().getReturnType(), operands,
677-
callingConv, extraFnAttr);
679+
callingConv, sideEffect, extraFnAttr);
678680
}
679681

680682
cir::CallOp
681683
createIndirectCallOp(mlir::Location loc, mlir::Value ind_target,
682684
cir::FuncType fn_type,
683685
mlir::ValueRange operands = mlir::ValueRange(),
684686
cir::CallingConv callingConv = cir::CallingConv::C,
687+
cir::SideEffect sideEffect = cir::SideEffect::All,
685688
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
686689

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

690693
return createCallOp(loc, mlir::SymbolRefAttr(), fn_type.getReturnType(),
691-
resOperands, callingConv, extraFnAttr);
694+
resOperands, callingConv, sideEffect, extraFnAttr);
692695
}
693696

694697
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
695698
mlir::ValueRange operands = mlir::ValueRange(),
696699
cir::CallingConv callingConv = cir::CallingConv::C,
700+
cir::SideEffect sideEffect = cir::SideEffect::All,
697701
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
698702
return createCallOp(loc, callee, cir::VoidType(), operands, callingConv,
699-
extraFnAttr);
703+
sideEffect, extraFnAttr);
700704
}
701705

702706
cir::CallOp
@@ -705,10 +709,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
705709
mlir::Type returnType = cir::VoidType(),
706710
mlir::ValueRange operands = mlir::ValueRange(),
707711
cir::CallingConv callingConv = cir::CallingConv::C,
712+
cir::SideEffect sideEffect = cir::SideEffect::All,
708713
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
709714
cir::CallOp tryCallOp =
710715
create<cir::CallOp>(loc, callee, returnType, operands, callingConv,
711-
/*exception=*/getUnitAttr());
716+
sideEffect, /*exception=*/getUnitAttr());
712717
if (extraFnAttr) {
713718
tryCallOp->setAttr("extra_attrs", extraFnAttr);
714719
} else {
@@ -724,20 +729,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
724729
createTryCallOp(mlir::Location loc, cir::FuncOp callee,
725730
mlir::ValueRange operands,
726731
cir::CallingConv callingConv = cir::CallingConv::C,
732+
cir::SideEffect sideEffect = cir::SideEffect::All,
727733
cir::ExtraFuncAttributesAttr extraFnAttr = {}) {
728734
return createTryCallOp(loc, mlir::SymbolRefAttr::get(callee),
729735
callee.getFunctionType().getReturnType(), operands,
730-
callingConv, extraFnAttr);
736+
callingConv, sideEffect, extraFnAttr);
731737
}
732738

733739
cir::CallOp
734740
createIndirectTryCallOp(mlir::Location loc, mlir::Value ind_target,
735741
cir::FuncType fn_type, mlir::ValueRange operands,
736-
cir::CallingConv callingConv = cir::CallingConv::C) {
742+
cir::CallingConv callingConv = cir::CallingConv::C,
743+
cir::SideEffect sideEffect = cir::SideEffect::All) {
737744
llvm::SmallVector<mlir::Value, 4> resOperands({ind_target});
738745
resOperands.append(operands.begin(), operands.end());
739746
return createTryCallOp(loc, mlir::SymbolRefAttr(), fn_type.getReturnType(),
740-
resOperands, callingConv);
747+
resOperands, callingConv, sideEffect);
741748
}
742749

743750
struct GetMethodResults {

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

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3574,6 +3574,39 @@ def DeleteArrayOp : CIR_Op<"delete.array">,
35743574
// CallOp and TryCallOp
35753575
//===----------------------------------------------------------------------===//
35763576

3577+
def SE_All : I32EnumAttrCase<"All", 1, "all">;
3578+
def SE_Pure : I32EnumAttrCase<"Pure", 2, "pure">;
3579+
def SE_Const : I32EnumAttrCase<"Const", 3, "const">;
3580+
3581+
def SideEffect : I32EnumAttr<
3582+
"SideEffect", "allowed side effects of a function",
3583+
[SE_All, SE_Pure, SE_Const]> {
3584+
let description = [{
3585+
The side effect attribute specifies the possible side effects of the callee
3586+
of a call operation. This is an enumeration attribute and all possible
3587+
enumerators are:
3588+
3589+
- all: The callee can have any side effects. This is the default if no side
3590+
effects are explicitly listed.
3591+
- pure: The callee may read data from memory, but it cannot write data to
3592+
memory. This has the same effect as the GNU C/C++ attribute
3593+
`__attribute__((pure))`.
3594+
- const: The callee may not read or write data from memory. This has the
3595+
same effect as the GNU C/C++ attribute `__attribute__((const))`.
3596+
3597+
Examples:
3598+
3599+
```mlir
3600+
%0 = cir.const #cir.int<0> : !s32i
3601+
%1 = cir.const #cir.int<1> : !s32i
3602+
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(all)
3603+
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(pure)
3604+
%2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(const)
3605+
```
3606+
}];
3607+
let cppNamespace = "::cir";
3608+
}
3609+
35773610
class CIR_CallOp<string mnemonic, list<Trait> extra_traits = []> :
35783611
Op<CIR_Dialect, mnemonic,
35793612
!listconcat(extra_traits,
@@ -3624,6 +3657,7 @@ class CIR_CallOp<string mnemonic, list<Trait> extra_traits = []> :
36243657
OptionalAttr<FlatSymbolRefAttr>:$callee,
36253658
Variadic<CIR_AnyType>:$arg_ops,
36263659
DefaultValuedAttr<CallingConv, "CallingConv::C">:$calling_conv,
3660+
DefaultValuedAttr<SideEffect, "SideEffect::All">:$side_effect,
36273661
ExtraFuncAttr:$extra_attrs,
36283662
OptionalAttr<ASTCallExprInterface>:$ast
36293663
);
@@ -3676,12 +3710,15 @@ def CallOp : CIR_CallOp<"call", [NoRegionArguments]> {
36763710
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
36773711
CArg<"mlir::ValueRange", "{}">:$operands,
36783712
CArg<"CallingConv", "CallingConv::C">:$callingConv,
3713+
CArg<"SideEffect", "SideEffect::All">:$sideEffect,
36793714
CArg<"mlir::UnitAttr", "{}">:$exception), [{
36803715
$_state.addOperands(operands);
36813716
if (callee)
36823717
$_state.addAttribute("callee", callee);
36833718
$_state.addAttribute("calling_conv",
36843719
CallingConvAttr::get($_builder.getContext(), callingConv));
3720+
$_state.addAttribute("side_effect",
3721+
SideEffectAttr::get($_builder.getContext(), sideEffect));
36853722
if (exception)
36863723
$_state.addAttribute("exception", exception);
36873724
if (resType && !isa<VoidType>(resType))
@@ -3693,13 +3730,16 @@ def CallOp : CIR_CallOp<"call", [NoRegionArguments]> {
36933730
"FuncType":$fn_type,
36943731
CArg<"mlir::ValueRange", "{}">:$operands,
36953732
CArg<"CallingConv", "CallingConv::C">:$callingConv,
3733+
CArg<"SideEffect", "SideEffect::All">:$sideEffect,
36963734
CArg<"mlir::UnitAttr", "{}">:$exception), [{
36973735
$_state.addOperands(ValueRange{ind_target});
36983736
$_state.addOperands(operands);
36993737
if (!fn_type.isVoid())
37003738
$_state.addTypes(fn_type.getReturnType());
37013739
$_state.addAttribute("calling_conv",
37023740
CallingConvAttr::get($_builder.getContext(), callingConv));
3741+
$_state.addAttribute("side_effect",
3742+
SideEffectAttr::get($_builder.getContext(), sideEffect));
37033743
if (exception)
37043744
$_state.addAttribute("exception", exception);
37053745
// Create region placeholder for potential cleanups.
@@ -3742,7 +3782,8 @@ def TryCallOp : CIR_CallOp<"try_call",
37423782
CArg<"mlir::ValueRange", "{}">:$operands,
37433783
CArg<"mlir::ValueRange", "{}">:$contOperands,
37443784
CArg<"mlir::ValueRange", "{}">:$landingPadOperands,
3745-
CArg<"CallingConv", "CallingConv::C">:$callingConv), [{
3785+
CArg<"CallingConv", "CallingConv::C">:$callingConv,
3786+
CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
37463787
$_state.addOperands(operands);
37473788
if (callee)
37483789
$_state.addAttribute("callee", callee);
@@ -3751,6 +3792,8 @@ def TryCallOp : CIR_CallOp<"try_call",
37513792

37523793
$_state.addAttribute("calling_conv",
37533794
CallingConvAttr::get($_builder.getContext(), callingConv));
3795+
$_state.addAttribute("side_effect",
3796+
SideEffectAttr::get($_builder.getContext(), sideEffect));
37543797

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

37823826
$_state.addAttribute("calling_conv",
37833827
CallingConvAttr::get($_builder.getContext(), callingConv));
3828+
$_state.addAttribute("side_effect",
3829+
SideEffectAttr::get($_builder.getContext(), sideEffect));
37843830

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

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

41934239
Examples:

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ let cppNamespace = "::cir" in {
3737
InterfaceMethod<
3838
"Return the calling convention of the call operation",
3939
"cir::CallingConv", "getCallingConv", (ins)>,
40+
InterfaceMethod<
41+
"Return the side effects of the call operation",
42+
"cir::SideEffect", "getSideEffect", (ins)>,
4043
];
4144
}
4245

@@ -50,20 +53,20 @@ let cppNamespace = "::cir" in {
5053
>,
5154
InterfaceMethod<"",
5255
"bool", "hasLocalLinkage", (ins), [{}],
53-
/*defaultImplementation=*/[{
54-
return cir::isLocalLinkage($_op.getLinkage());
56+
/*defaultImplementation=*/[{
57+
return cir::isLocalLinkage($_op.getLinkage());
5558
}]
5659
>,
5760
InterfaceMethod<"",
5861
"bool", "hasExternalWeakLinkage", (ins), [{}],
59-
/*defaultImplementation=*/[{
60-
return cir::isExternalWeakLinkage($_op.getLinkage());
62+
/*defaultImplementation=*/[{
63+
return cir::isExternalWeakLinkage($_op.getLinkage());
6164
}]
6265
>,
6366
InterfaceMethod<"",
6467
"bool", "isExternalLinkage", (ins), [{}],
65-
/*defaultImplementation=*/[{
66-
return cir::isExternalLinkage($_op.getLinkage());
68+
/*defaultImplementation=*/[{
69+
return cir::isExternalLinkage($_op.getLinkage());
6770
}]
6871
>,
6972
InterfaceMethod<"",
@@ -87,8 +90,8 @@ let cppNamespace = "::cir" in {
8790
}]
8891
>,
8992
];
90-
let extraClassDeclaration = [{
91-
bool hasDefaultVisibility();
93+
let extraClassDeclaration = [{
94+
bool hasDefaultVisibility();
9295
bool canBenefitFromLocalAlias();
9396
}];
9497
}

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,10 @@ static void AddAttributesFromFunctionProtoType(CIRGenBuilderTy &builder,
345345
/// attributes that restrict how the frontend generates code must be
346346
/// added here rather than getDefaultFunctionAttributes.
347347
///
348-
void CIRGenModule::constructAttributeList(StringRef Name,
349-
const CIRGenFunctionInfo &FI,
350-
CIRGenCalleeInfo CalleeInfo,
351-
mlir::NamedAttrList &funcAttrs,
352-
cir::CallingConv &callingConv,
353-
bool AttrOnCallSite, bool IsThunk) {
348+
void CIRGenModule::constructAttributeList(
349+
StringRef Name, const CIRGenFunctionInfo &FI, CIRGenCalleeInfo CalleeInfo,
350+
mlir::NamedAttrList &funcAttrs, cir::CallingConv &callingConv,
351+
cir::SideEffect &sideEffect, bool AttrOnCallSite, bool IsThunk) {
354352
// Implementation Disclaimer
355353
//
356354
// UnimplementedFeature and asserts are used throughout the code to track
@@ -364,6 +362,7 @@ void CIRGenModule::constructAttributeList(StringRef Name,
364362

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

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

@@ -466,11 +467,13 @@ void CIRGenModule::constructAttributeList(StringRef Name,
466467
getDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite, funcAttrs);
467468
}
468469

469-
static cir::CIRCallOpInterface emitCallLikeOp(
470-
CIRGenFunction &CGF, mlir::Location callLoc, cir::FuncType indirectFuncTy,
471-
mlir::Value indirectFuncVal, cir::FuncOp directFuncOp,
472-
SmallVectorImpl<mlir::Value> &CIRCallArgs, bool isInvoke,
473-
cir::CallingConv callingConv, cir::ExtraFuncAttributesAttr extraFnAttrs) {
470+
static cir::CIRCallOpInterface
471+
emitCallLikeOp(CIRGenFunction &CGF, mlir::Location callLoc,
472+
cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,
473+
cir::FuncOp directFuncOp,
474+
SmallVectorImpl<mlir::Value> &CIRCallArgs, bool isInvoke,
475+
cir::CallingConv callingConv, cir::SideEffect sideEffect,
476+
cir::ExtraFuncAttributesAttr extraFnAttrs) {
474477
auto &builder = CGF.getBuilder();
475478
auto getOrCreateSurroundingTryOp = [&]() {
476479
// In OG, we build the landing pad for this scope. In CIR, we emit a
@@ -521,10 +524,11 @@ static cir::CIRCallOpInterface emitCallLikeOp(
521524
assert(callingConv == cir::CallingConv::C && "NYI");
522525
if (indirectFuncTy) {
523526
callOpWithExceptions = builder.createIndirectTryCallOp(
524-
callLoc, indirectFuncVal, indirectFuncTy, CIRCallArgs);
527+
callLoc, indirectFuncVal, indirectFuncTy, CIRCallArgs, callingConv,
528+
sideEffect);
525529
} else {
526-
callOpWithExceptions =
527-
builder.createTryCallOp(callLoc, directFuncOp, CIRCallArgs);
530+
callOpWithExceptions = builder.createTryCallOp(
531+
callLoc, directFuncOp, CIRCallArgs, callingConv, sideEffect);
528532
}
529533
callOpWithExceptions->setAttr("extra_attrs", extraFnAttrs);
530534

@@ -544,12 +548,12 @@ static cir::CIRCallOpInterface emitCallLikeOp(
544548
if (indirectFuncTy) {
545549
// TODO(cir): Set calling convention for indirect calls.
546550
assert(callingConv == cir::CallingConv::C && "NYI");
547-
return builder.createIndirectCallOp(callLoc, indirectFuncVal,
548-
indirectFuncTy, CIRCallArgs,
549-
cir::CallingConv::C, extraFnAttrs);
551+
return builder.createIndirectCallOp(
552+
callLoc, indirectFuncVal, indirectFuncTy, CIRCallArgs,
553+
cir::CallingConv::C, sideEffect, extraFnAttrs);
550554
}
551555
return builder.createCallOp(callLoc, directFuncOp, CIRCallArgs, callingConv,
552-
extraFnAttrs);
556+
sideEffect, extraFnAttrs);
553557
}
554558

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

757761
cir::CallingConv callingConv;
762+
cir::SideEffect sideEffect;
758763
CGM.constructAttributeList(FnName, CallInfo, Callee.getAbstractInfo(), Attrs,
759-
callingConv,
764+
callingConv, sideEffect,
760765
/*AttrOnCallSite=*/true,
761766
/*IsThunk=*/false);
762767

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

838843
cir::CIRCallOpInterface callLikeOp = emitCallLikeOp(
839844
*this, callLoc, indirectFuncTy, indirectFuncVal, directFuncOp,
840-
CIRCallArgs, isInvoke, callingConv, extraFnAttrs);
845+
CIRCallArgs, isInvoke, callingConv, sideEffect, extraFnAttrs);
841846

842847
if (E)
843848
callLikeOp->setAttr("ast",

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2681,10 +2681,11 @@ void CIRGenModule::setCIRFunctionAttributes(GlobalDecl GD,
26812681
cir::FuncOp func, bool isThunk) {
26822682
// TODO(cir): More logic of constructAttributeList is needed.
26832683
cir::CallingConv callingConv;
2684+
cir::SideEffect sideEffect;
26842685

26852686
// Initialize PAL with existing attributes to merge attributes.
26862687
mlir::NamedAttrList PAL{func.getExtraAttrs().getElements().getValue()};
2687-
constructAttributeList(func.getName(), info, GD, PAL, callingConv,
2688+
constructAttributeList(func.getName(), info, GD, PAL, callingConv, sideEffect,
26882689
/*AttrOnCallSite=*/false, isThunk);
26892690
func.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get(
26902691
&getMLIRContext(), PAL.getDictionary(&getMLIRContext())));

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,8 @@ class CIRGenModule : public CIRGenTypeCache {
367367
CIRGenCalleeInfo CalleeInfo,
368368
mlir::NamedAttrList &Attrs,
369369
cir::CallingConv &callingConv,
370-
bool AttrOnCallSite, bool IsThunk);
370+
cir::SideEffect &sideEffect, bool AttrOnCallSite,
371+
bool IsThunk);
371372

372373
/// Helper function for getDefaultFunctionAttributes. Builds a set of function
373374
/// attributes which can be simply added to a function.

0 commit comments

Comments
 (0)