[Vm-dev] Re: [Pharo-project] is there a way to avoid all the #tryNamedPrimitive:with: * in ProtoObject

Mariano Martinez Peck marianopeck at gmail.com
Wed Jan 25 17:57:01 UTC 2012


On Wed, Jan 25, 2012 at 6:40 PM, Eliot Miranda <eliot.miranda at gmail.com>wrote:

>
>
>
> On Wed, Jan 25, 2012 at 3:13 AM, Mariano Martinez Peck <
> marianopeck at gmail.com> wrote:
>
>>
>>
>>
>> On Wed, Jan 25, 2012 at 2:43 AM, Eliot Miranda <eliot.miranda at gmail.com>wrote:
>>
>>>
>>>
>>>
>>> On Tue, Jan 24, 2012 at 2:02 PM, Mariano Martinez Peck <
>>> marianopeck at gmail.com> wrote:
>>>
>>>>
>>>> sorry...here is the attached.
>>>>
>>>
>>> OK. What you've done will work.  Some criticism: Since the method is new
>>> the flushCache should be unneeded.
>>>
>>
>> True.
>>
>>
>>>  I think either copying a template method (e.g. stored in a class
>>> variable) and  changing its argument count,
>>>
>>
>> Let's see if I understand. I have attaached a new version that supposes
>> to do this. I tested and seems to work.
>> Is it better?  If you agree, I can put a nice comment and commit.
>>
>>
>>> or creating a method directly (look at the generate: method) would be
>>> much faster.  Speed can be important in the debugger.
>>>
>>
>> I am not sure if I understood. Maybe you mean something along the lines
>> of:
>>
>> MethodNode new
>> selector: #tryNamedPrimitive:arg2:
>> arguments: (OrderedCollection with: (TempVariableNode new name: 'arg1'
>> index: 0 type: 2 scope: 0; nowHasDef; nowHasRef; beMethodArg; yourself)
>> with: (TempVariableNode new name: 'arg1' index: 0 type: 2 scope: 0;
>> nowHasDef; nowHasRef; beMethodArg; yourself))
>> precedence: 3
>> temporaries: #()
>> block: BlockNode new
>> encoder: (EncoderForV3PlusClosures new
>> instVarNamed: 'class' put: UndefinedObject; yourself)
>> primitive: 117
>>
>> and then send #generate:
>>
>
> No, I meant create the method directly as does the body of
> generate:using:.  e.g. if the method looks like
>
> tryNamedPrimitiveWith: a with: b ... with: n
>      <primitive: 'function to be filled in later' module: 'module to be
> filled in later'>
>     ^ContextPart primitiveFailToken
>
> then it looks something like
>
> tempMethodForNamedPrimitive: functionName inModule: moduleName numArgs:
> nArgs
>
> literals := { { moduleName. functionName. 0. 0. }.
>    Smalltalk bindingOf: #ContextPart.
>    #primitiveFailToken.
>   self additionalMethodStateForNamedPrimitiveWithNArgs: nArgs.
>    Smalltalk bindingOf: #Object }.
>  bytes := #( 17 "push lit var 1 (ContextPart)" 210 "send lit 2 with 0
> args (primitiveFailToken)" 124 "return top" ).
>  method := aCompiledMethodClass
> newBytes: bytes size
> trailerBytes: CompiledMethodTrailer empty
>  nArgs: nargs
> nTemps: nargs
> nStack: 0
>  nLits: literals size
> primitive: 117.
> 1 to: literals size do: [:i | method literalAt: i put: (literals at: i)].
>   0 to: bytes size - 1 do: [:i| method byteAt: method initialPC + i put:
> (bytes at: i + 1)].
>   ^method
>
> But since the bytecodes don't change with the argument count it is even
> faster to create the above, copy it and smash the selector and argument
> count into the copy.  MethodWrappers4.2 has an example of this kind of
> thing.
>
>
Ok. I will try to give it a try to this approach as well.
But what happened with the version I attached in the previous email that
uses a template method and just change the arguments number?
It looks quite fast since the only thing I have to do is to set the
arguments number (which are bitAnd:, bitShift: and bitOr: only), and quite
easy to understand at the same time.



>
>
>> but I get lost to get this working...the previous solution seems easier.
>>
>
> Do you want to build Trabants or BMWs?
>

I didn't understand. Which solution which be each car?


> Documentation helps.
>
>

I didn't understand either. If you refer to the fact I didn't put comments
in the .cs I attached, I explicitly said in my previous email "If you
agree, I can put a nice comment and commit.". It already took me the whole
morning (yes, I am completly noob here) to do the version of changing the
argument count, so I was waiting the confirmation in the solution before
documenting it and make it better.



>
>> Thanks!
>>
>>
>>
>>
>>>
>>>> On Tue, Jan 24, 2012 at 11:01 PM, Mariano Martinez Peck <
>>>> marianopeck at gmail.com> wrote:
>>>>
>>>>>
>>>>>
>>>>>>>> Analogously, one needs a way of invoking named primitives in the
>>>>>>>> debugger, and using tryNamedPrimitive[:with:with:...] et al has exactly the
>>>>>>>> same weaknesses as tryPrimitiveN above.  So introducing a primitive to run
>>>>>>>> named primitives is in keeping with tryPrimitive:withArgs:.  Using the
>>>>>>>> VisualWorks approach is feasible but violates Occam's razor.
>>>>>>>>
>>>>>>>
>>>>>>> What what about temporally (to be removed in the future) just when
>>>>>>> we are using older VMs?
>>>>>>>
>>>>>>
>>>>>> If temporary, then fine.  But its all work :)
>>>>>>
>>>>>
>>>>>
>>>>> Well...writing papers can be boring ;)
>>>>> Please, could you take a look to the attached .cs?  I tried to do what
>>>>> I had in mind. Since this part of the system is new for me, I have not sure
>>>>> it is correct, nor how to test it. So far what I did for testing it is to
>>>>> take the  #tryNamedPrimitiveIn: aCompiledMethod for: aReceiver withArgs:
>>>>> arguments
>>>>> and (temporally) remove the <primitive: 218 error: ec> and define 'ec'
>>>>> as a temporal variable. Since it will be nil, the following code will be
>>>>> executed.
>>>>> Then I put a halt in #open: fileName forWrite: writeMode   from
>>>>> StandardFileStream and then I do it:
>>>>> FileDirectory default forceNewFileNamed: 'xxxxx'.
>>>>> Once in the debugger, I went to:
>>>>>
>>>>> StandardFileStream retryWithGC:[self primOpen: f writable: writeMode]
>>>>>                     until:[:id| id notNil]
>>>>>                     forFileNamed: fileName.
>>>>>
>>>>> did a "through" in the close, and be sure I could do "into" and "step"
>>>>> for #self primOpen: f writable: writeMode  (which is a named primitive).
>>>>>
>>>>> is this ok?
>>>>>
>>>>> Thanks Eliot!
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Why do we need them in ProtoObject?
>>>>>>>>>
>>>>>>>>
>>>>>>>> Once tryNamedPrimitiveIn:for:withArgs: is implemented in all
>>>>>>>> relevant virtual machines we don't need them.  You'll notice that there is
>>>>>>>> no trace of the tryPrimitiveN methods anymore, even though they're in
>>>>>>>> Smalltalk-80.
>>>>>>>>
>>>>>>>>
>>>>>>>>> Because I'm not sure that adding primitive to VM is always a good
>>>>>>>>> solution.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Agreed.  But it is in keeping with the primitive for invoking
>>>>>>>> numbered primitives,  tryPrimitive:withArgs:.
>>>>>>>>
>>>>>>>>
>>>>>>>> HTH
>>>>>>>> Eliot
>>>>>>>>
>>>>>>>>
>>>>>>>>> Stef
>>>>>>>>>
>>>>>>>>> > On Mon, Jan 23, 2012 at 8:52 AM, Mariano Martinez Peck <
>>>>>>>>> marianopeck at gmail.com> wrote:
>>>>>>>>> > Hi guys. I usually like to take a look to ProtoObject and see
>>>>>>>>> what is really needed for the minimal object. But having 30% of the methods
>>>>>>>>> being  #tryNamedPrimitive:with: *  is not fun.
>>>>>>>>> > So...I wonder, do you think there could be another way so that
>>>>>>>>> to avoid having all those methods in ProtoObject ?
>>>>>>>>> >
>>>>>>>>> > Yes there is.  I implemented primitive 218 in Cog,
>>>>>>>>> primitiveDoNamedPrimitiveWithArgs, which is accessed via
>>>>>>>>> >
>>>>>>>>> >
>>>>>>>>> >               tryNamedPrimitiveIn: aCompiledMethod for:
>>>>>>>>> aReceiver withArgs: arguments
>>>>>>>>> >                       | selector theMethod spec receiverClass |
>>>>>>>>> >                       <primitive: 218 error: ec>
>>>>>>>>> >                       ec ifNotNil:
>>>>>>>>> >                               ["If ec is an integer other than
>>>>>>>>> -1 there was a problem with primitive 218,
>>>>>>>>> >                                 not with the external primitive
>>>>>>>>> itself.  -1 indicates a generic failure (where
>>>>>>>>> >                                 ec should be nil) but ec = nil
>>>>>>>>> means primitive 218 is not implemented.  So
>>>>>>>>> >                                 interpret -1 to mean the
>>>>>>>>> external primitive failed with a nil error code."
>>>>>>>>> >                                ec isInteger ifTrue:
>>>>>>>>> >                                       [ec = -1
>>>>>>>>> >                                               ifTrue: [ec := nil]
>>>>>>>>> >                                               ifFalse: [self
>>>>>>>>> primitiveFailed]].
>>>>>>>>> >                               ^{PrimitiveFailToken. ec}].
>>>>>>>>> >                       "Assume a nil error code implies the
>>>>>>>>> primitive is not implemented and fall back on the old code."
>>>>>>>>> >                       "Hack. Attempt to execute the named
>>>>>>>>> primitive from the given compiled method"
>>>>>>>>> >                       arguments size > 8 ifTrue:
>>>>>>>>> >                               [^{PrimitiveFailToken. nil}].
>>>>>>>>> >                       selector := #(
>>>>>>>>> >                               tryNamedPrimitive
>>>>>>>>> >                               tryNamedPrimitive:
>>>>>>>>> >                               tryNamedPrimitive:with:
>>>>>>>>> >                               tryNamedPrimitive:with:with:
>>>>>>>>> >                               tryNamedPrimitive:with:with:with:
>>>>>>>>> >
>>>>>>>>> tryNamedPrimitive:with:with:with:with:
>>>>>>>>> >
>>>>>>>>> tryNamedPrimitive:with:with:with:with:with:
>>>>>>>>> >
>>>>>>>>> tryNamedPrimitive:with:with:with:with:with:with:
>>>>>>>>> >
>>>>>>>>> tryNamedPrimitive:with:with:with:with:with:with:with:) at: arguments size+1.
>>>>>>>>> >                       receiverClass := self objectClass:
>>>>>>>>> aReceiver.
>>>>>>>>> >                       theMethod := receiverClass lookupSelector:
>>>>>>>>> selector.
>>>>>>>>> >                       theMethod == nil ifTrue:
>>>>>>>>> >                               [^{PrimitiveFailToken. nil}].
>>>>>>>>> >                       spec := theMethod literalAt: 1.
>>>>>>>>> >                       spec replaceFrom: 1 to: spec size with:
>>>>>>>>> (aCompiledMethod literalAt: 1) startingAt: 1.
>>>>>>>>> >                       Smalltalk unbindExternalPrimitives.
>>>>>>>>> >                       ^self object: aReceiver perform: selector
>>>>>>>>> withArguments: arguments inClass: receiverClass
>>>>>>>>> >
>>>>>>>>> > (cf tryPrimitive: withArgs:) and used in
>>>>>>>>> >
>>>>>>>>> >
>>>>>>>>> >               doPrimitive: primitiveIndex method: meth receiver:
>>>>>>>>> receiver args: arguments
>>>>>>>>> >                       "Simulate a primitive method whose index
>>>>>>>>> is primitiveIndex.  The simulated receiver
>>>>>>>>> >                        and arguments are given as arguments to
>>>>>>>>> this message. Any primitive which provokes
>>>>>>>>> >                        execution needs to be intercepted and
>>>>>>>>> simulated to avoid execution running away."
>>>>>>>>> >
>>>>>>>>> >                       | value |
>>>>>>>>> >                       "If successful, push result and return
>>>>>>>>> resuming context, else ^ { PrimitiveFailToken. errorCode }"
>>>>>>>>> >                       (primitiveIndex = 19) ifTrue:
>>>>>>>>> >                               [ToolSet
>>>>>>>>> >                                       debugContext: self
>>>>>>>>> >                                       label:'Code simulation
>>>>>>>>> error'
>>>>>>>>> >                                       contents: nil].
>>>>>>>>> >
>>>>>>>>> >                       "ContextPart>>blockCopy:; simulated to get
>>>>>>>>> startpc right"
>>>>>>>>> >                       (primitiveIndex = 80 and: [(self
>>>>>>>>> objectClass: receiver) includesBehavior: ContextPart])
>>>>>>>>> >                               ifTrue: [^self push:
>>>>>>>>> ((BlockContext newForMethod: receiver method)
>>>>>>>>> >
>>>>>>>>> home: receiver home
>>>>>>>>> >
>>>>>>>>> startpc: pc + 2
>>>>>>>>> >
>>>>>>>>> nargs: (arguments at: 1))].
>>>>>>>>> >                       (primitiveIndex = 81 and: [(self
>>>>>>>>> objectClass: receiver) == BlockContext]) "BlockContext>>value[:value:...]"
>>>>>>>>> >                               ifTrue: [^receiver pushArgs:
>>>>>>>>> arguments from: self].
>>>>>>>>> >                       (primitiveIndex = 82 and: [(self
>>>>>>>>> objectClass: receiver) == BlockContext]) "BlockContext>>valueWithArguments:"
>>>>>>>>> >                               ifTrue: [^receiver pushArgs:
>>>>>>>>> arguments first from: self].
>>>>>>>>> >                       primitiveIndex = 83 "afr 9/11/1998 19:50"
>>>>>>>>> "Object>>perform:[with:...]"
>>>>>>>>> >                               ifTrue: [^self send: arguments
>>>>>>>>> first
>>>>>>>>> >                                                       to:
>>>>>>>>> receiver
>>>>>>>>> >                                                       with:
>>>>>>>>> arguments allButFirst
>>>>>>>>> >                                                       super:
>>>>>>>>> false].
>>>>>>>>> >                       primitiveIndex = 84 "afr 9/11/1998 19:50 &
>>>>>>>>> eem 8/18/2009 17:04" "Object>>perform:withArguments:"
>>>>>>>>> >                               ifTrue: [^self send: arguments
>>>>>>>>> first
>>>>>>>>> >                                                       to:
>>>>>>>>> receiver
>>>>>>>>> >                                                       with:
>>>>>>>>> (arguments at: 2)
>>>>>>>>> >
>>>>>>>>> startClass: nil].
>>>>>>>>> >                       primitiveIndex = 100 "eem 8/18/2009 16:57"
>>>>>>>>> "Object>>perform:withArguments:inSuperclass:"
>>>>>>>>> >                               ifTrue: [^self send: arguments
>>>>>>>>> first
>>>>>>>>> >                                                       to:
>>>>>>>>> receiver
>>>>>>>>> >                                                       with:
>>>>>>>>> (arguments at: 2)
>>>>>>>>> >
>>>>>>>>> startClass: (arguments at: 3)].
>>>>>>>>> >
>>>>>>>>> >                       "Mutex>>primitiveEnterCriticalSection
>>>>>>>>> >
>>>>>>>>>  Mutex>>primitiveTestAndSetOwnershipOfCriticalSection"
>>>>>>>>> >                       (primitiveIndex = 186 or: [primitiveIndex
>>>>>>>>> = 187]) ifTrue:
>>>>>>>>> >                               [| active effective |
>>>>>>>>> >                                active := Processor activeProcess.
>>>>>>>>> >                                effective := active
>>>>>>>>> effectiveProcess.
>>>>>>>>> >                                "active == effective"
>>>>>>>>> >                                value := primitiveIndex = 186
>>>>>>>>> >                                                       ifTrue:
>>>>>>>>> [receiver primitiveEnterCriticalSectionOnBehalfOf: effective]
>>>>>>>>> >                                                       ifFalse:
>>>>>>>>> [receiver primitiveTestAndSetOwnershipOfCriticalSectionOnBehalfOf:
>>>>>>>>> effective].
>>>>>>>>> >                                ^(value isArray
>>>>>>>>> >                                   and: [value size = 2
>>>>>>>>> >                                   and: [value first ==
>>>>>>>>> PrimitiveFailToken]])
>>>>>>>>> >                                       ifTrue: [value]
>>>>>>>>> >                                       ifFalse: [self push:
>>>>>>>>> value]].
>>>>>>>>> >
>>>>>>>>> >                       primitiveIndex = 188 ifTrue: "eem
>>>>>>>>> 5/27/2008 11:10 Object>>withArgs:executeMethod:"
>>>>>>>>> >                               [^MethodContext
>>>>>>>>> >                                       sender: self
>>>>>>>>> >                                       receiver: receiver
>>>>>>>>> >                                       method: (arguments at: 2)
>>>>>>>>> >                                       arguments: (arguments at:
>>>>>>>>> 1)].
>>>>>>>>> >
>>>>>>>>> >                       "Closure primitives"
>>>>>>>>> >                       (primitiveIndex = 200 and: [self ==
>>>>>>>>> receiver]) ifTrue:
>>>>>>>>> >
>>>>>>>>> "ContextPart>>closureCopy:copiedValues:; simulated to get startpc right"
>>>>>>>>> >                               [^self push: (BlockClosure
>>>>>>>>> >
>>>>>>>>> outerContext: receiver
>>>>>>>>> >
>>>>>>>>> startpc: pc + 2
>>>>>>>>> >
>>>>>>>>> numArgs: arguments first
>>>>>>>>> >
>>>>>>>>> copiedValues: arguments last)].
>>>>>>>>> >                       ((primitiveIndex between: 201 and: 205)
>>>>>>>>>                "BlockClosure>>value[:value:...]"
>>>>>>>>> >                       or: [primitiveIndex between: 221 and:
>>>>>>>>> 222]) ifTrue: "BlockClosure>>valueNoContextSwitch[:]"
>>>>>>>>> >                               [^receiver
>>>>>>>>> simulateValueWithArguments: arguments caller: self].
>>>>>>>>> >                       primitiveIndex = 206 ifTrue:
>>>>>>>>>                              "BlockClosure>>valueWithArguments:"
>>>>>>>>> >                               [^receiver
>>>>>>>>> simulateValueWithArguments: arguments first caller: self].
>>>>>>>>> >
>>>>>>>>> >                       primitiveIndex = 118 ifTrue:
>>>>>>>>> "tryPrimitive:withArgs:; avoid recursing in the VM"
>>>>>>>>> >                               [(arguments size = 2
>>>>>>>>> >                                and: [arguments first isInteger
>>>>>>>>> >                                and: [arguments last class ==
>>>>>>>>> Array]]) ifFalse:
>>>>>>>>> >                                       [^ContextPart
>>>>>>>>> primitiveFailTokenFor: nil].
>>>>>>>>> >                                ^self doPrimitive: arguments
>>>>>>>>> first method: meth receiver: receiver args: arguments last].
>>>>>>>>> >
>>>>>>>>> >                       value := primitiveIndex = 120 "FFI method"
>>>>>>>>> >                                               ifTrue: [(meth
>>>>>>>>> literalAt: 1) tryInvokeWithArguments: arguments]
>>>>>>>>> >                                               ifFalse:
>>>>>>>>> >
>>>>>>>>> [primitiveIndex = 117 "named primitives"
>>>>>>>>> >
>>>>>>>>> ifTrue: [self tryNamedPrimitiveIn: meth for: receiver withArgs: arguments]
>>>>>>>>> >
>>>>>>>>> ifFalse:
>>>>>>>>> >
>>>>>>>>>       [receiver tryPrimitive: primitiveIndex withArgs: arguments]].
>>>>>>>>> >                       ^(value isArray
>>>>>>>>> >                           and: [value size = 2
>>>>>>>>> >                           and: [value first ==
>>>>>>>>> PrimitiveFailToken]])
>>>>>>>>> >                               ifTrue: [value]
>>>>>>>>> >                               ifFalse: [self push: value]
>>>>>>>>> >
>>>>>>>>> > (find attached).  But these need implementing in the standard VM
>>>>>>>>> before they can be used in Pharo, Squeak, etc.
>>>>>>>>> >
>>>>>>>>> >
>>>>>>>>> > Thanks
>>>>>>>>> >
>>>>>>>>> > --
>>>>>>>>> > Mariano
>>>>>>>>> > http://marianopeck.wordpress.com
>>>>>>>>> >
>>>>>>>>> >
>>>>>>>>> >
>>>>>>>>> >
>>>>>>>>> > --
>>>>>>>>> > best,
>>>>>>>>> > Eliot
>>>>>>>>> >
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> best,
>>>>>>>> Eliot
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Mariano
>>>>>>> http://marianopeck.wordpress.com
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> best,
>>>>>> Eliot
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Mariano
>>>>> http://marianopeck.wordpress.com
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Mariano
>>>> http://marianopeck.wordpress.com
>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> best,
>>> Eliot
>>>
>>>
>>>
>>
>>
>> --
>> Mariano
>> http://marianopeck.wordpress.com
>>
>>
>>
>
>
> --
> best,
> Eliot
>
>
>


-- 
Mariano
http://marianopeck.wordpress.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20120125/0466e9bf/attachment-0001.htm


More information about the Vm-dev mailing list