Shop OBEX P1 Docs P2 Docs Learn Events
Code bloat when linking against full PropWare library — Parallax Forums

Code bloat when linking against full PropWare library

DavidZemonDavidZemon Posts: 2,973
edited 2014-12-09 20:14 in Propeller 1
If I link my basic "Hello, world!" program against the full PropWare library, I get massive code bloat (9k download). If I take only the necessary code from PropWare's cpp files and drop it in a file "extra.cpp" and then archive extra.cpp into libextra.a and link against that, it works perfectly (5k download).

How do I go about determining what extra code is getting linked into my executable?

Thanks,
David

Comments

  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-11-30 09:06
    I'm already using -ffunction-sections and -fdata-sections with -Wl,--gc-sections
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-11-30 09:27
    Okay... I think I've found the problem at least.

    I used ld's verbose option and found some differences between the compilations.
    Here's a colorful screenshot of the diff

    Here's the specific lines that only show up when I link against PropWare's full library:
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)_floatsisf.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)_loadfloat.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)eqsf2.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)gesf2.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)lesf2.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)fixsfsi.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)fixunssfsi.o
    (/opt/parallax/lib/gcc/propeller-elf/4.6.1/short-doubles/libgcc.a)floatunsisf.o
    
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-11-30 10:45
    nm shows some symbols matching up to the symbols in the previous post. But I'm not sure why they show up in main library and not simple case
    david@balrogJr:~/External/Kits/Embedded/Parallax/Library/PropWare/bin$ propeller-elf-nm PropWare/lmm/libPropWare_lmm.a 
    
    printer.cpp.obj:
             U ___addsf3
             U __clkfreq
             U CNT
             U ___cxa_pure_virtual
             U DIRA
             U ___divsf3
             U __DIVSI
             U __DIVSI_ret
             U ___eqsf2
             U ___fixsfsi
             U ___fixunssfsi
             U ___floatsisf
             U ___floatunsisf
             U ___gesf2
    
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-11-30 11:07
    It all comes down to printer.h. It uses the preprocessor to enable or disable floating-point support in printf. Since it is used only in the printer.h header file, I figured it would be safe to enable that option when compiling the PropWare library. Apparently, I was wrong. For some reason, having the option enabled means that floating point routines are always linked in when using the Printer class. I don't know how to avoid it.

    When I disable floating point support during compilation of the library, it remains disabled when compiling my executable. This part is even more confusing to me. What am I missing here?
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-11-30 20:08
    Been bashing my head on this problem all day. StackOverflow thread has been started:
    http://stackoverflow.com/questions/27221559/static-library-unused-symbols-definitions-in-header
  • ersmithersmith Posts: 6,053
    edited 2014-12-03 08:47
    It all comes down to printer.h. It uses the preprocessor to enable or disable floating-point support in printf. Since it is used only in the printer.h header file, I figured it would be safe to enable that option when compiling the PropWare library. Apparently, I was wrong. For some reason, having the option enabled means that floating point routines are always linked in when using the Printer class. I don't know how to avoid it.

    When I disable floating point support during compilation of the library, it remains disabled when compiling my executable. This part is even more confusing to me. What am I missing here?

    Do you mean you'd like to use preprocessor defines in printer.h to control what's already been compiled into the PropWare library? That's not how things work. Once the code is compiled into a library, whatever options were in effect at that time are frozen into the object file. If floating point was enabled at library compilation time, then the floating point symbols are in the library (and will always be in the library, unless you recompile the entire library).

    If you are, in fact, recompiling the library every time rather than linking against it, then using the preprocessor will work. But otherwise, you'll have to use the linker to control things, not the preprocessor. For example, you could have two versions of PropWare, one with floating point and one without, and then select at link time which one to use.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-03 08:58
    My understanding of how class compilation has been rectified thanks to the SO post. I thought that, if my methods were defined inline in the class, their definitions would not be placed in the library. Silly me.

    I tried playing with the prepocessor so that, when compiling the library, only method prototypes (and no prototypes using floats) are given. Unfortunately, that didn't work either. Dietmar from SO has suggested a new class structure that doesn't require any preprocessor magic though. I'm liking that a lot and will give it a try tonight (but it will ruin CLion's beautiful syntax highlighting and parameter inspections inside the format string :( )
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-03 19:54
    This is crazy... I'm now using templates in an embedded system. I think it's worth it though... I think that is better than providing two functions (print and printi) like Simple.

    Anyway, upon attempting to use templates, I'm getting a new error:
    /opt/parallax/bin/propeller-elf-g++    -fno-threadsafe-statics -fno-rtti -save-temps -Os -ffunction-sections -fdata-sections     -m32bit-doubles -Wall -std=gnu++0x -mlmm -isystem /home/david/External/Kits/Embedded/Parallax/Library/PropWare/simple -isystem /home/david/External/Kits/Embedded/Parallax/Library/PropWare    -o CMakeFiles/Hello_Demo.dir/Hello_Demo.cpp.obj -c /home/david/External/Kits/Embedded/Parallax/Library/PropWare/Examples/Hello/Hello_Demo.cpp
    In file included from /home/david/External/Kits/Embedded/Parallax/Library/PropWare/Examples/Hello/Hello_Demo.cpp:17:0:
    /home/david/External/Kits/Embedded/Parallax/Library/PropWare/PropWare/printer.h:412:9: error: template with C linkage
    

    That seems so odd to me. I've switch to using g++. I tried removing the "-fno-rtti" flag.

    This simple program functions correctly:
    #include <stdio.h>
    
    class MyTemplateClass {
    public:
    
        template <typename T, T... Fargs>
        void set (const T value) {
            this->x = value;
        }
    
        unsigned int x;
    };
    
    int main () {
        MyTemplateClass test1;
        MyTemplateClass test2;
    
        test1.set(3);
        test2.set('D');
    
        printf("Hello, world! %d", test1.x);
        printf("Hello, world! %c", test2.x);
    
        return 0;
    }
    

    Any ideas?
  • ersmithersmith Posts: 6,053
    edited 2014-12-04 06:28
    Sounds like your template is inside an extern C block:
    extern "C" {
    ...
    template blah
    ...
    }
    
    will produce the C linkage error. It's pretty common for header files to have the extern "C" statement in them.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-06 11:28
    Well... I'm getting closer! Still haven't found the extern "C" issue. But I was able to copy the code into a scrap project with no links to PropWare classes and it builds and runs correctly! Rather than copy all of the UART classes too, I just wrapped the Simple print methods in a PrintCapable class

    Code:
    https://gist.github.com/DavidZemon/f29e4a76093cc39ee4ef

    Hopefully I can find the extern "C" culprit by the end of the day...
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-06 13:55
    I'm beyond confused now. I have two files that have been run through the preprocessor... each one is functionally identical.... but one compiles, and the other fails with the "template using C..." error.

    The files are long. If you want to take a look, I'd encourage the use of a diff tool.

    I am open to any ideas out there!
  • ersmithersmith Posts: 6,053
    edited 2014-12-07 15:31
    This is indeed deeply weird. I looked at your PropWare_Scratch_broken.ii, and removing all the lines starting with '#' causes the error to change:
    ersmith@octavian:/tmp$ propeller-elf-g++ -std=c++0x -c PropWare_Scratch_broken.ii 
    In file included from /home/david/External/Documents/Programming/PropellerProjects/PropWare_Scratch/PropWare_Scratch.cpp:360:0:
    /home/david/External/Kits/Embedded/Parallax/Library/PropWare/PropWare/printer.h:411:9: error: template with C linkage
    
    ersmith@octavian:/tmp$ grep -v '^#' PropWare_Scratch_broken.ii  > foo_bad.ii
    
    ersmith@octavian:/tmp$ propeller-elf-g++ -std=c++0x -c foo_bad.ii foo_bad.ii:162:13: error: redeclaration of C++ built-in type 'wchar_t' [-fpermissive]
    

    Somehow the lines inserted by the preprocessor must implicitly be tracking whether a file was C or C++; possibly using the C++ form:
    #include <class>
    
    automatically triggers C++ mode, versus using:
    #Include <class.h>
    

    Is it possible that your original sources differed in how they did the includes?

    (Looking at the gcc documentation, in the discussion of preprocessor output it does mention that the preprocessor has a flag, 4, which is put on # lines "to indicate that the following text should be wrapped in an implicit extern "C" block"). That flag is present on all the lines relating to printer.h in the failing example, but not in the example that works. So there's something different about how the #include of printer.h happened in those two examples.)
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-07 19:12
    (Looking at the gcc documentation, in the discussion of preprocessor output it does mention that the preprocessor has a flag, 4, which is put on # lines "to indicate that the following text should be wrapped in an implicit extern "C" block"). That flag is present on all the lines relating to printer.h in the failing example, but not in the example that works. So there's something different about how the #include of printer.h happened in those two examples.)

    WOH! That's not something I was expecting.

    I brought this on myself. I'd set the PropWare directory to be included via "-isystem". For whatever reason, that was setting the directory to C-only. According to this SO thread, it looks like the problem could also be resolved by patching PropGCC - but I've switched away from using "-isystem" in propware for the moment and that's fixed the problem!!! Finally! Now to see if all this trouble pays off in terms of code-size...
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-07 20:18
    Well I've got some major code bloat issues to work out, but it is functional at least. A basic "hello world" using Printer.printf takes 9.6 kB :/ Ouch. Using SimplexUART.puts takes only 4k. More work to be done still, but for anyone curious to check out printf with templates (lol), it's on github.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-12-09 20:14
    Well, finally working. The code bloat was due to having `virtual` on all the methods. I guess SynchronousPrinter won't be polymorphic :/ In fact... it's not even going to inherit from Printer in the next iteration :(

    PropWare's
    pwOut.printf("Hello, world! %03d 0x%02X" CRLF, i, i);
    
    sits at 5,908 kB.

    vs. Simple's
    printi("Hello, world! %03d 0x%02x\n", i, i);
    
    sits at 4,872 kB.

    However, simplifying it by removing the variable parameters yields 4,124 kB for PropWare using printf, 3,736 kB using Simple's putStr, and 4,828 kB using Simple's printi. Pretty cool I think - especially when this same Printer class can be easily used for printing on LCDs, I2C implementations, or anything else that implements the put_char and puts methods :D
Sign In or Register to comment.