Skip to content
Open
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
43 changes: 43 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -959,5 +959,48 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> {
`<` custom<RecordMembers>($data) `>`
}];
}
//===----------------------------------------------------------------------===//
// InlineAttr
//===----------------------------------------------------------------------===//

def CIR_InlineKind : CIR_I32EnumAttr<"InlineKind", "inlineKind", [
I32EnumAttrCase<"NoInline", 1, "never">,
I32EnumAttrCase<"AlwaysInline", 2, "always">,
I32EnumAttrCase<"InlineHint", 3, "hint">
]> {
let genSpecializedAttr = 0;
}

def CIR_InlineAttr : CIR_EnumAttr<CIR_InlineKind, "inline"> {
let summary = "Inline attribute";
let description = [{
Inline attribute represents user directives for inlining behavior.
This attribute is only used by `cir.func` operations.

Values:
- `never`: Prevents the function from being inlined (__attribute__((noinline)))
- `always`: Forces the function to be inlined (__attribute__((always_inline)))
- `hint`: Suggests the function should be inlined (inline keyword)

Example:
```
cir.func @noinline_func(%arg0: !s32i) -> !s32i #cir.inline<never> {
cir.return %arg0 : !s32i
}
cir.func @always_inline_func() -> !s32i #cir.inline<always> {
%0 = cir.const #cir.int<42> : !s32i
cir.return %0 : !s32i
}
```
}];

let cppClassName = "InlineAttr";

let extraClassDeclaration = [{
bool isNoInline() const { return getValue() == InlineKind::NoInline; };
bool isAlwaysInline() const { return getValue() == InlineKind::AlwaysInline; };
bool isInlineHint() const { return getValue() == InlineKind::InlineHint; };
}];
}

