Bug 43745 - [avr] g++ puts VTABLES in SRAM
Summary: [avr] g++ puts VTABLES in SRAM
Status: RESOLVED WONTFIX
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.7.0
: P4 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: addr-space, missed-optimization
Depends on: 49868
Blocks:
  Show dependency treegraph
 
Reported: 2010-04-13 08:15 UTC by Tomasz Francuz
Modified: 2017-09-04 08:53 UTC (History)
11 users (show)

See Also:
Host:
Target: avr
Build:
Known to work:
Known to fail:
Last reconfirmed: 2012-01-07 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tomasz Francuz 2010-04-13 08:15:31 UTC
On AVR target g++ generates code which copies object’s VTABLES from FLASH to SRAM wasting the memory. Due to the Harvard architecture of AVR processors the solution is not trivial. This behavior can be observed in any c++ program which has object with virtual method, e.g:
Class test
{
	virtual void example();
};

The VTABLE of class test will be generated in FLASH and next copied to SRAM, any reference to virtual example() method will take the method address from SRAM.
Comment 1 Eric Weddington 2010-04-14 19:52:53 UTC
What is your suggestion?
Comment 2 Tomasz Francuz 2010-04-14 20:39:55 UTC
(In reply to comment #1)
> What is your suggestion?

Because VTABLES are generated during compile time and they are const data there is no need to copy them into SRAM. Appropriate addresses of virtual methods can be read directly from FLASH and used to make indirect call as usual. As far as I know the problem is that AVR port of gcc doesn’t have appropriate isns to do that. The problem is extremely important because for example my program uses 964 bytes of SRAM, from which 860 bytes are used for VTABLES (wasted). This is a typical situation for any C++ program on AVR.
The only problem is speed penalty, LD instruction on AVR takes 1 cycle, whereas LPM instruction needed for FLASH access 3 cycles, which gives total 2 or 6 cycles to read virtual method address respectively. So probably the best solution is to add another gcc option (or pragma) to choose between storing VTABLES only in FLASH (slower code execution) or moving it to SRAM (faster execution for the cost of higher SRAM memory occupation).

Comment 3 Michał Walenciak 2011-02-06 19:30:06 UTC
Another idea is to simplify virtual calls.
At the final link, probably most of the virtual calls could be converted into regural calls (as whole program is known, and some base classes may have only one (or zero) descendant, so all virtual calls will affect only it).
Comment 4 Georg-Johann Lay 2011-11-21 15:50:11 UTC
(In reply to comment #0)

> Class test
> {
>     virtual void example();
> };

Would you please post some real code that shows the problem and actually *compiles* with, e.g.

avr-g++ -c vtable.cpp

instead of the lines above that trigger syntax error?
Comment 5 Tomasz Francuz 2011-11-24 21:56:17 UTC
Ok, here is the code:
class test
{
 public:
  test() {};
  virtual void vfunction();
};

void test::vfunction()
{
}


int main()
{
}

After compilation 6 bytes of SRAM is occupied by test object vtable. Here is a resulting part of map file:
*(.data)
 .data          0x00800100        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr51/crtm128.o
 .data          0x00800100        0x6 gpp.o
                0x00800100                vtable for test
 .data          0x00800106        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_exit.o)
 *(.data*)
Comment 6 Riccardo Manfrin 2012-11-29 11:57:58 UTC
I'm also experiencing this issue and would like a patch or something to be able to directly access vtables from flash on need.
I'd like to know if anyone has found even a temporary fix to that (even though it means losing to a non portable solution/bad code ...).
R
Comment 7 Côme David 2013-04-26 11:54:03 UTC
Hello.

I'm currently facing the exact same problem. I have a several classes with virtual methods and 50% of my RAM is taken by the vtables of these classes.

I'm still willing to know if someone has come up with a solution/workaround for this.

Thank you !
Comment 8 Georg-Johann Lay 2013-06-11 13:17:44 UTC
This needs extensions in the C++ front => Component = c++
Comment 9 Jackie Rosen 2014-02-16 13:17:02 UTC Comment hidden (spam)
Comment 10 Oscar García 2015-12-07 18:21:47 UTC
This problem can be mitigated using "pointer to interfaces".

Instead of:

class A
{
public:
A();
void FncEatingVTableRamA();
void FncEatingVTableRamB();
void FncEatingVTableRamC();
void FncEatingVTableRamD();
void FncEatingVTableRamE();
void FncEatingVTableRamF();

virtual void FncASingleVirtualFunctionPutAllVTableInRam();
}

I use to change it to:
class IVirtual
{
virtual void FncASingleVirtualFunctionPutAllVTableInRam();
}

class ANotInRamVTable
{
IVirtual* pVirtual;
public:
    A(IVirtual* pV)
    {
       pVirtual=pV
    };
    void FncEatingVTableRamA();
    void FncEatingVTableRamB();
    void FncEatingVTableRamC();
    void FncEatingVTableRamD();
    void FncEatingVTableRamE();
    void FncEatingVTableRamF();

void FncNOTVirtual()
{
    pVirtual->FncASingleVirtualFunctionPutAllVTableInRam();
}

class DerivedA:public IVirtual //Not derived actually
{
//REALIZE DerivedA will have VTABLE in ram because of IVirtual

    ANotInRamVTable aNotInRam;

    DerivedA():aNotInRam(this){};
    void FncASingleVirtualFunctionPutAllVTableInRam();
}

It will not solve the problem, and I agree this is an important obstacle to make reusable/scalable code in reduced-RAM environments but it helps sometimes.
Comment 11 Georg-Johann Lay 2017-07-31 09:08:16 UTC
This cannot be fixed in GCC as C++ doesn't support ISO/IEC DTR 18037 named address spaces.  This feature requires that the C++ front-end knows about ASes and handles them properly.  This doesn't imply that ASes are exposed on language level; knowing about ASes "internally" would be sufficient.  As it's pretty much clear that C++ WG21 will never accept such qualifiers (not even the C WG14) did, just close this as WON'T FIX.

All that can be done is proposing work-arounds: Use "Embedded-C++" coding convention that avoids VTABLEs, or use a device that can host .rodata in flash like families -mmcu=avrtiny and -mmcu=avrxmega3.
Comment 12 Matthijs Kooijman 2017-09-02 08:24:04 UTC
Apologies if this is an obvious question, but I'm not familiar with gcc/g++ internals. Georg-Johann, you say this requires address space support in c++, but I'm not sure I follow you there. Two things:
 - You say WG21 will never add AS support to C++, but also say that language support for AS is not needed, only internal support in gcc/g++. So that means what WG21 does is not relevant for vtable handling in particular?
 - Even if AS would not be used, what prevents g++ from emitting the vtables in the `progmem.data` section and generating vtable-invocation code using LPM instructions? This behaviour could be toggled using a commandline option, or some gcc-specific attribute on a class?

I would be happy if you could comment on the feasibility of these two approaches, thanks!
Comment 13 Georg-Johann Lay 2017-09-02 20:54:46 UTC
(In reply to Matthijs Kooijman from comment #12)
> Apologies if this is an obvious question, but I'm not familiar with gcc/g++
> internals. Georg-Johann, you say this requires address space support in c++,
> but I'm not sure I follow you there. Two things:
> You say WG21 will never add AS support to C++, but also say that language
> support for AS is not needed, only internal support in gcc/g++. So that
> means what WG21 does is not relevant for vtable handling in particular?

Front-end maintainers usually follow the WGs in what they implement and are willing to approve.  When you propose some non-standard extensions — even as a working patch with testcases, documentation, etc. — the maintainers will reject it.  They fear it might be inconsistent with existing features or the code "just being dropped" and maintenance burden is up to them.

Adding AS in the c++ front-end without exposing them to the language (i.e. you still can't use __flash in c++) will be rejected by the maintainers as "too specific", see below for similar case.

> Even if AS would not be used, what prevents g++ from emitting the vtables
> in the `progmem.data` section and generating vtable-invocation code using
> LPM instructions?

vtables are generated by the c++ front-end.  Some hacking in the avr back-end might be enough to but these tables in flash, but you cannot access it correctly without qualifying all accesses.  These qualifiers must be added by the c++ FE so that any pointers / accesses / (internal) variables derived from it use the correct AS.

We just saw the maintainers rejecting PR49857 (which is about putting tree-switch-converted lookup-tables into flash / named AS) as "too specific".  The avr part was approved.  The only change to the middle-end was a well documented hook (statically) invoked only once in tree-switch-conversion module.  The maintainers proposed "more generic" solution; none of proposals would work and none of them would be more generic because the only object that's opt to such optimization is CSWTCH from tree-switch-conversion.

I got the impression it wasn't even understood why one cannot just patch sections.  And patching ASes won't work if any pass might copy a pointer. The tables must be read-only, in static storage, not public, not weak, not linkonce, not comdat, and must be artificial, i.e. cooked up by the compiler (otherwise inline asm will break).  So the only use case was CSWTCH anyway...

For details see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49857#c17

vtables are even more restrictive because it would be an ABI change: all modules accessing the vtable must agree upon it's AS, i.e. you cannot specify the AS per command option.

> or some gcc-specific attribute on a class?

Attributes won't work — due to the same reason for why progmem does not work with c / c++: with progmem: you'd need inline asm.  And because vtables are artificial, you'll never gat a hand on them.

And to be honest:  My current impression is that avr-gcc is a dead horse.  http://gcc.gnu.org/ml/gcc/2017-07/msg00224.html

Maintainers are proposing to deprecate bunch of backends as a side effect of deprecating two essential features that are "old code" and not used by the targets they get their bucks for.  Sooner or later they will succeed, effectively throwing bunch of targets into the dust bin.

With that perspective and my recent impressions, I think working on avr-gcc has become a waste of time.
Comment 14 Matthijs Kooijman 2017-09-04 08:53:47 UTC
Thanks for the additional explanations!