How to change calling convention of avr-gcc

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry if this has already been answered. I haven't had success searching on the forums except for this thread https://www.avrfreaks.net/forum/avr-gcc-register-usage-changeable?skey=calling%20convention but seeing that it was a long time ago I am wondering if this is a possibility now with the latest version of avr-gcc.

 

 

 

Basically I am coding in C using Atmel Studio + avr-gcc + libc.  

 

I also have a library which was written using the IAR compiler which I am linking to. It is a pre-compiled library from a vendor, modifying the code is not an option.

 

I realised after futile attempts to call the IAR compiled functions from my avr-gcc code that avr-gcc and IAR use different function calling conventions (different set of registers for the function arguments and return value).

 

Is there a way to customise the calling convention at the avr-gcc side, like specifying which registers I want to use? Or is this IAR library basically unusable? For some reason, coding my main application for IAR is not an option.

 

 

 

 

Thanks in advance!!!

Last Edited: Fri. Feb 9, 2018 - 04:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there a way to customise the calling convention at the avr-gcc side

Not to my knowledge, although you can reserve some registers by declaring register variables.  This isn't the way I'd go, though.  I'd sooner create a wrapper for the IAR function, written in assembler, probably.

 

I'm not an IAR user, so I'm not certain what the ABI is.  The GCC ABI can be found here:

https://gcc.gnu.org/wiki/avr-gcc

 

It should be a straightforward matter to create a wrapper which re-arranges the arguments passed by GCC (in registers and/or on the stack) and places them where the IAR function expects them to be, then does the reverse for the return value.

 

IINM, IAR uses a two-stack machine model, so you'd need to pay attention to how you'll handle that.  GCC uses a single stack, and grows a stack frame on it to pass arguments which won't fit into registers.

 

Certainly doable.  It invariably introduce some overhead, but unless this is a time critical piece of code it shouldn't pose any problems.

 

EDIT:  Perhaps this?:

http://supp.iar.com/FilesPublic/UPDINFO/007051/ew/doc/infocenter/tutor_mixingcasm.html

And starting on page 131 of:

http://netstorage.iar.com/SuppDB/Public/UPDINFO/006220/EW430_CompilerReference.pdf

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Fri. Feb 9, 2018 - 05:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is the IAR cide even linkable? That is are the binaries ELF-DWARF?

But anyway second vote for a "shim layer" in Asm to massage the registers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Binary compatibility. The answer depends on whether or not you have binary compatible objects or library that can be linked against. If "yes" (elf32-avr, same representations of headers, sections, relocations, offsets, symbols, debug-info,same convention w.r.t. leading undescores, layout of vtables, etc.), linking is easy. If "no" and you just have a blob of non-relocatable ihex, then there's more work to do.

Call interface: you can add new function attributes to avr-gcc backend and look how it works out. If that's not an option and you are restricted to your application, you can try C or asm.

asm: On C/C++ level, declare external functions that are implemented in assembler and .global on asm level. These functions receive and return according to the avr-gcc ABI as of http://gcc.gnu.org/wiki/avr-gcc This might be quite some work, e.g. when you have to convert structures that are used both by gcc and iar but have different layout or calling convention (stack vs. regs). Same for address spaces and assertions about location of read-only data. For example, if you have a const char* and gcc assumes to find it in RAM but iar to reside in flash then you have a problem.

The asm part has to cater for: moving values to their respective registers, saving call-saved registers that are destroyed, layout stack if it receives or returns arguments.

If iar assumes that specific registers never change or might change without notice (like avr-gcc's zero_reg) this will need further attention.

If the code on either side needs SFRS like: EIND, RAMPX/Y/Z, SREG.T this might need further attention.

If the code uses function pointers for callbacks, e.g. gcc calls iar code that gets f-pointer from gcc for callback, this might get tricky.

C: Simple[tm] cases might be handled by inline-asm and local reg vars for adjustment.

avrfreaks does not support Opera. Profile inactive.

Last Edited: Fri. Feb 9, 2018 - 01:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Possibly the easiest option is to start using IAR yourself ;-)

 

(hope you have deep pockets!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IMO, your time would be better spent on recreating the source code for your "library", then it would be usable with any compiler in the future!

 

Jim

 

Mission: Improving the readiness of hams world wide : flinthillsradioinc.com

Interests: Ham Radio, Solar power, futures & currency trading - whats yours?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have no idea of the cost of IAR. Looked on their site but they seem to be ashamed of it themselves.

Personallly I verye much dislike the whole idea of binary blobs to attempt peaople to buy into their stuff..

But I'm wondering. How complex is that library you want to use. What does it do?

 

Maybe there are alternatives for your library. Have you looked on github? There is an absolutely crazy amount of source code over there.

 

Edit:

could there be some debug code in your library to help re-create it's sources with a bit of reverse-enigneering?

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

Last Edited: Sat. Feb 10, 2018 - 12:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Paul it was 3,000 a while back though I can't remember if that was £ or $. For an "industrial " product you can easily amortize such costs but if you are writing software to make 3 of something it effectively costs 1,000 per unit!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for all the inputs! Not sure how long it will take to create these wrappers, or how much unnecessary bloat in code size this would create. That is if I make a new wrapper for every different function I need to call. I guess if I could create a single "universal" function call wrapper then it might be worth the effort. Hmm... via function pointers perhaps...  Meanwhile I should request a quote from IAR. Funny that they don't publish the prices.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The complexity kind of depends on how close the ABIs are. GCC for example assigns registers in pairs from R25 downwards. So first parameter in R25:R24, next in R32:R22 and so on. Let's say IAR uses R16 upwards so R17:R16 for parameter 1, R19:R18 for parm 2 and so on. Then you could just have generic map_1_parm:, map_2_parm:, map_3_parm: routines. So if the function to be mapped was:

int mac(int * a, int b, int c);

Then mac_wrapped() would be something like:

mac_wrapped:
    call map_3_parm
    call mac
    jmp map_ret_val

The map_ret_val: there is something to take the return register (pair) from IAR and map it back to R25:R24 as expected for GCC.

 

The routine mac_wrapped() (and all the others) could presumably be auto-generated with Python or similar by just reading the .h files and looking at the function interfaces. This does rely on all the calls to the IAR functions in your code using mac_wrapped(&a, 37, 59) instead of the previous mac(&a, 37, 59). Again you could write some Python to auto-generate this by reading the main source files (because it knows which functions are to be mapped too).