[ObjCDirectPreconditionThunk] Adding a flag to with objc_direct symbols' prefix (#170616)
## TL;DR This is a stack of PRs implementing features to expose direct methods ABI. You can see the RFC, design, and discussion [here](https://discourse.llvm.org/t/rfc-optimizing-code-size-of-objc-direct-by-exposing-function-symbols-and-moving-nil-checks-to-thunks/88866). The stack of the following four PRs completes the whole feature. https://github.com/llvm/llvm-project/pull/170616 **Flag `-fobjc-direct-precondition-thunk` set up** https://github.com/llvm/llvm-project/pull/170617 Code refactoring to ease later reviews https://github.com/llvm/llvm-project/pull/170618 Thunk generation https://github.com/llvm/llvm-project/pull/170619 Optimizations, some class objects can be known to be realized ## Implementation details 1. Add a flag. I used `-fobjc-direct-precondition-thunk` instead of `-fobjc-direct-caller-thunks` as discussed in this PR. 2. Clean up and set up helper functions to implement later a. `canMessageReceiverBeNull` / `canClassObjectBeUnrealized` these two functions will be helpful later to determine which function (true implementation or nil check thunk) we should dispatch a call to. Formatting. b. `getSymbolNameForMethod` has a new argument `includePrefixByte`, which allows us to erase the prefixing `\01` when the flag is enabled c. `usePreconditionThunk` is the single source of truth of what we should do. It not only checks for the flag, but also whether the method is qualified and we are in the right runtime. A method that `usePreconditionThunk` is either `shouldHavePreconditionThunk` or `shouldHavePreconditionInline`. ## Tests Driver tests --------- Signed-off-by: Peter Rong <PeterRong@meta.com> Co-authored-by: Kyungwoo Lee <kyulee@meta.com>
This commit is contained in:
parent
71925cbdf8
commit
3023c1069e
@ -210,6 +210,8 @@ ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy, Benign)
|
||||
/// Replace certain message sends with calls to ObjC runtime entrypoints
|
||||
CODEGENOPT(ObjCConvertMessagesToRuntimeCalls , 1, 1, Benign)
|
||||
CODEGENOPT(ObjCAvoidHeapifyLocalBlocks, 1, 0, Benign)
|
||||
/// Generate direct method precondition thunks to expose symbols and optimize nil checks.
|
||||
CODEGENOPT(ObjCDirectPreconditionThunk, 1, 0, Benign)
|
||||
|
||||
|
||||
// The optimization options affect frontend options, which in turn do affect the AST.
|
||||
|
||||
@ -161,6 +161,9 @@ def warn_drv_unsupported_diag_option_for_flang : Warning<
|
||||
def warn_drv_unsupported_option_for_processor : Warning<
|
||||
"ignoring '%0' option as it is not currently supported for processor '%1'">,
|
||||
InGroup<OptionIgnored>;
|
||||
def warn_drv_unsupported_option_for_runtime : Warning<
|
||||
"ignoring '%0' option as it is not currently supported for runtime '%1'">,
|
||||
InGroup<OptionIgnored>;
|
||||
def warn_drv_unsupported_openmp_library : Warning<
|
||||
"the library '%0=%1' is not supported, OpenMP will not be enabled">,
|
||||
InGroup<OptionIgnored>;
|
||||
|
||||
@ -3799,6 +3799,11 @@ defm objc_avoid_heapify_local_blocks : BoolFOption<"objc-avoid-heapify-local-blo
|
||||
PosFlag<SetTrue, [], [ClangOption], "Try">,
|
||||
NegFlag<SetFalse, [], [ClangOption], "Don't try">,
|
||||
BothFlags<[], [CC1Option], " to avoid heapifying local blocks">>;
|
||||
defm objc_direct_precondition_thunk : BoolFOption<"objc-direct-precondition-thunk",
|
||||
CodeGenOpts<"ObjCDirectPreconditionThunk">, DefaultFalse,
|
||||
PosFlag<SetTrue, [], [ClangOption, CC1Option],
|
||||
"Move precondition checks for direct methods into thunks">,
|
||||
NegFlag<SetFalse>>;
|
||||
defm disable_block_signature_string : BoolFOption<"disable-block-signature-string",
|
||||
CodeGenOpts<"DisableBlockSignatureString">, DefaultFalse,
|
||||
PosFlag<SetTrue, [], [ClangOption], "Disable">,
|
||||
|
||||
@ -382,11 +382,9 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method,
|
||||
return MessageSendInfo(argsInfo, signatureType);
|
||||
}
|
||||
|
||||
bool CGObjCRuntime::canMessageReceiverBeNull(CodeGenFunction &CGF,
|
||||
const ObjCMethodDecl *method,
|
||||
bool isSuper,
|
||||
const ObjCInterfaceDecl *classReceiver,
|
||||
llvm::Value *receiver) {
|
||||
bool CGObjCRuntime::canMessageReceiverBeNull(
|
||||
CodeGenFunction &CGF, const ObjCMethodDecl *method, bool isSuper,
|
||||
const ObjCInterfaceDecl *classReceiver, llvm::Value *receiver) {
|
||||
// Super dispatch assumes that self is non-null; even the messenger
|
||||
// doesn't have a null check internally.
|
||||
if (isSuper)
|
||||
@ -399,8 +397,7 @@ bool CGObjCRuntime::canMessageReceiverBeNull(CodeGenFunction &CGF,
|
||||
|
||||
// If we're emitting a method, and self is const (meaning just ARC, for now),
|
||||
// and the receiver is a load of self, then self is a valid object.
|
||||
if (auto curMethod =
|
||||
dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {
|
||||
if (auto curMethod = dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {
|
||||
auto self = curMethod->getSelfDecl();
|
||||
if (self->getType().isConstQualified()) {
|
||||
if (auto LI = dyn_cast<llvm::LoadInst>(receiver->stripPointerCasts())) {
|
||||
@ -466,11 +463,11 @@ clang::CodeGen::emitObjCProtocolObject(CodeGenModule &CGM,
|
||||
}
|
||||
|
||||
std::string CGObjCRuntime::getSymbolNameForMethod(const ObjCMethodDecl *OMD,
|
||||
bool includeCategoryName) {
|
||||
bool includeCategoryName,
|
||||
bool includePrefixByte) {
|
||||
std::string buffer;
|
||||
llvm::raw_string_ostream out(buffer);
|
||||
CGM.getCXXABI().getMangleContext().mangleObjCMethodName(OMD, out,
|
||||
/*includePrefixByte=*/true,
|
||||
includeCategoryName);
|
||||
CGM.getCXXABI().getMangleContext().mangleObjCMethodName(
|
||||
OMD, out, includePrefixByte, includeCategoryName);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@ -117,7 +117,8 @@ public:
|
||||
virtual ~CGObjCRuntime();
|
||||
|
||||
std::string getSymbolNameForMethod(const ObjCMethodDecl *method,
|
||||
bool includeCategoryName = true);
|
||||
bool includeCategoryName = true,
|
||||
bool includePrefixByte = true);
|
||||
|
||||
/// Generate the function required to register all Objective-C components in
|
||||
/// this compilation unit with the runtime library.
|
||||
@ -322,10 +323,12 @@ public:
|
||||
MessageSendInfo getMessageSendInfo(const ObjCMethodDecl *method,
|
||||
QualType resultType,
|
||||
CallArgList &callArgs);
|
||||
|
||||
bool canMessageReceiverBeNull(CodeGenFunction &CGF,
|
||||
const ObjCMethodDecl *method, bool isSuper,
|
||||
const ObjCInterfaceDecl *classReceiver,
|
||||
llvm::Value *receiver);
|
||||
|
||||
static bool isWeakLinkedClass(const ObjCInterfaceDecl *cls);
|
||||
|
||||
/// Destroy the callee-destroyed arguments of the given method,
|
||||
|
||||
@ -717,6 +717,35 @@ public:
|
||||
/// Return true iff an Objective-C runtime has been configured.
|
||||
bool hasObjCRuntime() { return !!ObjCRuntime; }
|
||||
|
||||
/// Check if a direct method should use precondition thunks (exposed symbols).
|
||||
/// This applies to ALL direct methods (including variadic).
|
||||
/// Returns false if OMD is null or not a direct method.
|
||||
///
|
||||
/// Also checks the runtime family, currently we only support NeXT.
|
||||
/// TODO: Add support for GNUStep as well.
|
||||
bool usePreconditionThunk(const ObjCMethodDecl *OMD) const {
|
||||
return OMD && OMD->isDirectMethod() &&
|
||||
getLangOpts().ObjCRuntime.allowsDirectDispatch() &&
|
||||
getLangOpts().ObjCRuntime.isNeXTFamily() &&
|
||||
getCodeGenOpts().ObjCDirectPreconditionThunk;
|
||||
}
|
||||
|
||||
/// Check if a direct method should use precondition thunks at call sites.
|
||||
/// This applies only to non-variadic direct methods.
|
||||
/// Returns false if OMD is null or not eligible for thunks (variadic
|
||||
/// methods).
|
||||
bool shouldHavePreconditionThunk(const ObjCMethodDecl *OMD) const {
|
||||
return OMD && usePreconditionThunk(OMD) && !OMD->isVariadic();
|
||||
}
|
||||
|
||||
/// Check if a direct method should have inline precondition checks at call
|
||||
/// sites. This applies to direct methods that cannot use thunks (variadic
|
||||
/// methods). These methods get exposed symbols but need inline precondition
|
||||
/// checks instead of thunks. Returns false if OMD is null or not eligible.
|
||||
bool shouldHavePreconditionInline(const ObjCMethodDecl *OMD) const {
|
||||
return OMD && usePreconditionThunk(OMD) && OMD->isVariadic();
|
||||
}
|
||||
|
||||
const std::string &getModuleNameHash() const { return ModuleNameHash; }
|
||||
|
||||
/// Return a reference to the configured OpenCL runtime.
|
||||
|
||||
@ -4098,6 +4098,19 @@ static void RenderObjCOptions(const ToolChain &TC, const Driver &D,
|
||||
}
|
||||
}
|
||||
|
||||
// Forward -fobjc-direct-precondition-thunk to cc1
|
||||
// Defaults to false and needs explict turn on for now
|
||||
// TODO: switch to default true and needs explict turn off in the future.
|
||||
// TODO: add support for other runtimes
|
||||
if (Args.hasFlag(options::OPT_fobjc_direct_precondition_thunk,
|
||||
options::OPT_fno_objc_direct_precondition_thunk, false)) {
|
||||
if (Runtime.isNeXTFamily()) {
|
||||
CmdArgs.push_back("-fobjc-direct-precondition-thunk");
|
||||
} else {
|
||||
D.Diag(diag::warn_drv_unsupported_option_for_runtime)
|
||||
<< "-fobjc-direct-precondition-thunk" << Runtime.getAsString();
|
||||
}
|
||||
}
|
||||
// When ObjectiveC legacy runtime is in effect on MacOSX, turn on the option
|
||||
// to do Array/Dictionary subscripting by default.
|
||||
if (Arch == llvm::Triple::x86 && T.isMacOSX() &&
|
||||
|
||||
@ -597,6 +597,23 @@
|
||||
// CHECK_DISABLE_DIRECT: -fobjc-disable-direct-methods-for-testing
|
||||
// CHECK_NO_DISABLE_DIRECT-NOT: -fobjc-disable-direct-methods-for-testing
|
||||
|
||||
// RUN: %clang -### --target=arm64-apple-macos10 -xobjective-c -fobjc-direct-precondition-thunk %s 2>&1 | FileCheck -check-prefix=CHECK_DIRECT_PRECONDITION_THUNK %s
|
||||
// RUN: %clang -### --target=arm64-apple-macos10 -xobjective-c -fno-objc-direct-precondition-thunk %s 2>&1 | FileCheck -check-prefix=CHECK_NO_DIRECT_PRECONDITION_THUNK %s
|
||||
// RUN: %clang -### --target=arm64-apple-macos10 -xobjective-c -fobjc-direct-precondition-thunk -fno-objc-direct-precondition-thunk %s 2>&1 | FileCheck -check-prefix=CHECK_NO_DIRECT_PRECONDITION_THUNK %s
|
||||
// RUN: %clang -### --target=arm64-apple-macos10 -xobjective-c -fno-objc-direct-precondition-thunk -fobjc-direct-precondition-thunk %s 2>&1 | FileCheck -check-prefix=CHECK_DIRECT_PRECONDITION_THUNK %s
|
||||
// RUN: %clang -### --target=arm64-apple-macos10 -xobjective-c %s 2>&1 | FileCheck -check-prefix=CHECK_NO_DIRECT_PRECONDITION_THUNK %s
|
||||
// CHECK_DIRECT_PRECONDITION_THUNK: "-fobjc-direct-precondition-thunk"
|
||||
// CHECK_NO_DIRECT_PRECONDITION_THUNK-NOT: -fobjc-direct-precondition-thunk
|
||||
|
||||
// Test that -fobjc-direct-precondition-thunk emits a warning when used with GNU runtime
|
||||
// and that the flag is not passed to cc1.
|
||||
// RUN: %clang --target=x86_64-linux-gnu -fobjc-runtime=gnustep-2.0 -fobjc-direct-precondition-thunk -### -c -xobjective-c %s 2>&1 | FileCheck -check-prefix=CHECK_GNUSTEP_WARN %s
|
||||
// CHECK_GNUSTEP_WARN: warning: ignoring '-fobjc-direct-precondition-thunk' option as it is not currently supported for runtime 'gnustep-2.0' [-Woption-ignored]
|
||||
// CHECK_GNUSTEP_WARN-NOT: "-fobjc-direct-precondition-thunk"
|
||||
// RUN: %clang --target=x86_64-linux-gnu -fobjc-runtime=gcc -fobjc-direct-precondition-thunk -### -c -xobjective-c %s 2>&1 | FileCheck -check-prefix=CHECK_GCC_WARN %s
|
||||
// CHECK_GCC_WARN: warning: ignoring '-fobjc-direct-precondition-thunk' option as it is not currently supported for runtime 'gcc' [-Woption-ignored]
|
||||
// CHECK_GCC_WARN-NOT: "-fobjc-direct-precondition-thunk"
|
||||
|
||||
// RUN: %clang -### -S -fjmc --target=x86_64-unknown-linux %s 2>&1 | FileCheck -check-prefixes=CHECK_JMC_WARN,CHECK_NOJMC %s
|
||||
// RUN: %clang -### -S -fjmc --target=x86_64-pc-windows-msvc %s 2>&1 | FileCheck -check-prefixes=CHECK_JMC_WARN,CHECK_NOJMC %s
|
||||
// RUN: %clang -### -S -fjmc -g --target=x86_64-pc-windows-msvc %s 2>&1 | FileCheck -check-prefix=CHECK_JMC %s
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user