[llvm-dev] Memory scope proposal

Mehdi Amini via llvm-dev llvm-dev at lists.llvm.org
Mon Apr 18 09:45:55 PDT 2016

> On Apr 18, 2016, at 9:12 AM, Tom Stellard via llvm-dev <llvm-dev at lists.llvm.org> wrote:
> Here is the initial proposal with some formatting fixed:
> Currently, the LLVM IR uses a binary value (SingleThread/CrossThread) to
> represent synchronization scope on atomic instructions. We would like to
> enhance the representation of memory scopes in LLVM IR to allow more values
> than just the current two. The intention of this email is to invite
> comments on our proposal. There are some discussion before and it can be
> found here:
> https://groups.google.com/forum/#!searchin/llvm-dev/hsail/llvm-dev/46eEpS5h0E4/i3T9xw-DNVYJ
> Here is our new proposal:
> =================================================================
> We still let the bitcode store memory scopes as "unsigned integers", since
> that is the easiest way to maintain compatibility. The values 0 and 1 are
> special. All other values are meaningful only within that bc file. In
> addition, "a global metadata in the file" will provide a map from unsigned
> integers to string symbols which should be used to interpret all the
> non-standard integers. If the global metadata is empty or non-existent,
> then all non-zero values will be mapped to "system", which is the current
> behavior.
> The proposed syntax for synchronization scope is as follows:
> * Synchronization scopes are of arbitrary width, but implemented as
>  unsigned in the bitcode, just like address spaces.
> * Cross-thread is default.
> * Keyword "singlethread" is unchanged
> * New syntax "synchscope(n)" for other target-specific scopes.
> * There is no keyword for cross-thread, but it can be specified as
>  "synchscope(0)".

Why not going with a metadata attachment directly and kill the "singlethread" keyword? Something like:
Something like:

 cmpxchg i32* %addr, i32 42, i32 0 monotonic monotonic, 3, !memory.scope{!42}
 cmpxchg i32* %addr, i32 42, i32 0 monotonic monotonic, 3, !memory.scope{!43}


!42 = !{"singlethread"}
!43 = !{"L2"}

I also avoids manipulating/pruning the global map, and target won't depend on integer to keep bitcode compatibility.


> The proposed new integer implementation expanded synchronization scopes are
> as follows:
> ***********************************************************************
> | Format       | Single Thread   | System (renamed) | Intermediate    |
> ----------------------------------------------------------------------|
> | Bitcode      | zero            | one              | unsigned n      |
> | Assembly     | singlethread,   | empty (default), | synchscope(n-1) |
> |                synchscope(~0U)   synchscope(0)                      | 
> | In-memory    | ~0U             | zero             | unsigned n-1    |
> | SelectionDAG | ~0U             | zero             | unsigned n-1    |
> ***********************************************************************
> The choice of “~0U” for singlethread makes it easy to maintain backward
> compatibility in the bitcode. The values 0 and 1 remain unchanged in the
> bitcode, and the reader simply decrements them by one to compute the
> correct value in the in-memory data-structure.
> Name Mapping
> Now we comes to name mapping from integers to strings. If a CLANG front end
> wants to map a language that has memory scopes (e.g. OpenCL) to LLVM IR,
> how does it determine what syncscopes to use? Without any rules, each
> target can define its own meaning for the scopes, can give them any name,
> and can map them to the LLVM-IR unit values in any way. In this case, I
> think each target have to provide a mapping function that maps a specific
> language’s name for a scope into that targets name for a scope that has
> conservatively the same semantics. Namely, the act of supporting a new
> language that has memory scopes requires every target to support that
> language to be updated accordingly.
> Therefore, in order to allow front end writers to share memory scope
> definitions when they match to avoid the effort of updating all targets for
> each language,it's better to define standard memory scope names. A target
> is free to implement them or not, but if a target does implement them they
> must have the defined relational semantics (e.g., hierarchical nesting). If
> a target does implement them then it will be able to support any language
> that uses them, including languages not yet invented. A new memory scope
> name can be added if the existing ones are insufficient.
> With the first try, we can define the standard scopes with what a common
> language that has memory scopes needs, e.g., OpenCL uses system, device,
> workgroup, workitem. It uses the same approach as LLVM has done for debug
> information. There are standard debug entities (that a common language (C)
> needs), and each new language uses those standard entities where there is a
> match, and subsequently defines only the delta.
> A bitcode example with the proposal
> *****************************************************************
> define void  <at> test(i32* %addr) {
> ; forward compatibility
>  cmpxchg i32* %addr, i32 42, i32 0 singlethread monotonic monotonic
> ; new synchscope that will be defined by each backend
>  cmpxchg i32* %addr, i32 42, i32 0 synchscope(2) monotonic monotonic, 2
>  cmpxchg i32* %addr, i32 42, i32 0 synchscope(3) monotonic monotonic, 3
>  ret void
> }
> !synchscope = metadata !{{i32 0, !"SingleThread"}, {i32 2, !"WorkGroup"},
> ...}
> *****************************************************************
> =================================================================
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev

More information about the llvm-dev mailing list