Regent is implemented as a Terra language extension. Every Regent source file must therefore start with:
This loads the Regent compiler and enables hooks to start Regent on
certain keywords (
The top-level of a Regent source file executes in a Lua/Terra context, and Lua, Terra, and Regent constructs can be freely mixed at this level. For example:
Most Terra language features can also be used in Regent tasks, and compilation of Regent programs proceeds similarly to Terra. For example, Lua variables referenced in Regent tasks are specialized prior to type checking, and are effectively constant from the perspective of Regent.
The following Terra features are not supported in Regent:
o:f()does not automatically dereference Regent’s
In general, use Terra’s raw pointer types (
&T) with caution. Regent
may execute tasks in a distributed environment, so a pointer created
in one task might not be valid in another. As long as pointers stay
within a task, it is ok to use raw pointers (and traditional C APIs
Tasks are the fundamental unit of execution in Regent. Tasks are similar to functions in most other programming languages: tasks take arguments and (optionally) return a value, and contain a body of statements which execute top-to-bottom. Unlike traditional functions, tasks must explicitly specify any interactions with the calling context through privileges, coherence modes, and constraints.
Regent programs execute in a top-level Lua/Terra context, but Regent
tasks cannot be called from Lua/Terra. Instead, a Regent program may
begin execution of tasks by calling
regentlib.start with a task
argument. This task becomes the top-level task in the Regent program,
and may call other tasks as desired.
The call does not return, and is typically placed at the end of a Regent source file. At this time, the runtime is not reentrant, so even if the call did return, it would still not be possible to launch another top-level task.
Privileges describe how a task interacts with region-typed
arguments. For example,
reads is required in order to read from a
region argument, and
writes is required to modify a
region. Reductions allow the application of certain commutative
operators to regions. Note that privileges in general apply only to
region-typed, and not immediate arguments passed by-value (such as
ptr data types).
Privileges are most frequently seen in the
where clause of a
Coherence modes specify a task’s expectations of isolation with respect to sibling tasks on the marked regions. Regent supports four coherence modes:
The modes behave as follows:
exclusive mode (the default) guarrantees that tasks will execute
in a manner that preserves the original sequential semantics of
atomic mode allows tasks to be reordered in a manner that
preserves serializability, similar to a transation-based
system. Atomicity is provided at the level of a task.
simultaneous mode allows tasks to run concurrently as long as
they use the same physical instance for all simultaneous
regions. This guarrantees that the regions in question behave with
shared memory semantics, similar to pthreads, etc.
relaxed mode allows marked tasks to run concurrently with no
Coherence modes are most frequently seen in the
where clause of a
Constraints specify the desired relationships between
regions. Constraints are checked at compile time and must be satisfied
by the caller. The supported constraints are disjointness (
Copy operations copy the contents of one region to another (for all or some subset of fields). The number and types of fields so named must match.
Fill operations replace the contents of a region (for all or some subset of fields) with a single specified value. The type of the value must match the named fields.
Field spaces are sets of fields, and behave similarly to Terra structs. For example, field spaces may be instantiated by casting an anonymous struct to the appropriate type.
Field spaces differ from structs in that they may take region-typed arguments. Such arguments are useful for declaring recursive data types. References to field spaces with arguments must be escaped.
Index spaces are sets of indices, used most frequently to define the set of keys in a region. Index spaces may be unstructured (i.e. indices are opaque pointers), or structured (i.e. indices are N-dimensional points with an implied geometric relationship). Unstructured index spaces are created with a maximum size and are initially empty. Structured index spaces are created with an extent and optional start, and are pre-allocated.
Regions are the cross-product between an index space and a field space. The name of the region exists in the scope of the declaration, so recursive data types may refer to the region being defined.
Partitions subdivide regions into subregions. Several partitioning operators are described below. Note that not all partitions are disjoint: some partitions, such as the image operator, may be aliased.
Produces roughly equal subregions, one for each color in the supplied color space. The resulting partition is guarranteed to be disjoint.
Partitions a region based on a coloring stored in a field of the region. The resulting partition is guarranteed to be disjoint.
Partitions a region by computing the image of each of the subregions of a partition through the supplied (pointer-typed) field of a region. The resulting partition is NOT guarranteed to be disjoint.
Partitions a region by computing the preimage of each of the subregions of a partition through the supplied (pointer-typed) field of a region. The resulting partition is guarranteed to be disjoint IF the supplied target partition is disjoint.
Computes the zipped union of the subregions in the supplied partitions. The resulting partition is NOT guarranteed to be disjoint.
Computes the zipped intersection of the subregions in the supplied partitions. The resulting partition is guarranteed to be disjoint IF either or both of the arguments are disjoint.
Computes the zipped difference of the subregions in the supplied partitions. The resulting partition is guarranteed to be disjoint IF the left-hand-side partition is disjoint.
Regent supports Terra-style metaprogramming. Metaprogramming can be used to accomplish a variety of purposes:
More generally, Regent can be used as a full-featured code generator for Legion, in the same way that Terra is used (by Regent itself) as a code generator for LLVM.
For the most part, these features work the same as in Terra. (For
example, types are still Lua expressions, and quotes can still be
inserted with the escape operator
.) Regent-specific features are
A symbol can be used as a variable or task parameter. To generate a fresh, unique symbol, call:
Regent provides an
rquote operator which is analogous to Terra’s
Regent provides an
rexpr operator which is analogous to Terra’s
`. (Unfortunately, Regent is not able to overload punctuation
operators at this time, making this somewhat more verbose than Terra.)
The example below shows how to generate a simple type-parametric task.
To inspect the contents of generated tasks, invoke Regent with the
-fpretty 1. On the code above, this produces the following
This can also be used to determine what optimizations are being triggered. (For example, leaf optimization is enabled on the tasks above.)