rick@jessica.stanford.edu (Rick Wong) (08/25/88)
Much has been written here about the Mac's 32K global DATA limit. I've got a nastier problem: the 32K JUMP TABLE limit is too small. On the Mac, code is divided into segments, to allow code to be swapped into memory as needed. Routines call routines in other segments indirectly, via the jump table. Also, Object Pascal uses the jump table for method dispatching. Since each entry in the jump table is 8 bytes, the maximum number of entries is about 4K (4091, accounting for extra fields before the jump table). At first, over 4000 procedures seems a huge number. It isn't. I am using MacApp, which is written in Object Pascal. Every method requires requires another jump table entry. The simplest MacApp application, which does "nothing," uses about 1100 entries with debugging, 600 without. The remaining entries don't seem so numer- ous in large MacApp applications. (By the way, the MPW 2.0.2 linker goes into an infinite loop when an application goes over this 4K limit.) Does anyone have any suggestions for dealing with this problem? (This looks like a job for Apple.) Rick Wong Courseware Authoring Tools Project, Stanford University rick@jessica.stanford.edu
oster@dewey.soe.berkeley.edu (David Phillip Oster) (08/25/88)
The 4000 global procedure limit is a problem. There is a workaround in C and a workaround in pascal. In C, divide your program into modules, each module in its own files. All those procedures that are called only from within that module (and for a well written program, that should be most of the procedures, should be declared "static" which keeps them visible only inside that file, and on most compilers generated local jsrs that don't use the jump table. Of course, if you pass out pointers to procedures, they will get jump table entries whether you declare them static or not. In Pascal, define helper procedures lexically inside other procedures: procedure entry; procedure helper; begin { helper } ... end { helper } ; begin { entry } ... helper; helper; end {entry } ; Beware, if you pass a pointer to such a procedure to the operating system, it will not work. Beyond that, and you have to go to executable code resources that take an integer a function selector, like the PACKs do. Yes, it is a pain, but it is a chance to improve the style and maintainability of your code: It is easier to understand a program where the original author has been careful to delimit the scope of the procedures: If you know that procedure "x" is only called inside file "a", then you don't have to waste time thinking about the implications if it were called from file "b". --- David Phillip Oster --When you asked me to live in sin with you Arpa: oster@dewey.soe.berkeley.edu --I didn't know you meant sloth. Uucp: {uwvax,decvax,ihnp4}!ucbvax!oster%dewey.soe.berkeley.edu
rick@Jessica.stanford.edu (Rick Wong) (08/26/88)
In article <25797@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu.UUCP (David Phillip Oster) writes: >The 4000 global procedure limit is a problem. There is a workaround in C >and a workaround in pascal. > [stuff about workarounds in C and Pascal] >--- David Phillip Oster --When you asked me to live in sin with you Sorry I didn't make myself clear enough. I'm programming primarily in Object Pascal (using MacApp). Although the jump table limit may be tractable for "traditional" languages, it is not for Object Pascal. MacApp encourages a particular programming style: -- Its error-recovery mechanism requires you to pass a procedure parameter to a failure-handling routine. MPW Pascal allocates a jump table entry for all procedure parameters, regardless of whether they are nested. -- When you write a new class, there are certain methods you almost always have to define, notably initializing, freeing, and inspect- ing methods. For deep class hierarchies, with additional proto- cols layered one on top of the other, this can get significant. -- Objects often have attributes you want access to. I consider directly manipulating an object's fields bad style, so I try to write methods that provide a clean interface for accessing object attributes. Writing lots of "Get..." and "Set..." methods quickly eats up jump table entries. -- For the user to do something to an object (e.g., drag, cut), MacApp requires you to create a separate "command" object for each operation (MacApp uses commands to implement its "Undo" protocol). Commands typically have about ten methods (DoIt, UndoIt, RedoIt, etc.). It's not uncommon for units defining fairly sophisticated objects to have on the order of 100 method definitions. Here's the kicker: Apple's implementation of method dispatching uses a jump table entry for EVERY method, regardless of whether it's only called from within the same segment. 4091 is not a large number. 8-( The only real solutions to this problem have to come from Apple, or some other development system developer (Think?). Either Object Pascal's method dispatching needs to be redesigned so it doesn't rely on the jump table, or the 32K jump table limit has to be lifted. Rick Wong Courseware Authoring Tools Project, Stanford University
ech@poseidon.UUCP (Edward C Horvath) (08/30/88)
> It's not uncommon for units defining fairly sophisticated objects to > have on the order of 100 method definitions. > Here's the kicker: Apple's implementation of method dispatching uses > a jump table entry for EVERY method, regardless of whether it's only > called from within the same segment. 4091 is not a large number. 8-( If all you say is true, you've got BAD trouble...however... The allocation of jumpTable entries should be a function of the linker, not the compiler or assembler: i.e. intra-segment calls should not create a jumpTable entry. Of course, it only takes one inter-segment call to a routine to force the routine's entry into the jumpTable. As a particular case, if MacApp does dynamic binding through some kind of "central dispatcher", you're well and truly sunk. Even using a PACK-like strategy doesn't help here, since the PACK glue is also 8 bytes a pop, but allocated from the 32K-limited central dispatcher's CODE segment. Typical glue: move.w #selector,-(sp) jmp common_entry(a5) Now, if there ISN'T a central dispatcher, then what you need to do is what was already suggested: try to group your methods into CODE segments to reduce inter-segment calls. In particular, linking offspring classes into the same segments as their parent classes will reduce inter-segment calls due to inheritance. My condolences: this sort of nonsense shouldn't be apparent to the application designer, particularly in an O-O programming environment. We groty systems-programming types should be the only ones contending with such issues. At least it ain't MS-DOS... Perhaps Larry Rosenstein (lsr@apple) could shed some light? =Ned Horvath=
lsr@Apple.COM (Larry Rosenstein) (08/30/88)
In article <3425@Portia.Stanford.EDU> rick@jessica.stanford.edu (Rick Wong) writes: > >Does anyone have any suggestions for dealing with this problem? >(This looks like a job for Apple.) This has been discussed at length on AppleLink. Unfortunately, there is no quick workaround. Object Pascal uses jump table entries as method and class IDs. Also, the method tables themselves contain the jump table offset for the method code. (I believe that there will be one jump table entry for each class, for each newly-defined method, and for each implementation of a method.) This was done primarily as an implementation convenience. When you optimize, the class IDs are assigned by the linker in sequence (no jump table entry needed), and many entries in the method tables are eliminated. This greatly reduces the number of jump table entries required. So one workaround is to always build an optimized version of your program. (I believe that the MacApp team is investigating whether it is possible to optimize an application, but still keep the MacApp debugger. This has not been done for the MacApp 2.0 beta release.) The linker only creates jump table entries when it has to. If a routine is only referenced from within its segment, then there is no need for a jump table entry. Combining some of your segments may help a bit, if they contain regular procedures and functions (not methods). I didn't think that MPW Pascal allocated a jump table entry for every procedure parameter. What you may be seeing is the fact that some MacApp units contain a {$B-} compiler flag to force all procedure references to have jump table entries. I think that the MacApp team has removed these directives from the MacApp 2.0 sources. You can add {$B+} directives to your units to tell the linker to use PC-relative addressing. (We put the $B- flag in to ensure that MacApp didn't save a PC-relative address away. This turned out to be overkill.) The problem is recognized by the MacApp team, and they are looking at ways to solve the problem. I agree that it is unfortunate for programmers to have to worry about this. Larry Rosenstein, Object Specialist Apple Computer, Inc. 20525 Mariani Ave, MS 46-B Cupertino, CA 95014 AppleLink:Rosenstein1 domain:lsr@Apple.COM UUCP:{sun,voder,nsc,decwrl}!apple!lsr