<br><br><div class="gmail_quote">On Tue, May 10, 2011 at 11:11 PM, Nicolas Cellier <span dir="ltr">&lt;<a href="mailto:nicolas.cellier.aka.nice@gmail.com">nicolas.cellier.aka.nice@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
With a single API, when one will implement another processor<br>
instruction set, you&#39;ll win.<br>
The naming of registers is application dependent, but these are just constants.<br>
I wonder if Eliot gave a look at gcc?<br></blockquote><div><br></div><div>Not really.  My naming is based on a) the approach to registers Peter Deutsch invented for HPS, the VisualWorks VM, which supports an acceptably efficient register-based calling convention for Smalltalk machine code, and b) the x86 ABI which determines which registers are callee-saved, caller-saved, scratch and the C result register.  One shouldn&#39;t consider the register names used in the Cog JIT as general names; they&#39;re specific to the calling convention implemented by the Cog JIT for Smalltalk execution.</div>
<div><br></div><div>HTH,</div><div>Eliot</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
Nicolas<br>
<br>
2011/5/11 Igor Stasenko &lt;<a href="mailto:siguctua@gmail.com">siguctua@gmail.com</a>&gt;:<br>
<div><div></div><div class="h5">&gt; Hi,<br>
&gt;<br>
&gt; i took a look at Cog&#39;s abstract opcode syntax.<br>
&gt; Check the CogRTLOpcodes class&gt;&gt;initialize for description,<br>
&gt; and check the Cogit category &#39;abstract instructions&#39; at instance side<br>
&gt; for actual syntax.<br>
&gt;<br>
&gt; Things i don&#39;t like: register naming.<br>
&gt; In cog you should use following register names:<br>
&gt;        FPReg := -1.<br>
&gt;        SPReg := -2.<br>
&gt;        ReceiverResultReg := GPRegMax := -3.<br>
&gt;        TempReg := -4.<br>
&gt;        ClassReg := -5.<br>
&gt;        SendNumArgsReg := -6.<br>
&gt;        Arg0Reg := -7.<br>
&gt;        Arg1Reg := GPRegMin := -8.<br>
&gt;        DPFPReg0 := -9.<br>
&gt;        DPFPReg1 := -10.<br>
&gt;        DPFPReg2 := -11.<br>
&gt;        DPFPReg3 := -12.<br>
&gt;        DPFPReg4 := -13.<br>
&gt;        DPFPReg5 := -14.<br>
&gt;        DPFPReg6 := -15.<br>
&gt;        DPFPReg7 := -16.<br>
&gt;<br>
&gt; They are mapping 1:1 to real registers on your machine.<br>
&gt; But while such names could make sense in some cases for Cog,<br>
&gt; in NativeBoost using register named like SendNumArgsReg will be confusing..<br>
&gt; Okay... actually this is not a big deal, i could always use aliases<br>
&gt; with different names (suggestions?).<br>
&gt;<br>
&gt; But i would really like you opinion on Cog&#39;s native code syntax.<br>
&gt; Here how the native code assembly looks in cog:<br>
&gt;<br>
&gt; genDoubleArithmetic: arithmeticOperator preOpCheck: preOpCheckOrNil<br>
&gt;        &quot;Receiver and arg in registers.<br>
&gt;         Stack looks like<br>
&gt;                return address&quot;<br>
&gt;        &lt;var: #preOpCheckOrNil declareC: &#39;AbstractInstruction<br>
&gt; *(*preOpCheckOrNil)(int rcvrReg, int argReg)&#39;&gt;<br>
&gt;        | jumpFailClass jumpFailAlloc jumpFailCheck jumpSmallInt doOp |<br>
&gt;        &lt;var: #jumpFailClass type: #&#39;AbstractInstruction *&#39;&gt;<br>
&gt;        &lt;var: #jumpFailAlloc type: #&#39;AbstractInstruction *&#39;&gt;<br>
&gt;        &lt;var: #jumpSmallInt type: #&#39;AbstractInstruction *&#39;&gt;<br>
&gt;        &lt;var: #jumpFailCheck type: #&#39;AbstractInstruction *&#39;&gt;<br>
&gt;        &lt;var: #doOp type: #&#39;AbstractInstruction *&#39;&gt;<br>
&gt;        self MoveR: Arg0Reg R: TempReg.<br>
&gt;        objectRepresentation genGetDoubleValueOf: ReceiverResultReg into: DPFPReg0.<br>
&gt;        self MoveR: Arg0Reg R: ClassReg.<br>
&gt;        jumpSmallInt := objectRepresentation genJumpSmallIntegerInScratchReg: TempReg.<br>
&gt;        objectRepresentation genGetCompactClassIndexNonIntOf: Arg0Reg into:<br>
&gt; SendNumArgsReg.<br>
&gt;        self CmpCq: objectMemory classFloatCompactIndex R: SendNumArgsReg.<br>
&gt;        jumpFailClass := self JumpNonZero: 0.<br>
&gt;        objectRepresentation genGetDoubleValueOf: Arg0Reg into: DPFPReg1.<br>
&gt;        doOp := self Label.<br>
&gt;        preOpCheckOrNil ifNotNil:<br>
&gt;                [jumpFailCheck := self perform: preOpCheckOrNil with: DPFPReg0 with:<br>
&gt; DPFPReg1].<br>
&gt;        self gen: arithmeticOperator operand: DPFPReg1 operand: DPFPReg0.<br>
&gt;        jumpFailAlloc := objectRepresentation<br>
&gt;                                                genAllocFloatValue: DPFPReg0<br>
&gt;                                                into: SendNumArgsReg<br>
&gt;                                                scratchReg: ClassReg<br>
&gt;                                                scratchReg: TempReg.<br>
&gt;        self MoveR: SendNumArgsReg R: ReceiverResultReg.<br>
&gt;        self RetN: 0.<br>
&gt;        &quot;We need to push the register args on two paths; this one and the<br>
&gt; interpreter primitive path.<br>
&gt;        But the interpreter primitive path won&#39;t unless regArgsHaveBeenPushed<br>
&gt; is false.&quot;<br>
&gt;        self assert: methodOrBlockNumArgs &lt;= self numRegArgs.<br>
&gt;        jumpFailClass jmpTarget: self Label.<br>
&gt;        preOpCheckOrNil ifNotNil:<br>
&gt;                [jumpFailCheck jmpTarget: jumpFailClass getJmpTarget].<br>
&gt;        self genPushRegisterArgsForNumArgs: methodOrBlockNumArgs.<br>
&gt;        jumpFailClass := self Jump: 0.<br>
&gt;        jumpSmallInt jmpTarget: self Label.<br>
&gt;        objectRepresentation genConvertSmallIntegerToIntegerInScratchReg: ClassReg.<br>
&gt;        self ConvertR: ClassReg Rd: DPFPReg1.<br>
&gt;        self Jump: doOp.<br>
&gt;        jumpFailAlloc jmpTarget: self Label.<br>
&gt;        self compileInterpreterPrimitive: (coInterpreter<br>
&gt;                                                                                functionPointerForCompiledMethod: methodObj<br>
&gt;                                                                                primitiveIndex: primitiveIndex).<br>
&gt;        jumpFailClass jmpTarget: self Label.<br>
&gt;        ^0<br>
&gt;<br>
&gt; And here an example of NativeBoost code:<br>
&gt;<br>
&gt; emitArgumentsCoercion: gen<br>
&gt;        &quot; input - none,<br>
&gt;        output - an arguments array in remmappable oops stack top&quot;<br>
&gt;        | asm proxy args argOop i |<br>
&gt;        asm := gen asm.<br>
&gt;        proxy := gen proxy.<br>
&gt;        args := gen reserveTemp.<br>
&gt;        argOop := gen reserveTemp.<br>
&gt;<br>
&gt;        asm label: &#39;emitArgumentsCoercion&gt;&gt;&#39;.<br>
&gt;<br>
&gt;        proxy createInstanceOf: Array size: (gen fnSpec arguments size).<br>
&gt;        proxy pushRemappableOop: EAX.<br>
&gt;<br>
&gt;        i := 0.<br>
&gt;        gen fnSpec arguments do: [:arg |<br>
&gt;                arg type readOop: (EBP ptr + arg offset) generator: gen.<br>
&gt;                asm mov: EAX to: argOop.<br>
&gt;                proxy popRemappableOop.<br>
&gt;                asm mov: EAX to: args.<br>
&gt;                proxy storePointer: i ofObject: args withValue: argOop.<br>
&gt;                i := i+1.<br>
&gt;                proxy pushRemappableOop: args.<br>
&gt;        ].<br>
&gt;<br>
&gt;        asm label: &#39;&lt;&lt;emitArgumentsCoercion&#39;.<br>
&gt;<br>
&gt;        gen releaseTemps: 2.<br>
&gt;<br>
&gt; The code is of course doing different things.. but these example is<br>
&gt; actually to indicate a following observation:<br>
&gt;<br>
&gt; even when i will introduce platform-neutral code, in most cases<br>
&gt; you won&#39;t see a pure assembly language, but instead you will see a mix<br>
&gt; of usual smalltalk code and assembler.<br>
&gt;<br>
&gt; If you can see, in both examples above you see a direct instructions<br>
&gt; intermixed with another methods.<br>
&gt; And it is totally normal, because for obvious reasons, even for<br>
&gt; assembler code, there are a lot of repetitious patterns,<br>
&gt; and you refactor repetitive code into separate methods (a thing which<br>
&gt; you normally do if you follow DRY principle).<br>
&gt;<br>
&gt; So, i mean: its okay. I could change the assembler syntax to anything.<br>
&gt; But code will still be looking mostly the same,<br>
&gt; for most of code-generation routines.<br>
&gt; Of course, if you write a pure-assembly routine, it will be pure. But<br>
&gt; in practice you won&#39;t do it too often,<br>
&gt; because you can reuse stuff which already been done, or even use<br>
&gt; higher-level abstract syntax (i have one somewhere in my garage ;).<br>
&gt;<br>
&gt; But still, question remains open: should i take a Cog&#39;s assembler<br>
&gt; syntax as a base,<br>
&gt; or create own?<br>
&gt; The pro for Cog-syntax that if some day we move code generation from<br>
&gt; VM level to language,<br>
&gt; then we can just use existing code.<br>
&gt;<br>
&gt; But from another side, if i have tool which can produce a native code,<br>
&gt; from VM&#39;s perspective its completely irrelevant , where this code<br>
&gt; comes from and what assembler syntax were used to generate it.<br>
&gt;<br>
&gt; Also, we don&#39;t have to constrain ourselves, because Cog&#39;s assembler<br>
&gt; (and its implementation) were designed by taking into account that it<br>
&gt; should be translatable to C,<br>
&gt; while in NB we&#39;re always could use tricks like #perform: and block<br>
&gt; closures, which of course not available in slang.<br>
&gt;<br>
&gt; Sorry for long tiresome posts.. But i need your feedback and opinion.<br>
&gt;<br>
&gt; --<br>
&gt; Best regards,<br>
&gt; Igor Stasenko AKA sig.<br>
&gt;<br>
&gt;<br>
<br>
</div></div></blockquote></div><br>