[PATCH] An llvm.noalias intrinsic

hfinkel at anl.gov hfinkel at anl.gov
Thu Apr 30 08:02:09 PDT 2015


Hi dberlin, rsilvera, chandlerc, reames,

This is the first in a series of patches to extend the scoped-noalias metadata implementation by providing an intrinsic for implicit alias scope assignment (in contrast to the alias.scope metadata which provides explicit scope assignment).

The general design for this was discussed at the end of last year (http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-November/078786.html), and while there will need to be further extensions (to support dynamic predicates, at least), this should address two core motivations:

 1. Provide a way to retain dominance information which is necessary to answer loop-sensitive queries

 2. Make it practical to use scoped-noalias metadata to model block-level restrict-qualified pointers from Clang.

To summarize from the llvmdev e-mail, the problem underlying (1) is that, when using the existing metadata scheme, we cannot differentiate between these two after inlining:

  void foo(float * restrict a, float * restrict b) {
    for (int i = 0; i < 1600; ++i)
      a[i] = b[i] + 1;
  }

and:

  void inner(float * restrict a, float * restrict b) {
    *a = *b + 1;
  }
  void foo(float * a, float * b) {
    for (int i = 0; i < 1600; ++i)
      inner(a+i, b+i);
  }

and we currently cheat and assume the former to enable vectorization. This is not correct, however, and the underlying issue is that the current scheme cannot differentiate directly between:

 1. Does access A alias with access B, both in loop L, within a loop iteration

 2. Does access A alias with the memory accessed by B is any loop iteration

the existing metadata really only can answer (a) but the vectorizer needs (b). The existing noalias attribute (like the restrict-qualifier on which it is based) does provide the necessary information (because it applies to all derived pointers, and thus those used in every loop iteration), but we loose it when converting to the existing metadata scheme.

Regarding problem (2), while in theory block-level restrict-qualified pointers can be modeled using the existing metadata scheme, it suffers from a lack of dominance information (from problem (1)), and is also difficult to apply in practice. The core issue is that to determine whether or not a variable is derived from some particular restrict-qualified pointer, you need to determine which accesses might be derived from that pointer via some capture. This is much easier to do after optimization (mem2reg, funcattrs to add nocapture attributes, etc.) than in the frontend directly. In practice, we really want to delay this determination until later in the pipeline (and not try to do it in the frontend).

To address these concerns, we'll add a new intrinsic for implicit scope assignment. The scope, in the sense of modeling restrict-qualified pointers, represents the set of restrict-qualified pointers from which some access might derive. This can be explicitly represented with alias.scope metadata. Then the noalias metadata specified with which scopes the tagged access does not alias. To use the new intrinsic, llvm.noalias, you tag all potentially relevant accesses with noalias metadata and derive all pointers in a particular scope from the return value of the llvm.noalias intrinsic with that scope as an argument.  The reason that all relevant accesses need to have noalias metadata, including those using pointers derived from the noalias intrinsic, is a compile-time optimization: we don't need to search for noalias intrinsics, a potentially expensive operation, if there is no noalias metadata tag, and thus, there should be no significant cost to the feature if you're not using it.

One issue (directly relevant in a later patch providing the implementation), is that the scope list provided as the metadata argument can have only one item. This is because we need the ability to search for other intrinsics with the same scope argument, and because metadata no longer have use lists, we can do this only by searching the use list of the MetadataAsValue wrapper (and, as a result, can only find complete matches, so we need to have trivial lists). This may be something we wish to address is some different way, because as it stands, we cannot combine adjacent noalias intrinsics.

To bring up a naming issue: I named the intrinsic llvm.noalias because it is like the noalias function argument. However, conceptually, is serves the same purpose as the alias.scope metadata (and works along with the noalias metadata, which is still needed), so maybe it should be named llvm.alias.scope?

Also, on the data dependence, while poses obvious difficulties on the optimizer (and may of the patches are to address this), a strict control dependence is not sufficient (so we can't make it more like the lifetime intrinsics which take a pointer argument). Specifically, we need to be able to represent this kind of construct:

  void foo(T * restrict x, T * restrict y) {
    for (int i = 0; i < 1600; ++i)
      x[2*i+1] = y[2*i] + 1;
  }

when inlined from the call foo(q, q).

http://reviews.llvm.org/D9375

Files:
  docs/LangRef.rst
  include/llvm/IR/Intrinsics.td

Index: docs/LangRef.rst
===================================================================
--- docs/LangRef.rst
+++ docs/LangRef.rst
@@ -10325,6 +10325,59 @@
 that the optimizer can otherwise deduce or facts that are of little use to the
 optimizer.
 
+'``llvm.noalias``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare <type>* @llvm.noalias.p<address space><type>(<type>* %ptr, metadata %scopes)
+
+Overview:
+"""""""""
+
+``llvm.noalias`` allows the optimizer to assume that pointers derived from the
+return value don't alias with those used by memory accesses tagged with
+associated ``noalias`` metadata.
+
+Arguments:
+""""""""""
+
+The first argument in the pointer on which the aliasing assumption is being
+placed. This pointer value is returned, but parts of the optimizer ignore this
+fact so that data dependencies on the return value will be maintained. The
+aliasing assumptions apply only to uses of pointers derived from the return
+value.
+
+The second argument is metadata that is a list of ``noalias`` metadata
+references. The format is identical to that required for ``noalias`` metadata.
+This list should have only one element.
+
+Semantics:
+""""""""""
+
+``llvm.noalias`` allows the optimizer to assume that pointers derived from the
+return value don't alias with those used by memory accesses tagged with
+``noalias`` metadata with compatible scopes. See the description of
+``alias.scope`` and ``noalias`` metadata for more information. The aliasing
+assumptions apply only to uses of pointers derived from the return value, and
+accesses using those derived pointers must themselves be tagged with
+``noalias`` metadata with compatible scopes.
+
+Unlike ``alias.scope`` metadata, which applies to only the access to which it
+is attached, the ``llvm.noalias`` intrinsic applies to all pointers derived
+from the return value. Placing an ``llvm.noalias`` intrinsic outside of a loop
+provides a mechanism for communicating that certain memory accesses don't alias
+with uses of a pointer in any loop iteration. This works only if the
+``llvm.noalias`` intrinsic itself dominates the loop body.
+
+Note that the use of ``alias.scope`` metadata does not block potential
+optimizations, as ``llvm.noalias`` might, and so use of  ``alias.scope``
+metadata when possible is recommended. Nevertheless, no code is generated for
+this intrinsic.
+
 .. _bitset.test:
 
 '``llvm.bitset.test``' Intrinsic
Index: include/llvm/IR/Intrinsics.td
===================================================================
--- include/llvm/IR/Intrinsics.td
+++ include/llvm/IR/Intrinsics.td
@@ -292,6 +292,13 @@
 // control dependencies will be maintained.
 def int_assume        : Intrinsic<[], [llvm_i1_ty], []>;
 
+// This intrinsic does not actually write to memory, but it is marked that way
+// to maintain proper control dependencies. The metadata argument refers to a
+// list of alias.scope metadata entries.
+def int_noalias       : Intrinsic<[llvm_anyptr_ty],
+                                  [LLVMMatchType<0>, llvm_metadata_ty],
+                                  [IntrReadWriteArgMem]>;
+
 // Stack Protector Intrinsic - The stackprotector intrinsic writes the stack
 // guard to the correct place on the stack frame.
 def int_stackprotector : Intrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>;

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D9375.24708.patch
Type: text/x-patch
Size: 3352 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150430/0bbe8bb9/attachment.bin>


More information about the llvm-commits mailing list