Add fences to module, runtime, compiler

[reviewed by Sung and Greg]

This patch adds fences for remote data in places that the language
implies them. These fences formalize the implied memory consistency model
for remote memory and allow implementations doing read-ahead or write-behind
on remote values (which I am about to commit in another patch).

on statements:
 Since an 'on' statement is totally serial, you can't add data races with
 it, so you should get the same view of memory on the locale to you to as
 on the locale you left (in a totally serial program). In order to do that,
 we need to add fences to the parent side and the child side of the 'on'
 statement.

begin statements:
 As an implementation reality, read-ahead and write-behind will be
 task- or thread- local. That's not necessarily true in all designs,
 but if they are task- or thread- local, we need to add a fence on the
 parent and child side of the begin block. The child side might contain
 an 'on' statement and no longer have the ability to add the required
 fences on the operations started in the parent.

sync variable uses:
 Add full memory consistency fences for all sync variable uses.

atomic types:
 Add fences that respect the order= argument for methods on atomic types.
 In order to keep the implementations in Atomics.chpl and NetworkAtomics.chpl
 from getting too messy, this patch modifies createTaskFunctions to add
 these fences to methods in those modules.

Details:

Compiler changes

compiler/include/driver.h
compiler/main/driver.cpp
 rename fCacheEnable to fCacheRemote

compiler/AST/expr.cpp
compiler/AST/primitive.cpp
compiler/include/primitive.h
compiler/optimizations/optimizeOnClauses.cpp

 Add primitives for start/finish fences:
  PRIM_START_RMEM_FENCE, PRIM_FINISH_RMEM_FENCE
 These are primitives so that some optimizations can ignore them
 (in particular, optimizeOnClauses should).

compiler/include/flags_list.h

 add flags:
  FLAG_ATOMIC_MODULE, FLAG_MEMORY_ORDER_TYPE, FLAG_NO_REMOTE_MEMORY_FENCE

 FLAG_ATOMIC_MODULE should be set on Atomics.chpl and NetworkAtomics.chpl
 so that a compiler pass can add appropriate fences to methods in those
 modules.

 FLAG_MEMORY_ORDER_TYPE should be set on extern type memory_order, so that
 the pass adding fences in Atomics.chpl and NetworkAtomics.chpl adds
 fences that respect the order= argument.

 FLAG_NO_REMOTE_MEMORY_FENCE should be applied to any function not in an
 Atomics module with a final order= argument with on statements that should
 not get a remote memory fence. Currently it is applied to _upEndCount since
 it contains an on statement but should only be run with release semantics.

compiler/passes/createTaskFunctions.cpp

 Add fences respecting order= argument around Atomic/NetworkAtomic methods
 Add chpl_rmem_consist_release/chpl_rmem_consist_acquire around the parent
  side of blocking 'on' statements.
 Add start/finish fences to task bodies and 'on' statement bodies.

Module changes

Atomics.chpl:
NetworkAtomics.chpl:
  Mark these Atomics modules with FLAG_ATOMIC_MODULE so that the compiler
  knows to add fences to atomic functions in here.
  Updates atomic_fence to call appropriate fence.

  Make waitFor do a series of relaxed consistency local atomic reads, so that
  the cache does not need to be repeatedly cleared in the waitFor loop.

  Specify the type of the order= argument explicitly since the pass
  adding the fences (in createTaskFunctions) runs before function resolution.

ChapelBase.chpl

  Updates _upEndCount and _downEndCount to have more limited memory
  consistency fence -- they both only need release fence, but by default
  the use of the Atomic variable would imply both acquire+release fence.

  Updates _waitEndCount to only do an acquire fence when waiting for all
  tasks to finish - since that is the only memory fence needed in that
  situation.

ChapelSyncvar.chpl

  Add calls to chpl_rmem_consist_release() and chpl_rmem_consist_acquire()
  before/after each sync variable operation. These functions do not take in an
  order argument the way the Atomics.chpl ones do since use of sync variable
  implies a full fence. They should not add overhead if the cache is not
  enabled since they will be inlined and checking a known-to-the-C-compiler
  indicating if --cache-remote was used.

MemConsistency.chpl

 Pull declarations of memory_order out of Atomics.chpl since they're not
 exactly about atomic operations. Add some memory consistency fences.
 This module is meant to mirror chpl-mem-consistency.h

modules/standard/CommDiagnostics.chpl
runtime/include/chpl-comm.h
runtime/src/chpl-comm.c
 Move start/stop commDiagnostics to e.g. chpl_gen_startCommDiagnostics
 so that release fence can be called to complete any pending operations
 (in order to make sure that they are counted correctly).

Runtime changes

chpl-mem-consistency.h
  Add fence functions that call chpl_cache_acquire or chpl_cache_release:
chpl_rmem_consist_release,
chpl_rmem_consist_acquire,
chpl_rmem_consist_maybe_release(order),
chpl_rmem_consist_maybe_acquire(order)
chpl_rmem_consist_fence(order)
 and chpl_cache_acquire or chpl_cache_release are currently no-ops;
 they will be filled in by a future commit.

Test changes

test/modules/sungeun/init/printModuleInitOrder.good
 adds MemConsistency as an automatically included module

test/multilocale/diten/needMultiLocales/remoteString.chpl
 add a fence to this test since otherwise it contains a data race.

'make check' passes with comm=none and GASNet/UDP/local



git-svn-id: http://svn.code.sf.net/p/chapel/code/trunk@23794 3a8e244f-b0f2-452b-bcba-4c88e055c3ca
Cache for remote data implementation and tests

== Introduction ==

