Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,38 @@ public interface IStructValueLocation : IValueLocation
public IValueLocation GetPrimitiveField(int fieldOffset);
public IStructValueLocation GetField(int fieldOffset, int fieldSize);
}

public class DereferencedLocation : IValueLocation
{
private readonly IValueLocation _location;

public DereferencedLocation(IValueLocation location)
{
_location = location;
}

public IEnumerable<CodeTreeNode> GenerateValueWrite(CodeTreeValueNode value)
{
// StructValueLocation is a pointer to the first field in a struct.
// No need to do a dereference in this case.
if (_location is StructValueLocation)
{
return _location.GenerateValueWrite(value);
}
return new MemoryWrite(_location.GenerateValueRead(), value).Enumerate();
}

public CodeTreeValueNode GenerateValueRead()
{
// StructValueLocation is a pointer to the first field in a struct.
// No need to do a dereference in this case.
if (_location is StructValueLocation)
{
return _location.GenerateValueRead();
}
return new MemoryRead(_location.GenerateValueRead());
}
}

public class VariableValueLocation : IValueLocation
{
Expand All @@ -44,14 +76,12 @@ public class StructValueLocation : IStructValueLocation
private class PrimitiveFieldLocation : IValueLocation
{
private readonly int _offset;
private readonly IFunctionContext _functionContext;
private readonly IFunctionVariable _temp;
private readonly IValueLocation _location;

public PrimitiveFieldLocation(IFunctionContext functionContext, IFunctionVariable temp, int offset)
public PrimitiveFieldLocation(IValueLocation location, int offset)
{
_offset = offset;
_functionContext = functionContext;
_temp = temp;
_location = location;
}

public IEnumerable<CodeTreeNode> GenerateValueWrite(CodeTreeValueNode value)
Expand All @@ -64,26 +94,23 @@ public CodeTreeValueNode GenerateValueRead()
return CodeTreeExtensions.Mem(StackLocationAddress).Read();
}

private CodeTreeValueNode StackLocationAddress => _functionContext.GenerateVariableRead(_temp) + _offset;
private CodeTreeValueNode StackLocationAddress => _location.GenerateValueRead() + _offset;
}

private readonly IFunctionContext _functionContext;
private readonly int _size;
private readonly int _offset;
private readonly IFunctionVariable _temp;
private readonly IValueLocation _location;

public StructValueLocation(IFunctionContext functionContext, IFunctionVariable temp, int size)
public StructValueLocation(IValueLocation location, int size)
{
_functionContext = functionContext;
_location = location;
_size = size;
_offset = 0;
_temp = temp;
}

private StructValueLocation(IFunctionContext functionContext, IFunctionVariable temp, int size, int offset)
private StructValueLocation(IValueLocation location, int size, int offset)
{
_functionContext = functionContext;
_temp = temp;
_location = location;
_size = size;
_offset = offset;
}
Expand All @@ -95,7 +122,7 @@ public IValueLocation GetPrimitiveField(int fieldOffset)
throw new NotSupportedException();
}

return new PrimitiveFieldLocation(_functionContext, _temp, _offset + fieldOffset);
return new PrimitiveFieldLocation(_location, _offset + fieldOffset);
}

public IStructValueLocation GetField(int fieldOffset, int fieldSize)
Expand All @@ -105,7 +132,7 @@ public IStructValueLocation GetField(int fieldOffset, int fieldSize)
throw new NotSupportedException();
}

return new StructValueLocation(_functionContext, _temp, fieldSize, _offset + fieldOffset);
return new StructValueLocation(_location, fieldSize, _offset + fieldOffset);
}

public IEnumerable<CodeTreeNode> GenerateValueWrite(CodeTreeValueNode value)
Expand All @@ -118,6 +145,6 @@ public CodeTreeValueNode GenerateValueRead()
return StackLocationAddress;
}

