Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functions with varargs support #196

Open
kant2002 opened this issue Aug 14, 2022 · 9 comments
Open

Functions with varargs support #196

kant2002 opened this issue Aug 14, 2022 · 9 comments
Labels
area:cil-interop Related to CIL (.NET) interop area:compiler Related to code compilation or type checking area:standard-support Related to the C standard support kind:feature New feature or request status:help-wanted Open for contributors

Comments

@kant2002
Copy link
Collaborator

kant2002 commented Aug 14, 2022

We need varargs support, since almost any simple tutorial use printf. Without that support we cannot go to public probably, otherwise Cesium always look as toy project.

Look for the number 196 in the code to find clues to implement this feature.

@ForNeVeR ForNeVeR added status:help-wanted Open for contributors area:cil-interop Related to CIL (.NET) interop area:standard-support Related to the C standard support area:compiler Related to code compilation or type checking labels Aug 14, 2022
@ForNeVeR
Copy link
Owner

I agree on that point. Any ideas how such a function could work? The problem is that sometimes it's hard to distinguish a vararg function from the call-site point of view: it may look (and even be declared?) as a normal function.

Could we reuse the existing facilities of vararg functions in the CLI? Do these facilities even exist?

@ForNeVeR ForNeVeR added the kind:feature New feature or request label Aug 14, 2022
@kant2002
Copy link
Collaborator Author

dotnet/runtime#10478 that's not possible today, even if it would be possible in the future. So plan would be to mimic this. I think we can pass variadic arguments as object[] and that's it. That's will make implementing va_start ambiguous when used incorrectly, but I think that fine. That unlock interop scenarios, which is what's important at this stage. If variadic function would be declared as regular one, and defined as variadic that's we should be able resolve at "link" time, and that's pretty advanced and arcane scenario. I do not think we can afford think too much about it, give that we do not know space very well

@ForNeVeR
Copy link
Owner

ForNeVeR commented Aug 16, 2022

I think we can pass variadic arguments as object[] and that's it.

To be honest, I'm not sure we can put a pointer into an object[]. Is a pointer even an object? 🤔

Maybe some workaround using stack arrays or linked lists will be required.

@kant2002
Copy link
Collaborator Author

kant2002 commented Aug 26, 2022

I do find some interesting sample which definitely will complicate our life.

#include <stdio.h>
#include <stdarg.h>

void function1(char* s, ...)
{
    va_list ap;
    int tmp;

    va_start(ap, s);
    tmp = va_arg(ap, int);
    printf(s, tmp);
    va_end(ap);
}

void function2(char* s, int d)
{
    printf(s, d);
}

typedef void (*functionX_t)(char*, int);
typedef void (*functionY_t)(char*, ...);

int main(int argc, char* argv[])
{
    int x = 42;

    /* swap! */
    functionX_t functionX = (functionX_t) function1;
    functionY_t functionY = (functionY_t) function2;

    function1("%d\n", x);
    function2("%d\n", x);
    functionX("%d\n", x);
    functionY("%d\n", x);

    return 0;
}

@ForNeVeR
Copy link
Owner

ForNeVeR commented Aug 26, 2022

What does the standard say about this? I'm not sure these signatures are technically compatible.

kant2002 added a commit to kant2002/Cesium that referenced this issue Sep 25, 2022
For now only char* arguments supported. Cannot do much with integers, since during emit phase I have lost information about expression type, and I cannot imagin what kind of IR node I need to preserve that, and at the same time do not overcomplicate things.
Related to ForNeVeR#196
kant2002 added a commit to kant2002/Cesium that referenced this issue Sep 25, 2022
For now only char* arguments supported. Cannot do much with integers, since during emit phase I have lost information about expression type, and I cannot imagin what kind of IR node I need to preserve that, and at the same time do not overcomplicate things.
Related to ForNeVeR#196
@BadRyuner
Copy link
Contributor

We can use __arglist and ArgIterator, which are built into DotNet (or copy the implementation, hehehehehe). They work well on Windows, but not so well on Unix. It would also solve the problem with 4 or 8 bytes in VA_ARG.

@ForNeVeR
Copy link
Owner

Well, I'd say we want something that will reliably work on all the supported platforms and runtimes.

@BadRyuner
Copy link
Contributor

BadRyuner commented Mar 27, 2024

We can switch to native va_list. I messed around with linux and windows and made a universal va_list that works on both operating systems.
Source: https://gist.github.com/BadRyuner/170d7900d1860730192fdfabd12921ce (produces same output on windows 11 and debian 12)
But a problem appears with functions that uses ... instead of va_list (e.g. printf instead of vprintf) This is the only problem blocking the way to a full transition to natively-like variadic functions, which works perfectly with native functions.
For dotnet functions, we can use calli hax to get all arguments (RCX, RDX, R8, R9) with a stack pointer and initialize the native va_list or use old va_list. But here pinvoke is the problem. For extern dllimport dotnet creates a wrapper method that nullifies unnecessary pointers and stack to protect the environment. Accordingly, additional arguments will not pass there (if we are talking about ... and not va_list).
My solution: use NativeLibrary.Load and NativeLibrary.GetExport for further use in calli, which can send any number of arguments. In doing so, calli does not use wrappers unlike PInvoke.
But here another problem arises... NetStandard 2.0 does not provide a NativeLibrary class. Here we have to either create two separate classes for PInvoke (LoadLibrary & GetProcAddress - Windows; dlopen & dlsym - Linux/MacOS) or drop it.

Also msvc generates its own wrapper over printf, where it packs the arguments into a va_list and calls vprintf.

@ForNeVeR
Copy link
Owner

I do not like the solution involving any such hacks. Remember that we have to support at least 9 OS/CPU combinations (x86/x64/ARM64 macOS/Win/Lin). I do not believe there's a reliable solution for all those at once.

And why would we want it anyway? Currently we aren't targeting that class of interop.

ForNeVeR added a commit to seclerp/Cesium that referenced this issue Apr 21, 2024
ForNeVeR added a commit that referenced this issue Apr 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:cil-interop Related to CIL (.NET) interop area:compiler Related to code compilation or type checking area:standard-support Related to the C standard support kind:feature New feature or request status:help-wanted Open for contributors
Projects
None yet
Development

No branches or pull requests

3 participants