Skip to content

Commit 4aba5ed

Browse files
authored
[CIR] Add inline function attributes (#162866)
Unlike the incubator, this adds the inline attribute directly to FuncOp instead of adding the ExtraFnAttr dict. This adds three new optional keywords to CIR: inline_always, inline_never and inline_hint. Just like in OGCG -O0 implies inline_never on functions withoutt the C++ `inline` keyword and no other inlining-related attribute. This patch also adapts all tests that use functions so they account for LLVM attributes being attached now.
1 parent 7be89bb commit 4aba5ed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+448
-152
lines changed

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,49 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> {
967967
`<` custom<RecordMembers>($data) `>`
968968
}];
969969
}
970+
//===----------------------------------------------------------------------===//
971+
// InlineAttr
972+
//===----------------------------------------------------------------------===//
973+
974+
def CIR_InlineKind : CIR_I32EnumAttr<"InlineKind", "inlineKind", [
975+
I32EnumAttrCase<"NoInline", 1, "never">,
976+
I32EnumAttrCase<"AlwaysInline", 2, "always">,
977+
I32EnumAttrCase<"InlineHint", 3, "hint">
978+
]> {
979+
let genSpecializedAttr = 0;
980+
}
981+
982+
def CIR_InlineAttr : CIR_EnumAttr<CIR_InlineKind, "inline"> {
983+
let summary = "Inline attribute";
984+
let description = [{
985+
Inline attribute represents user directives for inlining behavior.
986+
This attribute is only used by `cir.func` operations.
987+
988+
Values:
989+
- `never`: Prevents the function from being inlined (__attribute__((noinline)))
990+
- `always`: Forces the function to be inlined (__attribute__((always_inline)))
991+
- `hint`: Suggests the function should be inlined (inline keyword)
992+
993+
Example:
994+
```
995+
cir.func @noinline_func(%arg0: !s32i) -> !s32i inline(never) {
996+
cir.return %arg0 : !s32i
997+
}
998+
cir.func @always_inline_func() -> !s32i inline(always) {
999+
%0 = cir.const #cir.int<42> : !s32i
1000+
cir.return %0 : !s32i
1001+
}
1002+
```
1003+
}];
1004+
1005+
let cppClassName = "InlineAttr";
1006+
1007+
let extraClassDeclaration = [{
1008+
bool isNoInline() const { return getValue() == InlineKind::NoInline; };
1009+
bool isAlwaysInline() const { return getValue() == InlineKind::AlwaysInline; };
1010+
bool isInlineHint() const { return getValue() == InlineKind::InlineHint; };
1011+
}];
1012+
}
9701013

