diff --git a/src/doc/reference/opencilk-language-specification.md b/src/doc/reference/opencilk-language-specification.md index 8b076f6e..7a805054 100644 --- a/src/doc/reference/opencilk-language-specification.md +++ b/src/doc/reference/opencilk-language-specification.md @@ -1,15 +1,20 @@ --- layout: layouts/page.njk +author: Dorothy Curtis stylesheet: language-specification.css title: OpenCilk language specification +tagline: For people who need to know grammatical details about how Cilk is + integrated with C. date: 2022-07-14T21:37:03.433Z +tags: + - Grammar eleventyNavigation: key: Language specification +attribution: true --- -

OpenCilk Language Extension Specification
Version 1.0 (2021-02-01)

-

Copyright © 2020, 2021 Massachusetts Institute of Technology. All rights reserved.

+

Copyright © 2020, 2021, 2022 Massachusetts Institute of Technology. All rights reserved.

More information about OpenCilk can be found at opencilk.org

Feedback on this specification is encouraged and welcome; please send to @@ -73,14 +78,13 @@ eleventyNavigation:

  • cilk_for
  • cilk_sync
  • cilk_spawn
  • -

    A program that uses these keywords other than as defined in the grammar extension below is ill-formed.

    ## Grammar -

    The three keywords are used in the following new productions:

    +

    The four keywords are used in the following new productions:

    jump-statement:
    cilk_sync ;
    @@ -136,6 +140,12 @@ eleventyNavigation: are called the initialization, condition, and increment, respectively. (A semicolon is included in the grammar of declaration.)

    +A new form of Statement is introduced: + +cilk_scope { Statement* } + +Statements within cilk_scope are executed as usual. There is an implicit cilk_sync at the end of the statements included within the cilk_scope construct. + ## Semantics ### Tasking Execution Model @@ -178,7 +188,8 @@ eleventyNavigation:

    The serialization of a pure C or C++ program is itself.

    If a C or C++ program has defined behavior and does not use the tasking keywords or library functions, it is an OpenCilk with the same defined behavior.

    -

    The serializations of cilk_spawn and cilk_sync + +

    The serializations of cilk_scope, cilk_spawn and cilk_sync are empty.

    If an OpenCilk program has defined deterministic behavior, then that behavior is the same as the behavior of the C or C++ program derived from the original by removing @@ -671,9 +682,9 @@ else ((first) - (limit)) /

    The call to function f is the spawn point and the statement a++; is the continuation. The expression a + b and the initialization of - the temporary variable holding that value, and the evaluation of x\[g()] + the temporary variable holding that value, and the evaluation of x\\\\\\\[g()] take place before the spawn point. The execution of f, the assignment - to x\[g()], and the destruction of the temporary variable holding + to x\\\\\\\[g()], and the destruction of the temporary variable holding a + b take place in the child.

    If a statement is followed by an implicit sync, that sync is the spawn continuation.

    Programmer note: The sequencing may be more clear if

    @@ -721,115 +732,6 @@ a++; the serial execution order of the program) are destructed in an unspecified order before the earliest exception is caught.

    -# Hyperobjects - -## Description - -

    Cilk Plus defines a category of objects called “hyperobjects”. - Hyperobjects allow thread-safe access to shared objects by giving each parallel - strand running in parallel a separate instance of the object.

    -

    Parallel code uses a hyperobject by performing a hyperobject lookup operation. - The hyperobject lookup returns a reference to an object, called a view, - that is guaranteed not to be shared with any other active strands in the program. - The sequencing of a hyperobject lookup within an expression is not specified. The - runtime system creates a view when needed, using callback functions provided by - the hyperobject type. When strands synchronize, the hyperobject views are merged - into a single view, using another callback function provided by the hyperobject - type.

    -

    The view of a hyperobject visible to a program may change at any spawn or sync (including - the implicit spawns and syncs within a cilk_for loop). The identity - (address) of the view does not change within a single strand. The view of a given - hyperobject visible within a given strand is said to be associated with - that view. A hyperobject has the same view before the first spawn within a task - block as after a sync within the same task block, even though the thread ID may - not be the same (i.e., hyperobject views are not tied to threads). A hyperobject - has the same view upon entering and leaving a cilk_for loop and within - the first iteration (at least) of the cilk_for loop. A special view - is associated with a hyperobject when the hyperobject is initially created. This - special view is called the leftmost view or earliest view - because it is always visible to the leftmost (earliest) descendent in the depth-first, - left-to-right traversal of the program's spawn tree. The leftmost view is given - an initial value when the hyperobject is created.

    -

    Programmer note: If two expressions compute the same address for a view, - then they have not been scheduled in parallel. This property yields one of the simplest - ways by which a program can observe the runtime behavior of the scheduler.

    -

    Implementation note: An implementation can optimize hyperobject lookups - by performing them only when a view has (or might have) changed. This optimization - can be facilitated by attaching implementation-specific attributes to the hyperobject - creation, lookup, and/or destruction operations.

    - -## Reducers - -

    The vast majority of hyperobjects belong to a category known as “reducers.” - Each reducer type provides a reduce callback operation that merges - two views in a manner specific to the reducer. For a pair of views V1 - and V2, the result of calling reduce(V1, - V2) is notated as V1⊗V2. - Each reducer also provides an identity callback operation that initializes - a new view.

    -

    The reduce callback for a “classical” reducer implements - an operation ⊗ such that (a⊗b)⊗c==a⊗(b⊗c) - (i.e., ⊗ is associative). The view-initialization callback for such a reducer - sets the view to an identity value I such that I⊗v==v - and v⊗I==v for any value v of value_type. - Given an associative ⊗ and an identity I, the triplet (value_type, - ⊗, I) describes a mathematical monoid. For example, - (int, +, 0) is a monoid, as is (list, - concatenate, empty). If each individual view, R, - of a classical reducer is modified using only expressions that are equivalent to - RRv (where v is of - value_type), then the reducer computes the same value in the parallel - program as would be computed in the serialization of the program. (In actuality, - the “⊗” in the expression “RRv” - can represent a set of mutually-associative operations. For example, += - and -= are mutually associative.) For example, a spawned function or - cilk_for body can append items onto the view of a list reducer with - monoid (list, concatenate, empty). At the end - of the parallel section of code, the reducer's view contains the same list items - in the same order as would be generated in a serial execution of the same code.

    -

    Given a set of strands entering a sync, S1,S2,S3,…Sn, - associated with views V1,V2,V3,…Vn, - respectively such that Si is earlier in the serial ordering - than Si+1, a single view, W, emerges from the sync - with value W←V1⊗V2⊗V3⊗…⊗Vn, - such that the left-to-right order is maintained but the grouping (associativity) - of the operations is unspecified. The timing of this “reduction” is - unspecified – in particular, subsequences typically will be computed asynchronously - as child tasks complete. Every view except the one emerging from the sync is destroyed - after the merge. If any of the strands does not have an associated view, then the - invocation of the reduce callback function can be elided (i.e., the - missing view is treated as an identity).

    -

    A strand is never associated with more than one view for a given reducer, but multiple - strands can be associated with the same view if those strands are not scheduled - in parallel (at run time). Specifically, for a given reducer, the association of - a strand to a view of the reducer obeys the following rules:

    -
      -
    1. The strand that initializes the reducer is associated with the leftmost view.
    2. -
    3. If two strands execute in series (i.e., both strands are part of a larger strand), - then both are associated with the same view.
    4. -
    5. The child strand of a spawn is associated with the same view as the strand that - entered the spawn.
    6. -
    7. If the continuation strand of a spawn is scheduled in parallel with the child, then - the continuation strand is associated with a new view, initialized using identity. - The implementation may create the new view at any time up until the first hyperobject - lookup following the spawn. If the continuation strand does not perform a hyperobject - lookup, then the implementation is not required to create a view for that strand.
    8. -
    9. If the continuation strand of a spawn is not scheduled in parallel with the child - strand (i.e., the child and the continuation execute in series), then the continuation - strand is associated with the same view as the child strand.
    10. -
    11. The strand that emerges from a sync is associated with the same view as the leftmost - strand entering the sync.
    12. -
    -

    Even before the final reduction, the leftmost view of a reducer will contain the - same value as in the serial execution. Other views, however, will contain partial - values that are different from the serial execution.

    -

    If ⊗ is not associative or if identity does not yield a true - identity value then the result of a set of reductions will be non-deterministic - (i.e., it will vary based on runtime scheduling). Such “non-classical” - reducers are nevertheless occasionally useful. Note that, for a classical reducer, - the ⊗ operator needs to be associative, but does not need to be commutative.

    - - # Disclaimer and other legal information

    Copyright (c) 2020 Massachusetts Institute of Technology