Skip to content
Felix S. Klock II edited this page Jul 28, 2013 · 1 revision

You need to tell the compiler two things about your operation. You need to tell it how to emit native code for the optimization (see ImplementingPrimOps), and you need to register the operation's name, arity, etc in a table of operations.

Note that this page applies only to operations that are procedure-like. They must appear with op1, op2, etc instructions. The "operation" names used by instructions like reg/op2/branchf are entirely different. For those see the page on the PeepholeOptimizer.

The tables of operations for the Sparc backend are in source:trunk/larceny_src/src/Compiler/sparc.imp.sch. There are a couple of different tables for operations:

  • minimal-integrable-procedues - This seems to be the table for the operations we're adding. These operations don't correspond to procedures a normal programmer would use, but to operations only the compiler knows about.
  • r4rs-integrable-procedures - These are your standard primitive Scheme procedures, the ones that can't be easily implemented in terms of other Scheme procedures.
  • usual-integrable-procedures - These are all the procedures that Larceny has that Scheme doesn't. Some of them allow you to do nasty things to data structures--for example, with typetag-set! you can turn a floating point number into a string. Or a bignum. And don't even think about procedure-set!.
  • It's not an operation table, but at the end of the file there are a bunch of definitions of instruction names. This is where you add new instructions for the peephole optimizer to generate. The core instructions are in source:trunk/larceny_src/src/Compiler/common.imp.sch.

The compiler recognizes syntactic calls to integrable procedures and transforms them to uses of the corresponding operation. Adding your operation to the minimal-integrable-procedures table will make sure that if its external name is ever used in a call (with the right number of arguments), the compiler will produce the code associated with the internal operation name.

On the other hand, only syntactic calls--now look at source:trunk/larceny_src/src/Lib/Sparc/primop.sch. There are a bunch of definitions there that look like this:


  (define car (lambda (x) (car x)))

The compiler doesn't do anything with integrable procedures that aren't in calls--they're left as variable references. So the primops file defines a bunch of global variables to be procedures which wrap the operations.

But that's not enough to be able to type in, for example, .+:flo:flo at the repl and get something back. The environment of the repl is different from the environment that the primops are defined in--the system environment. This prevents people from derailing the Scheme system by messing with system procedures.

There are two other files that manage copying system environment bindings to the repl environment. Look in source:trunk/larceny_src/src/Lib/Common/toplevel.sch and source:trunk/larceny_src/src/Lib/Sparc/toplevel-target.sch, and you'll see long lists of calls to environment-set!. This is where the different environments are set up. (We only care about the "larceny" environment.) If you want, you can try adding a line in source:trunk/larceny_src/src/Lib/Sparc/toplevel-target.sch to put your new primop in the repl's environment.

You probably don't need to do that just for testing. This is just so no one has a nervous breakdown when (foo 1 2) does something but foo claims to be undefined. That's no fun the first time that happens to you.

Clone this wiki locally