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

Here are the ways PnkFelix has investigated implementing inline assembly:

Update (Jan 17 2007): oops, there already was a mac-scheme pseudo instruction called $.asm. Look into using that instead of introducing $inline-asm.

Add a new macscheme instruction, $inline-asm.

  • In this system, inline assembly sequences would be expressed as parts of a larger sequence of macscheme instructions. Scheme code would not be able to include inline assembly unless Twobit were extended to produce instances of the new instruction accordingly.
  • See changeset:3789 and changeset:3790
  • Example usage: a procedure that increments its (presumably fixnum) argument (assumes RESULT is mapped to ebx):

 (assemble 
 `((,$lambda ((,$.proc) 
              (,$args= 1) 
              (,$reg 1) 
              (,$inline-asm (add ebx 4)) 
              (,$return)) 
    0 
    #(#f #f 1 #f #f (x))) 
   (,$return)))
  • Pros: this approach stresses how dangerous this feature is; it is unlikely that a compiler optimization (old or new) is going to accidentally overlook the presence of inline assembly if we take this approach.

  • Cons: no immediate way to write inline assembly from Scheme code; Twobit would have to be further extended for such support.

  • Here is a piece of assembly that clobbers an existing register other than RESULT:


`((,$lambda ((,$.proc) 
             (,$args= 1) 
             (,$reg 1) 
             (,$inline-asm (add ebx 4) (mov ecx ebx))
             (,$reg 1)
             (,$return))
            0 
            #(#f #f 1 #f #f (x))) 
  (,$setglbl f)
  (,$const f)
  (,$return))
  • (this gets dangerous very fast; the code above is relying on the knowledge that ecx holds reg1. For fun, try putting some other registers in there, like esi or esp...)

Add a new primop, larceny-inline-asm

  • This gets tricky, because such a primop really only makes sense as an op that takes an immediate; ie, an op2imm, where the imm argument is the assembly to be spliced in.
    • But the Twobit system is really written (AFAICT) assuming that you start with normal op2s and later introduce some op2imms as an optimization...
    • That is, it appears to be assumed that every op2imm is expressible as an op2, which is not the case for this operation.
  • The other weird thing about it is that since we're passing an immediate value, we have to use an op2imm, which requires two arguments. So what is the use for the first argument of the primop?
    • At this point in the draft implementation, the first argument has no point; its just a value that is held in the result register prior to entry of the assembly code sequence.
    • This is still weird, and introduces an expressiveness problem, because there's no way to say "leave the result register alone!"
    • Thus, there is no way for the user to splice in assembly that inspects the value of the result register. It can inspect any other register, but not result, because its going to get clobbered by whatever value is computed by the first argument expression to the primop.
    • This is an artifact of the twobit code generator -- there is no way to remove this inexpressivity in a manner local to the IAssassin backend. If we care about being able to inspect the previous value of RESULT, we should investigate the MacScheme based implementation of inline assembly rather than the primop based implementation.
  • Also, this whole approach is fundamentally weird. The user can write assembly code that mutates any of the machine registers; my impression is that primops in general are not supposed to mutate the twobit registers besides the result register.
    • So we are inviting disaster.
    • (But isn't inline assembly inherently an invitation for disaster?)
    • ((at the very least, the entry for larceny-inline-asm in the instruction table need to be inspected carefully...))
  • See changeset:3791
  • Example usage: a procedure that increments its (presumably fixnum) argument (assumes RESULT is mapped to ebx):

(lambda (x) 
 (larceny-inline-asm x '((add ebx 4))))
  • Note that the x argment to larceny-inline-asm is significant, it moves the argument x into ebx. See note about about not being able to inspect result.
  • Note that the second argument must be a known assembly sequence at compile time. So if one attempt to parameterize the above example over the assembly sequence, you get an error (and hopefully that's the only behavior we'll see):

> (lambda (asm) (lambda (x) (larceny-inline-asm x asm)))


Error: ia86.t_op2inline-asm
Entering debugger; type "?" for help.
debug> 
  • Here is an interesting oddity that is probably a symptom of bugs (either in the implementation or inherently in the design):
    • Oh, never mind, the 17 is probably just being constant propagated. Still, I'll leave up the example since it is interesting.

> ((lambda (x) (larceny-inline-asm x '((add ebx 4) (mov ecx ebx))) x) 17)
17

> (define refactoring 
   (lambda (x) (larceny-inline-asm x '((add ebx 4) (mov ecx ebx))) x))
refactoring

> (refactoring 17)
18

Right now both approaches are implemented for the IasnLarceny backend.

  • This was done in a local manner; no other backend should be affected. (But some of the changes were admittedly a bit ugly.)
  • Since it was performed locally, other backends (e.g. CommonLarceny) could follow either approach in principle to get the same capability.

Future areas of exploration:

  • Implement larceny-inline-asm as a special primop (like .check). This affects Twobit, but at least its isolated to a file dedicated to handling weird things.
Clone this wiki locally