-
Notifications
You must be signed in to change notification settings - Fork 32
ImplementingPrimops
You implement an operation by registering with the operation's name a procedure that emits native code for that operation. The procedures have types something like this:
car-primop : assembly-structure -> void
vector-set!-primop : assembly-structure register register -> void
car
has arity 1, so its single argument is in the result register ($r.result
) and doesn't need to be given as a parameter. vector-set!
takes 3 arguments, one in the result register and the other two registers given as parameters.
If you look at source:trunk/larceny_src/src/Asm/Sparc/sparcprim-part1.sch}}}, though, you see code like the following:
(define-primop 'cons
(lambda (as r)
(emit-primop.4arg! as 'internal:cons $r.result r $r.result)))
The simple cons
operation is implemented in terms of a more general internal:cons
operation which takes its source and destination registers explicitly. internal:cons
could never appear in an op2
instruction, but it is the kind of operation name that occurs inside of reg/op2/setreg
instructions introduced by the peephole optimizer.
Sooner or later, after making your way through various abstractions, you get to the code that actually generates SparcInstructions.
The file source:trunk/larceny_src/src/Asm/Sparc/sparcasm2.sch defines a procedure for every Sparc instruction that the assembler uses. The procedures have a type something like this:
sparc.addr : assembly-structure register register register -> void
sparc.bne : assembly-structure label -> void
Let's look at the definition for internal:car
(found in sparcprim-part2.sch
):
(define-primop 'internal:car
(lambda (as src1 dest)
(internal-primop-invariant2 'internal:car src1 dest)
(if (not (unsafe-code))
(emit-single-tagcheck-assert-reg! as
$tag.pair-tag src1 #f $ex.car))
(sparc.ldi as src1 (- $tag.pair-tag) dest)))
The procedure for internal:car
expects both its source and destination registers to be mapped to real hardware registers so that it can use Sparc register instructions on MacScheme registers. Who maintains this invariant? Good question.
There's a helper procedure that emits code to check that the pointer has the right tag. Look at the design note for data representations at http://www.ccs.neu.edu/home/will/Larceny/notes/note2-repr.html. There are pointer tags and type tags (or subtags), two different things. Everything has a pointer tag, but only vector-likes, bytevector-likes, and procedures have typetags. Pairs don't--we just check the pointer tag. If we don't have a pair, we call the exception handler with the code $ex.car
and it does something interesting.
We've got a tagged pointer to a pair. We want the first thing from it. If we tried to load directly from the tagged pointer, we'd probably get a bus error from a misaligned address. We need to to subtract the bitpattern of the tag away, leaving the untagged pointer. Fortunately, Sparc's [code ldi] instruction lets us specify an immediate offset. And with that, we're done. You just need to know what SparcInstructions you need to generate, and make sure you don't violate any LarcenyInvariants.
- Always remember to take tags into account when doing loads and stores.
- Refer to registers by name.
- If you're using a Sparc register instructions on a MacScheme register, make sure it's a hardware register. Look for uses of
force-hwreg!
. - Refer to layout constants (tags, subtags, offsets, etc) by name as much as possible (look in source:trunk/larceny_src/src/Rts/layouts.cfg for names).