From 22e89a17ef14ce48dbc9d2475de3a07f02b0b696 Mon Sep 17 00:00:00 2001 From: Brandon Neth Date: Tue, 2 Jan 2024 12:17:27 -0700 Subject: [PATCH 1/5] Updated split-init documentation. --- Signed-off-by: Brandon Neth --- doc/rst/language/spec/variables.rst | 74 +++++++++++++++-------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/doc/rst/language/spec/variables.rst b/doc/rst/language/spec/variables.rst index bb4e3489e707..1c9fd4135e89 100644 --- a/doc/rst/language/spec/variables.rst +++ b/doc/rst/language/spec/variables.rst @@ -136,11 +136,47 @@ the variable is otherwise mentioned. It will consider the variable passed to an ``out`` intent argument as an assignment statement for this purpose. It will search only within block statements ``{ }``, ``local`` blocks, ``serial`` blocks, ``sync`` blocks, -``try`` blocks, ``try!`` blocks, and -conditionals. These assignment statements and calls to functions with +``try`` blocks, ``try!`` blocks, `select` blocks, and +conditionals. For ```select`` blocks and conditionals, the compiler will +ignore blocks that are statically known to be unreachable. +These assignment statements and calls to functions with ``out`` intent are called applicable assignment statements. They perform initialization, not assignment, of that variable. +Split initialization does not apply: + + * when the variable is a field, config variable, or ``extern`` variable. + * when an applicable assignment statement setting the variable could not + be identified + * when an applicable assignment statement is in at least one branch of a + conditional or ``select`` block but not in another, unless: + + * the variable is not an ``out`` intent formal, and + * every branch without an applicable assignment statement always + returns or throws. + + This rule prevents split-initialization when the applicable assignment + statement is in a conditional that has no ``else`` branch and the + ``if`` branch does not return or throw. + + * when an applicable assignment statement is in a ``try`` or ``try!`` + block which has ``catch`` clauses that mention the variable + + * when an applicable assignment statement is in a ``try`` or ``try!`` + with ``catch`` clauses unless: + + * the variable is not an ``out`` intent formal, and + * all catch clauses return or throw + +In the case that the variable is declared with no ``type-part`` or with a +generic declared type, and where multiple applicable assignment +statements are identified, all of the assignment statements need to +contain an initialization expression of the same type. + +Any variables declared in a particular scope that are initialized with +split init in multiple branches of a conditional or ``select`` +must be initialized in the same order in all branches. + *Example (simple-split-init.chpl)* The combination of statements ``const x;`` and ``x = 5;`` in the below @@ -229,40 +265,6 @@ initialization, not assignment, of that variable. 5 -Split initialization does not apply: - - * when the variable is a field, config variable, or ``extern`` variable. - * when an applicable assignment statement setting the variable could not - be identified - * when an applicable assignment statement is in one branch of a - conditional but not in the other, unless: - - * the variable is not an ``out`` intent formal, and - * the other branch always returns or throws. - - This rule prevents split-initialization when the applicable assignment - statement is in a conditional that has no ``else`` branch and the - ``if`` branch does not return or throw. - - * when an applicable assignment statement is in a ``try`` or ``try!`` - block which has ``catch`` clauses that mention the variable - - * when an applicable assignment statement is in a ``try`` or ``try!`` - with ``catch`` clauses unless: - - * the variable is not an ``out`` intent formal, and - * all catch clauses return or throw - -In the case that the variable is declared with no ``type-part`` or with a -generic declared type, and where multiple applicable assignment -statements are identified, all of the assignment statements need to -contain an initialization expression of the same type. - -Any variables declared in a particular scope that are initialized with -split init in both the ``then`` and ``else`` branches of a conditional -must be initialized in the same order in the ``then`` and ``else`` -branches. - .. _Default_Values_For_Types: Default Initialization From afbe602c9188db807261512a965fd142cc52364d Mon Sep 17 00:00:00 2001 From: Brandon Neth Date: Tue, 2 Jan 2024 15:16:05 -0700 Subject: [PATCH 2/5] changes for copy elision, examples for split init. --- Signed-off-by: Brandon Neth --- doc/rst/language/spec/variables.rst | 126 ++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 15 deletions(-) diff --git a/doc/rst/language/spec/variables.rst b/doc/rst/language/spec/variables.rst index 1c9fd4135e89..907c0086a858 100644 --- a/doc/rst/language/spec/variables.rst +++ b/doc/rst/language/spec/variables.rst @@ -136,8 +136,8 @@ the variable is otherwise mentioned. It will consider the variable passed to an ``out`` intent argument as an assignment statement for this purpose. It will search only within block statements ``{ }``, ``local`` blocks, ``serial`` blocks, ``sync`` blocks, -``try`` blocks, ``try!`` blocks, `select` blocks, and -conditionals. For ```select`` blocks and conditionals, the compiler will +``try`` blocks, ``try!`` blocks, ``select`` blocks, and +conditionals. For ``select`` blocks and conditionals, the compiler will ignore blocks that are statically known to be unreachable. These assignment statements and calls to functions with ``out`` intent are called applicable assignment statements. They perform @@ -157,7 +157,8 @@ Split initialization does not apply: This rule prevents split-initialization when the applicable assignment statement is in a conditional that has no ``else`` branch and the - ``if`` branch does not return or throw. + ``if`` branch does not return or throw. The same applies for a ``select`` + without an ``otherwise``. * when an applicable assignment statement is in a ``try`` or ``try!`` block which has ``catch`` clauses that mention the variable @@ -265,6 +266,98 @@ must be initialized in the same order in all branches. 5 + *Example (split-init-select-dce.chpl)* + + When the condition for a ``select`` or conditional statement + can be evaluated at compile-time, the compiler only considers + the path that will be taken: + + .. code-block:: chapel + + proc splitInitsBecauseDCE(type T) { + var x: int; + select T { + when int { + // split initialization occurs here when T==int + x = 1; + } + when string { + writeln("compiler ignores this block when T==int"); + } + } + writeln(x); + } + proc noSplitInitBecausePathUnknown(arg: int) { + var x: int; + select arg { + when 0 { + // no split init, not all paths return, throw, or initialize + x = 1; + } + when 1 { + writeln("this block not ignored by compiler."); + writeln("arg value unknown at compile-time."); + } + } + writeln(x); + } + proc main() { + splitInitsBecauseDCE(int); + noSplitInitBecausePathUnknown(0); + } + + .. BLOCK-test-chapeloutput + + 1 + 1 + + *Example (split-init-select-otherwise.chpl)* + + Remember to consider the path taken by select statements where + none of the cases match: + + .. code-block:: chapel + + proc noSplitInitMissingOtherwise(arg: int) { + var x: int; + select arg { + when 0 { + // no split init, not all paths return, throw, or initialize + x = 1; + } + when 1 { + // hint: consider the path taken when arg=2 + x = 2; + } + } + writeln(x); + } + proc splitInitHasOtherwise(arg: int) { + var x: int; + select arg { + when 0 { + //split init will occur here + x = 1; + } + when 1 { + x = 2; + } + otherwise { + //now all paths initialize, return, or throw. + return; + } + } + writeln(x); + } + proc main() { + noSplitInitMissingOtherwise(1); + splitInitHasOtherwise(2); + } + + .. BLOCK-test-chapeloutput + + 2 + .. _Default_Values_For_Types: Default Initialization @@ -943,7 +1036,21 @@ is not mentioned again, the copy will be elided. Since a ``return`` or immediately by a ``return`` or ``throw``. When searching forward from variable declarations, copy elision considers eliding copies only within block statements ``{ }``, ``local`` blocks, ``serial`` blocks, ``sync`` blocks, -``try`` blocks, ``try!`` blocks, and conditionals. +``try`` blocks, ``try!`` blocks, ``select`` blocks, and conditionals. Like +split initialization, the compiler ignores blocks that are statically known to +be unreachable. + + +Copy elision does not apply: + + * when the source variable is a reference, field, or module-level + variable + * when the copy statement is in a conditional or ``select`` branch and + at least one branch does not contain the copy, a ``return``, or a ``throw`` + * when the copy statement is in a ``try`` or ``try!`` block which has + ``catch`` clauses that mention the variable or which has ``catch`` + clauses that do not always ``throw`` or ``return``. + *Example (copy-elision.chpl)* @@ -1041,14 +1148,3 @@ block statements ``{ }``, ``local`` blocks, ``serial`` blocks, ``sync`` blocks, deinit 0 -Copy elision does not apply: - - * when the source variable is a reference, field, or module-level - variable - * when the copy statement is in one branch of a conditional but not in - the other, or when the other branch does not always ``return`` or - ``throw``. - * when the copy statement is in a ``try`` or ``try!`` block which has - ``catch`` clauses that mention the variable or which has ``catch`` - clauses that do not always ``throw`` or ``return``. - From 7b4170db8d8400613f2423826a53a2c154fdca7c Mon Sep 17 00:00:00 2001 From: Brandon Neth Date: Wed, 3 Jan 2024 10:44:05 -0700 Subject: [PATCH 3/5] copy elision example --- Signed-off-by: Brandon Neth --- doc/rst/language/spec/variables.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/rst/language/spec/variables.rst b/doc/rst/language/spec/variables.rst index 907c0086a858..abd000989df6 100644 --- a/doc/rst/language/spec/variables.rst +++ b/doc/rst/language/spec/variables.rst @@ -1129,6 +1129,23 @@ Copy elision does not apply: } elideCopyBothConditional(); + proc elideCopyParamSelect() { + type T = int; + var x = makeRecord(); + var y = x; // copy elided + select T { + when real { + writeln(x); + } + when string { + writeln(x); + } + otherwise { + writeln(y); // compiler can determine this is the only possible branch + } + } + } + elideCopyParamSelect(); .. BLOCK-test-chapeloutput init (default) @@ -1146,5 +1163,7 @@ Copy elision does not apply: init (default) block ending deinit 0 + init (default) + deinit 0 From d3fe0f7aef75a45b7ad63c58019468013f06c79c Mon Sep 17 00:00:00 2001 From: Brandon Neth Date: Mon, 29 Jan 2024 10:34:32 -0700 Subject: [PATCH 4/5] fix to split init to reflect changes from dce work. --- Signed-off-by: Brandon Neth --- doc/rst/language/spec/variables.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/rst/language/spec/variables.rst b/doc/rst/language/spec/variables.rst index abd000989df6..259f7cf26853 100644 --- a/doc/rst/language/spec/variables.rst +++ b/doc/rst/language/spec/variables.rst @@ -176,7 +176,8 @@ contain an initialization expression of the same type. Any variables declared in a particular scope that are initialized with split init in multiple branches of a conditional or ``select`` -must be initialized in the same order in all branches. +must be initialized in the same order in all branches that do not +unconditionally return. *Example (simple-split-init.chpl)* From 0256b22f6c4c873eb64e99026aa0cfd6655702d5 Mon Sep 17 00:00:00 2001 From: Brandon Neth Date: Mon, 12 Feb 2024 10:20:37 -0700 Subject: [PATCH 5/5] Revisions for reviewer feedback. --- Signed-off-by: Brandon Neth --- doc/rst/language/spec/variables.rst | 43 +++++++++++++++++------------ 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/doc/rst/language/spec/variables.rst b/doc/rst/language/spec/variables.rst index 259f7cf26853..289a64cd070a 100644 --- a/doc/rst/language/spec/variables.rst +++ b/doc/rst/language/spec/variables.rst @@ -148,12 +148,15 @@ Split initialization does not apply: * when the variable is a field, config variable, or ``extern`` variable. * when an applicable assignment statement setting the variable could not be identified - * when an applicable assignment statement is in at least one branch of a + * when an applicable assignment statement is in at least one branch of a conditional or ``select`` block but not in another, unless: * the variable is not an ``out`` intent formal, and - * every branch without an applicable assignment statement always - returns or throws. + * every branch without an applicable assignment statement always + returns or throws. Note that an ``if`` statement written without an + ``else`` is considered to have an empty ``else`` branch. Similarly, + a ``select`` without an ``otherwise`` is considered to have an empty + ``otherwise``. This rule prevents split-initialization when the applicable assignment statement is in a conditional that has no ``else`` branch and the @@ -267,7 +270,7 @@ unconditionally return. 5 - *Example (split-init-select-dce.chpl)* + *Example (split-init-select-ignored-path.chpl)* When the condition for a ``select`` or conditional statement can be evaluated at compile-time, the compiler only considers @@ -275,7 +278,7 @@ unconditionally return. .. code-block:: chapel - proc splitInitsBecauseDCE(type T) { + proc splitInitsBecausePathKnown(type T) { var x: int; select T { when int { @@ -283,7 +286,8 @@ unconditionally return. x = 1; } when string { - writeln("compiler ignores this block when T==int"); + //compiler ignores this block when T==int + writeln("T==string"); } } writeln(x); @@ -292,18 +296,19 @@ unconditionally return. var x: int; select arg { when 0 { - // no split init, not all paths return, throw, or initialize + // no split init, not all paths return, throw, or initialize x = 1; } when 1 { - writeln("this block not ignored by compiler."); - writeln("arg value unknown at compile-time."); + // this block not ignored by compiler + // arg value unknown at compile-time + writeln("no initialization"); } } writeln(x); } proc main() { - splitInitsBecauseDCE(int); + splitInitsBecausePathKnown(int); noSplitInitBecausePathUnknown(0); } @@ -314,8 +319,9 @@ unconditionally return. *Example (split-init-select-otherwise.chpl)* - Remember to consider the path taken by select statements where - none of the cases match: + For split initialization with a ``select`` statement, the ``otherwise`` + clause (or its absence) impact whether or not split initialization is + possible within the ``when`` clauses: .. code-block:: chapel @@ -323,13 +329,16 @@ unconditionally return. var x: int; select arg { when 0 { - // no split init, not all paths return, throw, or initialize + // no split init, not all paths return, throw, or initialize + // consider path taken when arg==2 x = 1; } when 1 { - // hint: consider the path taken when arg=2 x = 2; } + // since this select statement does not have an `otherwise`, + // the compiler considers it equivalent to an empty `otherwise`: + // otherwise { } } writeln(x); } @@ -337,14 +346,14 @@ unconditionally return. var x: int; select arg { when 0 { - //split init will occur here + // split init will occur here x = 1; } when 1 { x = 2; } otherwise { - //now all paths initialize, return, or throw. + // now all paths initialize, return, or throw. return; } } @@ -1037,7 +1046,7 @@ is not mentioned again, the copy will be elided. Since a ``return`` or immediately by a ``return`` or ``throw``. When searching forward from variable declarations, copy elision considers eliding copies only within block statements ``{ }``, ``local`` blocks, ``serial`` blocks, ``sync`` blocks, -``try`` blocks, ``try!`` blocks, ``select`` blocks, and conditionals. Like +``try`` blocks, ``try!`` blocks, ``select`` blocks, and conditionals. As with split initialization, the compiler ignores blocks that are statically known to be unreachable.