Skip to content
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
8 changes: 8 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
cir::BoolType getBoolTy() { return cir::BoolType::get(getContext()); }
cir::VoidType getVoidTy() { return cir::VoidType::get(getContext()); }

cir::IntType getUIntNTy(int n) {
return cir::IntType::get(getContext(), n, false);
}

cir::IntType getSIntNTy(int n) {
return cir::IntType::get(getContext(), n, true);
}

cir::PointerType getPointerTo(mlir::Type ty) {
return cir::PointerType::get(ty);
}
Expand Down
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 @@ -967,6 +967,49 @@ 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 inline(never) {
cir.return %arg0 : !s32i
}
cir.func @always_inline_func() -> !s32i 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; };
}];
}

//===----------------------------------------------------------------------===//
// CatchAllAttr & UnwindAttr
Expand Down
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 @@ -2476,6 +2476,10 @@ def CIR_FuncOp : CIR_Op<"func", [
Similarly, for global destructors both `global_dtor` and
`global_dtor(<priority>)` are available.

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 @@ -2510,6 +2514,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 @@ -69,24 +69,31 @@ 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 opFuncNoReturn() { 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 @@ -271,6 +278,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 @@ -288,6 +296,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
10 changes: 10 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,16 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
/*relative_layout=*/false);
}

mlir::Value createDynCastToVoid(mlir::Location loc, mlir::Value src,
bool vtableUseRelativeLayout) {
// TODO(cir): consider address space here.
assert(!cir::MissingFeatures::addressSpace());
cir::PointerType destTy = getVoidPtrTy();
return cir::DynamicCastOp::create(
*this, loc, destTy, cir::DynamicCastKind::Ptr, src,
cir::DynamicCastInfoAttr{}, vtableUseRelativeLayout);
}

Address createBaseClassAddr(mlir::Location loc, Address addr,
mlir::Type destType, unsigned offset,
bool assumeNotNull) {
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,9 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return emitLibraryCall(*this, fd, e,
cgm.getBuiltinLibFunction(fd, builtinID));

cgm.errorNYI(e->getSourceRange(), "unimplemented builtin call");
cgm.errorNYI(e->getSourceRange(),
std::string("unimplemented builtin call: ") +
getContext().BuiltinInfo.getName(builtinID));
return getUndefRValue(e->getType());
}

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
15 changes: 11 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,15 @@ static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
}

static Address emitDynamicCastToVoid(CIRGenFunction &cgf, mlir::Location loc,
QualType srcRecordTy, Address src) {
bool vtableUsesRelativeLayout =
cgf.cgm.getItaniumVTableContext().isRelativeLayout();
mlir::Value ptr = cgf.getBuilder().createDynCastToVoid(
loc, src.getPointer(), vtableUsesRelativeLayout);
return Address{ptr, src.getAlignment()};
}

static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
mlir::Location loc,
QualType srcRecordTy,
Expand Down Expand Up @@ -1979,10 +1988,8 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
bool isCastToVoid = destRecordTy.isNull();
assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");

if (isCastToVoid) {
cgm.errorNYI(loc, "emitDynamicCastToVoid");
return {};
}
if (isCastToVoid)
return emitDynamicCastToVoid(cgf, loc, srcRecordTy, src).getPointer();

// If the destination is effectively final, the cast succeeds if and only
// if the dynamic type of the pointer is exactly the destination type.
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 @@ -451,7 +451,7 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
curCGF = nullptr;

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

auto getPriority = [this](const auto *attr) -> int {
Expr *e = attr->getPriority();
Expand Down Expand Up @@ -1919,6 +1919,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 @@ -429,6 +429,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
34 changes: 34 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,36 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
}).failed())
return failure();

// Parse optional inline kind: inline(never|always|hint)
if (parser.parseOptionalKeyword("inline").succeeded()) {
if (parser.parseLParen().failed())
return failure();

llvm::StringRef inlineKindStr;
const std::array<llvm::StringRef, cir::getMaxEnumValForInlineKind()>
allowedInlineKindStrs{
cir::stringifyInlineKind(cir::InlineKind::NoInline),
cir::stringifyInlineKind(cir::InlineKind::AlwaysInline),
cir::stringifyInlineKind(cir::InlineKind::InlineHint),
};
if (parser.parseOptionalKeyword(&inlineKindStr, allowedInlineKindStrs)
.failed())
return parser.emitError(parser.getCurrentLocation(),
"expected 'never', 'always', or 'hint'");

std::optional<InlineKind> inlineKind =
cir::symbolizeInlineKind(inlineKindStr);
if (!inlineKind)
return parser.emitError(parser.getCurrentLocation(),
"invalid inline kind");

state.addAttribute(getInlineKindAttrName(state.name),
cir::InlineAttr::get(builder.getContext(), *inlineKind));

if (parser.parseRParen().failed())
return failure();
}

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

if (cir::InlineAttr inlineAttr = getInlineKindAttr()) {
p << " inline(" << cir::stringifyInlineKind(inlineAttr.getValue()) << ")";
}

// Print the body if this is not an external function.
Region &body = getOperation()->getRegion(0);
if (!body.empty()) {
Expand Down
Loading