From 907dfc003ab8e53fa610786c2e924bb4915b4b77 Mon Sep 17 00:00:00 2001 From: Brandon Neth Date: Mon, 4 Dec 2023 14:41:55 -0700 Subject: [PATCH] Split init tests and implementation for if-then-else DCE. Tests for select statement DCE, but not implementation yet. --- Signed-off-by: Brandon Neth --- frontend/lib/resolution/VarScopeVisitor.cpp | 8 + frontend/lib/resolution/VarScopeVisitor.h | 1 + frontend/lib/resolution/split-init.cpp | 16 +- frontend/test/resolution/testSplitInit.cpp | 261 +++++++++++++++++++- 4 files changed, 278 insertions(+), 8 deletions(-) diff --git a/frontend/lib/resolution/VarScopeVisitor.cpp b/frontend/lib/resolution/VarScopeVisitor.cpp index c3dabd76c327..8e777618415a 100644 --- a/frontend/lib/resolution/VarScopeVisitor.cpp +++ b/frontend/lib/resolution/VarScopeVisitor.cpp @@ -253,6 +253,12 @@ void VarScopeVisitor::exitScope(const AstNode* ast, RV& rv) { thenFrame->returnsOrThrows && elseFrame->returnsOrThrows) { parentFrame->returnsOrThrows = true; } + if (thenFrame && thenFrame->returnsOrThrows && thenFrame->isParamTrue) { + parentFrame->returnsOrThrows = true; + } + if (elseFrame && elseFrame->returnsOrThrows && elseFrame->isParamTrue) { + parentFrame->returnsOrThrows = true; + } } } else if (auto t = ast->toTry()) { handleTry(t, rv); @@ -502,10 +508,12 @@ bool VarScopeVisitor::enter(const Conditional* cond, RV& rv) { if (condRE.type().isParamTrue()) { // Don't need to process the false branch. cond->thenBlock()->traverse(rv); + currentThenFrame()->isParamTrue = true; return false; } else if (condRE.type().isParamFalse()) { if (auto elseBlock = cond->elseBlock()) { elseBlock->traverse(rv); + currentElseFrame()->isParamTrue = true; } return false; } diff --git a/frontend/lib/resolution/VarScopeVisitor.h b/frontend/lib/resolution/VarScopeVisitor.h index acca9343b46c..92a1880f3d36 100644 --- a/frontend/lib/resolution/VarScopeVisitor.h +++ b/frontend/lib/resolution/VarScopeVisitor.h @@ -250,6 +250,7 @@ struct VarFrame { // has the block already encountered a return or a throw? bool returnsOrThrows = false; + bool isParamTrue = false; // When processing a conditional or catch blocks, // instead of popping the SplitInitFrame for the then/else/catch blocks, // store them here, for use in handleExitScope(Conditional or Try). diff --git a/frontend/lib/resolution/split-init.cpp b/frontend/lib/resolution/split-init.cpp index 6fb9b72121b5..a3af44f61f8c 100644 --- a/frontend/lib/resolution/split-init.cpp +++ b/frontend/lib/resolution/split-init.cpp @@ -254,7 +254,8 @@ void FindSplitInits::handleConditional(const Conditional* cond, RV& rv) { if (elseFrame->eligibleVars.count(id) > 0) { // variable declared in this scope, so save the result allSplitInitedVars.insert(id); - } else if (thenFrame->mentionedVars.count(id) == 0) { + } else if (thenFrame == nullptr || + thenFrame->mentionedVars.count(id) == 0) { // variable inited in 'else' and not mentioned in 'then' locInitedVars.insert(id); } @@ -276,15 +277,13 @@ void FindSplitInits::handleConditional(const Conditional* cond, RV& rv) { // split init is OK if: // both then & else blocks initialize it before mentioning it // one initializes it and the other returns + // one initializes and its statically known to be the only path for (const auto& id : locInitedVars) { + bool thenInits = false; - if (thenFrame) { - thenInits = thenFrame->initedVars.count(id) > 0; - } + thenInits = thenFrame && thenFrame->initedVars.count(id) > 0; bool elseInits = false; - if (elseFrame) { - elseInits = elseFrame->initedVars.count(id) > 0; - } + elseInits = elseFrame && elseFrame->initedVars.count(id) > 0; if (thenInits && elseInits) { locSplitInitedVars.insert(id); @@ -292,6 +291,9 @@ void FindSplitInits::handleConditional(const Conditional* cond, RV& rv) { (thenReturnsThrows && elseInits)) { // one branch returns or throws and the other inits locSplitInitedVars.insert(id); + } else if ((thenInits && thenFrame && thenFrame->isParamTrue) || + (elseInits && elseFrame && elseFrame->isParamTrue)) { + locSplitInitedVars.insert(id); } else { frame->mentionedVars.insert(id); } diff --git a/frontend/test/resolution/testSplitInit.cpp b/frontend/test/resolution/testSplitInit.cpp index 8eefb7a738c0..fee5cf3f920f 100644 --- a/frontend/test/resolution/testSplitInit.cpp +++ b/frontend/test/resolution/testSplitInit.cpp @@ -1335,7 +1335,256 @@ static void test53() { {}); } +static void test54() { + testSplitInit("test54", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + var x; + if true then + return; + x = 11; + } + } + )"""", + {}); +} + +static void test55() { + testSplitInit("test55", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + var x; + if false then + return; + x = 11; + } + } + )"""", + {"x"}); +} + +static void test56() { + testSplitInit("test56", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + type T = int; + var x; + if T == int then + return; + x = 11; + } + } + )"""", + {}); +} + +static void test57() { + testSplitInit("test57", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + var x; + if false then + return; + x = 11; + } + } + )"""", + {"x"}); +} + + +static void test58() { + testSplitInit("test58", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + var x; + if true then + x = 2; + else + var y = 0; + x = 3; + return; + } + } + )"""", + {"x"}); +} + +static void test59() { + testSplitInit("test59", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + var x; + if false then + x = 2; + else + var y = 0; + x = 3; + return; + } + } + )"""", + {"x"}); +} + +static void test60() { + testSplitInit("test60", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + config const cond: bool = true; + proc test() { + var x; + if cond then + x = 2; + else + var y = 0; + x = 3; + return; + } + } + )"""", + {}); +} + +static void test61() { + testSplitInit("test61", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + config const cond: bool = true; + proc test() { + var x; + if false then + var y = 0; + else + x=2; + x = 3; + return; + } + } + )"""", + {"x"}); +} +static void test62() { + testSplitInit("test62", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + type T = int; + var x; + select T { + when int { + x = 11; + } + when real { + return; + } + } + } + } + )"""", + {"x"}); +} + +static void test63() { + testSplitInit("test63", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + proc test() { + type T = real; + var x; + select T { + when int { + x = 11; + } + when real { + return; + } + } + } + } + )"""", + {}); +} +static void test64() { + testSplitInit("test64", + R""""( + module M { + // this would be in the standard library... + operator =(ref lhs: int, rhs: int) { + __primitive("=", lhs, rhs); + } + + proc test() { + type T = real; + var x; + select T { + when int { + x = 11; + } + when real { + writeln(""); + } + } + x = 12; + } + } + )"""", + {"x"}); +} int main() { test1(); test2(); @@ -1392,6 +1641,16 @@ int main() { test51(); test52(); test53(); - + test54(); + test55(); + test56(); + test57(); + test58(); + test59(); + test60(); + test61(); + test62(); + test63(); + test64(); return 0; }