private CodeTreeValueNode StackLocationAddress => _functionContext.GenerateVariableRead(_temp) + _offset;
private CodeTreeValueNode StackLocationAddress => _location.GenerateValueRead() + _offset;
}
}
89 changes: 71 additions & 18 deletions src/sernick/Ast/Analysis/ControlFlowGraph/ControlFlowAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public StructValueLocation NewStructVariable(int size)
{
var temp = new TemporaryVariable();
_functionContext.AddLocal(temp, isStruct: true, size: size);
return new StructValueLocation(_functionContext, temp, size);
return new StructValueLocation(new VariableValueLocation(_functionContext, temp), size);
}
}

Expand Down Expand Up @@ -367,7 +367,7 @@ public override CodeTreeRoot VisitVariableDeclaration(VariableDeclaration node,
}

return node.InitValue.Accept(this,
param with { ResultVariable = new StructValueLocation(_currentFunctionContext, node, _structHelper.GetStructTypeSize(structType)) });
param with { ResultVariable = new StructValueLocation(new VariableValueLocation(_currentFunctionContext, node), _structHelper.GetStructTypeSize(structType)) });
}

public override CodeTreeRoot VisitAssignment(Assignment node, ControlFlowVisitorParam param)
Expand All @@ -377,7 +377,7 @@ public override CodeTreeRoot VisitAssignment(Assignment node, ControlFlowVisitor
return _pullOutSideEffects(node, param.Next, param.ResultVariable);
}

// there are two cases of lvalues:
// there are tree cases of lvalues:
switch (node.Left)
{
case VariableValue variableAccess:
Expand All @@ -387,6 +387,14 @@ public override CodeTreeRoot VisitAssignment(Assignment node, ControlFlowVisitor
return node.Right.Accept(this,
param with { ResultVariable = new VariableValueLocation(_currentFunctionContext, variable) });
}
case PointerDereference dereference:
{
// assigment to a de-referenced location
var pointerLocation = _variableFactory.NewPrimitiveVariable();
var dereferencedLocation = new DereferencedLocation(pointerLocation);
var assigment = node.Right.Accept(this, param with { ResultVariable = dereferencedLocation });
return dereference.Accept(this, param with { Next = assigment, ResultVariable = pointerLocation });
}
case StructFieldAccess fieldAccess:
{
// field access from struct variable
Expand All @@ -401,26 +409,30 @@ public override CodeTreeRoot VisitAssignment(Assignment node, ControlFlowVisitor

// adds the rhs of field access to `fieldPath`,
// and continues inspecting the lhs.
while (structFieldAccessNode is not VariableValue)
while (structFieldAccessNode is StructFieldAccess access)
{
// we only accept accesses of kind `variable.field.field.(...).field`
if (structFieldAccessNode is not StructFieldAccess access)
var lhsType = _typeChecking[access.Left] switch
{
break;
}

// NOTE: in the future, support a case `[pointer-typed expr].field.(...).field`

var lhsType = (StructType)_typeChecking[access.Left];
StructType lhsStruct => lhsStruct,
PointerType { Type: StructType lhsStruct } => lhsStruct, // we support automatic dereference
_ => throw new NotSupportedException($"Invalid lvalue in assignment {node}")
};
fieldPath.Add(_structHelper.GetStructFieldDeclaration(lhsType, access.FieldName));
structFieldAccessNode = access.Left;
}

fieldPath.Reverse();

var accessedVariable = _nameResolution.UsedVariableDeclarations[(VariableValue)structFieldAccessNode];
IValueLocation structLocation = structFieldAccessNode switch
{
PointerDereference => _variableFactory.NewPrimitiveVariable(),
VariableValue variableValue => new VariableValueLocation(_currentFunctionContext,
_nameResolution.UsedVariableDeclarations[variableValue]),
_ => throw new NotSupportedException()
};

IStructValueLocation variableLocation =
new StructValueLocation(_currentFunctionContext, accessedVariable, _structProperties.StructSizes[structType.Struct]);
new StructValueLocation(structLocation, _structProperties.StructSizes[structType.Struct]);

var isLhsPrimitiveType = _typeChecking[node.Left] is not StructType;
IValueLocation resultLocation;
Expand All @@ -444,9 +456,15 @@ public override CodeTreeRoot VisitAssignment(Assignment node, ControlFlowVisitor
);
}

return node.Right.Accept(this, param with { ResultVariable = resultLocation });
var assigment = node.Right.Accept(this, param with { ResultVariable = resultLocation });
if (structFieldAccessNode is PointerDereference pointerDereference)
{
// If structFieldAccessNode is a pointer dereference then we first need to calculate the struct location.
return pointerDereference.Accept(this,
param with { ResultVariable = structLocation, Next = assigment });
}
return assigment;
}
// NOTE: in the future, add pointer dereference cases here
}

