[comp.sys.mac.programmer] 32K jump table limit

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