-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[CIR] Add support for function linkage and visibility #145600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1737,25 +1737,51 @@ def GetMemberOp : CIR_Op<"get_member"> { | |
|
||
def FuncOp : CIR_Op<"func", [ | ||
AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface, | ||
DeclareOpInterfaceMethods<CIRGlobalValueInterface>, | ||
IsolatedFromAbove | ||
]> { | ||
let summary = "Declare or define a function"; | ||
let description = [{ | ||
The `cir.func` operation defines a function, similar to the `mlir::FuncOp` | ||
built-in. | ||
|
||
The function linkage information is specified by `linkage`, as defined by | ||
`GlobalLinkageKind` attribute. | ||
|
||
Example: | ||
|
||
```mlir | ||
// External function definitions. | ||
cir.func @abort() | ||
|
||
// A function with internal linkage. | ||
cir.func internal @count(%x: i64) -> (i64) | ||
return %x : i64 | ||
|
||
// Linkage information | ||
cir.func linkonce_odr @some_method(...) | ||
``` | ||
}]; | ||
|
||
let arguments = (ins SymbolNameAttr:$sym_name, | ||
CIR_VisibilityAttr:$global_visibility, | ||
TypeAttrOf<CIR_FuncType>:$function_type, | ||
UnitAttr:$dso_local, | ||
DefaultValuedAttr<CIR_GlobalLinkageKind, | ||
"cir::GlobalLinkageKind::ExternalLinkage">:$linkage, | ||
OptionalAttr<StrAttr>:$sym_visibility, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this attribute for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that it's used by some general MLIR interfaces. |
||
UnitAttr:$comdat, | ||
OptionalAttr<DictArrayAttr>:$arg_attrs, | ||
OptionalAttr<DictArrayAttr>:$res_attrs); | ||
|
||
let regions = (region AnyRegion:$body); | ||
|
||
let skipDefaultBuilders = 1; | ||
|
||
let builders = [OpBuilder<(ins "llvm::StringRef":$sym_name, | ||
"FuncType":$type)>]; | ||
let builders = [OpBuilder<(ins | ||
"llvm::StringRef":$sym_name, "FuncType":$type, | ||
CArg<"cir::GlobalLinkageKind", "cir::GlobalLinkageKind::ExternalLinkage">:$linkage) | ||
>]; | ||
|
||
let extraClassDeclaration = [{ | ||
/// Returns the region on the current operation that is callable. This may | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -406,14 +406,34 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, | |
/*DontDefer=*/true, ForDefinition); | ||
} | ||
|
||
// Already emitted. | ||
if (!funcOp.isDeclaration()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this was a little jarring to me... any chance we could add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is part of I'm on the fence as to whether this is sufficient justification for keeping such a problematic name. The other thing to consider here is how
We haven't implemented ifuncs in the incubator, but when we do they'll need to be handled here. I'm not even sure how we get a function that is materializable, or if that's possible from the clang front end. It turns out that |
||
return; | ||
|
||
setFunctionLinkage(gd, funcOp); | ||
setGVProperties(funcOp, funcDecl); | ||
assert(!cir::MissingFeatures::opFuncMaybeHandleStaticInExternC()); | ||
maybeSetTrivialComdat(*funcDecl, funcOp); | ||
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes()); | ||
|
||
CIRGenFunction cgf(*this, builder); | ||
curCGF = &cgf; | ||
{ | ||
mlir::OpBuilder::InsertionGuard guard(builder); | ||
cgf.generateCode(gd, funcOp, funcType); | ||
} | ||
curCGF = nullptr; | ||
|
||
setNonAliasAttributes(gd, funcOp); | ||
assert(!cir::MissingFeatures::opFuncAttributesForDefinition()); | ||
|
||
if (const ConstructorAttr *ca = funcDecl->getAttr<ConstructorAttr>()) | ||
errorNYI(funcDecl->getSourceRange(), "constructor attribute"); | ||
if (const DestructorAttr *da = funcDecl->getAttr<DestructorAttr>()) | ||
errorNYI(funcDecl->getSourceRange(), "destructor attribute"); | ||
|
||
if (funcDecl->getAttr<AnnotateAttr>()) | ||
errorNYI(funcDecl->getSourceRange(), "deferredAnnotations"); | ||
} | ||
|
||
mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) { | ||
|
@@ -855,10 +875,12 @@ static bool shouldBeInCOMDAT(CIRGenModule &cgm, const Decl &d) { | |
void CIRGenModule::maybeSetTrivialComdat(const Decl &d, mlir::Operation *op) { | ||
if (!shouldBeInCOMDAT(*this, d)) | ||
return; | ||
if (auto globalOp = dyn_cast_or_null<cir::GlobalOp>(op)) | ||
if (auto globalOp = dyn_cast_or_null<cir::GlobalOp>(op)) { | ||
globalOp.setComdat(true); | ||
|
||
assert(!cir::MissingFeatures::opFuncSetComdat()); | ||
} else { | ||
auto funcOp = cast<cir::FuncOp>(op); | ||
funcOp.setComdat(true); | ||
} | ||
} | ||
|
||
void CIRGenModule::updateCompletedType(const TagDecl *td) { | ||
|
@@ -1028,6 +1050,17 @@ CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) { | |
return getCIRLinkageForDeclarator(vd, linkage, isConstant); | ||
} | ||
|
||
cir::GlobalLinkageKind CIRGenModule::getFunctionLinkage(GlobalDecl gd) { | ||
const auto *fd = cast<FunctionDecl>(gd.getDecl()); | ||
|
||
GVALinkage linkage = astContext.GetGVALinkageForFunction(fd); | ||
|
||
if (const auto *dtor = dyn_cast<CXXDestructorDecl>(fd)) | ||
errorNYI(fd->getSourceRange(), "getFunctionLinkage: CXXDestructorDecl"); | ||
|
||
return getCIRLinkageForDeclarator(fd, linkage, /*IsConstantVariable=*/false); | ||
} | ||
|
||
static cir::GlobalOp | ||
generateStringLiteral(mlir::Location loc, mlir::TypedAttr c, | ||
cir::GlobalLinkageKind lt, CIRGenModule &cgm, | ||
|
@@ -1534,6 +1567,27 @@ void CIRGenModule::setGVPropertiesAux(mlir::Operation *op, | |
assert(!cir::MissingFeatures::opGlobalPartition()); | ||
} | ||
|
||
void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl, | ||
cir::FuncOp func, | ||
bool isIncompleteFunction, | ||
bool isThunk) { | ||
// NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we | ||
// represent them in dedicated ops. The correct attributes are ensured during | ||
// translation to LLVM. Thus, we don't need to check for them here. | ||
|
||
assert(!cir::MissingFeatures::setFunctionAttributes()); | ||
assert(!cir::MissingFeatures::setTargetAttributes()); | ||
|
||
// TODO(cir): This needs a lot of work to better match CodeGen. That | ||
// ultimately ends up in setGlobalVisibility, which already has the linkage of | ||
// the LLVM GV (corresponding to our FuncOp) computed, so it doesn't have to | ||
// recompute it here. This is a minimal fix for now. | ||
if (!isLocalLinkage(getFunctionLinkage(globalDecl))) { | ||
const Decl *decl = globalDecl.getDecl(); | ||
func.setGlobalVisibilityAttr(getGlobalVisibilityAttrFromDecl(decl)); | ||
} | ||
} | ||
|
||
cir::FuncOp CIRGenModule::getOrCreateCIRFunction( | ||
StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable, | ||
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition, | ||
|
@@ -1576,8 +1630,9 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction( | |
// If there are two attempts to define the same mangled name, issue an | ||
// error. | ||
auto fn = cast<cir::FuncOp>(entry); | ||
assert((!isForDefinition || !fn || !fn.isDeclaration()) && | ||
"Duplicate function definition"); | ||
if (isForDefinition && fn && !fn.isDeclaration()) { | ||
errorNYI(d->getSourceRange(), "Duplicate function definition"); | ||
} | ||
if (fn && fn.getFunctionType() == funcType) { | ||
return fn; | ||
} | ||
|
@@ -1598,6 +1653,9 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction( | |
invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()), | ||
mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl); | ||
|
||
if (d) | ||
setFunctionAttributes(gd, funcOp, /*isIncompleteFunction=*/false, isThunk); | ||
|
||
// 'dontDefer' actually means don't move this to the deferredDeclsToEmit list. | ||
if (dontDefer) { | ||
// TODO(cir): This assertion will need an additional condition when we | ||
|
@@ -1668,6 +1726,20 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, | |
|
||
func = builder.create<cir::FuncOp>(loc, name, funcType); | ||
|
||
assert(!cir::MissingFeatures::opFuncAstDeclAttr()); | ||
assert(!cir::MissingFeatures::opFuncNoProto()); | ||
|
||
assert(func.isDeclaration() && "expected empty body"); | ||
|
||
// A declaration gets private visibility by default, but external linkage | ||
// as the default linkage. | ||
func.setLinkageAttr(cir::GlobalLinkageKindAttr::get( | ||
&getMLIRContext(), cir::GlobalLinkageKind::ExternalLinkage)); | ||
mlir::SymbolTable::setSymbolVisibility( | ||
func, mlir::SymbolTable::Visibility::Private); | ||
|
||
assert(!cir::MissingFeatures::opFuncExtraAttrs()); | ||
|
||
if (!cgf) | ||
theModule.push_back(func); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we elaborate
dso_local
from a simple boolean flag into something like aRuntimePreemption
enum that could be eitherNonPreemptable
orPreemptable
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a 1-to-1 correspondance between this and the
dso_local
representation in LLVM IR, and we are able to model the computation of this flag after the same computation in classic codegen. I don't think there's benefit in trying to generalize this as an intermediate state when we'll need to recreate it when lowering to LLVM IR anyway.