diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 7da61a261e21..96f32e16ddc1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3031,9 +3031,110 @@ void CIRGenModule::Release() { // TODO: FINISH THE REST OF THIS } -bool CIRGenModule::shouldEmitFunction(GlobalDecl GD) { - // TODO: implement this -- requires defining linkage for CIR - return true; +namespace { +// TODO(cir): This should be a common helper shared with CodeGen. +struct FunctionIsDirectlyRecursive + : public ConstStmtVisitor { + const StringRef name; + const Builtin::Context &builtinCtx; + FunctionIsDirectlyRecursive(StringRef name, + const Builtin::Context &builtinCtx) + : name(name), builtinCtx(builtinCtx) {} + + bool VisitCallExpr(const CallExpr *expr) { + const FunctionDecl *func = expr->getDirectCallee(); + if (!func) + return false; + AsmLabelAttr *attr = func->getAttr(); + if (attr && name == attr->getLabel()) + return true; + unsigned builtinId = func->getBuiltinID(); + if (!builtinId || !builtinCtx.isLibFunction(builtinId)) + return false; + StringRef builtinName = builtinCtx.getName(builtinId); + if (builtinName.starts_with("__builtin_") && + name == builtinName.slice(strlen("__builtin_"), StringRef::npos)) { + return true; + } + return false; + } + + bool VisitStmt(const Stmt *stmt) { + for (const Stmt *child : stmt->children()) + if (child && this->Visit(child)) + return true; + return false; + } +}; +} // namespace + +// isTriviallyRecursive - Check if this function calls another +// decl that, because of the asm attribute or the other decl being a builtin, +// ends up pointing to itself. +// TODO(cir): This should be a common helper shared with CodeGen. +bool CIRGenModule::isTriviallyRecursive(const FunctionDecl *func) { + StringRef name; + if (getCXXABI().getMangleContext().shouldMangleDeclName(func)) { + // asm labels are a special kind of mangling we have to support. + AsmLabelAttr *attr = func->getAttr(); + if (!attr) + return false; + name = attr->getLabel(); + } else { + name = func->getName(); + } + + FunctionIsDirectlyRecursive walker(name, astCtx.BuiltinInfo); + const Stmt *body = func->getBody(); + return body ? walker.Visit(body) : false; +} + +// TODO(cir): This should be a common helper shared with CodeGen. +bool CIRGenModule::shouldEmitFunction(GlobalDecl globalDecl) { + if (getFunctionLinkage(globalDecl) != + GlobalLinkageKind::AvailableExternallyLinkage) + return true; + + const auto *func = cast(globalDecl.getDecl()); + // Inline builtins declaration must be emitted. They often are fortified + // functions. + if (func->isInlineBuiltinDeclaration()) + return true; + + if (codeGenOpts.OptimizationLevel == 0 && !func->hasAttr()) + return false; + + // We don't import function bodies from other named module units since that + // behavior may break ABI compatibility of the current unit. + if (const Module *mod = func->getOwningModule(); + mod && mod->getTopLevelModule()->isNamedModule() && + astCtx.getCurrentNamedModule() != mod->getTopLevelModule()) { + // There are practices to mark template member function as always-inline + // and mark the template as extern explicit instantiation but not give + // the definition for member function. So we have to emit the function + // from explicitly instantiation with always-inline. + // + // See https://github.com/llvm/llvm-project/issues/86893 for details. + // + // TODO: Maybe it is better to give it a warning if we call a non-inline + // function from other module units which is marked as always-inline. + if (!func->isTemplateInstantiation() || !func->hasAttr()) + return false; + } + + if (func->hasAttr()) + return false; + + if (func->hasAttr() && !func->hasAttr()) + assert(!MissingFeatures::setDLLImportDLLExport() && + "shouldEmitFunction for dllimport is NYI"); + + // PR9614. Avoid cases where the source code is lying to us. An available + // externally function should have an equivalent function somewhere else, + // but a function that calls itself through asm label/`__builtin_` trickery is + // clearly not equivalent to the real implementation. + // This happens in glibc's btowc and in some configure checks. + return !isTriviallyRecursive(func); } bool CIRGenModule::supportsCOMDAT() const { diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 827e7ac82839..16f95c164712 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -665,7 +665,9 @@ class CIRGenModule : public CIRGenTypeCache { // Finalize CIR code generation. void Release(); - bool shouldEmitFunction(clang::GlobalDecl GD); + bool isTriviallyRecursive(const clang::FunctionDecl *func); + + bool shouldEmitFunction(clang::GlobalDecl globalDecl); /// Returns a pointer to a global variable representing a temporary with /// static or thread storage duration. diff --git a/clang/test/CIR/CodeGen/linkage.c b/clang/test/CIR/CodeGen/linkage.c index 1b087f43ca81..49fdb643b2cb 100644 --- a/clang/test/CIR/CodeGen/linkage.c +++ b/clang/test/CIR/CodeGen/linkage.c @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-O0.cir +// RUN: FileCheck --input-file=%t-O0.cir %s -check-prefixes=CIR,CIR-O0 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -O1 -o %t-O1.cir +// RUN: FileCheck --input-file=%t-O1.cir %s -check-prefixes=CIR,CIR-O1 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM @@ -24,7 +26,8 @@ int get_var(void) { return var; } -// Should generate available_externally linkage. +// Should generate available_externally linkage when optimizing. inline int availableExternallyMethod(void) { return 0; } void callAvailableExternallyMethod(void) { availableExternallyMethod(); } -// CIR: cir.func available_externally @availableExternallyMethod +// CIR-O0-NOT: cir.func available_externally @availableExternallyMethod +// CIR-O1: cir.func available_externally @availableExternallyMethod