throw new NotSupportedException($"Invalid lvalue in assignment {node}");
Expand Down Expand Up @@ -495,8 +513,43 @@ public override CodeTreeRoot VisitStructFieldAccess(StructFieldAccess node, Cont
return _pullOutSideEffects(node, param.Next, param.ResultVariable);
}

// NOTE: this won't be true, once any (pointer-to-struct) expression can be on the left
throw new NotSupportedException("StructFieldAccess cannot contain control flow");
var (leftVariable, leftVariableValueNode) = GenerateTemporaryAst(node.Left);
var fieldAccess = node with { Left = leftVariableValueNode };

// Not sure if this is correct but for sure this doesn't generate an efficient code.
IValueLocation leftVariableLocation = _typeChecking[node.Left] switch
{
PointerType => new VariableValueLocation(_currentFunctionContext, leftVariable),
StructType structType => new StructValueLocation(
new VariableValueLocation(_currentFunctionContext, leftVariable),
_structHelper.GetStructTypeSize(structType)),
_ => throw new NotSupportedException()
};

return node.Left.Accept(this,
param with
{
Next = _pullOutSideEffects(fieldAccess, param.Next, param.ResultVariable),
ResultVariable = leftVariableLocation,
}
);
}

public override CodeTreeRoot VisitPointerDereference(PointerDereference node, ControlFlowVisitorParam param)
{
if (!_nodesWithControlFlow.Contains(node))
{
return _pullOutSideEffects(node, param.Next, param.ResultVariable);
}

var (pointerVariable, pointerNode) = GenerateTemporaryAst(node.Pointer);
var dereference = node with { Pointer = pointerNode };
return node.Pointer.Accept(this,
param with
{
Next = _pullOutSideEffects(dereference, param.Next, param.ResultVariable),
ResultVariable = new VariableValueLocation(_currentFunctionContext, pointerVariable)
});
}

public override CodeTreeRoot VisitIdentifier(Identifier node, ControlFlowVisitorParam param)
Expand Down
49 changes: 42 additions & 7 deletions src/sernick/Ast/Analysis/ControlFlowGraph/SideEffectsAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,9 @@ public override List<TreeWithEffects> VisitAssignment(Assignment node, SideEffec
{
return GenerateVariableAssignmentTree(variableDeclaration, node.Right, param);
}

break;
case PointerDereference pointerDereference:

break;
}

Expand Down Expand Up @@ -397,6 +399,18 @@ public override List<TreeWithEffects> VisitIntLiteralValue(IntLiteralValue node,
)
};
}

public override List<TreeWithEffects> VisitNullPointerLiteralValue(NullPointerLiteralValue node, Unit param)
{
return new List<TreeWithEffects>
{
new (
new HashSet<VariableDeclaration>(),
new HashSet<VariableDeclaration>(),
new Constant(new RegisterValue(0))
)
};
}

