Skip to content

Commit

Permalink
[CIR][CIRGen][Lowering] Introduce, use and lower ExtraFuncAttr and In…
Browse files Browse the repository at this point in the history
…lineAttr (llvm#134)

Setting inline attributes based on user input and command line options.
This is optional as functions do not need such an attribute will not get
the attribute.
  • Loading branch information
htyu authored and lanza committed Jul 8, 2023
1 parent b03a902 commit 6d09811
Show file tree
Hide file tree
Showing 64 changed files with 389 additions and 144 deletions.
49 changes: 49 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,53 @@ def ASTFunctionDeclAttr : ASTDecl<"FunctionDecl", "fndecl">;
def ASTVarDeclAttr : ASTDecl<"VarDecl", "vardecl">;
def ASTRecordDeclAttr : ASTDecl<"RecordDecl", "recdecl">;


//===----------------------------------------------------------------------===//
// ExtraFuncAttr
//===----------------------------------------------------------------------===//

def ExtraFuncAttr : CIR_Attr<"ExtraFuncAttributes", "extra"> {
let summary = "Represents aggregated attributes for a function";
let description = [{
This is a wrapper of dictionary attrbiute that contains extra attributes of
a function.
}];

let parameters = (ins "DictionaryAttr":$elements);

let assemblyFormat = [{ `(` $elements `)` }];

// Printing and parsing also available in CIRDialect.cpp
}


def NoInline : I32EnumAttrCase<"NoInline", 1, "no">;
def AlwaysInline : I32EnumAttrCase<"AlwaysInline", 2, "always">;
def InlineHint : I32EnumAttrCase<"InlineHint", 3, "hint">;

def InlineKind : I32EnumAttr<"InlineKind", "inlineKind", [
NoInline, AlwaysInline, InlineHint
]> {
let cppNamespace = "::mlir::cir";
}

def InlineAttr : CIR_Attr<"Inline", "inline"> {
let summary = "Inline attribute";
let description = [{
Inline attributes represents user directives.
}];

let parameters = (ins "InlineKind":$value);

let assemblyFormat = [{
`<` $value `>`
}];

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

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

The `extra_attrs`, which is an aggregate of function-specific attributes is
required and mandatory to describle additional attributes that are not listed
above. Though mandatory, the prining of the attribute can be omitted if it is
empty.

Example:

```mlir
Expand Down Expand Up @@ -1532,6 +1537,7 @@ def FuncOp : CIR_Op<"func", [
UnitAttr:$no_proto,
DefaultValuedAttr<GlobalLinkageKind,
"GlobalLinkageKind::ExternalLinkage">:$linkage,
ExtraFuncAttr:$extra_attrs,
OptionalAttr<StrAttr>:$sym_visibility,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs,
Expand Down
64 changes: 64 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
builder.getContext(), mlir::cir::GlobalLinkageKind::ExternalLinkage));
mlir::SymbolTable::setSymbolVisibility(
f, mlir::SymbolTable::Visibility::Private);

setExtraAttributesForFunc(f, FD);

if (!curCGF)
theModule.push_back(f);
}
Expand All @@ -1787,6 +1790,67 @@ mlir::Location CIRGenModule::getLocForFunction(const clang::FunctionDecl *FD) {
return theModule->getLoc();
}

void CIRGenModule::setExtraAttributesForFunc(FuncOp f,
const clang::FunctionDecl *FD) {
mlir::NamedAttrList attrs;

if (!FD) {
// If we don't have a declaration to control inlining, the function isn't
// explicitly marked as alwaysinline for semantic reasons, and inlining is
// disabled, mark the function as noinline.
if (codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) {
auto attr = mlir::cir::InlineAttr::get(
builder.getContext(), mlir::cir::InlineKind::AlwaysInline);
attrs.set(attr.getMnemonic(), attr);
}
} else if (FD->hasAttr<NoInlineAttr>()) {
// Add noinline if the function isn't always_inline.
auto attr = mlir::cir::InlineAttr::get(builder.getContext(),
mlir::cir::InlineKind::NoInline);
attrs.set(attr.getMnemonic(), attr);
} else if (FD->hasAttr<AlwaysInlineAttr>()) {
// (noinline wins over always_inline, and we can't specify both in IR)
auto attr = mlir::cir::InlineAttr::get(builder.getContext(),
mlir::cir::InlineKind::AlwaysInline);
attrs.set(attr.getMnemonic(), attr);
} else if (codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) {
// If we're not inlining, then force everything that isn't always_inline
// to carry an explicit noinline attribute.
auto attr = mlir::cir::InlineAttr::get(builder.getContext(),
mlir::cir::InlineKind::NoInline);
attrs.set(attr.getMnemonic(), attr);
} 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.
auto CheckForInline = [](const FunctionDecl *FD) {
auto CheckRedeclForInline = [](const FunctionDecl *Redecl) {
return Redecl->isInlineSpecified();
};
if (any_of(FD->redecls(), CheckRedeclForInline))
return true;
const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern();
if (!Pattern)
return false;
return any_of(Pattern->redecls(), CheckRedeclForInline);
};
if (CheckForInline(FD)) {
auto attr = mlir::cir::InlineAttr::get(builder.getContext(),
mlir::cir::InlineKind::InlineHint);
attrs.set(attr.getMnemonic(), attr);
} else if (codeGenOpts.getInlining() == CodeGenOptions::OnlyHintInlining) {
auto attr = mlir::cir::InlineAttr::get(builder.getContext(),
mlir::cir::InlineKind::NoInline);
attrs.set(attr.getMnemonic(), attr);
}

}

f.setExtraAttrsAttr(mlir::cir::ExtraFuncAttributesAttr::get(
builder.getContext(),
attrs.getDictionary(builder.getContext())));
}

/// If the specified mangled name is not in the module,
/// create and return a CIR Function with the specified type. If there is
/// something in the module with the specified name, return it potentially
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,9 @@ class CIRGenModule : public CIRGenTypeCache {
void ReplaceUsesOfNonProtoTypeWithRealFunction(mlir::Operation *Old,
mlir::cir::FuncOp NewFn);

void setExtraAttributesForFunc(mlir::cir::FuncOp f,
const clang::FunctionDecl *FD);

// TODO: CodeGen also passes an AttributeList here. We'll have to match that
// in CIR
mlir::cir::FuncOp
Expand Down
24 changes: 23 additions & 1 deletion clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,21 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
hasAlias = true;
}

// If extra func attributes are present, parse them.
NamedAttrList extraAttrs;
if (::mlir::succeeded(parser.parseOptionalKeyword("extra"))) {
if (parser.parseLParen().failed())
return failure();
if (parser.parseOptionalAttrDict(extraAttrs).failed())
return failure();
if (parser.parseRParen().failed())
return failure();
}
state.addAttribute(getExtraAttrsAttrName(state.name),
mlir::cir::ExtraFuncAttributesAttr::get(
builder.getContext(),
extraAttrs.getDictionary(builder.getContext())));

// Parse the optional function body.
auto *body = state.addRegion();
OptionalParseResult parseResult = parser.parseOptionalRegion(
Expand Down Expand Up @@ -1529,14 +1544,21 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
p, *this,
{getSymVisibilityAttrName(), getAliaseeAttrName(),
getFunctionTypeAttrName(), getLinkageAttrName(), getBuiltinAttrName(),
getNoProtoAttrName()});
getNoProtoAttrName(), getExtraAttrsAttrName()});


if (auto aliaseeName = getAliasee()) {
p << " alias(";
p.printSymbolName(*aliaseeName);
p << ")";
}

if (!getExtraAttrs().getElements().empty()) {
p << " extra(";
p.printOptionalAttrDict(getExtraAttrs().getElements().getValue());
p << " )";
}

// Print the body if this is not an external function.
Region &body = getOperation()->getRegion(0);
if (!body.empty()) {
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerAttrToLLVMIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
//
//===----------------------------------------------------------------------===//

#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/IR/DialectRegistry.h"
#include "mlir/Target/LLVMIR/LLVMTranslationInterface.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "llvm/ADT/ArrayRef.h"

Expand All @@ -34,6 +37,25 @@ class CIRDialectLLVMIRTranslationInterface
mlir::NamedAttribute attribute,
mlir::LLVM::ModuleTranslation &moduleTranslation) const override {
// TODO: Implement this
auto func = dyn_cast<mlir::LLVM::LLVMFuncOp>(op);
if (!func)
return mlir::success();
llvm::Function *llvmFunc = moduleTranslation.lookupFunction(func.getName());
if (auto extraAttr = attribute.getValue()
.dyn_cast<mlir::cir::ExtraFuncAttributesAttr>()) {
for (auto attr : extraAttr.getElements()) {
if (auto inlineAttr = attr.getValue().dyn_cast<mlir::cir::InlineAttr>()) {
if (inlineAttr.isNoInline())
llvmFunc->addFnAttr(llvm::Attribute::NoInline);
else if (inlineAttr.isAlwaysInline())
llvmFunc->addFnAttr(llvm::Attribute::AlwaysInline);
else if (inlineAttr.isInlineHint())
llvmFunc->addFnAttr(llvm::Attribute::InlineHint);
else
llvm_unreachable("Unknown inline kind");
}
}
}
return mlir::success();
}
};
Expand Down
38 changes: 36 additions & 2 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,35 @@ class CIRFuncLowering : public mlir::OpConversionPattern<mlir::cir::FuncOp> {
public:
using OpConversionPattern<mlir::cir::FuncOp>::OpConversionPattern;

/// Returns the name used for the linkage attribute. This *must* correspond to
/// the name of the attribute in ODS.
static StringRef getLinkageAttrNameString() { return "linkage"; }

/// Only retain those attributes that are not constructed by
/// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out argument
/// attributes.
void
filterFuncAttributes(mlir::cir::FuncOp func, bool filterArgAndResAttrs,
SmallVectorImpl<mlir::NamedAttribute> &result) const {
for (auto attr : func->getAttrs()) {
if (attr.getName() == mlir::SymbolTable::getSymbolAttrName() ||
attr.getName() == func.getFunctionTypeAttrName() ||
attr.getName() == getLinkageAttrNameString() ||
(filterArgAndResAttrs &&
(attr.getName() == func.getArgAttrsAttrName() ||
attr.getName() == func.getResAttrsAttrName())))
continue;

// `CIRDialectLLVMIRTranslationInterface` requires "cir." prefix for
// dialect specific attributes, rename them.
if (attr.getName() == func.getExtraAttrsAttrName()) {
std::string cirName = "cir." + func.getExtraAttrsAttrName().str();
attr.setName(mlir::StringAttr::get(getContext(), cirName));
}
result.push_back(attr);
}
}

mlir::LogicalResult
matchAndRewrite(mlir::cir::FuncOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
Expand Down Expand Up @@ -737,9 +766,14 @@ class CIRFuncLowering : public mlir::OpConversionPattern<mlir::cir::FuncOp> {
Loc = FusedLoc.getLocations()[0];
}
assert(Loc.isa<mlir::FileLineColLoc>() && "expected single location here");

auto linkage = convertLinkage(op.getLinkage());
auto fn = rewriter.create<mlir::LLVM::LLVMFuncOp>(Loc, op.getName(),
llvmFnTy, linkage);
SmallVector<mlir::NamedAttribute, 4> attributes;
filterFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);

auto fn = rewriter.create<mlir::LLVM::LLVMFuncOp>(
Loc, op.getName(), llvmFnTy, linkage, false, mlir::LLVM::CConv::C,
mlir::SymbolRefAttr(), attributes);

rewriter.inlineRegionBefore(op.getBody(), fn.getBody(), fn.end());
if (failed(rewriter.convertRegionTypes(&fn.getBody(), *typeConverter,
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/String.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void test() {
// CHECK-NEXT: cir.call @_ZN6StringC2EPKc(%2, %3) : (!cir.ptr<!ty_22class2EString22>, !cir.ptr<!s8i>) -> ()
// CHECK-NEXT: cir.return

// CHECK: cir.func @_Z4testv() {
// CHECK: cir.func @_Z4testv()
// CHECK: cir.call @_ZN6StringC1Ev(%0) : (!cir.ptr<!ty_22class2EString22>) -> ()
// CHECK: cir.call @_ZN6StringC1Ei(%1, %3) : (!cir.ptr<!ty_22class2EString22>, !s32i) -> ()
// CHECK: cir.call @_ZN6StringC1EPKc(%2, %5) : (!cir.ptr<!ty_22class2EString22>, !cir.ptr<!s8i>) -> ()
8 changes: 4 additions & 4 deletions clang/test/CIR/CodeGen/agg-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// CHECK: !ty_22struct2Eyep_22 = !cir.struct<"struct.yep_", !u32i, !u32i>

struct Zero {
void yolo();
void yolo();
};

void f() {
Expand All @@ -14,7 +14,7 @@ void f() {
Zero z1 = Zero{};
}

// CHECK: cir.func @_Z1fv() {
// CHECK: cir.func @_Z1fv()
// CHECK: %0 = cir.alloca !ty_22struct2EZero22, cir.ptr <!ty_22struct2EZero22>, ["z0", init]
// CHECK: %1 = cir.alloca !ty_22struct2EZero22, cir.ptr <!ty_22struct2EZero22>, ["z1"]
// CHECK: cir.call @_ZN4ZeroC1Ev(%0) : (!cir.ptr<!ty_22struct2EZero22>) -> ()
Expand All @@ -33,7 +33,7 @@ typedef struct yep_ {

void use() { yop{}; }

// CHECK: cir.func @_Z3usev() {
// CHECK: cir.func @_Z3usev()
// CHECK: %0 = cir.alloca !ty_22struct2Eyep_22, cir.ptr <!ty_22struct2Eyep_22>, ["agg.tmp0"] {alignment = 4 : i64}
// CHECK: %1 = "cir.struct_element_addr"(%0) <{member_index = 0 : index, member_name = "Status"}> : (!cir.ptr<!ty_22struct2Eyep_22>) -> !cir.ptr<!u32i>
// CHECK: %2 = cir.const(#cir.int<0> : !u32i) : !u32i
Expand Down Expand Up @@ -63,7 +63,7 @@ void yo() {
Yo ext2 = {Y, &ext};
}

// CHECK: cir.func @_Z2yov() {
// CHECK: cir.func @_Z2yov()
// CHECK: %0 = cir.alloca !ty_22struct2EYo22, cir.ptr <!ty_22struct2EYo22>, ["ext"] {alignment = 8 : i64}
// CHECK: %1 = cir.alloca !ty_22struct2EYo22, cir.ptr <!ty_22struct2EYo22>, ["ext2", init] {alignment = 8 : i64}
// CHECK: %2 = cir.const(#cir.const_struct<{#cir.int<1000070000> : !u32i,#cir.null : !cir.ptr<!void>,#cir.int<0> : !u64i}> : !ty_22struct2EYo22) : !ty_22struct2EYo22
Expand Down
8 changes: 4 additions & 4 deletions clang/test/CIR/CodeGen/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ void a0() {
int a[10];
}

// CHECK: cir.func @_Z2a0v() {
// CHECK: cir.func @_Z2a0v()
// CHECK-NEXT: %0 = cir.alloca !cir.array<!s32i x 10>, cir.ptr <!cir.array<!s32i x 10>>, ["a"] {alignment = 16 : i64}

void a1() {
int a[10];
a[0] = 1;
}

// CHECK: cir.func @_Z2a1v() {
// CHECK: cir.func @_Z2a1v()
// CHECK-NEXT: %0 = cir.alloca !cir.array<!s32i x 10>, cir.ptr <!cir.array<!s32i x 10>>, ["a"] {alignment = 16 : i64}
// CHECK-NEXT: %1 = cir.const(#cir.int<1> : !s32i) : !s32i
// CHECK-NEXT: %2 = cir.const(#cir.int<0> : !s32i) : !s32i
Expand All @@ -26,7 +26,7 @@ int *a2() {
return &a[0];
}

// CHECK: cir.func @_Z2a2v() -> !cir.ptr<!s32i> {
// CHECK: cir.func @_Z2a2v() -> !cir.ptr<!s32i>
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!s32i>, cir.ptr <!cir.ptr<!s32i>>, ["__retval"] {alignment = 8 : i64}
// CHECK-NEXT: %1 = cir.alloca !cir.array<!s32i x 4>, cir.ptr <!cir.array<!s32i x 4>>, ["a"] {alignment = 16 : i64}
// CHECK-NEXT: %2 = cir.const(#cir.int<0> : !s32i) : !s32i
Expand All @@ -41,7 +41,7 @@ void local_stringlit() {
}

// CHECK: cir.global "private" constant internal @".str" = #cir.const_array<"whatnow\00" : !cir.array<!s8i x 8>> : !cir.array<!s8i x 8> {alignment = 1 : i64}
// CHECK: cir.func @_Z15local_stringlitv() {
// CHECK: cir.func @_Z15local_stringlitv()
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!s8i>, cir.ptr <!cir.ptr<!s8i>>, ["s", init] {alignment = 8 : i64}
// CHECK-NEXT: %1 = cir.get_global @".str" : cir.ptr <!cir.array<!s8i x 8>>
// CHECK-NEXT: %2 = cir.cast(array_to_ptrdecay, %1 : !cir.ptr<!cir.array<!s8i x 8>>), !cir.ptr<!s8i>
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/assign-operator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ int main() {
}
}

// CHECK: cir.func @main() -> !s32i {
// CHECK: cir.func @main() -> !s32i
// CHECK: %0 = cir.alloca !s32i, cir.ptr <!s32i>, ["__retval"] {alignment = 4 : i64}
// CHECK: %1 = cir.alloca !ty_22struct2EStringView22, cir.ptr <!ty_22struct2EStringView22>, ["sv", init] {alignment = 8 : i64}
// CHECK: cir.call @_ZN10StringViewC2Ev(%1) : (!cir.ptr<!ty_22struct2EStringView22>) -> ()
Expand Down
Loading

0 comments on commit 6d09811

Please sign in to comment.