Image Map Image Map
Page 1 of 3 123 LastLast
Results 1 to 10 of 27

Thread: DOS Smart linker

  1. #1

    Default DOS Smart linker

    My experience has been Borland products and I used them all the way up to Borland C++ 5.02 for DOS stuff. One thing that is annoying though is that TLINK is not smart and if any part of an OBJ file needs to be linked in, it links in the entire thing even if only one function was needed. Later after they stopped being able to produce DOS code and could only emit win32 code, Borland added an incremental linker that seems to be smart - it will only link in the functions that are needed. Ironically it does this in a win32 EXE where executable size vs. memory available isn't nearly as critical as it is in DOS where it can't do it!

    There was a technique that could be used to get around the lack of a smart linker and still produce the smallest possible executables for DOS, but it was a PITA to implement. It required putting each function in its own CPP file and then making a library out of all of them. The linker would then pull only the functions from the LIB file as needed. Again, a PITA to impllment and maintain.

    This got my wondering - were there other DOS linkers/C++ packages that supported smart linking since Borland did not?

  2. #2

    Default

    wlink from watcom, I believe is a smart depending on what you tell it... so maybe not.

    if you name a LIB file, all objects inside it get linked, but if you name a specific object in that library only that object gets linked in.

    One thing that is annoying though is that TLINK is not smart and if any part of an OBJ file needs to be linked in, it links in the entire thing even if only one function was needed.
    I dont know any that do that. If you compile a file that has 50 functions into a single object file, I know of no linker that would pull a single function out of it.

  3. #3

    Default

    Quote Originally Posted by BloodyCactus View Post
    I dont know any that do that. If you compile a file that has 50 functions into a single object file, I know of no linker that would pull a single function out of it.
    I did not think so until this morning either. Smart linking or dead code elimination, I'm not sure what it is, but I have a CPP file with a class in it and a dozen or so member functions. One of those functions calls a qsort_s which is not present in borland cppbuilder6, but it present in a newer version. If I don't use that one function that invokes qsort_s, the linker doesn't complain that qsort_s is missing. Yet all the other functions are linked in and working. So I decided to do a test like this:

    Code:
    void myfunc1();
    
    void myfunc2()
    {
      myfunc1();
    }
    
    void main()
    {
      myfunc2();
    }
    If I compile this, it complains that myfunc1() is missing at link time. If I remark out the myfunc2() function call in main, but still leave the myfunc2() function active, two things happen. #1 - no more linker error about myfunc1() despite myfunc2() being still present and calling myfunc1(), and #2 - myfunc2() disappears from the map file. If I look at the obj file, it still mentions myfunc2(). I think the code for myfunc2() is in the OBJ, but it is not being linked/pulled into the exe. I did another test where I forced the program to pull in myfunc2() from a different CPP file to see if it recompiles the original CPP/OBJ during a make. It did not rebuild it, but went back to complaining about myfunc1() being unavailable. I think that shows that it is indeed in the OBJ, but only pulled in if needed.

    The same test above where I remark out myfunc2() call in main under Borland C++ 3.1 for example still fails complaining about the myfunc1() missing.

  4. #4
    Join Date
    May 2003
    Location
    Back of Burke (Guday!), Australia
    Posts
    2,924

    Default

    The later Turbo Pascal did things inhouse (1 Step Compiler or whatever you want to call it), with the later Unit based versions (v4 onwards) utililsing what was used I thought.

    But that probably doesn't help you.

  5. #5

    Default

    I have worked with object file formats in the past. Mostly Intel and Microsoft OMF and a little with Borland's TPU.

    A smart linker needs to know the boundaries of the individual functions within your object file. Usually, the compiler has to generate special "comment" records to convey that information. I would suggest looking at the code generation options with the compiler. Maybe smart linking support had to be specifically turned on.

  6. #6
    Join Date
    Jan 2007
    Location
    Pacific Northwest, USA
    Posts
    34,356
    Blog Entries
    18

    Default

    Looking at your example, I suspect you may be observing the compiler optimizing out "dead code". Commenting out the sole reference to a function in a program module tells the compiler that the function body itself is unneeded and so not included in the compiled output.

    You can check this by looking at the assembly code output from the compiler.

  7. #7

    Default

    I wondered that too so I made a myfunc3() in a second module that might or might not call in myfunc2() to test it. From what I can see it does not rebuild the OBJ for the first module when I toggle whether it calls myfunc2(), so myfunc2() is always present.

    To be fair, these compilers are a far cry from gcc that I've worked with on AVR's. It will do whole program optimization and is tough to trick NOT to cut things out that it deems unimportant.

    Compile to assembly is a good idea - I can't access it through the GUI, but bcc32 has the option still so I can test it.

  8. #8
    Join Date
    Jan 2007
    Location
    Pacific Northwest, USA
    Posts
    34,356
    Blog Entries
    18

    Default

    "Dead code" elimination is some of the easiest optimizations to perform from a compiler writer's viewpoint. There's no downside to it--if you don't ever execute code, you can leave it out even before you start machine code generation. This even extends to basic block elimination--if you can't get there from here, leave "there" out. e.g.,

    Code:
       if (1) 
        statement-1;
      else
       statement-2;
    There's no point in even compiling statement-2, so most decent compilers will just leave it out of code generation. You can see a similar thing in gcc as regards unreferenced arguments to a function. To avoid the warning message, you incorporate a statement that generates no code. For example:

    Code:
      int myfunc( int a, int b, int c)
      {
        (void) c;
        return a+b;
      }
    The "(void) c" cast doesn't generate any code, but counts as a reference to an otherwise unreferenced argument.

  9. #9

    Default

    Absolutely Chuck - it is amazing the optimizing can be done. I use this often for AVR's:

    Code:
    #define Delay_ns(__ns) {if((unsigned long) (F_CPU/1000000000.0 * __ns) != F_CPU/1000000000.0 * __ns)\
                             __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000000000.0 * __ns)+1);\
                           else __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000000000.0 * __ns));}
    #define Delay_us(__us) {if((unsigned long) (F_CPU/1000000.0 * __us) != F_CPU/1000000.0 * __us)\
                             __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000000.0 * __us)+1);\
                           else __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000000.0 * __us));}
    #define Delay_ms(__ms) {if((unsigned long) (F_CPU/1000.0 * __ms) != F_CPU/1000.0 * __ms)\
                             __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000.0 * __ms)+1);\
                           else __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000.0 * __ms));}
    #define Delay_s(__s)   {if((unsigned long) (F_CPU/1.0 * __s) != F_CPU/1.0 * __s)\
                             __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1.0 * __s)+1);\
                           else __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1.0 * __s));}
    It has all sorts of nightmares for an 8 bit microcontroller, even floating point, but when given a static value, it compiles and eliminates that down to a single __builtin_avr_delay_cycles call with a static value.

    I did compile my example to assembly and sure enough, the myfunc2() is present along with its call to myfunc1()!

  10. #10
    Join Date
    Jan 2007
    Location
    Pacific Northwest, USA
    Posts
    34,356
    Blog Entries
    18

    Default

    So, what does the assembly call to Myfunc1 in Myfunc2 look like? Something doesn't compute here.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •