This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/TM4C1294NCPDT: Is there a way of changing the calling conventions in some functions?

Part Number: TM4C1294NCPDT

Tool/software: TI C/C++ Compiler

I'm writing an OS and I'm stuck trying to prepare the initial stack frame for new tasks. The calling convention for ARM states that some arguments are passed on register r0-r3 (and this depends on the size of each argument), other arguments go on stack. Also if the argument is a floating point number it is passed on different registers, what makes this problem even harder.This convention makes difficult for a create_task function to prepare the initial stack frame for new tasks. Is there a way of changing this? 

  • There is no way to change the calling convention for a particular function. The TI compiler does support the "naked" attribute, which suppresses the prolog and epilog, but that's not really what you need.

    Think of the initial frame as the startup code for a new process. That frame wasn't actually created by calling a function, and you won't be returning from it like a normal function. Thus, there is no need for that frame to expect its arguments to be in the same place the C calling convention expects them.

    If you're trying to write this function in C code, do you really need arbitrary parameters? If you limit the number of parameters, you should be able to compile the function and see where the compiler expects the parameters to be, and model your frame creation from that. If you limit it to zero parameters, it should be very easy.
  • Thanks for your help, Archaeologist. I think you understood exactly my question. But let me make some more explanations to illustrate exactly what I'm trying to do.

    I'd like to define a function that create tasks (even tasks with different number and types of parameters) in a easier way. I think the way most RTOSes pass arguments to tasks is very strange and hard to use. I know all of them implement this feature the way they do for a reason, but I still want to try to find another way.

    Here is a draft of what I want to do:

    void task1();
    void task2(int i);
    void task3(char c, int i, const char* s);
    
    create_task(void* task, size_t params_size, ...);
    
    int main()
    {
        create_task(task1, 0);
        create_task(task2, sizeof(int), 13);
        create_task(task3, sizeof(char) + sizeof(int) + sizeof(char*), 'A', 13, "string");
    
        return 0;
    }

    You see? If I could change the calling conventions for my task functions I would be able to define the task functions exactly how we do with any other C function. Also, I could pass any number and type of arguments to that functions. I would just need to inform create_task about the total size of the arguments that would be pushed on the stack. That way it would be easy to copy the arguments from create_task's stack frame to the stack frame of the newly created task. I'm assuming a calling convention where I'd pass every argument on the stack.

    But you already said I can't change the calling convention. So, I'm just presenting my idea, even though now I know it's not possible to implement it.

    It would be interesting if there was something like:

    void task1(int i, char c) __attribute__((args_in_stack));

  • Well, be aware that when you use variadic arguments ("..."), all of the arguments passed through "..." are by necessity passed on the stack, as well as the last "known" parameter. If you had some way to tell "create_task" the number of arguments and the type of each argument (consider the printf format string, for example), you could in theory write code in "create_task" to pull those arguments from the stack. However, I suspect this will end up being far more trouble to implement than it is worth.

    You could marshall the arguments into a va_list and narrow your API to pass va_list instead of a wide variety of possible option combinations:

    void create_task(void *task, size_t n_args, ...)
    {
    va_list args;
    va_start(args, n_args);
    v_helper(task, n_args, args);
    va_end(args);
    }

    void magic_helper_1(void *task, size_t n_args, va_list args)
    {
    /* OS magic to build this fake function to
    create the initial frame without
    worrying about variadic arguments */
    magic_OS_stuff;
    }

    void magic_helper_2(void)
    {
    /* OS magic to build this fake function;
    at the end, call launchpad */
    launchpad(n_args, args);
    }

    void launchpad(size_t n_args, va_list args)
    {
    /* this function is written in C */
    <whatever> = va_arg(args, int);
    /* now do the real task */
    task();
    }
  • Archaeologist,

    You said the TI compiler does support the "naked" attribute. I tested and the compiler really compiled a program using that feature, but naked is not listed on Section 5.16.2 Function Attributes of ARM Optimizing C/C++ Compiler v18.1.0.LTS User's Guide. I also did a word search through out that file and didn't find any occurrence of the word naked in the manual. Is the manual outdated or the naked attribute is not officially supported?

  • Thank you for bringing this problem to our attention.  I filed CODEGEN-4631 in the SDOWP system to have the ARM compiler manual updated to document the attribute naked.  You are welcome to follow it with the SDOWP link below in my signature.

    Thanks and regards,

    -George