From 19f9e97d44bcda34971dd7c1e515a16d44027049 Mon Sep 17 00:00:00 2001 From: Aman Goel Date: Mon, 27 Nov 2023 11:16:10 -0800 Subject: [PATCH] [PCompiler] Correct foreach -> while transformation (#684) * [PCompiler] Correct foreach -> while transformation to account for continue in loop body * [Tst] Adds more regression tests for foreach with break/continue * [Tst] Minor commments * [Tst] Exclude unsupported tests from PSym regression suite --- .../Backend/CSharp/CSharpCodeGenerator.cs | 2 +- .../CompilerCore/Backend/IRTransformer.cs | 34 ++++++++++-------- .../java/psym/TestSymbolicRegression.java | 4 +++ .../Feature2Stmts/Correct/foreach2/foreach2.p | 32 +++++++++++++++++ .../Feature2Stmts/Correct/foreach3/foreach3.p | 33 +++++++++++++++++ .../Feature2Stmts/Correct/foreach4/foreach4.p | 36 +++++++++++++++++++ .../DynamicError/foreach2/foreach2.p | 32 +++++++++++++++++ .../DynamicError/foreach3/foreach3.p | 33 +++++++++++++++++ .../DynamicError/foreach4/foreach4.p | 36 +++++++++++++++++++ 9 files changed, 226 insertions(+), 16 deletions(-) create mode 100644 Tst/RegressionTests/Feature2Stmts/Correct/foreach2/foreach2.p create mode 100644 Tst/RegressionTests/Feature2Stmts/Correct/foreach3/foreach3.p create mode 100644 Tst/RegressionTests/Feature2Stmts/Correct/foreach4/foreach4.p create mode 100644 Tst/RegressionTests/Feature2Stmts/DynamicError/foreach2/foreach2.p create mode 100644 Tst/RegressionTests/Feature2Stmts/DynamicError/foreach3/foreach3.p create mode 100644 Tst/RegressionTests/Feature2Stmts/DynamicError/foreach4/foreach4.p diff --git a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs index ec4f062ba..e615fbca7 100644 --- a/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs +++ b/Src/PCompiler/CompilerCore/Backend/CSharp/CSharpCodeGenerator.cs @@ -1435,7 +1435,7 @@ private void WriteExpr(CompilationContext context, StringWriter output, IPExpr p break; case IntLiteralExpr intLiteralExpr: - context.Write(output, $"((PrtInt){intLiteralExpr.Value})"); + context.Write(output, $"((PrtInt)({intLiteralExpr.Value}))"); break; case KeysExpr keysExpr: diff --git a/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs b/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs index f1820708c..e8599f1cb 100644 --- a/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs +++ b/Src/PCompiler/CompilerCore/Backend/IRTransformer.cs @@ -581,12 +581,12 @@ private IPStmt SimplifyForeachStmt(ForeachStmt foreachStmt) // collectionCopy = collection; // var i: int; // var sizeof: int; - // i = 0; + // i = -1; // sizeof = sizeof(collectionCopy); - // while(i < sizeof) { + // while(i < (sizeof - 1)) { + // i = i + 1; // item = collectionCopy[i]; // body; - // i = i + 1; // } var location = foreachStmt.SourceLocation; @@ -608,14 +608,25 @@ private IPStmt SimplifyForeachStmt(ForeachStmt foreachStmt) sizeVar.Type = PrimitiveType.Int; function.AddLocalVariable(sizeVar); - // i = 0; - newBody.Add(new AssignStmt(location, new VariableAccessExpr(location, iVar), new IntLiteralExpr(location, 0))); + // i = -1; + newBody.Add(new AssignStmt(location, new VariableAccessExpr(location, iVar), new IntLiteralExpr(location, -1))); // sizeof = sizeof(collection) newBody.Add(new AssignStmt(location, new VariableAccessExpr(location, sizeVar), new SizeofExpr(location, collectionCopy))); - // while(i < sizeof) - IPExpr cond = new BinOpExpr(location, BinOpType.Lt, new VariableAccessExpr(location, iVar), new VariableAccessExpr(location, sizeVar)); + // while(i < (sizeof - 1)) + IPExpr cond = new BinOpExpr(location, BinOpType.Lt, + new VariableAccessExpr(location, iVar), + new BinOpExpr(location, BinOpType.Sub, + new VariableAccessExpr(location, sizeVar), + new IntLiteralExpr(location, 1))); + + // inside loop: i = i+1; + IPStmt incrementI = new AssignStmt(location, new VariableAccessExpr(location, iVar), + new BinOpExpr(location, + BinOpType.Add, + new VariableAccessExpr(location, iVar), + new IntLiteralExpr(location, 1))); // inside loop: item = collection[i] IPExpr accessExpr; @@ -634,14 +645,7 @@ private IPStmt SimplifyForeachStmt(ForeachStmt foreachStmt) } IPStmt assignItem = new AssignStmt(location, new VariableAccessExpr(location, item), accessExpr); - // inside loop: i = i+1; - IPStmt incrementI = new AssignStmt(location, new VariableAccessExpr(location, iVar), - new BinOpExpr(location, - BinOpType.Add, - new VariableAccessExpr(location, iVar), - new IntLiteralExpr(location, 1))); - - newBody.Add(new WhileStmt(location, cond, new CompoundStmt(location, new List{ assignItem, body, incrementI }))); + newBody.Add(new WhileStmt(location, cond, new CompoundStmt(location, new List{ incrementI, assignItem, body }))); return new CompoundStmt(location, newBody); } diff --git a/Src/PRuntimes/PSymRuntime/src/test/java/psym/TestSymbolicRegression.java b/Src/PRuntimes/PSymRuntime/src/test/java/psym/TestSymbolicRegression.java index 6062092ae..1b7761a13 100644 --- a/Src/PRuntimes/PSymRuntime/src/test/java/psym/TestSymbolicRegression.java +++ b/Src/PRuntimes/PSymRuntime/src/test/java/psym/TestSymbolicRegression.java @@ -80,6 +80,10 @@ private static void createExcludeList() { excluded.add("../../../Tst/RegressionTests/Feature2Stmts/DynamicError/receive7"); // TODO Unsupported: continue statement + excluded.add("../../../Tst/RegressionTests/Feature2Stmts/Correct/foreach2"); + excluded.add("../../../Tst/RegressionTests/Feature2Stmts/Correct/foreach4"); + excluded.add("../../../Tst/RegressionTests/Feature2Stmts/DynamicError/foreach2"); + excluded.add("../../../Tst/RegressionTests/Feature2Stmts/DynamicError/foreach4"); excluded.add("../../../Tst/RegressionTests/Feature2Stmts/DynamicError/continue1"); // TODO Unsupported: receive in state exit functions diff --git a/Tst/RegressionTests/Feature2Stmts/Correct/foreach2/foreach2.p b/Tst/RegressionTests/Feature2Stmts/Correct/foreach2/foreach2.p new file mode 100644 index 000000000..a387ef2e6 --- /dev/null +++ b/Tst/RegressionTests/Feature2Stmts/Correct/foreach2/foreach2.p @@ -0,0 +1,32 @@ +/******************** + * This example explains the usage of foreach iterator with continue + * ******************/ + + + machine Main { + var ss: set[int]; + + start state Init { + entry { + var iter: int; + var sum: int; + ss += (100); + ss += (134); + ss += (245); + + foreach(iter in ss) + { + print format ("Iter = {0}, Sum = {1}", iter, sum); + assert sum <= 345, "Incorrect sum inside loop"; + if (iter == 134) { + continue; + } + sum = sum + iter; + } + + print format ("Final Sum = {0}", sum); + assert sum == 345, "Incorrect sum outside loop"; + } + } + } + diff --git a/Tst/RegressionTests/Feature2Stmts/Correct/foreach3/foreach3.p b/Tst/RegressionTests/Feature2Stmts/Correct/foreach3/foreach3.p new file mode 100644 index 000000000..1c292b3ac --- /dev/null +++ b/Tst/RegressionTests/Feature2Stmts/Correct/foreach3/foreach3.p @@ -0,0 +1,33 @@ +/******************** + * This example explains the usage of foreach iterator with break + * ******************/ + + + machine Main { + var ss: set[int]; + + start state Init { + entry { + var iter: int; + var sum: int; + ss += (100); + ss += (123); + ss += (134); + ss += (245); + + foreach(iter in ss) + { + print format ("Iter = {0}, Sum = {1}", iter, sum); + assert sum <= 100, "Incorrect sum inside loop"; + sum = sum + iter; + if (iter == 123) { + break; + } + } + + print format ("Final Sum = {0}", sum); + assert sum == 223, "Incorrect sum outside loop"; + } + } + } + diff --git a/Tst/RegressionTests/Feature2Stmts/Correct/foreach4/foreach4.p b/Tst/RegressionTests/Feature2Stmts/Correct/foreach4/foreach4.p new file mode 100644 index 000000000..8eb3f9d43 --- /dev/null +++ b/Tst/RegressionTests/Feature2Stmts/Correct/foreach4/foreach4.p @@ -0,0 +1,36 @@ +/******************** + * This example explains the usage of foreach iterator with break and continue + * ******************/ + + + machine Main { + var ss: set[int]; + + start state Init { + entry { + var iter: int; + var sum: int; + ss += (100); + ss += (134); + ss += (123); + ss += (245); + + foreach(iter in ss) + { + print format ("Iter = {0}, Sum = {1}", iter, sum); + assert sum <= 100, "Incorrect sum inside loop"; + if (iter == 134) { + continue; + } + sum = sum + iter; + if (iter == 123) { + break; + } + } + + print format ("Final Sum = {0}", sum); + assert sum == 223, "Incorrect sum outside loop"; + } + } + } + diff --git a/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach2/foreach2.p b/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach2/foreach2.p new file mode 100644 index 000000000..6192bf25a --- /dev/null +++ b/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach2/foreach2.p @@ -0,0 +1,32 @@ +/******************** + * This example explains the usage of foreach iterator with continue + * ******************/ + + + machine Main { + var ss: set[int]; + + start state Init { + entry { + var iter: int; + var sum: int; + ss += (100); + ss += (134); + ss += (245); + + foreach(iter in ss) + { + print format ("Iter = {0}, Sum = {1}", iter, sum); + assert sum <= 345, "Incorrect sum inside loop"; + if (iter == 134) { + continue; + } + sum = sum + iter; + } + + print format ("Final Sum = {0}", sum); + assert sum != 345, "Should get triggered"; + } + } + } + diff --git a/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach3/foreach3.p b/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach3/foreach3.p new file mode 100644 index 000000000..19dfa7ab8 --- /dev/null +++ b/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach3/foreach3.p @@ -0,0 +1,33 @@ +/******************** + * This example explains the usage of foreach iterator with break + * ******************/ + + + machine Main { + var ss: set[int]; + + start state Init { + entry { + var iter: int; + var sum: int; + ss += (100); + ss += (123); + ss += (134); + ss += (245); + + foreach(iter in ss) + { + print format ("Iter = {0}, Sum = {1}", iter, sum); + assert sum <= 100, "Incorrect sum inside loop"; + sum = sum + iter; + if (iter == 123) { + break; + } + } + + print format ("Final Sum = {0}", sum); + assert sum != 223, "Should get triggered"; + } + } + } + diff --git a/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach4/foreach4.p b/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach4/foreach4.p new file mode 100644 index 000000000..3c3a2395b --- /dev/null +++ b/Tst/RegressionTests/Feature2Stmts/DynamicError/foreach4/foreach4.p @@ -0,0 +1,36 @@ +/******************** + * This example explains the usage of foreach iterator with break and continue + * ******************/ + + + machine Main { + var ss: set[int]; + + start state Init { + entry { + var iter: int; + var sum: int; + ss += (100); + ss += (134); + ss += (123); + ss += (245); + + foreach(iter in ss) + { + print format ("Iter = {0}, Sum = {1}", iter, sum); + assert sum <= 100, "Incorrect sum inside loop"; + if (iter == 134) { + continue; + } + sum = sum + iter; + if (iter == 123) { + break; + } + } + + print format ("Final Sum = {0}", sum); + assert sum != 223, "Should get triggered"; + } + } + } +