Porting CM3 to a new platform (hardware architecture / operating system combination) essentially consists of the following tasks:
Since the compiler is written almost entirely in Modula-3, you then face the task of compiling, assembling, and linking the code including all your fine new additions for the new target platform. You generally have two choices here:
In the following sections we'll look at all the things needed to be done in turn. We'll assume that you use the gcc backend for code generation, and that your platform is already supported by gcc. If not, you will have to write the necessary gcc extensions, too; but this would be beyond the scope of this article.
The first thing you do is choosing a name for the new platform, like FreeBSD4 or LINUXLIBC6. We'll assume GREATOS in the following description. You then edit the following files:
Create the directories listed below. Start again by copying the files for some similar system, then check all the contents for the new platform GREATOS.
The interfaces in this directory define simple access to POSIX standard input/output, signal, and setjmp/longjmp. Check all the type definitions and sizes against the corresponding C declarations in your system header files in /usr/include and /usr/include/sys. It is very important that the jmp_buf size is correct and that setjmp/longjmp save all necessary state, including floating point information, as this is used for thread switching.
Simply define the correct dtoa interface. Use a symbol from your system libraries or the supplied code from another platform.
This directory contains the basic system dependent runtime code for your platform. Start with the RTMachine.i3 interface and continue with the signal and thread bits. You will probably disable the VM protection needed by the incremental and generational garbage collector for the time being by setting VMHeap = FALSE. VM protection needs page protection support from the underlying operating system, and a set of wrappers for all system calls. (I'll write up something on this topic later.) You can add this feature later. Comment all things not needed in the accompanying m3makefile.
This directory contains the Unix / POSIX interfaces needed by the core library. You do not need to write and check everything right now, but quite a lot of this stuff is needed. Start with Utypes.i3, Ustat.i3, Uerror.i3, Uexec.i3, Usignal.i3, Uuio.i3, Udir.i3, Unix.i3. I think this should be enough for a simple working compiler :-)
If you've got so far, you've almost finished your task. There are still some simple adjustments to be made. Edit the following files:
Add an entry to the _FloatPieces array for your GREATOS.
Add an entry for the exception implementation to EXCEPTION_IMPL. You will probably just want to use ex_frame for now.
Define the date implementation your GREATOS uses. Just choose between the existing ones; one of them should fit your needs.
Add an entry to _UnixPieces for GREATOS that defines the source directories for your system interfaces.
Add GREATOS to the platform definition.
Choose the random implementation for floating point numbers by adding an entry in _RandomPieces.
Add entries for architecture and operating system name for GREATOS.
Add an entry for GREATOS to Platform_info in m3-sys/m3cc/src/m3makefile. This defines the GNU platform description used for your new target machine. You should now be able to build a cross compiler on your host system with the command
cm3 -build -DM3CC_HOST=HOST_SYSTEM -DM3CC_TARGET=GREATOS
You may also need to add some more magic stuff depending on your new target architecture (look for DS3100 and ALPHA_OSF for examples). The generated cross-compiler will be in HOST_SYSTEM/cm3cg. Copy this executable to the bin directory of your installation (usually /usr/local/cm3/bin) as cm3cg-GREATOS. Do not accidentally overwrite the existing code generator for HOST_SYSTEM!
There's now also a script to automate the building of the cross-compiler.
% ./build-cross-backend.sh -h usage build-cross-backend.sh: build-cross-backend.sh [-f] M3_CROSS_TARGET builds a cm3cg backend for target platform M3_CROSS_TARGET options: -f force distclean before compilation
You now should have a working compiler backend for GREATOS, but you still need a compiler frontend that knows about GREATOS. Recompile the following packages in this order:
You can use the scripts in cm3/scripts to perform the compilations. Backup the existing compiler in /usr/local/cm3/bin/cm3-old, and install the new compiler as /usr/local/cm3/bin/cm3.
Note: You can only compile some platform specific code in m3core and libm3 with a compiler that includes exactly the same list of target platforms in Target.i3, InfoModule.m3 and Compiler.i3; otherwise version stamps will get messed up. So you have to make your port-specific changes, build and ship the compiler packages m3middle, m3linker, m3front, m3quake, cm3, install the compiler, and then build the libraries m3core and libm3 and the compiler packages again (do-cm3-core.sh).
If you have built and installed the cross compiler frontend and backend, you need to edit /usr/local/cm3/bin/cm3.cfg. Change the definition for m3back to ... cm3cg-GREATOS and set M3_BOOTSTRAP = TRUE at the end of the file. Also change the TARGET to GREATOS at the top.
You should now be able to compile all the packages from the previous step again, this time generating assembler code in the GREATOS subdirectories.
The following script will archive the cross-compiled assembler code:
% ./pack-crossbuild.sh -h usage pack-crossbuild.sh: pack-crossbuild.sh cross_target will archive the following cross-compiled packages: m3core libm3 m3middle m3linker m3front m3quake cm3 m3scanner m3tools m3cgcat m3cggen m3bundle bitvector digraph parseparams realgeometry set slisp sortedtableextras table-list tempfiles The archives will be saved in a cm3/CROSS_TARGET.
Another possibility is to use rsync to copy the generated code, as is done in copy-bootarchives.sh:
#!/bin/sh # $Id: porting.html,v 1.15 2007/06/22 15:48:05 hosking Exp $ if [ -n "$ROOT" -a -d "$ROOT" ] ; then sysinfo="$ROOT/scripts/sysinfo.sh" else root=`pwd` while [ -n "$root" -a ! -f "$root/scripts/sysinfo.sh" ] ; do root=`dirname $root` done sysinfo="$root/scripts/sysinfo.sh" if [ ! -f "$sysinfo" ] ; then echo "scripts/sysinfo.sh not found" 1>&2 exit 1 fi export root fi . "$sysinfo" . "$ROOT/scripts/pkginfo.sh" RSYNC=${RSYNC:-rsync} DEST=${DEST:-lamancha.opendarwin.org:work/cm3} if [ -z "$1" ] ; then echo "please specify a cross compilation target platform" 1>&2 exit 1 fi CROSS_TARGET="$1" shift if [ -n "$1" ] ; then PKGS="$@" else PKGS="" fi P="" P="${P} m3-libs/m3core" P="${P} m3-libs/libm3" P="${P} m3-sys/m3middle" [ "${M3OSTYPE}" = "WIN32" ] && P="${P} m3-sys/m3objfile" P="${P} m3-sys/m3linker" [ "${GCC_BACKEND}" != yes ] && P="${P} m3-sys/m3back" [ "${GCC_BACKEND}" != yes ] && P="${P} m3-sys/m3staloneback" P="${P} m3-sys/m3front" P="${P} m3-sys/m3quake" P="${P} m3-sys/cm3" P="${P} m3-tools/m3bundle" if [ -n "${PKGS}" ] ; then res="" for s in ${PKGS}; do for p in ${P}; do case ${p} in *${s}*) res="${res} ${p}";; # echo "res = ${res}";; esac done done P="${res}" fi for p in ${P}; do echo ${RSYNC} -avz ${ROOT}/${p}/${CROSS_TARGET}/ ${DEST}/${CROSS_TARGET}/${p}/ ${CROSS_TARGET}/ ${RSYNC} -avz ${ROOT}/${p}/${CROSS_TARGET}/ ${DEST}/${CROSS_TARGET}/${p}/${CRO SS_TARGET}/ done
Copy the m3-libs and m3-sys hierarchies (or just the packages named above) including the generated assembler code to the new platform. Use the system assembler, C compiler, archiver, and linker to build all the libraries in the GREATOS sub-directory. As I don't know about these tools before, I cannot give more detailed instructions about the exact commands needed.
The script ppc-cross-build.sh may be used as a template for automating most of the recurring tasks of the cross-compilation bootstrap:
#!/bin/sh # This script is an example of how to automate a cross-compilation for # bootstrapping cm3 on a new target platform. Values are set for # cross-compilation from FreeBSD4 to PPC_DARWIN running on # lamancha.opendarwin.org. TARGET=${TARGET:-PPC_DARWIN} WORK=${WORK:-/Users/wagner/work} CM3DEST=${CM3DEST:-/Users/wagner/local/cm3} CM3DESTBIN=${CM3DEST}/bin CM3DESTHOST=${CM3DESTHOST:-lamancha.opendarwin.org} ./boot-cm3-core.sh ${TARGET} || exit 1 ./copy-bootarchives.sh ${TARGET} || exit 1 ssh ${CM3DESTHOST} \ cd ${WORK}/cm3/scripts '&&' \ ./boot-cm3-build-on-target.sh ${TARGET} \; \ [ -f ${CM3DESTBIN}/cm3 ] '&&' mv ${CM3DESTBIN}/cm3 ${CM3DESTBIN}/cm3.bak \;\ cp ${WORK}/cm3/${TARGET}/m3-sys/cm3/${TARGET}/cm3 ${CM3DESTBIN}
It also uses boot-cm3-build-on-target.sh which performs the necessary build steps on the target machine if parameterized appropriately.
Create an empty cm3 directory structure on your target system (cm3/scripts/create-skel.sh). Install the newly linked cm3 executable and the default configuration file cm3.cfg in the bin directory of this structure. Add this directory to your PATH.
Use
cm3 -build -DM3CC_HOST=GREATOS -DM3CC_TARGET=GREATOS
to build the code generator and install it as /usr/local/cm3/bin/cm3cg.
You should now have a working compiler and proceed to build itself with it. You can use the scripts cm3/scripts/do-cm3-min.sh and cm3/scripts/boot-cm3-with-m3.sh, or just cm3/scripts/do-pkg.sh with all the package names, i.e.
./do-pkg.sh build m3core libm3 m3middle m3linker m3front m3quake cm3 ./do-pkg.sh buildship m3core libm3 m3middle m3linker m3front m3quake cm3
Only do the second step if the build succeeded. You can now start to clean everything up, add all the missing bits (system interfaces, vm protection, etc.), and build all the rest of the CM3 packages.