forked from OSchip/llvm-project
[Clang] Add option to allow marking pass-by-value args as noalias.
After the recent discussion on cfe-dev 'Can indirect class parameters be noalias?' [1], it seems like using using noalias is problematic for current C++, but should be allowed for C-only code. This patch introduces a new option to let the user indicate that it is safe to mark indirect class parameters as noalias. Note that this also applies to external callers, e.g. it might not be safe to use this flag for C functions that are called by C++ functions. In targets that allocate indirect arguments in the called function, this enables more agressive optimizations with respect to memory operations and brings a ~1% - 2% codesize reduction for some programs. [1] : http://lists.llvm.org/pipermail/cfe-dev/2020-July/066353.html Reviewed By: rjmccall Differential Revision: https://reviews.llvm.org/D85473
This commit is contained in:
parent
2e61cd1295
commit
a874d63344
|
@ -395,6 +395,10 @@ CODEGENOPT(KeepStaticConsts, 1, 0)
|
|||
/// Whether to not follow the AAPCS that enforce at least one read before storing to a volatile bitfield
|
||||
CODEGENOPT(ForceAAPCSBitfieldLoad, 1, 0)
|
||||
|
||||
/// Assume that by-value parameters do not alias any other values.
|
||||
CODEGENOPT(PassByValueIsNoAlias, 1, 0)
|
||||
|
||||
|
||||
#undef CODEGENOPT
|
||||
#undef ENUM_CODEGENOPT
|
||||
#undef VALUE_CODEGENOPT
|
||||
|
|
|
@ -4322,6 +4322,9 @@ def fno_signed_wchar : Flag<["-"], "fno-signed-wchar">,
|
|||
def fcompatibility_qualified_id_block_param_type_checking : Flag<["-"], "fcompatibility-qualified-id-block-type-checking">,
|
||||
HelpText<"Allow using blocks with parameters of more specific type than "
|
||||
"the type system guarantees when a parameter is qualified id">;
|
||||
def fpass_by_value_is_noalias: Flag<["-"], "fpass-by-value-is-noalias">,
|
||||
HelpText<"Allows assuming by-value parameters do not alias any other value. "
|
||||
"Has no effect on non-trivially-copyable classes in C++.">, Group<f_Group>;
|
||||
|
||||
// FIXME: Remove these entirely once functionality/tests have been excised.
|
||||
def fobjc_gc_only : Flag<["-"], "fobjc-gc-only">, Group<f_Group>,
|
||||
|
|
|
@ -2201,6 +2201,13 @@ void CodeGenModule::ConstructAttributeList(
|
|||
if (AI.getIndirectByVal())
|
||||
Attrs.addByValAttr(getTypes().ConvertTypeForMem(ParamType));
|
||||
|
||||
auto *Decl = ParamType->getAsRecordDecl();
|
||||
if (CodeGenOpts.PassByValueIsNoAlias && Decl &&
|
||||
Decl->getArgPassingRestrictions() == RecordDecl::APK_CanPassInRegs)
|
||||
// When calling the function, the pointer passed in will be the only
|
||||
// reference to the underlying object. Mark it accordingly.
|
||||
Attrs.addAttribute(llvm::Attribute::NoAlias);
|
||||
|
||||
// TODO: We could add the byref attribute if not byval, but it would
|
||||
// require updating many testcases.
|
||||
|
||||
|
|
|
@ -1453,6 +1453,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
|||
std::string(Args.getLastArgValue(OPT_fsymbol_partition_EQ));
|
||||
|
||||
Opts.ForceAAPCSBitfieldLoad = Args.hasArg(OPT_ForceAAPCSBitfieldLoad);
|
||||
|
||||
Opts.PassByValueIsNoAlias = Args.hasArg(OPT_fpass_by_value_is_noalias);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// RUN: %clang_cc1 -fpass-by-value-is-noalias -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns %s -o - 2>&1 | FileCheck --check-prefix=WITH_NOALIAS %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns %s -o - 2>&1 | FileCheck --check-prefix=NO_NOALIAS %s
|
||||
|
||||
// A struct large enough so it is not passed in registers on ARM64.
|
||||
struct Foo {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
};
|
||||
|
||||
// WITH_NOALIAS: define void @take(%struct.Foo* noalias %arg)
|
||||
// NO_NOALIAS: define void @take(%struct.Foo* %arg)
|
||||
void take(struct Foo arg) {}
|
|
@ -0,0 +1,73 @@
|
|||
// RUN: %clang_cc1 -fpass-by-value-is-noalias -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns %s -o - 2>&1 | FileCheck --check-prefix=WITH_NOALIAS %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns %s -o - 2>&1 | FileCheck --check-prefix=NO_NOALIAS %s
|
||||
|
||||
// A trivial struct large enough so it is not passed in registers on ARM64.
|
||||
struct Foo {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
};
|
||||
|
||||
// Make sure noalias is added to indirect arguments with trivially copyable types
|
||||
// if -fpass-by-value-is-noalias is provided.
|
||||
|
||||
// WITH_NOALIAS: define void @_Z4take3Foo(%struct.Foo* noalias %arg)
|
||||
// NO_NOALIAS: define void @_Z4take3Foo(%struct.Foo* %arg)
|
||||
void take(Foo arg) {}
|
||||
|
||||
int G;
|
||||
|
||||
// NonTrivial is not trivially-copyable, because it has a non-trivial copy
|
||||
// constructor.
|
||||
struct NonTrivial {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
|
||||
NonTrivial(const NonTrivial &Other) {
|
||||
a = G + 10 + Other.a;
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure noalias is not added to indirect arguments that are not trivially
|
||||
// copyable even if -fpass-by-value-is-noalias is provided.
|
||||
|
||||
// WITH_NOALIAS: define void @_Z4take10NonTrivial(%struct.NonTrivial* %arg)
|
||||
// NO_NOALIAS: define void @_Z4take10NonTrivial(%struct.NonTrivial* %arg)
|
||||
void take(NonTrivial arg) {}
|
||||
|
||||
// Escape examples. Pointers to the objects passed to take() may escape, depending on whether a temporary copy is created or not (e.g. due to NRVO).
|
||||
struct A {
|
||||
A(A **where) : data{"hello world 1"} {
|
||||
*where = this; //Escaped pointer 1 (proposed UB?)
|
||||
}
|
||||
|
||||
A() : data{"hello world 2"} {}
|
||||
|
||||
char data[32];
|
||||
};
|
||||
A *p;
|
||||
|
||||
// WITH_NOALIAS: define void @_Z4take1A(%struct.A* noalias %arg)
|
||||
// NO_NOALIAS: define void @_Z4take1A(%struct.A* %arg)
|
||||
void take(A arg) {}
|
||||
|
||||
// WITH_NOALIAS: define void @_Z7CreateAPP1A(%struct.A* noalias sret align 1 %agg.result, %struct.A** %where)
|
||||
// NO_NOALIAS: define void @_Z7CreateAPP1A(%struct.A* noalias sret align 1 %agg.result, %struct.A** %where)
|
||||
A CreateA(A **where) {
|
||||
A justlikethis;
|
||||
*where = &justlikethis; //Escaped pointer 2 (should also be UB, then)
|
||||
return justlikethis;
|
||||
}
|
||||
|
||||
// elsewhere, perhaps compiled by a smarter compiler that doesn't make a copy here
|
||||
void test() {
|
||||
take({&p}); // 1
|
||||
take(CreateA(&p)); // 2
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// RUN: %clang_cc1 -fpass-by-value-is-noalias -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns -fobjc-runtime-has-weak -fobjc-arc -fobjc-dispatch-method=mixed %s -o - 2>&1 | FileCheck --check-prefix=WITH_NOALIAS %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns -fobjc-runtime-has-weak -fobjc-arc -fobjc-dispatch-method=mixed %s -o - 2>&1 | FileCheck --check-prefix=NO_NOALIAS %s
|
||||
|
||||
@interface Bar
|
||||
@property char value;
|
||||
@end
|
||||
|
||||
// A struct large enough so it is not passed in registers on ARM64, but with a
|
||||
// weak reference, so noalias should not be added even with
|
||||
// -fpass-by-value-is-noalias.
|
||||
struct Foo {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
Bar *__weak f;
|
||||
};
|
||||
|
||||
// WITH_NOALIAS: define void @take(%struct.Foo* %arg)
|
||||
// NO_NOALIAS: define void @take(%struct.Foo* %arg)
|
||||
void take(struct Foo arg) {}
|
Loading…
Reference in New Issue