Is there a way to control codegen function prototypes

9 views (last 30 days)
Is there any way to see the internals of why codegen decided to define a prototype the way it did? Or to force a particular function prototype for non-entry point functions?
I have a fairly complex function, let's call it functionA, which calls several internal functions.
outStruct = functionA( struct1, double1, double2, struct2)
When I use codegen to generate functionA as an entry point, the prototype looks the way I'd expect it to:
extern void functionA( const struct1_T *struct1, double double1, double double2, const struct2_T *struct2, struct0_T *outStruct)
Now, I want to add a second function, test_functionA, which coder.load's some test structures from a file, then calls functionA on the data.
function test_functionA()
coder.inline('never');
inputs = coder.load('data');
struct1 = coder.ignoreConst(inputs.struct1);
double1 = coder.ignoreConst(inputs.double1);
double2 = coder.ignoreConst(inputs.double2);
struct2 = coder.ignoreConst(inputs.struct2);
o = functionA(struct1,double1,double2,struct2);
% then I print elements of o, so o won't get optimized out
What I was hoping -- and what happens in my simple test cases trying to reproduce this -- was that test_funcitonA C++ code does in fact call the functionA using the already generated library code. But what actually happens, is that test_functionA calls a copy of the function, with a totally different prototype
void b_functionA(const double struct1_field1[8], double double2, double struct2_field1, struct0_T *outStruct)
Coder has obviously folded some constants and folded constants within the structures into separate arguments, even though I told it to ignoreConst.
Is there some way to force a particular function prototype in the generated code? I'd have thought that since functionA is an entry point with specified args, that test_functionA would use that same definition, rather than some strange optimized specialization.
Possibly related ... functionA lives in a package. When I codegen test_functionA, the copy of functionA that it creates lives in its own namespace. I can't seem to reproduce that with simple files.
Is there a way to sort of debug what codegen thinks it is dealing with ? Like some sort of AST output of the code before it starts to optimize?
  4 Comments
Denis Gurchenkov
Denis Gurchenkov on 5 May 2023
Hi Jon,
Three thoughts here:
  1. One possible thing to try is to disable the compiler optimization that splits structs into individual fields:
cfg = coder.config('lib'); % or whatever config you use
cfg.EnableStructExplosion = false;
codegen ...... -config cfg .....
I don't expect this to fully resolve the issue but it should made the prototype of functionA to be more similar to what you expect.
2. Can you speak more about your end goal, why do you need the same functionA to be an entry point and also invoked from test_functionA? If you explain the intent, maybe someone can suggest a different solution to get there.
3. To fully resolve the issue, we need figure out why codegen thinks that the version of functionA that you already have (the entry point) is not good enough to be called from test_functionA. Something makes it unable to reuse the function in that 2nd call site, and makes it to create a new one. This is hard to diagnose without a specific example. If you can attach reproduction steps (.m files and doit.m that calls codegen) we'll take a look. If you prefer to not to post your code publicly, please contact the technical support, the support request will get routed to the coder team and we can debug further (we still need repros though).
Thanks,
Denis
MATLAB Coder dev team
Jon
Jon on 5 May 2023
Well, problem isn't totally solved, I have things working today.
There must have been something funny with my paths, since, today the main problem of agressive struct explosion went away today even without using EnableStructExplosion.
But, for whatever reason, one of my non-constant uint32 scalars was still being optimized away in the non-entry point function. This despite wrapping each argument with coder.ignoreConst in my test_functionA. (For what it is worth, this scalar, after some manipulation, was being used to index into other variables).
Once I had the rest of the prototype matching, I was able to simply force that uint32 to change within the test_functionA, to avoid it being optimized away, and that seemed to work now.
1) I don't see EnableStructExplosion in the documentation (or in the autocomplete on the cfg object), but that seems like it could be useful to a lot of people. For that matter, there seem to be some properties which
2) Just for completeness, on the use case: I am integrating some new algorithms, written in matlab, with an existing c++ project (written in c++). Since the inputs are somewhat complicated, I wanted to also provide some "unit test" code to run the new function on the target, with realistic inputs, so we could check timing on the target before we get too far down the road. So, test_functionA was simply loading in some inputs (coder.load -- which turn into compile time constants in generated code), and I wanted to call functionA with those inputs. If I allowed StructExplosion, the non-entry point call was getting exploded, while the entry point version was not.
3) It seems that in the end, for some reason coder was ignoring my ignoreConst on one particular parameter. I was able to get it working by putting that parameter in a loop, so it wasn't actually constant within test_functionA. I'll have to spend some time stripping things out to get to a minimum reproducible scenario.
Thanks for your help!

Sign in to comment.

Answers (0)

Categories

Find more on MATLAB Coder in Help Center and File Exchange

Products


Release

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!