However, Heizer shipped a demo of a stack that could be used on those XCMDs and recompile the 680x0 code to PowerPC (!).
Full text and the script from this stack for the curious:
Sample “Fat” XCMD compiled by CompileIt
The script of this field (click “See Script” button below) contains the source HyperTalk for the XCMD in this stack, which was compiled by CompileIt with a developmental binary recompiler module added. The recompiler will be made available from Heizer Software as an add-on to CompileIt, hopefully in the first half of 1995. CompileIt 3.0 (still under development) will integrate the recompiler and include improved symbol table management.
The recompiler allows XCMD developers to create “fat” XCMDs that run in any version of HyperCard, but when they are on a Power Macintosh they use native code for accelerated performance. You can also build mostly-native XCMDs with a 68K wrapper that exits gracefully if called on a 68K Mac. These XCMDs gain the full performance advantage of the PowerPC without any need for a native version of HyperCard. If and when Apple ships a native code version of HyperCard, these fat XCMDs will continue to take advantage of their native code acceleration, while the stack and card scripts come up to the native-code enhanced speed the fat XCMDs already enjoy.
How to Write a Fat XCMD (details subject to change):
Just write your XCMD or XFCN for CompileIt as you normally would, but separate out the main loops into subroutines and/or functions called by the main handler. Then duplicate these new handlers and give the copies slightly different names. Collect the copies at the end of your script, followed by a special function that lists them for recompilation (see the example script). You can also use this function to determine if it is safe to call the native code: your XCMD will bomb if you try to call native code handlers on a 68K Mac.
Don’t try to do too much in one native-code handler. To get the performance we all hope for, the PowerPC must put all its variables in registers. There are only 18 integer (plus 20 floating-point) registers available for variables; when these are used up in a handler, variables must be flushed to memory, which makes everything much slower.
How Much Faster Is It?
Apple has been telling developers that most code runs two to five times faster by going native. This is not strictly true. Programs with small, numerically intensive loops will do very well. Large program loops (like Excel, larger than your typical XCMD) will only go about 30% faster than emulated. XCMDs that mostly work on character strings will probably see no improvement at all, because these operations are done by Toolbox routines in ROM, which are already in native code. Your best improvement will be seen in floating-point operations. The CompileIt recompiler has been designed especially to optimize small loops with floating-point operations, which the PowerPC does well.
Anything that does not execute at least dozens of times in one XCMD call is probably not worth taking native, because the cost of calling the XCMD in the first place will dominate the first ten or more times through the loop.
What About Native-Only XCMDs?
As of this writing, Apple has not published guidelines for native-only XCMDs, but I do not expect them to perform significantly better than fat XCMDs, for the simple reason that the cost of loading the XCMD into memory, moving its handle high, and locking it down is the same whether native or emulated — it could even be somewhat higher for native XCMDs because they are bigger. CompileIt 3.0 will support the creation of native-only XCMDs if that is appropriate, along with other native-code or fat code resources.
One hurdle yet to be overcome is the file format for these: Apple is restricting the use of their native-code PEF file format to big developers willing to pay large $$ license fees. We are still looking at ways to get around that (our fat XCMDs do not use PEF).
The code:
-- Sample “Fat” XCMD compiled with (developmental) CompileIt!
-- This script will not run as-is in HyperCard, it must be compiled
global x:F -- shared variable required for return float value
on TestNativeX theTest -- compile this with Sane turned off
-- validate the test number and (if necessary) the CPU..
put theTest+0 into testnum
if testnum<1 or testnum>4 then -- invalid param, say what?
answer "Usage: TestNativeX theTest -- theTest:" & return ¬
& " -- 1= 68K integer, 2= 68K Sane," & return ¬
& " -- 3= native int, 4= native float"
exit TestNativeX
end if
if testnum>2 then if not ThisIsPowerPC() then
answer "That test requires a Power Macintosh"
exit TestNativeX
end if
-- initialize test data..
if testnum mod 2 =0 then -- floating-point data..
put 2147483648.00000190921128 into x
put 536870912.00000047730282 into y
put -1152921504606846976.0 into z
put 1 into testData
else put 0 into testData
if testnum>2 then put "(PowerPC)" into theCPU
else put "(68K)" into theCPU
put Ticks into here -- synchronize clocks..
repeat while Ticks=here
end repeat
put Ticks into here -- start timer
if testData>0 then -- floating-point test..
if testnum=2 then
Do68Kfloat y,z
put 1000 into testData -- else too slow!
else DoPPCfloat y,z
get (Ticks-here)*testData
answer "x*y+z = " & x & ", " & it & " ticks " & theCPU
else -- integer test..
if testnum=1 then put Do68Kint(2,3) into testData
else put DoPPCint(2,3) into testData
get Ticks-here
answer "x*y+5 = " & testData & ", " & it & " ticks " & theCPU
end if
end TestNativeX
function Do68Kint a,b -- identical 68K & native for comparison
repeat 1000000
put a*b+5 into c
end repeat
return c
end Do68Kint
on Do68Kfloat yy,zz -- 68K is simpler than PowerPC, because it’s slower
put x into xx
repeat 10000 -- then multiply time x 1,000
put xx^1*yy+zz into rr
end repeat
put rr into x
end Do68Kfloat
-- best: collect all native-code handlers at end..
function DoPPCint a,b -- identical 68K & native for comparison
repeat 1000000
put a*b+5 into c
end repeat
return c
end DoPPCint
on DoPPCfloat yy,zz
put x into xx
repeat 10000000 -- yes, ten million!
put xx^1*yy+zz into rr
end repeat
put rr into x
end DoPPCfloat
function ThisIsPowerPC -- this must be after last native handler
-- call this function once before calling native handlers
-- check for PowerPC Mixed-Mode Manager..
if Gestalt("mixd",ppc)≠0 then put false into ppc
if not ppc then return false -- important: exit now if not PowerPC
-- translate the 68K into PowerPC code..
INLINE ">Recompile,DoPPCint" -- recompiles at compile-time,
INLINE ">Recompile,DoPPCfloat" -- also initializes run-time
INLINE ">PPClibrary" -- last, necessary to link library & Toolbox
return true
end ThisIsPowerPC