[clang] a874d63 - [Clang] Add option to allow marking pass-by-value args as noalias.

Florian Hahn via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 12 06:56:28 PDT 2020


Author: Florian Hahn
Date: 2020-09-12T14:56:13+01:00
New Revision: a874d63344093752c912d01de60211f65745ea6f

URL: https://github.com/llvm/llvm-project/commit/a874d63344093752c912d01de60211f65745ea6f
DIFF: https://github.com/llvm/llvm-project/commit/a874d63344093752c912d01de60211f65745ea6f.diff

LOG: [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

Added: 
    clang/test/CodeGen/pass-by-value-noalias.c
    clang/test/CodeGenCXX/pass-by-value-noalias.cpp
    clang/test/CodeGenObjC/pass-by-value-noalias.m

Modified: 
    clang/include/clang/Basic/CodeGenOptions.def
    clang/include/clang/Driver/Options.td
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/Frontend/CompilerInvocation.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index ec77f68062e7..740d54471051 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -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

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 922ad580a53e..f196c1b72d27 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -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>,

diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index a4b35edb1bd9..adb68979568e 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -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.
 

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index fbccff11562c..0d8b0f9d07ef 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -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;
 }
 

diff  --git a/clang/test/CodeGen/pass-by-value-noalias.c b/clang/test/CodeGen/pass-by-value-noalias.c
new file mode 100644
index 000000000000..f77ce2b1e35b
--- /dev/null
+++ b/clang/test/CodeGen/pass-by-value-noalias.c
@@ -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) {}

diff  --git a/clang/test/CodeGenCXX/pass-by-value-noalias.cpp b/clang/test/CodeGenCXX/pass-by-value-noalias.cpp
new file mode 100644
index 000000000000..fd96a36d3d6e
--- /dev/null
+++ b/clang/test/CodeGenCXX/pass-by-value-noalias.cpp
@@ -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
+}

diff  --git a/clang/test/CodeGenObjC/pass-by-value-noalias.m b/clang/test/CodeGenObjC/pass-by-value-noalias.m
new file mode 100644
index 000000000000..08252800dba2
--- /dev/null
+++ b/clang/test/CodeGenObjC/pass-by-value-noalias.m
@@ -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
+
+ at interface Bar
+ at property char value;
+ at 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) {}


        


More information about the cfe-commits mailing list