-
Notifications
You must be signed in to change notification settings - Fork 1
[SPEC] Constexpr Specification #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sir-NoChill
wants to merge
6
commits into
master
Choose a base branch
from
spec/constexpr
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+189
−21
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
d78dd67
spec: add constant-expression (constexpr) specification
Sir-NoChill 28400b1
spec: register constexpr page in the toctree
Sir-NoChill 5407940
spec: require global initializers to be constant expressions
Sir-NoChill abc8691
docs(constexpr): clarify array sizes are constexpr, not dynamic
Sir-NoChill 256b9ad
docs(constexpr): clarify operators for constexpr
Sir-NoChill 2d22a9a
fix(gazprea): array sized at elaboration
Sir-NoChill File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| .. _sec:constexpr: | ||
|
|
||
| Constant Expressions | ||
| ==================== | ||
|
|
||
| A constant expression (sometimes called a constexpr) is an expression that can | ||
| be fully evaluated by the compiler at compile time. This feature is primarily | ||
| for specifying the size of | ||
| :ref:`statically-sized arrays <ssec:array>`. | ||
|
|
||
| In *Gazprea*, a ``constexpr`` is not a keyword, but a property of a ``const`` | ||
| variable. A ``const`` variable is considered a ``constexpr`` if and only if its | ||
| initializer expression meets a strict set of criteria: | ||
|
|
||
| .. _ssec:constexpr_rules: | ||
|
|
||
| Rules for Constant Expressions | ||
| ------------------------------ | ||
|
|
||
| An expression is a valid ``constexpr`` if it is composed exclusively of: | ||
|
|
||
| 1. Literals of base types (``boolean``, ``integer``, ``real``, ``character``). | ||
| 2. The operators ``+``, ``-``, ``*``, ``/``, ``not``, ``and``, ``or``, | ||
| between two or more ``constexpr``\ s. | ||
| 3. Constructors for aggregate types, provided that the aggregate is const and | ||
| all members are ``constexpr``\ s. | ||
| 4. Index or field access on ``constexpr`` aggregate types. | ||
| 5. Other variables that are themselves valid ``constexpr``\ s. | ||
|
|
||
| An expression is **not** a ``constexpr`` if it contains: | ||
|
|
||
| 1. References to ``var`` variables. | ||
| 2. Function or procedure calls. | ||
| 3. Any I/O operations (``<-``). | ||
|
|
||
| The compiler must perform this validation recursively. When checking if a | ||
| variable is a ``constexpr``, the compiler must trace its entire dependency | ||
| chain. If the chain ever depends on a runtime value, the check fails. | ||
|
|
||
| The only expressions that *must* be ``constexpr`` are global constants. Other | ||
| constexprs arising from constants inside function scope may also be constexprs | ||
| but the implementation does not need to enforce or necessarily identify this. | ||
| Students should also note that MLIR has a constant propagation pass built in, | ||
| so doing constant folding yourself may not be necessary depending on your | ||
| implementation. | ||
|
|
||
| **Examples:** | ||
|
|
||
| **Note**: we will annotate the scope explicitly in these examples. Some | ||
| 'illegal' examples here would be legal within a non-global scope. | ||
|
|
||
| :: | ||
|
|
||
| // ---------------------------- | ||
| // in global scope | ||
| // ---------------------------- | ||
|
|
||
| // Legal Global Constant Expressions | ||
| const A = 10; | ||
| const B = A * 2; // Depends on another constexpr | ||
| const C = B + 5; // C is 25 | ||
|
|
||
| // Illegal Global Constant Expressions | ||
| var x = 10; | ||
| const Y = x + 5; // Not a constexpr: depends on a 'var' | ||
|
|
||
| function get_val() returns integer { return 100; } | ||
| const Z = get_val(); // Not a constexpr: depends on a function call | ||
|
|
||
| .. _ssec:constexpr_aggregates: | ||
|
|
||
| Constant Expressions with Aggregate Types | ||
| ----------------------------------------- | ||
|
|
||
| Arrays and tuples can also be ``constexpr``\ s if they meet specific criteria, | ||
| allowing them to be used to define other constants. | ||
|
|
||
| #. Arrays | ||
|
|
||
| A ``const`` array is a ``constexpr`` if: | ||
|
|
||
| 1. Its size is a valid ``constexpr``. | ||
| 2. All of its element initializers are valid ``constexpr``\ s. | ||
|
|
||
| A ``vector`` (the dynamically-sized type) can never be a ``constexpr`` | ||
| aggregate, since its size is determined at runtime. An inferred-size array | ||
| such as ``integer[*] X = [1, 2, 3]`` must be a ``constexpr``, meaning its | ||
| initializer is itself a ``constexpr``: ``[*]`` denotes an inferred size, not | ||
| a dynamic one. | ||
|
|
||
| :: | ||
|
|
||
| // ---------------------------- | ||
| // in global scope | ||
| // ---------------------------- | ||
|
|
||
| const WIDTH = 5; | ||
| const integer[WIDTH] LOOKUP_TABLE = [10, 20, 30, 40, 50]; // Legal constexpr array | ||
|
|
||
| const ELEMENT = LOOKUP_TABLE[3]; // Legal: ELEMENT is a constexpr with value 30 | ||
| integer[ELEMENT] my_array = 0; // Legal: static array of size 30, zero-filled | ||
|
|
||
| const integer[2] BAD_TABLE = [10, get_val()]; // Illegal: initializer is not a constexpr | ||
| // also illegal if a procedure since | ||
| // procedure calls are not allowed | ||
| // within declarations | ||
|
|
||
| A ``constexpr`` can appear anywhere a ``const`` declaration is legal, | ||
| including inside functions, procedures, and control-flow blocks. However, | ||
| **not every** ``const`` variable is a ``constexpr``. ``const`` means only | ||
| that the variable is immutable within its scope; ``constexpr`` is the | ||
| stronger property that the value is fully known at compile time. For | ||
| example: | ||
|
|
||
| :: | ||
|
|
||
| // ---------------------------------- | ||
| // in local/function/non-global scope | ||
| // ---------------------------------- | ||
| var integer x; | ||
| x <- std_input; | ||
| const integer y = x; // Legal: y is immutable, but NOT a constexpr | ||
| // because its value depends on runtime input. | ||
| integer[y] arr; // Illegal: an explicit array size must be a | ||
| // constexpr, and y is not a constexpr. | ||
| vector<integer> v; // Legal: a vector is the dynamically-sized type; | ||
| // use it when the size is only known at runtime. | ||
|
|
||
| The compiler propagates the constexpr property through local scopes | ||
| normally; there is no restriction on where in a block the declaration | ||
| appears, as long as its entire dependency chain satisfies the rules above. | ||
|
|
||
| #. Tuples | ||
|
|
||
| A ``const`` tuple is a ``constexpr`` if all of its fields are initialized | ||
| with valid constant expressions. | ||
|
|
||
| :: | ||
|
|
||
| // ---------------------------- | ||
| // in global scope | ||
| // ---------------------------- | ||
| const CONFIG = (true, 10 * 2); // Legal constexpr tuple | ||
|
|
||
| const IS_ENABLED = CONFIG.1; // Legal: IS_ENABLED is a constexpr with value 'true' | ||
| const VALUE = CONFIG.2; // Legal: VALUE is a constexpr with value 20 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we agreed that literals only was too restrictive? I thought the reason we were restricting to literals only was so the teams did not have to do constant propagation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I remember you saying that the solution is to store the constants values in the symbols, and that you were going to give them the MLIR tool to do symbolic expression evaluation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what happens right now is that all of this is automatically taken care of by constant propagation, anything needing constexprs that is not directly computable via constant propagation is done in a ctors block in the IR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suppose we have this:
Does
yneed actor? I can see how I could storexas a literal and do my own constant propagation in my AST. But the math part is trickier, in that it seems like you have to generate the MLIR expression and then see if it can be simplified. Ideallyzcould be evaluated as a compile time error but it actually takes a lot of work!Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I currently have a miniature interpreter embedded in sema to evaluate constexprs. So the current implementation would emit a 'global error' without nice errors enabled, if you go through the errors then you'll see that
zfails to evaluate since 10.0/0.0 is invalid (maybe it should be a nan, but this is registered as a type of global error for now).If we change it to be legal (
const integer z = 10.0) then we get the following mlir:The other way to do this is to have the expressions fully analyzed in sema (so our mini interpreter) and then attach them to the globals as attributes, but I have not played with that yet.
All of that said, the students can leave all of this to mlir, they do not need to implement any constant propagation themselves, it just makes the code generation a bit messier at first. I am doing it this way to make the IR pretty.
Doing it a different way you could declare the globals and then have all of the value definitions generated in main. LLVM constant propagation would lower it correctly and evaluate the constants when we lower to LLVM IR, what I did was hoist it to its own special block to add the constraint that globals cannot be changed from anywhere else in the program (set_global must have gaz.init as its parent).