/// true through IsAligned.
bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const;
+ /// Determine if this function provides an inline implementation of a builtin.
+ bool isInlineBuiltinDeclaration() const;
+
/// Determine whether this is a destroying operator delete.
bool isDestroyingOperatorDelete() const;
return Params == FPT->getNumParams();
}
+bool FunctionDecl::isInlineBuiltinDeclaration() const {
+ if (!getBuiltinID())
+ return false;
+
+ const FunctionDecl *Definition;
+ return hasBody(Definition) && Definition->isInlineSpecified();
+}
+
bool FunctionDecl::isDestroyingOperatorDelete() const {
// C++ P0722:
// Within a class C, a single object deallocation function with signature
}
static CGCallee EmitDirectCallee(CodeGenFunction &CGF, const FunctionDecl *FD) {
+
if (auto builtinID = FD->getBuiltinID()) {
- return CGCallee::forBuiltin(builtinID, FD);
+ // Replaceable builtin provide their own implementation of a builtin. Unless
+ // we are in the builtin implementation itself, don't call the actual
+ // builtin. If we are in the builtin implementation, avoid trivial infinite
+ // recursion.
+ if (!FD->isInlineBuiltinDeclaration() ||
+ CGF.CurFn->getName() == FD->getName())
+ return CGCallee::forBuiltin(builtinID, FD);
}
llvm::Constant *calleePtr = EmitFunctionDeclPointer(CGF.CGM, FD);
else if (const auto *SA = FD->getAttr<SectionAttr>())
F->setSection(SA->getName());
+ if (FD->isInlineBuiltinDeclaration()) {
+ F->addAttribute(llvm::AttributeList::FunctionIndex,
+ llvm::Attribute::NoBuiltin);
+ }
+
if (FD->isReplaceableGlobalAllocationFunction()) {
// A replaceable global allocation function does not act like a builtin by
// default, only if it is invoked by a new-expression or delete-expression.
--- /dev/null
+// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -DWITH_DECL | FileCheck --check-prefix=CHECK-WITH-DECL %s
+// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -UWITH_DECL | FileCheck --check-prefix=CHECK-NO-DECL %s
+// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -DWITH_SELF_REFERENCE_DECL | FileCheck --check-prefix=CHECK-SELF-REF-DECL %s
+//
+// CHECK-WITH-DECL-NOT: @llvm.memcpy
+// CHECK-NO-DECL: @llvm.memcpy
+// CHECK-SELF-REF-DECL: @llvm.memcpy
+//
+#include <memcpy-nobuiltin.inc>
+void test(void *dest, void const *from, size_t n) {
+ memcpy(dest, from, n);
+
+ static char buffer[1];
+ memcpy(buffer, from, 2); // expected-warning {{'memcpy' will always overflow; destination buffer has size 1, but size argument is 2}}
+}
--- /dev/null
+#include <stddef.h>
+extern void *memcpy(void *dest, void const *from, size_t n);
+
+#ifdef WITH_DECL
+inline void *memcpy(void *dest, void const *from, size_t n) {
+ char const *ifrom = from;
+ char *idest = dest;
+ while (n--)
+ *idest++ = *ifrom++;
+ return dest;
+}
+#endif
+#ifdef WITH_SELF_REFERENCE_DECL
+inline void *memcpy(void *dest, void const *from, size_t n) {
+ if (n != 0)
+ memcpy(dest, from, n);
+ return dest;
+}
+#endif