diff --git a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs index 9c4002fa..ae2a6075 100644 --- a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs +++ b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs @@ -335,6 +335,9 @@ public void ImplicitReturnDisallowedNonMain() => DoesNotCompile(@"int foo() int unused; }", "Function foo has no return statement."); + [Fact] + public Task ReturnInsideInfiniteForTest() => DoTest("int test() { for(;;) { return; } }"); + [Fact] public Task ConsumeUnusedResult() => DoTest(@" int test () { return 1; } diff --git a/Cesium.CodeGen.Tests/verified/CodeGenLabelStatementTests.GotoBackwardsDeclaration.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenLabelStatementTests.GotoBackwardsDeclaration.verified.txt index f4ec6331..eabe9424 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenLabelStatementTests.GotoBackwardsDeclaration.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenLabelStatementTests.GotoBackwardsDeclaration.verified.txt @@ -1,4 +1,4 @@ -System.Int32 ::main() +System.Int32 ::main() Locals: System.Int32 V_0 IL_0000: nop @@ -7,6 +7,8 @@ IL_0003: add IL_0004: stloc.0 IL_0005: br IL_0000 + IL_000a: ldc.i4.0 + IL_000b: ret System.Int32 ::() Locals: diff --git a/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt new file mode 100644 index 00000000..bcd35cc6 --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenMethodTests.ReturnInsideInfiniteForTest.verified.txt @@ -0,0 +1,6 @@ +System.Int32 ::test() + IL_0000: nop + IL_0001: ret + IL_0002: nop + IL_0003: br IL_0000 + IL_0008: nop diff --git a/Cesium.CodeGen/Cesium.CodeGen.csproj b/Cesium.CodeGen/Cesium.CodeGen.csproj index f1c067c7..9adc88e7 100644 --- a/Cesium.CodeGen/Cesium.CodeGen.csproj +++ b/Cesium.CodeGen/Cesium.CodeGen.csproj @@ -9,6 +9,7 @@ + diff --git a/Cesium.CodeGen/Contexts/AssemblyContext.cs b/Cesium.CodeGen/Contexts/AssemblyContext.cs index 39b5951e..7fb74ace 100644 --- a/Cesium.CodeGen/Contexts/AssemblyContext.cs +++ b/Cesium.CodeGen/Contexts/AssemblyContext.cs @@ -50,7 +50,7 @@ public void EmitTranslationUnit(string name, TranslationUnit translationUnit) var nodes = translationUnit.ToIntermediate(); var context = new TranslationUnitContext(this, name); var scope = context.GetInitializerScope(); - nodes = nodes.Select(node => BlockItemLowering.Lower(scope, node)).ToList(); + nodes = nodes.Select(node => BlockItemLowering.LowerDeclaration(scope, node)).ToList(); foreach (var node in nodes) BlockItemEmitting.EmitCode(scope, node); } diff --git a/Cesium.CodeGen/Contexts/BlockScope.cs b/Cesium.CodeGen/Contexts/BlockScope.cs index 7dd05baa..d6341f99 100644 --- a/Cesium.CodeGen/Contexts/BlockScope.cs +++ b/Cesium.CodeGen/Contexts/BlockScope.cs @@ -111,4 +111,16 @@ public void PushPragma(IPragma blockItem) { } /// public void RemovePragma(Predicate predicate) where T : IPragma { } + + public void MergeScope(BlockScope scope) + { + foreach (var (variableName, variable) in scope._variables) + { + _variables.Add(variableName, variable); + } + foreach (var (variableName, variableDefinition) in scope._variableDefinition) + { + _variableDefinition.Add(variableName, variableDefinition); + } + } } diff --git a/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs b/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs index e8cd3bec..8a46f03a 100644 --- a/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs +++ b/Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs @@ -89,7 +89,7 @@ public void EmitCode(IEmitScope scope) assembly.EntryPoint = entryPoint; } - EmitCode(context, functionScope); + EmitCode(functionScope); } /// @@ -265,11 +265,10 @@ private static MethodDefinition GenerateSyntheticEntryPointSimple( return syntheticEntrypoint; } - private void EmitCode(TranslationUnitContext context, FunctionScope scope) + private void EmitCode(FunctionScope scope) { - var loweredStmt = (CompoundStatement) BlockItemLowering.Lower(scope, Statement); + var loweredStmt = BlockItemLowering.LowerBody(scope, Statement); var transformed = ControlFlowChecker.CheckAndTransformControlFlow( - context, scope, loweredStmt, FunctionType.ReturnType, diff --git a/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs b/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs index 2c6a8e57..4680863e 100644 --- a/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs +++ b/Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs @@ -15,6 +15,7 @@ class CodeBlockVertex { public IBlockItem? BlockItem { get; } public bool Terminator { get; } + public bool? Reached { get; set; } public CodeBlockVertex(IBlockItem? blockItem, bool terminator = false) { @@ -123,6 +124,27 @@ private static (CodeBlockVertex, List) AddStatementToGraph(IMut } } + private static void AnalyzeReachability(IMutableVertexAndEdgeSet graph) + { + var start = graph.Vertices.First(); + AnalyzeReachability(graph, start); + foreach (var item in graph.Vertices) + { + if (item.Reached == null) + item.Reached = false; + } + } + + private static void AnalyzeReachability(IMutableVertexAndEdgeSet graph, CodeBlockVertex vertex) + { + if (vertex.Reached != null) return; + vertex.Reached = true; + foreach (var node in graph.Edges.Where(x => x.Source == vertex).Select(x => x.Target)) + { + AnalyzeReachability(graph, node); + } + } + private static CodeBlockVertex FillGraph(IMutableVertexAndEdgeSet graph, CompoundStatement statement) { var start = new CodeBlockVertex(null); @@ -274,7 +296,6 @@ private static (bool Success, IBlockItem Result) InsertSyntheticReturn(IBlockIte } public static IBlockItem CheckAndTransformControlFlow( - TranslationUnitContext context, FunctionScope scope, CompoundStatement block, IType returnType, @@ -283,18 +304,20 @@ bool isMain { var dset = new AdjacencyGraph(); var terminator = FillGraph(dset, block); + AnalyzeReachability(dset); var preTerminators = dset.Edges.Where(x => x.Target == terminator).Select(x => x.Source).ToArray(); if (preTerminators.Length == 0) // log Console.WriteLine("Code does not terminate"); - var isVoidFn = returnType.Resolve(context) == context.TypeSystem.Void; + var isVoidFn = returnType.Equals(CTypeSystem.Void); var isReturnRequired = !isVoidFn && !isMain; foreach (var preTerminator in preTerminators) { if (preTerminator.BlockItem is ReturnStatement) continue; + if (preTerminator.Reached == false) continue; if (isReturnRequired) { @@ -324,6 +347,12 @@ bool isMain } } + if (preTerminators.All(pt => pt.Reached == false)) + { + var retn = new ReturnStatement(isMain ? new ConstantLiteralExpression(new IntegerConstant(0)) : null); + block.Statements.Add(retn); + } + return block; } } diff --git a/Cesium.CodeGen/Ir/Lowering/BlockItemLowering.cs b/Cesium.CodeGen/Ir/Lowering/BlockItemLowering.cs index 230659d8..5705a7bb 100644 --- a/Cesium.CodeGen/Ir/Lowering/BlockItemLowering.cs +++ b/Cesium.CodeGen/Ir/Lowering/BlockItemLowering.cs @@ -58,7 +58,64 @@ private static IBlockItem MakeLoop( return Lower(scope, new CompoundStatement(stmts, scope)); } - public static IBlockItem Lower(IDeclarationScope scope, IBlockItem blockItem) + public static CompoundStatement LowerBody(FunctionScope scope, IBlockItem blockItem) + { + CompoundStatement compoundStatement = (CompoundStatement)Lower(scope, blockItem); + var blockScope = new BlockScope(scope, BreakLabel: null, ContinueLabel: null); + var linearizedStatement = new CompoundStatement(Linearize(compoundStatement, blockScope).ToList(), blockScope); + return linearizedStatement; + } + + public static IBlockItem LowerDeclaration(IDeclarationScope scope, IBlockItem blockItem) + { + return Lower(scope, blockItem); + } + + private static IEnumerable Linearize(CompoundStatement compoundStatement, BlockScope scope) + { + Debug.Assert(compoundStatement.EmitScope != null); + BlockScope currentScope = (BlockScope)compoundStatement.EmitScope; + scope.MergeScope(currentScope); + foreach (var statement in compoundStatement.Statements) + { + if (statement is CompoundStatement nestedCompound) + { + foreach (var nestedStatement in Linearize(nestedCompound, scope)) + { + yield return nestedStatement; + } + } + else if (statement is LabelStatement labelStatement) + { + if (labelStatement.Expression is CompoundStatement nestedLabeledCompound) + { + bool first = true; + foreach (var nestedStatement in Linearize(nestedLabeledCompound, scope)) + { + if (first) + { + first = false; + yield return new LabelStatement(labelStatement.Identifier, nestedStatement, true); + } + else + { + yield return nestedStatement; + } + } + } + else + { + yield return statement; + } + } + else + { + yield return statement; + } + } + } + + private static IBlockItem Lower(IDeclarationScope scope, IBlockItem blockItem) { switch (blockItem) { diff --git a/Directory.Packages.props b/Directory.Packages.props index 2779af0a..1bae248f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,6 +12,7 @@ +