Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
Getting g++ to compile constant objects directly into memory
View unanswered posts
View posts from last 24 hours

 
Reply to topic    Gentoo Forums Forum Index Portage & Programming
View previous topic :: View next topic  
Author Message
Akkara
Administrator
Administrator


Joined: 28 Mar 2006
Posts: 5163
Location: &akkara

PostPosted: Fri May 17, 2013 6:24 am    Post subject: Getting g++ to compile constant objects directly into memory Reply with quote

Here's a problem I keep coming back to, unable to find a good answer for.

Here's a sample program to show the problem:
Code:
class Color {
    private:
        unsigned    c;

    public:
        Color(int r = 0, int g = 0, int b = 0)
            : c( (r & 0xFF) + 256*(g & 0xFF) + 65536*(b & 0xFF) )
        {}

        Color   &red(int r)
        {
            c &= ~0xFF;
            c |= (r & 0xFF);
            return *this;
        }

        Color   &green(int g)
        {
            c &= ~(256 * 0xFF);
            c |= 256 * (g & 0xFF);
            return *this;
        }

        Color   &blue(int b)
        {
            c &= ~(65536 * 0xFF);
            c |= 65536 * (b & 0xFF);
            return *this;
        }
};

static const Color  color_tab[] = {
    Color().red(0xFF),
    Color().red(0xFF).green(0xC0),
    Color().red(0xC0).green(0xFF),
    Color().green(0xFF),
    Color().green(0xFF).blue(0xC0),
    Color().green(0xC0).blue(0xFF),
    Color().blue(0xFF),
    Color().blue(0xFF).red(0xC0),
    Color().blue(0xC0).red(0xFF)
};

static const Color  color_tab2[] = { // Try it with the constructor alone in case that makes a difference
    Color(0xFF,0x00,0x00),
    Color(0xFF,0xC0,0x00),
    Color(0xC0,0xFF,0x00)
};

There's a static const global table filled with simple objects. The constructors are all simple. Just a bunch of arithmetic operations on integers. No file-opening or pointers or allocations or virtuals or anything complicated like that.

It ought to be possible to compile these tables directly into memory.

But it doesn't.

It gets almost the whole way there: it resolves to the final constant values that are needed. But instead of sticking those into memory, it instead emits a procedure consisting of long chains of move X into memory at address A. This is the result when compiled with g++ -O2 -S:
Code:
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .type   _GLOBAL__sub_I_z_try2.c__, @function
_GLOBAL__sub_I_z_try2.c__:
.LFB7:
        .cfi_startproc
        movl    $255, _ZL9color_tab(%rip)
        movl    $49407, _ZL9color_tab+4(%rip)
        movl    $65472, _ZL9color_tab+8(%rip)
        movl    $65280, _ZL9color_tab+12(%rip)
        movl    $12648192, _ZL9color_tab+16(%rip)
        movl    $16760832, _ZL9color_tab+20(%rip)
        movl    $16711680, _ZL9color_tab+24(%rip)
        movl    $16711872, _ZL9color_tab+28(%rip)
        movl    $12583167, _ZL9color_tab+32(%rip)
        movl    $255, _ZL10color_tab2(%rip)
        movl    $49407, _ZL10color_tab2+4(%rip)
        movl    $65472, _ZL10color_tab2+8(%rip)
        ret
        .cfi_endproc
        .size   _GLOBAL__sub_I_z_try2.c__, .-_GLOBAL__sub_I_z_try2.c__
        .section        .init_array,"aw"
        .align 8
        .quad   _GLOBAL__sub_I_z_try2.c__
        .local  _ZL9color_tab
        .comm   _ZL9color_tab,36,32
        .local  _ZL10color_tab2
        .comm   _ZL10color_tab2,12,4
Why does it do such a silly thing?

All those constants like $49407 could have been put directly into the _ZL9color_tab memory area using assembler initialized-storage directives, without using a procedure to act as an intermediary. Instead, this takes around 3x the space that would otherwise be required: the data itself is in two places (space for the table itself, and an equivalent amount of space in all those $-prefixed numbers serving as initializers), plus there's the load and store opcodes, the addressing offsets, and the procedure call/return.

(I also tried it with char r, g, b as the private data members, in case the arithmetic was causing problems. That result is even worse.)

I know, memory is cheap and all that. But what if it's a more constrained embedded system. Or perhaps it's a big table. It's stuff like this that's keeping me wedded to ugly #define macro constants and C-like code instead of finally making the long-overdue switch to a more object-oriented style.

Perhaps there's an option or language feature I'm not aware of? Maybe a newer compiler finally fixes it? I'm currently trying it on g++ 4.7.2. But I've been trying versions starting around 4.3 or 4.4 and the results have been similar with each.

Ideas?
Back to top
View user's profile Send private message
MustrumR
n00b
n00b


Joined: 15 Nov 2011
Posts: 54
Location: Right here

PostPosted: Fri May 17, 2013 3:47 pm    Post subject: Reply with quote

Try making the constructor constexpr.
Back to top
View user's profile Send private message
John R. Graham
Administrator
Administrator


Joined: 08 Mar 2005
Posts: 7718
Location: Somewhere over Atlanta, Georgia

PostPosted: Fri May 17, 2013 4:14 pm    Post subject: Reply with quote

I doubt that will help as, according to the generated assembler snippet Akkara provided, the compiler is already resolving each constructor / method invocation to an integer constant. It's not generating code to calculate the value to fill but only to do the actual fill.

- John
_________________
This space intentionally left blank.
Back to top
View user's profile Send private message
Akkara
Administrator
Administrator


Joined: 28 Mar 2006
Posts: 5163
Location: &akkara

PostPosted: Sat May 18, 2013 3:30 am    Post subject: Reply with quote

MustrumR wrote:
Try making the constructor constexpr.

Thanks for that suggestion! It seems like it leads somewhere.

Specifically, the constructor-initialized table gets filled directly, no longer through an intermediate procedure. But for that to happen, in addition to constexpr, it seems the table also has to be declared extern... Which in regular code appears in the header file anyway, unlike the ill-chosen static in this abbreviated example.

The build-it-by-parts version can't seem to use constexpr - apparently assignment isn't allowed in a procedure declared that way.

I'll take some more playing with it before I can write more definitively. But thanks again for this. It is the first time I've seen a compile-time-instantiable object actually get instantiated right down to the in-memory bit-patterns, using no extra support code. This is a big deal in embedded systems where you'll often have plenty of ROM but only 4 or 8 K (not M) of RAM. And although the table is a const and could well be in ROM, the old initialize-with-procedure style requires it to use up valuable RAM just so the initializer can write to it.
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Portage & Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum