[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:
Florian Hahn 2020-09-12 14:23:36 +01:00
parent 2e61cd1295
commit a874d63344
7 changed files with 127 additions and 0 deletions

View File

@ -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

View File

@ -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>,

View File

@ -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.

View File

@ -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;
}

View File

@ -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) {}

View File

@ -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
}

View File

@ -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) {}