Skip to content

Commit

Permalink
Infinite loop check for return
Browse files Browse the repository at this point in the history
Current Cesium think that this loop is never finished.

```
int test() {
  for(;;) {
     return 5;
  }
}
```
  • Loading branch information
kant2002 committed Jul 26, 2024
1 parent 25729c7 commit 773b55c
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 9 deletions.
3 changes: 3 additions & 0 deletions Cesium.CodeGen.Tests/CodeGenMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
System.Int32 <Module>::main()
System.Int32 <Module>::main()
Locals:
System.Int32 V_0
IL_0000: nop
Expand All @@ -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 <Module>::<SyntheticEntrypoint>()
Locals:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
System.Int32 <Module>::test()
IL_0000: nop
IL_0001: ret
IL_0002: nop
IL_0003: br IL_0000
IL_0008: nop
1 change: 1 addition & 0 deletions Cesium.CodeGen/Cesium.CodeGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Mono.Cecil" />
<PackageReference Include="QuikGraph" />
<PackageReference Include="QuikGraph.Graphviz" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Cesium.CodeGen/Contexts/AssemblyContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
12 changes: 12 additions & 0 deletions Cesium.CodeGen/Contexts/BlockScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,16 @@ public void PushPragma(IPragma blockItem) { }

/// <inheritdoc />
public void RemovePragma<T>(Predicate<T> 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);
}
}
}
7 changes: 3 additions & 4 deletions Cesium.CodeGen/Ir/BlockItems/FunctionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void EmitCode(IEmitScope scope)
assembly.EntryPoint = entryPoint;
}

EmitCode(context, functionScope);
EmitCode(functionScope);
}

/// <summary>
Expand Down Expand Up @@ -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,
Expand Down
33 changes: 31 additions & 2 deletions Cesium.CodeGen/Ir/ControlFlow/ControlFlowChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -123,6 +124,27 @@ private static (CodeBlockVertex, List<CodeBlockVertex>) AddStatementToGraph(IMut
}
}

private static void AnalyzeReachability(IMutableVertexAndEdgeSet<CodeBlockVertex, CodeBlockEdge> 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<CodeBlockVertex, CodeBlockEdge> 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<CodeBlockVertex, CodeBlockEdge> graph, CompoundStatement statement)
{
var start = new CodeBlockVertex(null);
Expand Down Expand Up @@ -274,7 +296,6 @@ private static (bool Success, IBlockItem Result) InsertSyntheticReturn(IBlockIte
}

public static IBlockItem CheckAndTransformControlFlow(
TranslationUnitContext context,
FunctionScope scope,
CompoundStatement block,
IType returnType,
Expand All @@ -283,18 +304,20 @@ bool isMain
{
var dset = new AdjacencyGraph<CodeBlockVertex, CodeBlockEdge>();
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)
{
Expand Down Expand Up @@ -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;
}
}
59 changes: 58 additions & 1 deletion Cesium.CodeGen/Ir/Lowering/BlockItemLowering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IBlockItem> 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)
{
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageVersion Include="Mono.Cecil" Version="0.11.4" />
<PackageVersion Include="QuikGraph" Version="2.5.0" />
<PackageVersion Include="QuikGraph.Graphviz" Version="2.5.0" />
<PackageVersion Include="Verify.Xunit" Version="16.3.5" />
<PackageVersion Include="xunit" Version="2.4.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.3" />
Expand Down

0 comments on commit 773b55c

Please sign in to comment.