On Tue, Mar 3, 2009 at 11:58 AM, John M McIntosh < johnmci@smalltalkconsulting.com> wrote:
Er, well the shift left/right assembler operations are signed or unsigned, but TYPED languages usually figure out which one to use based on the type of the operators.
Forget about assembler. In C "signed >> expr" is an arithmetic (signed) shift; "unsigned >> expr" is a logical (unsigned) one. In Smalltalk "negative bitShift: negative" is an arithmetic shift, "non-negative bitShift: negative" is a logical one. These are perfectly compatible, and both are useful. In Smalltalk you can always write (foo bitAnd: (1 bitShift: preShiftFieldWidth) - 1) bitShift: expr if you want to perform a logical shift.
But since Smalltalk is type-less,
No, that's not it. In fact, Smalltalk is strongly typed. Try bit shifting true. Quite possible in C; not possible in Smalltalk. Smalltalk has infinite precision so an unsigned value is simply a non-negative one. The reason, I think, that Slang casts to unsigned is a hack to do with the default type of oops being sqInt (signed). But this is a mistake. Look at the definition of primitiveBitShift and ask yourself why this even works?
primitiveBitShift | integerReceiver integerArgument shifted | integerArgument := self popInteger. integerReceiver := self popPos32BitInteger. self successful ifTrue: [ integerArgument >= 0 ifTrue: [ "Left shift -- must fail if we lose bits beyond 32" self success: integerArgument <= 31. shifted := integerReceiver << integerArgument. self success: (shifted >> integerArgument) = integerReceiver. ] ifFalse: [ "Right shift -- OK to lose bits" self success: integerArgument >= -31. shifted := integerReceiver bitShift: integerArgument. ]. ]. self successful ifTrue: [self push: (self positive32BitIntegerFor: shifted)] ifFalse: [self unPop: 2]
It took me a few minutes of staring at shifted := integerReceiver bitShift: integerArgument. (which can't possibly work for negative values if Slang, as it does, casts to unsigned) before I realised that
integerReceiver := self popPos32BitInteger.
means the primitive simply fails for negative receivers (!!!!!!!!!!!!!).
I've noticed this before and I've forgotten it (I'm a bit like a goldfish in that regard) and I find it no less shocking each time I trip over it.
What if you code up a unsigned shift operator so there is no question that
you want to do unsigned shifting, which also makes the SLANG coder just a bit more aware he has a choice to make, casting is ok, but people forget. Also I can do senders of, then later wonder why am I not doing that here/there?
Right. I've recently added #asUnsignedInteger as a concise cast to unsigned.
Having been bitten by signed shifting issues in the past, I can agree it's
unpleasant.
So look, surely shifts should be signed by default and be unsigned if the argument is unsigned, e.g./i.e.
!Integer methodsFor: '*VMMaker-interpreter simulator' stamp: 'eem 2/19/2009 18:33'! asUnsignedInteger self assert: self >= 0. ^self! !
and in CCodeGenerator>>initializeCTranslationDictionary: ... #asUnsignedInteger #generateAsUnsignedInteger:on:indent: ...
and !CCodeGenerator methodsFor: 'C translation' stamp: 'eem 2/15/2009 16:38'! generateAsUnsignedInteger: msgNode on: aStream indent: level "Generate the C code for this message onto the given stream."
aStream nextPutAll:'((usqInt)'. self emitCExpression: msgNode receiver on: aStream. aStream nextPut: $)! !
and I autochange all senders of >> and bitShift: throughout the plugins, primitives and Interpreter to use asUnsignedInteger. Do I dare? Am I insane?
PS I note we have methods to do unsigned compare logic when dealing with oop address so that *we* know we are doing something special, since people just couldn't manage the casts correctly.
On 3-Mar-09, at 11:50 AM, Eliot Miranda wrote:
Hi All,
I'm being bitten by Slang's treatment of bitShift: & >>. In both cases (generateBitShift:on:indent: & generateShiftRight:on:indent:) Slang generates an unsigned shift by explicitly casting the shifted expression to usqInt. I can understand the benefit of having an unsigned shift. But there are times when one really needs a signed shift. Further, the Smalltalk versions of both bitShift: and >> are signed shifts.
Dare I change e.g. generateShiftRight:on:indent: to leave the expression alone and generate either a signed or an unsigned shift based on the variable's declaration? Or must I live with a maddening cCode: '(signed)' inSmalltalk: [] carbuncle?
E.
--
John M. McIntosh johnmci@smalltalkconsulting.com Corporate Smalltalk Consulting Ltd. http://www.smalltalkconsulting.com ===========================================================================