[squeak-dev] [NativeBoost] Need your ideas, what platform-neutral assembler syntax you would like to have

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Wed May 11 06:11:48 UTC 2011


With a single API, when one will implement another processor
instruction set, you'll win.
The naming of registers is application dependent, but these are just constants.
I wonder if Eliot gave a look at gcc?

Nicolas

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



More information about the Squeak-dev mailing list