A SeExpr program describes computation on a single point of a larger domain that contains many such points, with the understanding that the compiler/interpreter will run that computation on every point of the domain. In pseudo-code for some SeExpr program, prog, the whole computation becomes:
foreach($pt : all_points)
prog($pt)
This simple approach is easy to understand but ignores certain aspects of the computation. Determining how often and in what circumstances variables and expressions change - determining the lifetime of variables and expressions - exposes these aspects. The ideal time to determine these lifetimes is at compile-time during type checking. So the lifetime qualifier is currently being added to the type system, so that these lifetimes will be determined during type checking and then be available to the compiler after the type check. There are three reasons for determining these lifetimes. First, there are certain optimizations the compiler can preform if it knows the the lifetimes of variables and expressions. Second, there are certain computations that we want to restrict so that computation is reasonably fast. (Mainly, we wish to restrict string operations in certain contexts.) Third, there are certain functions that expect some (or all) of their arguments to have specific lifetimes.
The rest of this post outlines the different lifetimes and their uses.
Constant
First we have values that never change, i.e.
$foo = [1,2,3] + 4;
$bar = $foo / 2;
$baz = $bar * $u;
This can be simplified down to:
$foo = [5,6,7];
$bar = $foo / 2;
$baz = $bar * $u;
Or even:
$foo = [5,6,7];
$bar = [2.5,3,3.5];
$baz = [2.5,3,3.5] * $u;
Most decent compilers will do these optimizations automatically. However, there are functions that expect certain arguments to be constant. Without including the constant lifetime qualifier, these arguments could not be checked during compilation. Without such a check, wrong program behavior, such as segfaults and incorrect results, could unexpectedly happen when the program is run.
Uniform
Next we have values that don't change within certain known bounds. A simplistic example is shading hair. Each hair can have a slightly different color, and so a $color variable does not change over an individual hair, but will change over different hairs.
If we have a program:
foreach($pt : $hair : all_hairs)
$color = calculate_color($hair)
$result = use_color_and_point($color, $pt);
We can change our model of execution:
foreach($hair : all_hairs)
$color = calculate_color($hair)
foreach($pt : $hair)
$result = use_color_and_point($color, $pt);
This new model, while more complex, is more efficient since the color of a hair is only computed once per hair rather than for every point on every hair. If calculate_color() is a time-intensive function, using this model can be significantly faster than the original simple model. Uniform values and expressions are more difficult for compilers to recognize, and so it is less certain that compilers will automatically include these optimizations without some hints (or explicit declarations of uniform values and expressions) from the type system. Also, like with constant values, there are certain functions that require uniform arguments, which can now be explicitly checked.
Varying
Finally, we have values that change at every point. In the example in the section above, the final variable, $result, is varying. It depends directly on the current point, and so its value could change at every point. Without the lifetime qualifier, every variable and expression is assumed to be varying.
Also, there are certain computation expressions (string operations) that we wish to ban from being varying. These operations are useful but time-intensive. By restricting them to constant or uniform lifetimes, we restrict how often they are computed.
New Whole Computation Model
Instead of the simple model for the whole computation at the beginning of this post:
foreach($pt : all_points)
prog($pt)
We now compile a program, prog, into three distinct, yet interrelated, sections:
For a program, prog, its varying section, vary-prog, can depend on both its constant and uniform sections, const-prog and unif-prog. Likewise, the uniform section, unif-prog can depend on the constant section, const-prog.
The new whole computation model in pseudo-code is:
$const_values = const-prog();
foreach($section : all_sections)
$unif_values = unif-prog($section, $const_values);
foreach($pt : $section)
vary-prog($pt, $section, $unif_values, $const_values);
This model is much more complex, but in general most of these changes will not effect users. Unless there is an error, users can continue to write their programs as if their programs will be computed within the original computation model.
Lifetime Interactions
Lifetimes interact in a fairly intuitive manner:
The general rule follows this restriction list:
Most restrictive
Least restrictive
A more restrictive lifetime can replace a less restrictive lifetime, but not the other way around.
* Currently, all functions have no side-effects.
Errors Caused By Lifetimes
Lifetimes do introduce new restrictions to the SeExpr Language. These new restrictions can appear in the form of errors.
Lifetime Error. Expected: Constant Received: Uniform
Lifetime Error. Expected: Constant Received: Varying
Lifetime Error. Expected: Uniform Received: Varying
The above errors appear when functions or certain string operations expect a certain lifetime from their arguments and they arguments they have recieved do not have those lifetimes.
If Statement Impact on Lifetimes
Possibly unintuitively, an if statement can change the lifetime of any variables assigned within its then and else blocks in three ways. All changes effect the lifetimes of the variables after the if statement. The lifetimes of the variables within the then and/or else blocks are not effected under any circumstances.
The three different impacts can overlap. See below for an explanation. (Short version: The least restrictive lifetime is the result.)
Conditional Value Impact
The first if statement impact happens when the lifetime of its conditional value is less restrictive than the assigned variables' lifetimes. After the if statement, the lifetime becomes the same as the conditional value's lifetime. There are three cases:
Examples:
The following example causes an error:
if(<uniform value>) {
$foo = <constant value>;
} else {
$foo = <different constant value>;
}
$bar = func_taking_constant($foo); => Lifetime Error. Expected: Constant Received: Uniform
$baz = func_taking_uniform($foo);
The following example does not cause an error:
if(<uniform value>) {
$foo = <constant value>;
$bar = func_taking_constant($foo);
} else {
$foo = <different constant value>;
$bar = func_taking_constant($foo);
}
$baz = func_taking_uniform($foo);
Different Lifetimes from Different Blocks Impact
The second if statement impact on lifetime happens when a variable is assigned in both the then and the else block with different lifetimes. The lifetime of the variable after the if block is the less restrictive lifetime. There are four cases:
Examples:
The following example causes an error:
if(<constant value>) {
$foo = <constant value>;
$bar = func_taking_constant($foo);
} else {
$foo = <uniform value>;
$bar = func_taking_uniform($foo);
}
$baz = func_taking_constant($foo); => Lifetime Error. Expected: Constant Received: Uniform
The following example does not cause an error:
if(<constant value>) {
$foo = <constant value>;
$bar = func_taking_constant($foo);
} else {
$foo = <uniform value>;
$bar = func_taking_uniform($foo);
}
$baz = func_taking_uniform($foo);
Different Lifetimes from before and within If Statement Impact
The third type of if statement impact happens when the lifetime of a variable before an if statement does not match the lifetime of the assignment of the same variable within the if statement. The lifetime of the variable after the if block is the less restrictive lifetime. There are four cases:
Examples:
The following example causes an error:
$foo = <varying value>;
if(<constant value>) {
$foo = <constant value>;
$bar = func_taking_constant($foo);
}
$baz = func_taking_constant($foo); => Lifetime Error. Expected: Constant Received: Varying
The following example does not cause an error:
$foo = <varying value>;
if(<constant value>) {
$foo = <constant value>;
$bar = func_taking_constant($foo);
}
$baz = func_taking_varying($foo);
If Statement Impact Interactions
There are too many different ways for the various if statement impacts on lifetimes to interact to list here. The general rule still applies, however: The least restrictive impact is the result.
Examples:
The following examples cause errors:
$foo = <varying value>;
if(<uniform value>) {
$foo = <constant value>;
}
$bar = func_taking_constant($foo); => Lifetime Error. Expected: Constant Received: Varying
if(<varying value>) {
$foo = <uniform value>;
} else {
$foo = <constant value>;
}
$bar = func_taking_uniform($foo); => Lifetime Error. Expected: Uniform Received: Varying
The following example does not cause an error:
$foo = <constant value>;
if(<uniform value>) {
$foo = <varying value>;
}
$bar = func_taking_varying($foo);