Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LLVM assembly: call a function using varargs

I want to define a function in LLVM assembly that takes as argument:

  • an identifier to a sub-function
  • a vararg

This function should do some preprocessing, find the correct function for the identifier and call it using the vararg, and return its result.

Something like:

define ??? @1 (i32 %identifier, ...vararg...)
{
  switch i32 %identifier, label %def, i32 1, label %a
a:
  %1 = tail call @function_for_a, ...vararg...
  ret ??? %1
def:
  ret void
}

It doesn't seem to be possible. Is there a way to do that still? I think it should be possible using plain assembler.

This is intended to be a dispatching function for an object-oriented language. I would prefer it to be fast.

What I would like is a way to:

  • remove from the stack the first argument used by @1
  • branch to the second function.

The second function would then be executed in place of the first (it's a tail call), but with a list of argument that is not known exactly to the first function (the first function's vararg).

like image 827
Mildred Avatar asked Aug 10 '11 17:08

Mildred


2 Answers

First: You can't use tail call if you want to pass varargs:

http://llvm.org/docs/LangRef.html

  1. The optional "tail" marker indicates that the callee function does not access any allocas or varargs in the caller.

Second: What is your Calling Conventions?

Third: To handle varargs (like in C) you need to use va_* functions to create a new va_list and copy all parameters into it:

http://llvm.org/docs/LangRef.html#int-varargs

Last: Every function which will be called by this dispatcher must use va_* functions to get its arguments.

UPDATE:

You should know which calling convention will you use (what is the default) before you will say about stack as storage of function arguments. Then. You can't access not pass "..." argument without va_* functions, because it is the ONLY way to access them in LLVM assembly.

There is a way of doing smth like in C, here the printf will call vfprintf with all "..." arguments and without knowing how many arguments to pass

// 3-clause BSD licensed to The Regents of the University of California.

int
printf(const char *fmt, ...)
{
        int ret;
        va_list ap;

        va_start(ap, fmt);
        ret = vfprintf(stdout, fmt, ap);
        va_end(ap);
        return (ret);
}

Vfprintf is declared in special way to get "..." and to extract arguments from it:

int
vfprintf(FILE *fp, const char *fmt0, __va_list ap)
{
...
va_arg(ap, type) //to get next arg of type `type`
like image 118
osgx Avatar answered Oct 23 '22 15:10

osgx


(This grew too big for a comment. I'm afraid I don't have much practical experience with LLVM, so take this with a grain of salt)

I've been thinking about this and I doubt you're going to be able to write such a function.

Consider writing this function in x86_64 assembler language using the C calling convention, or really any that supports varargs (see pg. 20 for an example). Normally you would shift the registers (rdi<-rsi, rsi<-rdx and so on) before branching, but you shouldn't do that if the arguments are e.g. floats, so you have to know about the types! Or you have to use a vfprintf-like function.

Similar arguments exist for other architectures, so I'd consider thinking about another way to solve the problem. In particular couldn't you just replace the call to @1 with a look up in a jump table and a branch to the function pointer specified by %identifier? This could be made into a function that checks %identifier and returns the correct function pointer and handles invalid identifiers appropriately.

like image 28
user786653 Avatar answered Oct 23 '22 16:10

user786653