Skip to content

Commit

Permalink
(#120) CodeGen: add for scope and break statement
Browse files Browse the repository at this point in the history
  • Loading branch information
Griboedoff committed May 3, 2022
1 parent fc6060c commit f3eadeb
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 10 deletions.
25 changes: 25 additions & 0 deletions Cesium.CodeGen.Tests/CodeGenBreakStatementTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Cesium.CodeGen.Tests;

public class CodeGenBreakStatementTests : CodeGenTestBase
{
private static Task DoTest(string source)
{
var assembly = GenerateAssembly(default, source);

var moduleType = assembly.Modules.Single().GetType("<Module>");
return VerifyMethods(moduleType);
}

[Fact]
public Task BreakInFor() => DoTest(@"int main()
{
for(;;) break;
}");

[Fact]
public Task BreakNotInFor() => Assert.ThrowsAsync<InvalidOperationException>(
() => DoTest(@"int main()
{
break;
}"));
}
19 changes: 19 additions & 0 deletions Cesium.CodeGen/Contexts/ForScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Cesium.CodeGen.Contexts.Meta;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Cesium.CodeGen.Contexts;

internal record ForScope(IDeclarationScope Parent) : IDeclarationScope
{
public AssemblyContext AssemblyContext => Parent.AssemblyContext;
public ModuleDefinition Module => Parent.Module;
public TypeSystem TypeSystem => Parent.TypeSystem;
public IReadOnlyDictionary<string, FunctionInfo> Functions => Parent.Functions;
public TranslationUnitContext Context => Parent.Context;
public MethodDefinition Method => Parent.Method;
public Dictionary<string, VariableDefinition> Variables => throw new NotImplementedException("No declarations in for for now");
public ParameterDefinition? GetParameter(string name) => Parent.GetParameter(name);

public Instruction? EndInstruction { get; set; }
}
1 change: 1 addition & 0 deletions Cesium.CodeGen/Contexts/IDeclarationScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ internal interface IDeclarationScope
TranslationUnitContext Context { get; }
MethodDefinition Method { get; }
Dictionary<string, VariableDefinition> Variables { get; }
ParameterDefinition? GetParameter(string name);
}
1 change: 1 addition & 0 deletions Cesium.CodeGen/Extensions/BlockItemEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal static class BlockItemEx
Ast.ExpressionStatement s => new ExpressionStatement(s),
Ast.IfElseStatement s => new IfElseStatement(s),
Ast.ForStatement s => new ForStatement(s),
Ast.BreakStatement _ => new BreakStatement(),
_ => throw new NotImplementedException($"Statement not supported, yet: {blockItem}.")
};
}
20 changes: 20 additions & 0 deletions Cesium.CodeGen/Ir/BlockItems/BreakStatement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Cesium.CodeGen.Contexts;
using Mono.Cecil.Cil;

namespace Cesium.CodeGen.Ir.BlockItems;

internal class BreakStatement : IBlockItem
{
public IBlockItem Lower() => this;

public void EmitTo(IDeclarationScope scope)
{
if (scope is not ForScope forScope)
throw new InvalidOperationException("Can't break not from for statement");

var endInstruction = scope.Method.Body.GetILProcessor().Create(OpCodes.Nop);
scope.Method.Body.Instructions.Add(Instruction.Create(OpCodes.Br, endInstruction));

forScope.EndInstruction = endInstruction;
}
}
15 changes: 10 additions & 5 deletions Cesium.CodeGen/Ir/BlockItems/ForStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,27 @@ public IBlockItem Lower()

public void EmitTo(IDeclarationScope scope)
{
var bodyProcessor = scope.Method.Body.GetILProcessor();
var forScope = new ForScope(scope);

var bodyProcessor = forScope.Method.Body.GetILProcessor();
var instructions = bodyProcessor.Body.Instructions;
var stub = bodyProcessor.Create(OpCodes.Nop);

_initExpression?.EmitTo(scope);
_initExpression?.EmitTo(forScope);
var brToTest = bodyProcessor.Create(OpCodes.Br, stub);
bodyProcessor.Append(brToTest);
var loopStartIndex = instructions.Count;
_body.EmitTo(scope);
_updateExpression?.EmitTo(scope);
_body.EmitTo(forScope);
_updateExpression?.EmitTo(forScope);
var testStartIndex = instructions.Count;
_testExpression.EmitTo(scope);
_testExpression.EmitTo(forScope);
var testStart = instructions[testStartIndex];
brToTest.Operand = testStart;

var loopStart = instructions[loopStartIndex];
bodyProcessor.Emit(OpCodes.Brtrue, loopStart);

if (forScope.EndInstruction != null)
bodyProcessor.Append(forScope.EndInstruction);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ public IdentifierConstantExpression(Ast.ConstantExpression expression)
public ILValue Resolve(IDeclarationScope scope)
{
scope.Variables.TryGetValue(Identifier, out var var);
var par = scope switch
{
FunctionScope funcScope => funcScope.GetParameter(Identifier),
_ => throw new NotImplementedException($"Can't get parameter from {scope.GetType()}"),
};
var par = scope.GetParameter(Identifier);

switch (var, par)
{
Expand Down
12 changes: 12 additions & 0 deletions Cesium.IntegrationTests/for_with_break.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
int main(int argc, char *argv[])
{
int i;
int j = 0;
for (i = 0; i < 10000; ++i)
{
if (j == 42)
break;
++j;
}
return j;
}

0 comments on commit f3eadeb

Please sign in to comment.