It's easy to write Chapel programs that create many small messages. For
example:

   var A:[1..100] int;
   ....
   on Locales[1] {
     for a in A {
       use a
     }
   }

That bit of code might be for example using the vector in a distributed
matrix-vector multiply. Or maybe it's just some poorly written code. Either
way, it results in a 100 8-byte GETs from Locale 1 to Locale 0.

One way around this problem would be to create a local copy of A, enabling
the Chapel compiler's ability to optimize whole array assignment. While this
approach does reduce the number of messages, and is probably the right idea
in a library context, it has a few drawbacks:

  - the obvious solution might use to much memory (imagine than A is large and
    distributed; we can't just copy the whole thing).
  - a tiled/blocked solution adds complexity.
  - if many such local copies are used throughout a program, we might end
    up with too much memory used for buffering, especially on large systems.
  - as a communication optimization that would need to be located within the
    code using A, it diminishes the Chapel promise of separating algorithm
    from data distribution.

== Caching Remote Data ==

The basic idea here is to add a new runtime component that caches remote data.
This cache allows for:
  - data re-use without adding communication
  - automatic prefetching triggered by sequential access
  - automatic write-behind for puts
  - user- (or compiler-) requested prefetching similar to __builtin_prefetch
  - bounded communication buffers when the cache is used

Philosophically, the cache needs to exist on each locale, but in the
implementation, there is one cache per pthread.

What about memory consistency? The natural and key question one should ask
about any cache is how it ensures that the program still runs in a sane
manner.  More specifically, you might want to know the answers to these three
questions:

  1) Do non-parallel programs still work as they would without the cache?
  2) How can tasks on two locales ever share data?
  3) Does the cache introduce a lot of memory consistency traffic?

The key idea in the design of this cache is to avoid all memory consistency
traffic. Instead, all data in the cache is considered invalid once the memory
consistency model requires us to read new values. This is reasonable as
long as Chapel has weak or release consistency or "sequential consistency for
data race free programs" as the memory model.

The implementation creates one cache per pthread. You can find more
information about the design in the comment at the top of chpl-cache.c.

== Changed Files ==

runtime/include/chpl-cache.h
runtime/src/chpl-cache-support.c
runtime/src/chpl-cache.c
 new files implementing the cache itself.

runtime/include/chpl-cache-task-decls.h declares task-local data
 (to store the last acquire sequence number per-task).

runtime/include/chpl-comm-compiler-macros.h
  - chpl_gen_comm_get chpl_gen_comm_put
    chpl_gen_comm_get_strd chpl_get_comm_put_strd
    call e.g. chpl_cache_comm_get if chpl_cache_enabled()
  - add new function, chpl_gen_comm_prefetch(), to do a local
    or remote prefetch. Of course this function can't do anything
    if the cache is not enabled.

runtime/include/comm/gasnet/chpl-comm-task-decls.h
 enables memory fences and task-private cache data.

runtime/include/chpl-mem-consistency.h
 includes chpl-cache.h to for memory fences

runtime/src/comm/gasnet/comm-gasnet.c
 initialize the cache in the comm-layer initialize call

test/optimizations/cache-remote/
 a new test directory with tests particularly relevant to
 the cache (these could also be viewed as memory consistency tests).

runtime/src/Makefile.share
  build chpl-cache.c  (which includes chpl-cache-support.c)



git-svn-id: http://svn.code.sf.net/p/chapel/code/trunk@23795 3a8e244f-b0f2-452b-bcba-4c88e055c3ca
Add a remote prefetch primitive

[reviewed by Sung and Greg]

While prefetch is arguably just a function call, I believe it makes sense for
it to be a primitive since future compiler optimizations might want to easily
add this call. It also should not disrupt e.g.  alias analysis or other
optimizations the way that an arbitrary function call can.

compiler/include/primitive.h
compiler/AST/primitive.cpp
  add PRIM_CHPL_COMM_REMOTE_PREFETCH is OK

compiler/AST/checkAST.cpp
  PRIM_CHPL_COMM_REMOTE_PREFETCH is OK

compiler/AST/expr.cpp
  uncomment the 7-argument codegenCall, fix comment in PRIM_CHPL_COMM_GET,
  use codegenCall to implement PRIM_CHPL_COMM_REMOTE_PREFETCH.

compiler/optimizations/optimizeOnClauses.cpp
  put PRIM_CHPL_COMM_REMOTE_PREFETCH with PRIM_CHPL_COMM_GET as a 'slow'
  primitive.

compiler/optimizations/copyPropagation.cpp
  inform copy propogation about which arguments to CHPL_COMM_REMOTE_PREFETCH
  are pointers.

test/optimizations/cache-remote/ferguson/reduces_comm/ra_prefetch.chpl
  this test case verifies that with the cache enabled, the prefetch
  primitive gives a speedup. At present this is the only use of the
  primitive.

I've been able to demonstrate a 4x speedup by adding prefetch
calls to a random-access example.  I used the primitive directly
in my benchmark, but a future module change could add something like this:

inline proc prefetch(ref x, len:int) {
   __primitive("chpl_comm_remote_prefetch", x.locale.id,
               x, len);
}



git-svn-id: http://svn.code.sf.net/p/chapel/code/trunk@23796 3a8e244f-b0f2-452b-bcba-4c88e055c3ca
Add two more performance test cases

[trivial and not reviewed]



git-svn-id: http://svn.code.sf.net/p/chapel/code/trunk@23797 3a8e244f-b0f2-452b-bcba-4c88e055c3ca
Adding puts and gets to reservedSymbolNames

[trivial and not reviewed]



git-svn-id: http://svn.code.sf.net/p/chapel/code/trunk@23798 3a8e244f-b0f2-452b-bcba-4c88e055c3ca