Skip to content

Commit 08dd9cc

Browse files
committed
[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
1 parent 8099dc6 commit 08dd9cc

File tree

7 files changed

+127
-0
lines changed

7 files changed

+127
-0
lines changed

clang/include/clang/Basic/CodeGenOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ CODEGENOPT(KeepStaticConsts, 1, 0)
393393
/// Whether to not follow the AAPCS that enforce at least one read before storing to a volatile bitfield
394394
CODEGENOPT(ForceAAPCSBitfieldLoad, 1, 0)
395395

396+
/// Assume that by-value parameters do not alias any other values.
397+
CODEGENOPT(PassByValueIsNoAlias, 1, 0)
398+
399+
396400
#undef CODEGENOPT
397401
#undef ENUM_CODEGENOPT
398402
#undef VALUE_CODEGENOPT

clang/include/clang/Driver/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4339,6 +4339,9 @@ def fno_signed_wchar : Flag<["-"], "fno-signed-wchar">,
43394339
def fcompatibility_qualified_id_block_param_type_checking : Flag<["-"], "fcompatibility-qualified-id-block-type-checking">,
43404340
HelpText<"Allow using blocks with parameters of more specific type than "
43414341
"the type system guarantees when a parameter is qualified id">;
4342+
def fpass_by_value_is_noalias: Flag<["-"], "fpass-by-value-is-noalias">,
4343+
HelpText<"Allows assuming by-value parameters do not alias any other value. "
4344+
"Has no effect on non-trivially-copyable classes in C++.">, Group<f_Group>;
43424345

43434346
// FIXME: Remove these entirely once functionality/tests have been excised.
43444347
def fobjc_gc_only : Flag<["-"], "fobjc-gc-only">, Group<f_Group>,

clang/lib/CodeGen/CGCall.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,13 @@ void CodeGenModule::ConstructAttributeList(
21922192
if (AI.getIndirectByVal())
21932193
Attrs.addByValAttr(getTypes().ConvertTypeForMem(ParamType));
21942194

2195+
auto *Decl = ParamType->getAsRecordDecl();
2196+
if (CodeGenOpts.PassByValueIsNoAlias && Decl &&
2197+
Decl->getArgPassingRestrictions() == RecordDecl::APK_CanPassInRegs)
2198+
// When calling the function, the pointer passed in will be the only
2199+
// reference to the underlying object. Mark it accordingly.
2200+
Attrs.addAttribute(llvm::Attribute::NoAlias);
2201+
21952202
CharUnits Align = AI.getIndirectAlign();
21962203

21972204
// In a byval argument, it is important that the required

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
15391539
std::string(Args.getLastArgValue(OPT_fsymbol_partition_EQ));
15401540

15411541
Opts.ForceAAPCSBitfieldLoad = Args.hasArg(OPT_ForceAAPCSBitfieldLoad);
1542+
1543+
Opts.PassByValueIsNoAlias = Args.hasArg(OPT_fpass_by_value_is_noalias);
15421544
return Success;
15431545
}
15441546

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// 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
2+
// RUN: %clang_cc1 -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns %s -o - 2>&1 | FileCheck --check-prefix=NO_NOALIAS %s
3+
4+
// A struct large enough so it is not passed in registers on ARM64.
5+
struct Foo {
6+
int a;
7+
int b;
8+
int c;
9+
int d;
10+
int e;
11+
int f;
12+
};
13+
14+
// WITH_NOALIAS: define void @take(%struct.Foo* noalias %arg)
15+
// NO_NOALIAS: define void @take(%struct.Foo* %arg)
16+
void take(struct Foo arg) {}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// 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
2+
// RUN: %clang_cc1 -triple arm64-apple-iphoneos -emit-llvm -disable-llvm-optzns %s -o - 2>&1 | FileCheck --check-prefix=NO_NOALIAS %s
3+
4+
// A trivial struct large enough so it is not passed in registers on ARM64.
5+
struct Foo {
6+
int a;
7+
int b;
8+
int c;
9+
int d;
10+
int e;
11+
int f;
12+
};
13+
14+
// Make sure noalias is added to indirect arguments with trivially copyable types
15+
// if -fpass-by-value-is-noalias is provided.
16+
17+
// WITH_NOALIAS: define void @_Z4take3Foo(%struct.Foo* noalias %arg)
18+
// NO_NOALIAS: define void @_Z4take3Foo(%struct.Foo* %arg)
19+
void take(Foo arg) {}
20+
21+
int G;
22+
23+
// NonTrivial is not trivially-copyable, because it has a non-trivial copy
24+
// constructor.
25+
struct NonTrivial {
26+
int a;
27+
int b;
28+
int c;
29+
int d;
30+
int e;
31+
int f;
32+
33+
NonTrivial(const NonTrivial &Other) {
34+
a = G + 10 + Other.a;
35+
}
36+
};
37+
38+
// Make sure noalias is not added to indirect arguments that are not trivially
39+
// copyable even if -fpass-by-value-is-noalias is provided.
40+
41+
// WITH_NOALIAS: define void @_Z4take10NonTrivial(%struct.NonTrivial* %arg)
42+
// NO_NOALIAS: define void @_Z4take10NonTrivial(%struct.NonTrivial* %arg)
43+
void take(NonTrivial arg) {}
44+
45+
// 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).
46+
struct A {
47+
A(A **where) : data{"hello world 1"} {
48+
*where = this; //Escaped pointer 1 (proposed UB?)
49+
}
50+
51+
A() : data{"hello world 2"} {}
52+
53+
char data[32];
54+
};
55+
A *p;
56+
57+
// WITH_NOALIAS: define void @_Z4take1A(%struct.A* noalias %arg)
58+
// NO_NOALIAS: define void @_Z4take1A(%struct.A* %arg)
59+
void take(A arg) {}
60+
61+
// WITH_NOALIAS: define void @_Z7CreateAPP1A(%struct.A* noalias sret align 1 %agg.result, %struct.A** %where)
62+
// NO_NOALIAS: define void @_Z7CreateAPP1A(%struct.A* noalias sret align 1 %agg.result, %struct.A** %where)
63+
A CreateA(A **where) {
64+
A justlikethis;
65+
*where = &justlikethis; //Escaped pointer 2 (should also be UB, then)
66+
return justlikethis;
67+
}
68+
69+
// elsewhere, perhaps compiled by a smarter compiler that doesn't make a copy here
70+
void test() {
71+
take({&p}); // 1
72+
take(CreateA(&p)); // 2
73+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// 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
2+
// 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
3+
4+
@interface Bar
5+
@property char value;
6+
@end
7+
8+
// A struct large enough so it is not passed in registers on ARM64, but with a
9+
// weak reference, so noalias should not be added even with
10+
// -fpass-by-value-is-noalias.
11+
struct Foo {
12+
int a;
13+
int b;
14+
int c;
15+
int d;
16+
int e;
17+
Bar *__weak f;
18+
};
19+
20+
// WITH_NOALIAS: define void @take(%struct.Foo* %arg)
21+
// NO_NOALIAS: define void @take(%struct.Foo* %arg)
22+
void take(struct Foo arg) {}

0 commit comments

Comments
 (0)