Build fails if std::function is inside a PJOB()

Technical questions regarding the XTC tools and programming with XMOS.
amartelloni
New User
Posts: 2
Joined: Tue Jul 02, 2024 10:37 am

Build fails if std::function is inside a PJOB()

Post by amartelloni »

Hi all! Pretty new to XMOS here.

Summary:

Code that calls a std::function will fail to build with a linking error that reports

Code: Select all

"Error: Meta information ("_ZNSt3__110__function6__funcIPFffENS_9allocatorIS3_EES2_EclEOf.nstackwords") for function "_ZNSt3__110__function6__funcIPFffENS_9allocatorIS3_EES2_EclEOf" cannot be determined."
This only happens when the code is called from within a PJOB() in C using the macros defined in #include <xcore/parallel.h>

Context:

I'm porting this multi-layer perceptron library to XMOS:
https://github.com/MusicallyEmbodiedML/MLP_XMOS
Reason: we want support for backprop and training on the chip. (TFLite won't cut it.)

I had a successful build, including all unit tests, when wrapping it in a helloworld-style main.c built by hand with xcc. I've now built the MLP code as a static library and integrated it into the "Bare metal/explorer board" example from the xcore_iot framework:
https://github.com/MusicallyEmbodiedML/ ... orer_board
When I run a test with PJOB() on Tile 0 alongside other demo apps, it fails to build. By #if 0'ing the code, I've narrowed it down to the two calls to std::functions.

Searching for the error, this thread appears:
https://www.xcore.com/viewtopic.php?f=1 ... tion#p8537
It seems to apply to function pointers - std::function being a glorified version of them under the hood I suppose.
However, the same error appeared to me with another toy library I wrote (which would just generate a test sine tone). It's a C++ code and a C wrapper built as a static library. I cannot get it to build unless I either specify the #pragma stackfunction of all C wrapper functions, or move the C wrapper code from the library to the example project, leaving no extern "C"-declared function signature in the static library.

Do I really just suck it up and work around it? Function pointers are really useful for my use case - each layer will have a different user-configurable activation function, which specifies a derivative in the backprop. Even having crude C function pointers is better than "if this then call this, etc...". I've used function pointers in bare-metal ARM M0 with no memory manager and no heap, so what am I missing here?

Thanks for any replies!
User avatar
fabriceo
XCore Addict
Posts: 222
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi
as soon as you do multitasking you need to know the overall stack size required. When using function pointer, each of the possible function may require different stack size, thus you need a special instruction to say that to the compiler.
https://www.xmos.com/documentation/XM-0 ... ter-groups
hope this helps
amartelloni
New User
Posts: 2
Joined: Tue Jul 02, 2024 10:37 am

Post by amartelloni »

fabriceo wrote: Wed Jul 03, 2024 11:08 am Hi
as soon as you do multitasking you need to know the overall stack size required. When using function pointer, each of the possible function may require different stack size, thus you need a special instruction to say that to the compiler.
https://www.xmos.com/documentation/XM-0 ... ter-groups
hope this helps
Thanks fabriceo! That is a sigh of relief - at least C-style function pointers could in principle work. I've tried applying those attributes to the functions that are then passed as std::functions, but that doesn't seem to work. Does anyone know why, before I start re-writing all that using C function pointers? :) Thanks!
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1140
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

A manual option is to annotate your C++ functions with:

#pragma stackfunction <n>

where n is the number of words (32b) you want to allocate for your function. Obviously this is a manual task and you need to estimate the stack usage but it allows the XC compiler to reason about total application stack space across all threads on the tile with shared memory. This can also be used for modules using function pointers where you haven't annotated as described above.

https://www.xmos.com/download/XCC-Pragma-Directives.pdf

#pragma stackfunction 1024
void task1(void)
{
int divisor, dividend, quotient, remainder;

cout << "Enter dividend: ";
cin >> dividend;
...
Engineer at XMOS
User avatar
Ross
Verified
XCore Legend
Posts: 1070
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Be aware that if the compiler inlines the function then #pragma stackfunction will be lost.

You can avoid the inline with:

Code: Select all

__attribute__((noinline))
I'm afraid this is a bit gnarly at the moment.. but can be made to function (pardon the pun).
Technical Director @ XMOS. Opinions expressed are my own