Skip to content

Commit

Permalink
Unify FunctionCall and IndirectFunctionCall argument emitting
Browse files Browse the repository at this point in the history
  • Loading branch information
zawodskoj authored and ForNeVeR committed May 21, 2023
1 parent 03ee5ab commit a6e19d3
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 98 deletions.
11 changes: 11 additions & 0 deletions Cesium.CodeGen.Tests/CodeGenMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -418,4 +418,15 @@ int main()
return fooptr(123);
}", "Attempted to call non-function pointer");

// TODO [#196]
/* [Fact]
public Task VarargFunctionPointerCallTest() => DoTest(@"int foo(int a, ...) { return a; }
int main()
{
int (*fooptr)(int, ...) = &foo;
return fooptr(123, 456);
}"); */
}
50 changes: 5 additions & 45 deletions Cesium.CodeGen/Ir/Expressions/FunctionCallExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Cesium.CodeGen.Ir.Expressions;

internal class FunctionCallExpression : IExpression
internal class FunctionCallExpression : FunctionCallExpressionBase
{
private readonly IdentifierExpression _function;
private readonly IReadOnlyList<IExpression> _arguments;
Expand All @@ -35,7 +35,7 @@ public FunctionCallExpression(Ast.FunctionCallExpression expression)
_callee = null;
}

public IExpression Lower(IDeclarationScope scope)
public override IExpression Lower(IDeclarationScope scope)
{
if (_function.Identifier == "__builtin_offsetof_instance")
{
Expand Down Expand Up @@ -70,7 +70,6 @@ public IExpression Lower(IDeclarationScope scope)


return new IndirectFunctionCallExpression(
_function,
new GetValueExpression(new LValueLocalVariable(var.Type, var.Identifier)),
f,
_arguments.Select((a, index) =>
Expand Down Expand Up @@ -134,59 +133,20 @@ public IExpression Lower(IDeclarationScope scope)
}).ToList());
}

public void EmitTo(IEmitScope scope)
public override void EmitTo(IEmitScope scope)
{
if (_callee == null)
throw new AssertException("Should be lowered");

VariableDefinition? varArgBuffer = null;
var explicitParametersCount = _callee!.Parameters?.Parameters.Count ?? 0;
var varArgParametersCount = _arguments.Count - explicitParametersCount;
if (_callee!.Parameters?.IsVarArg == true)
{
// TODO: See https://github.com/ForNeVeR/Cesium/issues/285
// Using sparse population of the parameters on the stack. 8 bytes should be enough for anybody.
// Also we need perform localloc on empty stack, so we will use local variable to save vararg buffer to temporary variable.
if (varArgParametersCount == 0)
{
scope.AddInstruction(OpCodes.Ldnull);
}
else
{
scope.AddInstruction(OpCodes.Ldc_I4, varArgParametersCount * 8);
scope.AddInstruction(OpCodes.Localloc);
}

varArgBuffer = new VariableDefinition(scope.Context.TypeSystem.Void.MakePointerType());
scope.Method.Body.Variables.Add(varArgBuffer);
scope.AddInstruction(OpCodes.Stloc, varArgBuffer);
}

foreach (var argument in _arguments.Take(explicitParametersCount))
argument.EmitTo(scope);

if (_callee!.Parameters?.IsVarArg == true)
{
for (var i = 0; i < varArgParametersCount; i++)
{
var argument = _arguments[i + explicitParametersCount];
scope.AddInstruction(OpCodes.Ldloc, varArgBuffer!);
scope.AddInstruction(OpCodes.Ldc_I4, i * 8);
scope.AddInstruction(OpCodes.Add);
argument.EmitTo(scope);
scope.AddInstruction(OpCodes.Stind_I);
}

scope.AddInstruction(OpCodes.Ldloc, varArgBuffer!);
}
EmitArgumentList(scope, _callee.Parameters, _arguments);

var functionName = _function.Identifier;
var callee = _callee ?? throw new CompilationException($"Function \"{functionName}\" was not lowered.");

scope.Method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, callee.MethodReference));
}

