Skip to content

Commit

Permalink
Allow const eval expression in array declarations
Browse files Browse the repository at this point in the history
  • Loading branch information
kant2002 committed Sep 20, 2024
1 parent eec3a19 commit b8279d0
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 41 deletions.
6 changes: 6 additions & 0 deletions Cesium.CodeGen.Tests/CodeGenArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,10 @@ int main() {
return 1;
}");

[Fact]
public Task ConstExpressionSizeArrayTest() => DoTest(@"
int main() {
const char a[1 + 1] = { 255 };
}");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
System.Int32 <Module>::main()
Locals:
System.Byte* V_0
IL_0000: ldc.i4.2
IL_0001: conv.u
IL_0002: localloc
IL_0004: stloc.0
IL_0005: ldsflda <ConstantPool>/<ConstantPoolItemType2> <ConstantPool>::ConstDataBuffer0
IL_000a: ldloc V_0
IL_000e: ldc.i4.2
IL_000f: conv.u
IL_0010: call System.Void Cesium.Runtime.RuntimeHelpers::InitializeCompound(System.Void*,System.Void*,System.UInt32)
IL_0015: ldc.i4.0
IL_0016: ret

System.Int32 <Module>::<SyntheticEntrypoint>()
Locals:
System.Int32 V_0
IL_0000: call System.Int32 <Module>::main()
IL_0005: stloc.s V_0
IL_0007: ldloc.s V_0
IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32)
IL_000e: ldloc.s V_0
IL_0010: ret
85 changes: 49 additions & 36 deletions Cesium.CodeGen/ConstantEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,84 @@
using Cesium.CodeGen.Ir.Expressions.BinaryOperators;
using Cesium.CodeGen.Ir.Expressions.Constants;
using Cesium.Core;
using System.Diagnostics;

namespace Cesium.CodeGen;