#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD
5 changes: 5 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2449,6 +2449,10 @@ def CIR_FuncOp : CIR_Op<"func", [
without a prototype and, consequently, may contain calls with invalid
arguments and undefined behavior.

The `inline_never` keyword marks a function that should not be inlined.
The `inline_always` keyword marks a function that should always be inlined.
The `inline_hint` keyword suggests that the function should be inlined.

Example:

```mlir
Expand Down Expand Up @@ -2483,6 +2487,7 @@ def CIR_FuncOp : CIR_Op<"func", [
UnitAttr:$dso_local,
DefaultValuedAttr<CIR_GlobalLinkageKind,
"cir::GlobalLinkageKind::ExternalLinkage">:$linkage,
OptionalAttr<CIR_InlineAttr>:$inline_kind,
OptionalAttr<StrAttr>:$sym_visibility,
UnitAttr:$comdat,
OptionalAttr<DictArrayAttr>:$arg_attrs,
Expand Down
15 changes: 12 additions & 3 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,30 @@ struct MissingFeatures {
static bool opAllocaCaptureByInit() { return false; }

// FuncOp handling
static bool opFuncOpenCLKernelMetadata() { return false; }
static bool opFuncArmNewAttr() { return false; }
static bool opFuncArmStreamingAttr() { return false; }
static bool opFuncAstDeclAttr() { return false; }
static bool opFuncAttributesForDefinition() { return false; }
static bool opFuncCallingConv() { return false; }
static bool opFuncColdHotAttr() { return false; }
static bool opFuncCPUAndFeaturesAttributes() { return false; }
static bool opFuncExceptions() { return false; }
static bool opFuncExtraAttrs() { return false; }
static bool opFuncMaybeHandleStaticInExternC() { return false; }
static bool opFuncMinSizeAttr() { return false; }
static bool opFuncMultipleReturnVals() { return false; }
static bool opFuncNakedAttr() { return false; }
static bool opFuncNoDuplicateAttr() { return false; }
static bool opFuncNoUnwind() { return false; }
static bool opFuncOpenCLKernelMetadata() { return false; }
static bool opFuncOperandBundles() { return false; }
static bool opFuncOptNoneAttr() { return false; }
static bool opFuncParameterAttributes() { return false; }
static bool opFuncReadOnly() { return false; }
static bool opFuncSection() { return false; }
static bool opFuncUnwindTablesAttr() { return false; }
static bool opFuncWillReturn() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
static bool setFunctionAttributes() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }

// CallOp handling
static bool opCallAggregateArgs() { return false; }
Expand Down Expand Up @@ -265,6 +272,7 @@ struct MissingFeatures {
static bool objCBlocks() { return false; }
static bool objCGC() { return false; }
static bool objCLifetime() { return false; }
static bool hlsl() { return false; }
static bool openCL() { return false; }
static bool openMP() { return false; }
static bool opTBAA() { return false; }
Expand All @@ -282,6 +290,7 @@ struct MissingFeatures {
static bool sourceLanguageCases() { return false; }
static bool stackBase() { return false; }
static bool stackSaveOp() { return false; }
static bool stackProtector() { return false; }
static bool targetCIRGenInfoArch() { return false; }
static bool targetCIRGenInfoOS() { return false; }
static bool targetCodeGenInfoGetNullPointer() { return false; }
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
curCGF = nullptr;

setNonAliasAttributes(gd, fn);
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),
fn);
return fn;
}

Expand Down
87 changes: 86 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
curCGF = nullptr;

setNonAliasAttributes(gd, funcOp);
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
setCIRFunctionAttributesForDefinition(funcDecl, funcOp);

if (funcDecl->getAttr<ConstructorAttr>())
errorNYI(funcDecl->getSourceRange(), "constructor attribute");
Expand Down Expand Up @@ -1885,6 +1885,91 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
}
}

void CIRGenModule::setCIRFunctionAttributesForDefinition(
const clang::FunctionDecl *decl, cir::FuncOp f) {
assert(!cir::MissingFeatures::opFuncUnwindTablesAttr());
assert(!cir::MissingFeatures::stackProtector());

std::optional<cir::InlineKind> existingInlineKind = f.getInlineKind();
bool isNoInline =
existingInlineKind && *existingInlineKind == cir::InlineKind::NoInline;
bool isAlwaysInline = existingInlineKind &&
*existingInlineKind == cir::InlineKind::AlwaysInline;

if (!decl) {
assert(!cir::MissingFeatures::hlsl());

if (!isAlwaysInline &&
codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) {
// If inlining is disabled and we don't have a declaration to control
// inlining, mark the function as 'noinline' unless it is explicitly
// marked as 'alwaysinline'.
f.setInlineKindAttr(
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
}

return;
}

assert(!cir::MissingFeatures::opFuncArmStreamingAttr());
assert(!cir::MissingFeatures::opFuncArmNewAttr());
assert(!cir::MissingFeatures::opFuncOptNoneAttr());
assert(!cir::MissingFeatures::opFuncMinSizeAttr());
assert(!cir::MissingFeatures::opFuncNakedAttr());
assert(!cir::MissingFeatures::opFuncNoDuplicateAttr());
assert(!cir::MissingFeatures::hlsl());

// Handle inline attributes
if (decl->hasAttr<NoInlineAttr>() && !isAlwaysInline) {
// Add noinline if the function isn't always_inline.
f.setInlineKindAttr(
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
} else if (decl->hasAttr<AlwaysInlineAttr>() && !isNoInline) {
// Don't override AlwaysInline with NoInline, or vice versa, since we can't
// specify both in IR.
f.setInlineKindAttr(
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::AlwaysInline));
} else if (codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) {
// If inlining is disabled, force everything that isn't always_inline
// to carry an explicit noinline attribute.
if (!isAlwaysInline) {
f.setInlineKindAttr(
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
}
} else {
// Otherwise, propagate the inline hint attribute and potentially use its
// absence to mark things as noinline.
// Search function and template pattern redeclarations for inline.
if (auto *fd = dyn_cast<FunctionDecl>(decl)) {
// TODO: Share this checkForInline implementation with classic codegen.
// This logic is likely to change over time, so sharing would help ensure
// consistency.
auto checkForInline = [](const FunctionDecl *decl) {
auto checkRedeclForInline = [](const FunctionDecl *redecl) {
return redecl->isInlineSpecified();
};
if (any_of(decl->redecls(), checkRedeclForInline))
return true;
const FunctionDecl *pattern = decl->getTemplateInstantiationPattern();
if (!pattern)
return false;
return any_of(pattern->redecls(), checkRedeclForInline);
};
if (checkForInline(fd)) {
f.setInlineKindAttr(cir::InlineAttr::get(&getMLIRContext(),
cir::InlineKind::InlineHint));
} else if (codeGenOpts.getInlining() ==
CodeGenOptions::OnlyHintInlining &&
!fd->isInlined() && !isAlwaysInline) {
f.setInlineKindAttr(
cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline));
}
}
}

assert(!cir::MissingFeatures::opFuncColdHotAttr());
}

cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable,
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ class CIRGenModule : public CIRGenTypeCache {
void setFunctionAttributes(GlobalDecl gd, cir::FuncOp f,
bool isIncompleteFunction, bool isThunk);

/// Set extra attributes (inline, etc.) for a function.
void setCIRFunctionAttributesForDefinition(const clang::FunctionDecl *fd,
cir::FuncOp f);

void emitGlobalDefinition(clang::GlobalDecl gd,
mlir::Operation *op = nullptr);
void emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op);
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,13 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
hasAlias = true;
}

// Parse optional inline attribute: #cir.inline<never|always|hint>
cir::InlineAttr inlineAttr;
OptionalParseResult inlineResult = parser.parseOptionalAttribute(
inlineAttr, getInlineKindAttrName(state.name), state.attributes);
if (inlineResult.has_value() && failed(*inlineResult))
return failure();

// Parse the optional function body.
auto *body = state.addRegion();
OptionalParseResult parseResult = parser.parseOptionalRegion(
Expand Down Expand Up @@ -1801,6 +1808,11 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
p << ")";
}

if (cir::InlineAttr inlineAttr = getInlineKindAttr()) {
p << ' ';
p.printAttribute(inlineAttr);
}

// Print the body if this is not an external function.
Region &body = getOperation()->getRegion(0);
if (!body.empty()) {
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,7 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
attr.getName() == getLinkageAttrNameString() ||
attr.getName() == func.getGlobalVisibilityAttrName() ||
attr.getName() == func.getDsoLocalAttrName() ||
attr.getName() == func.getInlineKindAttrName() ||
(filterArgAndResAttrs &&
(attr.getName() == func.getArgAttrsAttrName() ||
attr.getName() == func.getResAttrsAttrName())))
Expand Down Expand Up @@ -1623,6 +1624,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(

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

// Add inline_kind attribute with "cir." prefix so amendOperation handles it
if (auto inlineKind = op.getInlineKind()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just set the no_inline, always_inline, or inline_hint attribute here? It looks like LLVMFuncOp doesn't have an inline_hint attribute at the moment, but that should be fixed.

fn->setAttr("cir.inline_kind",
cir::InlineAttr::get(getContext(), *inlineKind));
}

fn.setVisibility_Attr(mlir::LLVM::VisibilityAttr::get(
getContext(), lowerCIRVisibilityToLLVMVisibility(
op.getGlobalVisibilityAttr().getValue())));
Expand Down
45 changes: 45 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ class CIRDialectLLVMIRTranslationInterface
public:
using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;

/// Any named attribute in the CIR dialect, i.e, with name started with
/// "cir.", will be handled here.
virtual mlir::LogicalResult amendOperation(
mlir::Operation *op, llvm::ArrayRef<llvm::Instruction *> instructions,
mlir::NamedAttribute attribute,
mlir::LLVM::ModuleTranslation &moduleTranslation) const override {
if (auto func = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op)) {
amendFunction(func, instructions, attribute, moduleTranslation);
}
return mlir::success();
}

/// Translates the given operation to LLVM IR using the provided IR builder
/// and saving the state in `moduleTranslation`.
mlir::LogicalResult convertOperation(
Expand All @@ -47,6 +59,39 @@ class CIRDialectLLVMIRTranslationInterface

return mlir::success();
}

// Translate CIR's inline attribute to LLVM's function attributes.
void amendFunction(mlir::LLVM::LLVMFuncOp func,
llvm::ArrayRef<llvm::Instruction *> instructions,
mlir::NamedAttribute attribute,
mlir::LLVM::ModuleTranslation &moduleTranslation) const {
llvm::Function *llvmFunc = moduleTranslation.lookupFunction(func.getName());

if (auto inlineAttr =
mlir::dyn_cast<cir::InlineAttr>(attribute.getValue())) {
const auto toLLVMAttr =
[](const cir::InlineAttr &attr) -> llvm::Attribute::AttrKind {
switch (attr.getValue()) {
case cir::InlineKind::NoInline:
return llvm::Attribute::NoInline;
case cir::InlineKind::AlwaysInline:
return llvm::Attribute::AlwaysInline;
case cir::InlineKind::InlineHint:
return llvm::Attribute::InlineHint;
}
llvm_unreachable("Unknown cir::InlineKind value in inlineAttr");
};

llvmFunc->addFnAttr(toLLVMAttr(inlineAttr));
func->removeAttr(attribute.getName());
}

assert(!cir::MissingFeatures::opFuncOptNoneAttr());
assert(!cir::MissingFeatures::opFuncNoUnwind());
assert(!cir::MissingFeatures::opFuncColdHotAttr());
assert(!cir::MissingFeatures::opFuncUnwindTablesAttr());
assert(!cir::MissingFeatures::openCL());
}
};

void registerCIRDialectTranslation(mlir::DialectRegistry &registry) {
Expand Down
Loading