9711014
//===----------------------------------------------------------------------===//
9721015
// CatchAllAttr & UnwindAttr

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,10 @@ def CIR_FuncOp : CIR_Op<"func", [
24762476
Similarly, for global destructors both `global_dtor` and
24772477
`global_dtor(<priority>)` are available.
24782478

2479+
The `inline(never)` keyword marks a function that should not be inlined.
2480+
The `inline(always)` keyword marks a function that should always be inlined.
2481+
The `inline(hint)` keyword suggests that the function should be inlined.
2482+
24792483
Example:
24802484

24812485
```mlir
@@ -2510,6 +2514,7 @@ def CIR_FuncOp : CIR_Op<"func", [
25102514
UnitAttr:$dso_local,
25112515
DefaultValuedAttr<CIR_GlobalLinkageKind,
25122516
"cir::GlobalLinkageKind::ExternalLinkage">:$linkage,
2517+
OptionalAttr<CIR_InlineAttr>:$inline_kind,
25132518
OptionalAttr<StrAttr>:$sym_visibility,
25142519
UnitAttr:$comdat,
25152520
OptionalAttr<DictArrayAttr>:$arg_attrs,

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,31 @@ struct MissingFeatures {
6969
static bool opAllocaCaptureByInit() { return false; }
7070

7171
// FuncOp handling
72-
static bool opFuncOpenCLKernelMetadata() { return false; }
72+
static bool opFuncArmNewAttr() { return false; }
73+
static bool opFuncArmStreamingAttr() { return false; }
7374
static bool opFuncAstDeclAttr() { return false; }
74-
static bool opFuncAttributesForDefinition() { return false; }
7575
static bool opFuncCallingConv() { return false; }
76+
static bool opFuncColdHotAttr() { return false; }
7677
static bool opFuncCPUAndFeaturesAttributes() { return false; }
7778
static bool opFuncExceptions() { return false; }
7879
static bool opFuncExtraAttrs() { return false; }
7980
static bool opFuncMaybeHandleStaticInExternC() { return false; }
81+
static bool opFuncMinSizeAttr() { return false; }
8082
static bool opFuncMultipleReturnVals() { return false; }
83+
static bool opFuncNakedAttr() { return false; }
84+
static bool opFuncNoDuplicateAttr() { return false; }
8185
static bool opFuncNoUnwind() { return false; }
86+
static bool opFuncOpenCLKernelMetadata() { return false; }
8287
static bool opFuncOperandBundles() { return false; }
88+
static bool opFuncOptNoneAttr() { return false; }
8389
static bool opFuncParameterAttributes() { return false; }
8490
static bool opFuncReadOnly() { return false; }
8591
static bool opFuncSection() { return false; }
92+
static bool opFuncUnwindTablesAttr() { return false; }
8693
static bool opFuncWillReturn() { return false; }
8794
static bool opFuncNoReturn() { return false; }
88-
static bool setLLVMFunctionFEnvAttributes() { return false; }
8995
static bool setFunctionAttributes() { return false; }
96+
static bool setLLVMFunctionFEnvAttributes() { return false; }
9097

9198
// CallOp handling
9299
static bool opCallAggregateArgs() { return false; }
@@ -271,6 +278,7 @@ struct MissingFeatures {
271278
static bool objCBlocks() { return false; }
272279
static bool objCGC() { return false; }
273280
static bool objCLifetime() { return false; }
281+
static bool hlsl() { return false; }
274282
static bool openCL() { return false; }
275283
static bool openMP() { return false; }
276284
static bool opTBAA() { return false; }
@@ -288,6 +296,7 @@ struct MissingFeatures {
288296
static bool sourceLanguageCases() { return false; }
289297
static bool stackBase() { return false; }
290298
static bool stackSaveOp() { return false; }
299+
static bool stackProtector() { return false; }
291300
static bool targetCIRGenInfoArch() { return false; }
292301
static bool targetCIRGenInfoOS() { return false; }
293302
static bool targetCodeGenInfoGetNullPointer() { return false; }

clang/lib/CIR/CodeGen/CIRGenCXX.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
171171
curCGF = nullptr;
172172

173173
setNonAliasAttributes(gd, fn);
174-
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
174+
setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),
175+
fn);
175176
return fn;
176177
}
177178

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
451451
curCGF = nullptr;
452452

453453
setNonAliasAttributes(gd, funcOp);
454-
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
454+
setCIRFunctionAttributesForDefinition(funcDecl, funcOp);
455455

456456
auto getPriority = [this](const auto *attr) -> int {
457457
Expr *e = attr->getPriority();
@@ -1919,6 +1919,91 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
19191919
}
19201920
}
19211921

1922+
void CIRGenModule::setCIRFunctionAttributesForDefinition(
1923+
const clang::FunctionDecl *decl, cir::FuncOp f) {
1924+
assert(!cir::MissingFeatures::opFuncUnwindTablesAttr());
1925+
assert(!cir::MissingFeatures::stackProtector());
1926+
1927+
std::optional<cir::InlineKind> existingInlineKind = f.getInlineKind();
1928+
bool isNoInline =
1929+
existingInlineKind && *existingInlineKind == cir::InlineKind::NoInline;
1930+
bool isAlwaysInline = existingInlineKind &&
1931+
*existingInlineKind == cir::InlineKind::AlwaysInline;
1932+
1933+
if (!decl) {
1934+
assert(!cir::MissingFeatures::hlsl());
1935+
1936+
if (!isAlwaysInline &&
1937+
codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) {
1938+
// If inlining is disabled and we don't have a declaration to control
1939+
// inlining, mark the function as 'noinline' unless it is explicitly
1940+
// marked as 'alwaysinline'.
1941+
f.setInlineKindAttr(
1942+
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
1943+
}
1944+
1945+
return;
1946+
}
1947+
1948+
assert(!cir::MissingFeatures::opFuncArmStreamingAttr());
1949+
assert(!cir::MissingFeatures::opFuncArmNewAttr());
1950+
assert(!cir::MissingFeatures::opFuncOptNoneAttr());
1951+
assert(!cir::MissingFeatures::opFuncMinSizeAttr());
1952+
assert(!cir::MissingFeatures::opFuncNakedAttr());
1953+
assert(!cir::MissingFeatures::opFuncNoDuplicateAttr());
1954+
assert(!cir::MissingFeatures::hlsl());
1955+
1956+
// Handle inline attributes
1957+
if (decl->hasAttr<NoInlineAttr>() && !isAlwaysInline) {
1958+
// Add noinline if the function isn't always_inline.
1959+
f.setInlineKindAttr(
1960+
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
1961+
} else if (decl->hasAttr<AlwaysInlineAttr>() && !isNoInline) {
1962+
// Don't override AlwaysInline with NoInline, or vice versa, since we can't
1963+
// specify both in IR.
1964+
f.setInlineKindAttr(
1965+
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::AlwaysInline));
1966+
} else if (codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) {
1967+
// If inlining is disabled, force everything that isn't always_inline
1968+
// to carry an explicit noinline attribute.
1969+
if (!isAlwaysInline) {
1970+
f.setInlineKindAttr(
1971+
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
1972+
}
1973+
} else {
1974+
// Otherwise, propagate the inline hint attribute and potentially use its
1975+
// absence to mark things as noinline.
1976+
// Search function and template pattern redeclarations for inline.
1977+
if (auto *fd = dyn_cast<FunctionDecl>(decl)) {
1978+
// TODO: Share this checkForInline implementation with classic codegen.
1979+
// This logic is likely to change over time, so sharing would help ensure
1980+
// consistency.
1981+
auto checkForInline = [](const FunctionDecl *decl) {
1982+
auto checkRedeclForInline = [](const FunctionDecl *redecl) {
1983+
return redecl->isInlineSpecified();
1984+
};
1985+
if (any_of(decl->redecls(), checkRedeclForInline))
1986+
return true;
1987+
const FunctionDecl *pattern = decl->getTemplateInstantiationPattern();
1988+
if (!pattern)
1989+
return false;
1990+
return any_of(pattern->redecls(), checkRedeclForInline);
1991+
};
1992+
if (checkForInline(fd)) {
1993+
f.setInlineKindAttr(cir::InlineAttr::get(&getMLIRContext(),
1994+
cir::InlineKind::InlineHint));
1995+
} else if (codeGenOpts.getInlining() ==
1996+
CodeGenOptions::OnlyHintInlining &&
1997+
!fd->isInlined() && !isAlwaysInline) {
1998+
f.setInlineKindAttr(
1999+
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
2000+
}
2001+
}
2002+
}
2003+
2004+
assert(!cir::MissingFeatures::opFuncColdHotAttr());
2005+
}
2006+
19222007
cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
19232008
StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable,
19242009
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ class CIRGenModule : public CIRGenTypeCache {
429429
void setFunctionAttributes(GlobalDecl gd, cir::FuncOp f,
430430
bool isIncompleteFunction, bool isThunk);
431431

432+
/// Set extra attributes (inline, etc.) for a function.
433+
void setCIRFunctionAttributesForDefinition(const clang::FunctionDecl *fd,
434+
cir::FuncOp f);
435+
432436
void emitGlobalDefinition(clang::GlobalDecl gd,
433437
mlir::Operation *op = nullptr);
434438
void emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op);

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,36 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
17581758
}).failed())
17591759
return failure();
17601760

1761+
// Parse optional inline kind: inline(never|always|hint)
1762+
if (parser.parseOptionalKeyword("inline").succeeded()) {
1763+
if (parser.parseLParen().failed())
1764+
return failure();
1765+
1766+
llvm::StringRef inlineKindStr;
1767+
const std::array<llvm::StringRef, cir::getMaxEnumValForInlineKind()>
1768+
allowedInlineKindStrs{
1769+
cir::stringifyInlineKind(cir::InlineKind::NoInline),
1770+
cir::stringifyInlineKind(cir::InlineKind::AlwaysInline),
1771+
cir::stringifyInlineKind(cir::InlineKind::InlineHint),
1772+
};
1773+
if (parser.parseOptionalKeyword(&inlineKindStr, allowedInlineKindStrs)
1774+
.failed())
1775+
return parser.emitError(parser.getCurrentLocation(),
1776+
"expected 'never', 'always', or 'hint'");
1777+
1778+
std::optional<InlineKind> inlineKind =
1779+
cir::symbolizeInlineKind(inlineKindStr);
1780+
if (!inlineKind)
1781+
return parser.emitError(parser.getCurrentLocation(),
1782+
"invalid inline kind");
1783+
1784+
state.addAttribute(getInlineKindAttrName(state.name),
1785+
cir::InlineAttr::get(builder.getContext(), *inlineKind));
1786+
1787+
if (parser.parseRParen().failed())
1788+
return failure();
1789+
}
1790+
17611791
// Parse the optional function body.
17621792
auto *body = state.addRegion();
17631793
OptionalParseResult parseResult = parser.parseOptionalRegion(
@@ -1851,6 +1881,10 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
18511881
p << "(" << globalDtorPriority.value() << ")";
18521882
}
18531883

1884+
if (cir::InlineAttr inlineAttr = getInlineKindAttr()) {
1885+
p << " inline(" << cir::stringifyInlineKind(inlineAttr.getValue()) << ")";
1886+
}
1887+
18541888
// Print the body if this is not an external function.
18551889
Region &body = getOperation()->getRegion(0);
18561890
if (!body.empty()) {

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
15871587
attr.getName() == getLinkageAttrNameString() ||
15881588
attr.getName() == func.getGlobalVisibilityAttrName() ||
15891589
attr.getName() == func.getDsoLocalAttrName() ||
1590+
attr.getName() == func.getInlineKindAttrName() ||
15901591
(filterArgAndResAttrs &&
15911592
(attr.getName() == func.getArgAttrsAttrName() ||
15921593
attr.getName() == func.getResAttrsAttrName())))
@@ -1671,6 +1672,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
16711672

16721673
assert(!cir::MissingFeatures::opFuncMultipleReturnVals());
16731674

1675+
if (auto inlineKind = op.getInlineKind()) {
1676+
fn.setNoInline(inlineKind == cir::InlineKind::NoInline);
1677+
fn.setInlineHint(inlineKind == cir::InlineKind::InlineHint);
1678+
fn.setAlwaysInline(inlineKind == cir::InlineKind::AlwaysInline);
1679+
}
1680+
16741681
fn.setVisibility_Attr(mlir::LLVM::VisibilityAttr::get(
16751682
getContext(), lowerCIRVisibilityToLLVMVisibility(
16761683
op.getGlobalVisibilityAttr().getValue())));

0 commit comments

Comments
 (0)