internal static class ConstantEvaluator
{
public static IConstant GetConstantValue(IExpression expression)
{
var result = TryGetConstantValue(expression);
if (result.ErrorMessage is not null)
{
throw new CompilationException(result.ErrorMessage);
}

Debug.Assert(result.Constant != null);
return result.Constant;
}

public static (string? ErrorMessage, IConstant? Constant) TryGetConstantValue(IExpression expression)
{
switch (expression)
{
case ConstantLiteralExpression literal:
return literal.Constant;
return (null, literal.Constant);

case UnaryOperatorExpression unOp:
{
var constant = GetConstantValue(unOp.Target);
{
var constant = GetConstantValue(unOp.Target);

if (constant is not IntegerConstant constInt)
throw new CompilationException("Evaluated constant is not an integer");
if (constant is not IntegerConstant constInt)
return ("Evaluated constant is not an integer", null);

return unOp.Operator switch
{
UnaryOperator.Negation => new IntegerConstant(-constInt.Value),
UnaryOperator.BitwiseNot => new IntegerConstant(~constInt.Value),
UnaryOperator.LogicalNot => new IntegerConstant(constInt.Value != 0 ? 0 : 1),
UnaryOperator.AddressOf or UnaryOperator.Indirection => throw new CompilationException($"Operator {unOp.Operator} is not compile-time evaluable"),
_ => throw new ArgumentOutOfRangeException($"Invalid unary operator {unOp.Operator}"),
UnaryOperator.Negation => (null, new IntegerConstant(-constInt.Value)),
UnaryOperator.BitwiseNot => (null, new IntegerConstant(~constInt.Value)),
UnaryOperator.LogicalNot => (null, new IntegerConstant(constInt.Value != 0 ? 0 : 1)),
UnaryOperator.AddressOf or UnaryOperator.Indirection => ($"Operator {unOp.Operator} is not compile-time evaluable", null),
_ => ($"Invalid unary operator {unOp.Operator}", null),
};
}

case BinaryOperatorExpression binOp:
{
var leftConstant = GetConstantValue(binOp.Left);
var rightConstant = GetConstantValue(binOp.Right);
{
var leftConstant = GetConstantValue(binOp.Left);
var rightConstant = GetConstantValue(binOp.Right);

if (leftConstant is not IntegerConstant leftInt ||
rightConstant is not IntegerConstant rightInt)
throw new CompilationException("Evaluated constants are not integer");
if (leftConstant is not IntegerConstant leftInt ||
rightConstant is not IntegerConstant rightInt)
throw new CompilationException("Evaluated constants are not integer");

return binOp.Operator switch
return binOp.Operator switch
{
BinaryOperator.Add => new IntegerConstant(leftInt.Value + rightInt.Value),
BinaryOperator.Subtract => new IntegerConstant(leftInt.Value - rightInt.Value),
BinaryOperator.Multiply => new IntegerConstant(leftInt.Value * rightInt.Value),
BinaryOperator.Divide => new IntegerConstant(leftInt.Value / rightInt.Value),
BinaryOperator.Remainder => new IntegerConstant(leftInt.Value % rightInt.Value),
BinaryOperator.BitwiseLeftShift => new IntegerConstant(leftInt.Value << (int)rightInt.Value),
BinaryOperator.BitwiseRightShift => new IntegerConstant(leftInt.Value >> (int)rightInt.Value),
BinaryOperator.BitwiseOr => new IntegerConstant(leftInt.Value | rightInt.Value),
BinaryOperator.BitwiseAnd => new IntegerConstant(leftInt.Value & rightInt.Value),
BinaryOperator.BitwiseXor => new IntegerConstant(leftInt.Value ^ rightInt.Value),
BinaryOperator.Add => (null, new IntegerConstant(leftInt.Value + rightInt.Value)),
BinaryOperator.Subtract => (null, new IntegerConstant(leftInt.Value - rightInt.Value)),
BinaryOperator.Multiply => (null, new IntegerConstant(leftInt.Value * rightInt.Value)),
BinaryOperator.Divide => (null, new IntegerConstant(leftInt.Value / rightInt.Value)),
BinaryOperator.Remainder => (null, new IntegerConstant(leftInt.Value % rightInt.Value)),
BinaryOperator.BitwiseLeftShift => (null, new IntegerConstant(leftInt.Value << (int)rightInt.Value)),
BinaryOperator.BitwiseRightShift => (null, new IntegerConstant(leftInt.Value >> (int)rightInt.Value)),
BinaryOperator.BitwiseOr => (null, new IntegerConstant(leftInt.Value | rightInt.Value)),
BinaryOperator.BitwiseAnd => (null, new IntegerConstant(leftInt.Value & rightInt.Value)),
BinaryOperator.BitwiseXor => (null, new IntegerConstant(leftInt.Value ^ rightInt.Value)),
// boolean constants are needed here
BinaryOperator.GreaterThan => new IntegerConstant(leftInt.Value > rightInt.Value ? 1 : 0),
BinaryOperator.LessThan => new IntegerConstant(leftInt.Value < rightInt.Value ? 1 : 0),
BinaryOperator.GreaterThanOrEqualTo => new IntegerConstant(leftInt.Value >= rightInt.Value ? 1 : 0),
BinaryOperator.LessThanOrEqualTo => new IntegerConstant(leftInt.Value <= rightInt.Value ? 1 : 0),
BinaryOperator.EqualTo => new IntegerConstant(leftInt.Value == rightInt.Value ? 1 : 0),
BinaryOperator.NotEqualTo => new IntegerConstant(leftInt.Value != rightInt.Value ? 1 : 0),
BinaryOperator.LogicalAnd => new IntegerConstant((leftInt.Value != 0) && (rightInt.Value != 0) ? 1 : 0),
BinaryOperator.LogicalOr => new IntegerConstant((leftInt.Value != 0) || (rightInt.Value != 0) ? 1 : 0),
BinaryOperator.GreaterThan => (null, new IntegerConstant(leftInt.Value > rightInt.Value ? 1 : 0)),
BinaryOperator.LessThan => (null, new IntegerConstant(leftInt.Value < rightInt.Value ? 1 : 0)),
BinaryOperator.GreaterThanOrEqualTo => (null, new IntegerConstant(leftInt.Value >= rightInt.Value ? 1 : 0)),
BinaryOperator.LessThanOrEqualTo => (null, new IntegerConstant(leftInt.Value <= rightInt.Value ? 1 : 0)),
BinaryOperator.EqualTo => (null, new IntegerConstant(leftInt.Value == rightInt.Value ? 1 : 0)),
BinaryOperator.NotEqualTo => (null, new IntegerConstant(leftInt.Value != rightInt.Value ? 1 : 0)),
BinaryOperator.LogicalAnd => (null, new IntegerConstant((leftInt.Value != 0) && (rightInt.Value != 0) ? 1 : 0)),
BinaryOperator.LogicalOr => (null, new IntegerConstant((leftInt.Value != 0) || (rightInt.Value != 0) ? 1 : 0)),
_ => throw new ArgumentOutOfRangeException($"Invalid binary operator {binOp.Operator}"),
};
}

default:
throw new CompilationException($"Expression {expression} cannot be evaluated as constant expression.");
return ($"Expression {expression} cannot be evaluated as constant expression.", null);
}
}
}
14 changes: 9 additions & 5 deletions Cesium.CodeGen/Ir/Declarations/LocalDeclarationInfo.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Globalization;
using Cesium.Ast;
using Cesium.CodeGen.Extensions;
using Cesium.CodeGen.Ir.Expressions.Constants;
using Cesium.CodeGen.Ir.Types;
using Cesium.Core;
using Yoakke.SynKit.C.Syntax;
Expand Down Expand Up @@ -230,12 +231,15 @@ private static (IType, string? Identifier) ProcessDirectDeclarator(IDirectDeclar
}
else
{
if (sizeExpr is not ConstantLiteralExpression constantExpression ||
constantExpression.Constant.Kind != CTokenType.IntLiteral ||
!int.TryParse(constantExpression.Constant.Text, out var size))
throw new CompilationException($"Array size specifier is not integer {sizeExpr}.");
var constantResult = ConstantEvaluator.TryGetConstantValue(sizeExpr.ToIntermediate());
if (constantResult.Constant is { } constant
&& constant is IntegerConstant integerConstant)
{
type = CreateArrayType(type, (int)integerConstant.Value);
break;
}

type = CreateArrayType(type, size);
throw new CompilationException($"Array size specifier is not integer {sizeExpr}.");
}

break;
Expand Down

0 comments on commit b8279d0

Please sign in to comment.