Run-time parameters in gcc (inverse va_args/vararg

2019-09-01 01:57发布

问题:

I'm trying to make some improvements to a interpreter for microcontrollers that I'm working on. For executing built-in functions I currently have something like this (albeit a bit faster):

function executeBuiltin(functionName, functionArgs) {
  if (functionName=="foo") foo(getIntFromArg(functionArgs[0]));
  if (functionName=="bar") bar(getIntFromArg(functionArgs[0]),getBoolFromArg(functionArgs[1]),getFloatFromArg(functionArgs[2]));
  if (functionName=="baz") baz();
  ...
}

But it is for an embedded device (ARM) with very limited resources, and I need to cut down on the code size drastically. What I'd like to do is to have a general-purpose function for calling other functions with different arguments - something like this:

function executeBuiltin(functionName, functionArgs) {
  functionData = fast_lookup(functionName);
  call_with_args(functionData.functionPointer, functionData.functionArgumentTypes, functionArgs);
}

So I want to be able to call a standard C function and pass it whatever arguments it needs (which could all be of different types). For this, I need a call_with_args function.

I want to avoid re-writing every function to take argc+argv. Ideally each function that was called would be an entirely standard C function.

There's a discussion about this here - but has anything changed since 1993 when that post was written? Especially as I'm running on ARM where arguments are in registers rather than on the stack. Even if it's not in standard C, is there anything GCC specific that can be done?


UPDATE: It seems that despite behaviour being 'undefined' according to the spec, it looks like because of the way C calls work, you can pass more arguments to a function than it is expecting and everything will be fine, so you can unpack all the arguments into an array of uint32s, and can then just pass each uint32 to the function.

That makes writing 'nice' code for calls much easier, and it appears to work pretty well (on 32 bit platforms). The only problem seems to be when passing 64 bit numbers and compiling for 64bit x86 as it seems to do something particularly strange in that case.

回答1:

Would it be possible to do at compile time with macros? Something along the lines of: https://www.redhat.com/archives/libvir-list/2014-March/msg00730.html

If runtime was required, perhaps __buildin_apply_args() could be leveraged.



回答2:

from this document, section 5.5, Parameter Passing, it seems like parameters are passed both in registers and in stack, as with most of today platforms.

With "non standard C" I am thinking to pack the parameters and call the function following the documentation with some asm(). However you need a minimal information about the signature of the function being called anyway (I mean, how many bits for each argument to be passed).

From this point of view I would prefer to prepare an array of function names, an array of function pointers and an array of enumerated function signatures (in the number of bits of each argument... you don't need to differentiate void* from char* for example) and a switch/case on the signatures, and a switch/case on the last one. So I have reported two answers here.



回答3:

You can do a very simple serialization to pass arbitrary arguments. Create an array and memcpy sizeof(arg) bytes into it for each passed argument.

Or you can create structs for function arguments.

Every function takes a char* or a void*. Then you pass either a pointer to a struct with that functions parameters, or you define a set of macros or functions to encode and decode arbitrary data from an array and pass the pointer to that array.