public IType GetExpressionType(IDeclarationScope scope)
public override IType GetExpressionType(IDeclarationScope scope)
{
var functionName = _function.Identifier;
var callee = _callee ?? throw new AssertException($"Function \"{functionName}\" was not lowered.");
Expand Down
60 changes: 60 additions & 0 deletions Cesium.CodeGen/Ir/Expressions/FunctionCallExpressionBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Cesium.CodeGen.Contexts;
using Cesium.CodeGen.Extensions;
using Cesium.CodeGen.Ir.Types;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;

namespace Cesium.CodeGen.Ir.Expressions;

internal abstract class FunctionCallExpressionBase : IExpression
{
public abstract IExpression Lower(IDeclarationScope scope);
public abstract void EmitTo(IEmitScope scope);
public abstract IType GetExpressionType(IDeclarationScope scope);

protected void EmitArgumentList(IEmitScope scope, ParametersInfo? paramInfo, IReadOnlyList<IExpression> arguments)
{
var explicitParametersCount = paramInfo?.Parameters.Count ?? 0;
var varArgParametersCount = arguments.Count - explicitParametersCount;

VariableDefinition? varArgBuffer = null;

if (paramInfo?.IsVarArg == true)
{
// TODO: See https://github.com/ForNeVeR/Cesium/issues/285
// Using sparse population of the parameters on the stack. 8 bytes should be enough for anybody.
// Also we need perform localloc on empty stack, so we will use local variable to save vararg buffer to temporary variable.
if (varArgParametersCount == 0)
{
scope.AddInstruction(OpCodes.Ldnull);
}
else
{
scope.AddInstruction(OpCodes.Ldc_I4, varArgParametersCount * 8);
scope.AddInstruction(OpCodes.Localloc);
}

varArgBuffer = new VariableDefinition(scope.Context.TypeSystem.Void.MakePointerType());
scope.Method.Body.Variables.Add(varArgBuffer);
scope.AddInstruction(OpCodes.Stloc, varArgBuffer);
}

foreach (var argument in arguments.Take(explicitParametersCount))
argument.EmitTo(scope);

if (paramInfo?.IsVarArg == true)
{
for (var i = 0; i < varArgParametersCount; i++)
{
var argument = arguments[i + explicitParametersCount];
scope.AddInstruction(OpCodes.Ldloc, varArgBuffer!);
scope.AddInstruction(OpCodes.Ldc_I4, i * 8);
scope.AddInstruction(OpCodes.Add);
argument.EmitTo(scope);
scope.AddInstruction(OpCodes.Stind_I);
}

scope.AddInstruction(OpCodes.Ldloc, varArgBuffer!);
}
}
}
64 changes: 11 additions & 53 deletions Cesium.CodeGen/Ir/Expressions/IndirectFunctionCallExpression.cs
Original file line number Diff line number Diff line change
@@ -1,79 +1,32 @@
using Cesium.CodeGen.Contexts;
using Cesium.CodeGen.Contexts.Meta;
using Cesium.CodeGen.Extensions;
using Cesium.CodeGen.Ir.Types;
using Cesium.Core;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;

namespace Cesium.CodeGen.Ir.Expressions;

internal class IndirectFunctionCallExpression : IExpression
internal class IndirectFunctionCallExpression : FunctionCallExpressionBase
{
private readonly IdentifierExpression _function;
private readonly IReadOnlyList<IExpression> _arguments;
private readonly IExpression _callee;
private readonly FunctionType _calleeType;

public IndirectFunctionCallExpression(IdentifierExpression function, IExpression callee, FunctionType calleeType, IReadOnlyList<IExpression> arguments)
public IndirectFunctionCallExpression(IExpression callee, FunctionType calleeType, IReadOnlyList<IExpression> arguments)
{
_function = function;
_arguments = arguments;
_callee = callee;
_calleeType = calleeType;
}

public IExpression Lower(IDeclarationScope scope) => this;
public override IExpression Lower(IDeclarationScope scope) => this;

public void EmitTo(IEmitScope scope)
public override void EmitTo(IEmitScope scope)
{
if (_calleeType.Parameters?.IsVarArg == true)
throw new CompilationException("Vararg is not supported in function pointers");

if (_callee == null)
throw new AssertException("Should be lowered");

VariableDefinition? varArgBuffer = null;
var explicitParametersCount = _calleeType.Parameters?.Parameters.Count ?? 0;
var varArgParametersCount = _arguments.Count - explicitParametersCount;
if (_calleeType.Parameters?.IsVarArg == true)
{
// TODO: See https://github.com/ForNeVeR/Cesium/issues/285
// Using sparse population of the parameters on the stack. 8 bytes should be enough for anybody.
// Also we need perform localloc on empty stack, so we will use local variable to save vararg buffer to temporary variable.
if (varArgParametersCount == 0)
{
scope.AddInstruction(OpCodes.Ldnull);
}
else
{
scope.AddInstruction(OpCodes.Ldc_I4, varArgParametersCount * 8);
scope.AddInstruction(OpCodes.Localloc);
}

varArgBuffer = new VariableDefinition(scope.Context.TypeSystem.Void.MakePointerType());
scope.Method.Body.Variables.Add(varArgBuffer);
scope.AddInstruction(OpCodes.Stloc, varArgBuffer);
}

foreach (var argument in _arguments.Take(explicitParametersCount))
argument.EmitTo(scope);

if (_calleeType.Parameters?.IsVarArg == true)
{
for (var i = 0; i < varArgParametersCount; i++)
{
var argument = _arguments[i + explicitParametersCount];
scope.AddInstruction(OpCodes.Ldloc, varArgBuffer!);
scope.AddInstruction(OpCodes.Ldc_I4, i * 8);
scope.AddInstruction(OpCodes.Add);
argument.EmitTo(scope);
scope.AddInstruction(OpCodes.Stind_I);
}

scope.AddInstruction(OpCodes.Ldloc, varArgBuffer!);
}
EmitArgumentList(scope, _calleeType.Parameters, _arguments);

_callee.EmitTo(scope);

Expand All @@ -85,12 +38,17 @@ public void EmitTo(IEmitScope scope)
{
callSite.Parameters.Add(new ParameterDefinition(param.Type.Resolve(scope.Context)));
}

if (_calleeType.Parameters.IsVarArg)
{
callSite.Parameters.Add(new ParameterDefinition(scope.Context.TypeSystem.IntPtr));
}
}

scope.Method.Body.Instructions.Add(Instruction.Create(OpCodes.Calli, callSite));
}

public IType GetExpressionType(IDeclarationScope scope)
public override IType GetExpressionType(IDeclarationScope scope)
{
return _calleeType.ReturnType;
}
Expand Down
11 changes: 11 additions & 0 deletions Cesium.IntegrationTests/function_ptr.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@ int foo(int t)
return t + 2;
}

int foov(int t, ...)
{
return t + 2;
}

typedef int (*foo_t)(int x);
typedef int (*foov_t)(int x, ...);

int main(void)
{
foo_t x = &foo;

return x(40);

// TODO [#196]
// foov_t y = &foov;
// return (x(40) + y(40, 123, 456, "foo")) / 2;
}

0 comments on commit a6e19d3

Please sign in to comment.