public override List<TreeWithEffects> VisitEmptyExpression(EmptyExpression node, SideEffectsVisitorParam param)
{
Expand Down Expand Up @@ -441,15 +455,36 @@ public override List<TreeWithEffects> VisitStructValue(StructValue node, Unit pa
public override List<TreeWithEffects> VisitStructFieldAccess(StructFieldAccess node, Unit param)
{
var result = node.Left.Accept(this, param);
// NOTE: for pointer types, add derefs here
var structType = (StructType)_typeChecking.ExpressionsTypes[node.Left];
var field = _structHelper.GetStructFieldDeclaration(structType, node.FieldName);
if (result[^1].CodeTree is not CodeTreeValueNode value)

var leftType = _typeChecking[node.Left];

switch (leftType)
{
throw new NotSupportedException("Internal error");
case PointerType { Type: StructType structType }:
{
var field = _structHelper.GetStructFieldDeclaration(structType, node.FieldName);
if (result[^1].CodeTree is not CodeTreeValueNode value)
{
throw new NotSupportedException("Internal error");
}

// TODO: Access pointer field
break;
}
case StructType structType:
{
var field = _structHelper.GetStructFieldDeclaration(structType, node.FieldName);
if (result[^1].CodeTree is not CodeTreeValueNode value)
{
throw new NotSupportedException("Internal error");
}

result[^1] = result[^1] with { CodeTree = _structHelper.GenerateStructFieldRead(value, field) };
break;
}
default: throw new NotSupportedException("Invalid type for StructFieldAccess.");
}

result[^1] = result[^1] with { CodeTree = _structHelper.GenerateStructFieldRead(value, field) };
return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using sernick.Input.String;

namespace sernick.Ast.Analysis.VariableAccess;

using System.Diagnostics;
Expand Down Expand Up @@ -27,11 +29,26 @@ private readonly Dictionary<FunctionDefinition, Dictionary<Declaration, Variable
/// </summary>
public bool HasExclusiveWriteAccess(FunctionDefinition fun, VariableDeclaration variable) =>
_exclusiveWriteAccess.TryGetValue(variable, out var exclusiveFun) && ReferenceEquals(exclusiveFun, fun);

/// <summary>
/// Fake variable which we identify with accessing the heap memory.
/// </summary>
public static readonly VariableDeclaration HeapMemoryVariable = new (
new Identifier("heapMemory", (new StringLocation(0), new StringLocation(0))),
Type: null,
InitValue: null,
IsConst: false,
(new StringLocation(0), new StringLocation(0))
);

internal void AddFun(FunctionDefinition fun)
{
_variableAccessDictionary[fun] = new Dictionary<Declaration, VariableAccessMode>(
ReferenceEqualityComparer.Instance);
ReferenceEqualityComparer.Instance)
{
// We assume for now that every function writes and reads from heap
{HeapMemoryVariable, VariableAccessMode.WriteAndRead}
};
}

internal void AddVariableRead(FunctionDefinition fun, Declaration variable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void BinaryOperationsAreCompiledIntoBinaryOperationNodesWithVariableReads
Assert.Equal(expected, result, new CodeTreeNodeComparer());
}

[Theory(Skip = "Waiting for isomorphisms")]
[Theory]
[InlineData(Infix.Op.Plus, BinaryOperation.Add)]
[InlineData(Infix.Op.Minus, BinaryOperation.Sub)]
[InlineData(Infix.Op.Equals, BinaryOperation.Equal)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,11 @@ out _
Assert.True(variableAccessMap.HasExclusiveWriteAccess(foo, xDeclare));
Assert.True(variableAccessMap.HasExclusiveWriteAccess(foo, yDeclare));

Assert.Equal(2, variableAccessMap[foo].Count());
Assert.Equal(3, variableAccessMap[foo].Count());
Assert.Contains((xDeclare, VariableAccessMode.WriteAndRead), variableAccessMap[foo]);
Assert.Contains((yDeclare, VariableAccessMode.WriteAndRead), variableAccessMap[foo]);
Assert.Contains((VariableAccessMap.HeapMemoryVariable, VariableAccessMode.WriteAndRead),
variableAccessMap[foo]);
}

[Fact]
Expand Down