Compiling into C


Coprocessor functions created by bulk-define are interpreted by the compiler's stack machine. Therefore, they tend to run rather slowly. Once functions are debugged, they should be compiled into C and linked directly into the coprocessor binary. This process takes a bit longer than defining an interpreted function, but the resulting code is much faster. (Our preliminary experiments suggest the difference is over an order of magnitude.)

In order to compile and link C code, the user must have a private copy of the coprocessor.

Generating the C code

To generate C code, first define your functions using bulk-define. When building a non-trivial system, you would normally edit the bulk-define forms in a file. You would then load the file into Scheme, as if it were standard Scheme code, and then issue the dump command to generate C code. A typical application contains both user-defined coprocessor functions and regular scheme functions: separate them into different source files.

A set of functions is compiled into C using the command

  (dump-c-functions list-of-function-names optional-directory-name)

List-of-function-names is a list of names of functions (symbols) for which C should be generated. All the functions in the list must already have been defined using bulk-define and must have compiled successfully. (That is, coprocessor stack machine code should exist for all functions on the list and they should be runnable from the interpreter.) Functions in the list may call one another, but not other user-defined coprocessor functions outside the list.

The optional-directory-name is a string that contains the pathname of a directory. If no directory is specified, the name defaults to "envision-private/coprocessor-code." The pathname will be interpreted relative to the user's home directory.

The command dump-c-functions rewrites two files in your private coprocessor directory: "user-functions.h" and "user-functions.c" These files contain the C code for your new functions. Warning: these files will be overwritten each time you issue a dump command. If you wish to save previously generated C code, copy the files before dumping new functions.

Compiling the C code

When the C code has been created, recompile your local copy of the coprocessor. Do this as follows, where the commands in brackets should only be required if you use this directory to build binaries for more than one architecture. Also, if a simple "make" fails mysteriously, issuing the commands in brackets may help.

  cd ~/envision-private/coprocessor-code
  [make distclean]
  [make clean]

This will create a new binary coprocessor file in this directory called "icp". Move it to your favorite binary directory and update your coprocessor init file (probably named .envision-init) to contain its pathname. Restart Envision and the coprocessor: it should report that it is starting your new binary.

You may wish to create several customized coprocessor binaries, with different sets of functions preloaded. In this case, you should give them more imaginative filenames than "icp" when you move them to your binary directory. You can easily toggle between your different binaries by editing your coprocessor init file.

Loading the C code

When you start Envision with a customized coprocessor image, you will typically need to load one or more files of Scheme code. These files contain code that does not need to run on the coprocessor, code for graphical and file I/O, user interface functions, and shell functions that provide "smart" access to your coprocessor functions (e.g. polymorphism).

The coprocessor functions loaded in your customized image appear to live in their own package. Therefore, they are not directly callable from the user package or packages containing your Scheme code. Your Scheme files will need to import them into the correct packages.

The function bulk-import imports the specified function into the current package. When given two inputs, it imports the function but binds it to a different name in your current package. The inputs to bulk-import are not quoted.

  (bulk-import function-name)
  (bulk-import new-name old-name)

Creating and using digests

When experimenting with complex systems, most people have a collection of standard utility functions which they incorporate into many of their coprocessor binaries. For example, you might habitually include coprocessor functions supporting your image reader, your image displayer, some standard image processing functions (e.g. rotate image), and a standard edge finder. When you are using one of your binaries to debug new functions, you can use your utility functions because they are linked into the coprocessor.

However, suppose that you want to make a new binary including your standard utilities, as well as some new functions that you just wrote. Because your utilities were linked into the coprocessor image, their source code is not installed and therefore is not available to dump-c-functions. Re-loading the source files for your utilities will work, but it is slow and overwrites the linked (fully compiled) versions of your utilities with slower interpreted versions. This could be annoying if you do something other than immediately restart Envision after creating your new binary.

The best way to incorporate standard utilities is to put them in a "digest" file. To create a digest file, read the utility functions into Envision as above. Then issue the command

The first input is in the same format as required by dump-c-functions. You can save to any file, though it may be convenient to put them in the directory ~/envision-private/digest. The file contains your source code and the output of the compiler through step 6. (It's human-readable if you are curious or if you've lost your original source code for some function.)

Now, suppose you are working in a fresh instance of Envision. The command read-digest will read these functions into Envision. By default, it does not generate and install assembler for the functions. Installing the assembler is slow. Also, the runable code is probably already directly compiled into the coprocessor.

If you wish to install assembler for the functions, supply #t as the optional second argument to read-digest. Notice that this shadows any fully-compiled code that may have been linked into your coprocessor binary. You must also import the names of these functions into the current package using bulk-import.

Whether you installed the assembler or not, the functions from the digest can now be used in generating new coprocessor binaries. When you issue a dump-c-functions command, you can include some or all of the functions from your digest as well as any selection of new functions.

Other useful commands

The following two commands are also sometimes helpful.

Bulk-compile runs the early parts of the compiler, generating the common intermediate language. This is sufficient for dumping C code. It omits generating and installing the assembler. This is much faster than bulk-define, because passing assembler code across the interface to the coprocessor is slow.

Install-assembler completes the work usually done by bulk-define. That is, it generates the assembler, installs it in the coprocessor, and defines the callable scheme shell function.


Ownership, Maintenance and Disclaimers

Manual Top Page

Last modified