[squeak-dev] Re: here's a curio for compiler mavens...
Nicolas Cellier
nicolas.cellier.aka.nice at gmail.com
Tue Feb 14 22:50:56 UTC 2012
Le 14 février 2012 23:10, Eliot Miranda <eliot.miranda at gmail.com> a écrit :
> On Tue, Feb 14, 2012 at 5:30 AM, Nicolas Cellier
> <nicolas.cellier.aka.nice at gmail.com> wrote:
>>
>> Ah, I came up with a relatively explicit solution, but also quite
>> heavy in term of number of messages required to implement the
>> optimisation.
>> I'm curious to see your implementation :)
>
>
> similar, but should I commit it? I think mine is fine since it is only
> assignments that have storepop forms, so your generality doesn't win
> anything. but i could be wrong.
>
I was thinking of
1) nested ifTrue: ifFalse:
cond1 ifTrue: [x1 := y] ifFalse: [cond2 ifTrue: [x2 := y] ifFalse: [x3 := y]]
elseExpr statements last is a message, not an assignment, though we
can factor the pop out...
2) inlined loop messages don't require a final pop...
x <= y ifTrue: [x to: y do: [:i | (self at: i) doSome]] ifFalse: [y
to: x do: [:i | (self at: i) doSome]].
An encoder pushing a value in both branches just to pop it after
wouldn't be that well behaved...
I won't give any version publicly because I crashed several images in
the bootstrap process, and don't know if the bug is in the bootstrap
or in the code.
Nicolas
> !MessageNode methodsFor: 'code generation' stamp: 'eem 2/13/2012 15:25'!
> ifThenElseForEffectShouldSharePop
> | thenExpr elseExpr |
> thenExpr := arguments at: 1.
> elseExpr := arguments at: 2.
> ^((thenExpr isJust: NodeNil)
> or: [(elseExpr isJust: NodeNil)
> or: [thenExpr statements last isAssignmentNode
> and: [elseExpr statements last isAssignmentNode]]]) not
>
> "^(thenExpr isJust: NodeNil) not
> and: [(elseExpr isJust: NodeNil) not
> and: [thenExpr statements last isAssignmentNode not
> or: [elseExpr statements last isAssignmentNode not]]]"! !
>
>> Also, I'm sure that multiple-jump-elimination proposed by Igor and
>> other kind of optimization would be a perfect subject for proving
>> concepts of NewCompiler IR infrastructure.
>>
>> MessageNode>>doesEffectRequiresAPop: encoder
>> special > 0
>> ifTrue:
>> [^self perform: (MacroTesters at: special) with:
>> encoder].
>> ^super doesEffectRequiresAPop: encoder
>>
>> MessageNode>>doesEffectForIfRequiresAPop: encoder
>> "If both branch require a pop instruction, then the pop is factored
>> after the jump.
>> In this case, the translation of this node also require a final pop
>> that can further be eliminated eventually."
>> | thenExpr elseExpr |
>> thenExpr := arguments at: 1.
>> elseExpr := arguments at: 2.
>> ^((thenExpr isJust: NodeNil) or: [elseExpr isJust: NodeNil]) not
>> and: [(thenExpr doesEvaluatingEffectRequiresAPop: encoder)
>> and:
>> [elseExpr doesEvaluatingEffectRequiresAPop: encoder]].
>>
>> BlockNode>>doesEvaluatingEffectRequiresAPop: anEncoder
>> "Test whether a pop instruction is required to skip the value on
>> the
>> stack when this block is inlined."
>> ^statements size > 0 and: [statements last doesEffectRequiresAPop:
>> anEncoder]
>>
>> AssigmentNode>>doesEffectRequiresAPop: anEncoder
>> "Test whether a pop instruction is required to skip the value on
>> the stack"
>> ^(variable canStorePopInASingleInstruction: anEncoder) not
>>
>> InstanceVariableNode>>canStorePopInASingleInstruction: anEncoder
>> ^true
>>
>> etc...
>>
>> Le 14 février 2012 01:27, Eliot Miranda <eliot.miranda at gmail.com> a écrit
>> :
>> > OK, I fixed this (a pleasant Monday afternoon distraction) (at least I
>> > *think* I've fixed it; I still have to check the decompiler). But... in
>> > doing so I realised I had to fix CompiledMethod>>#= since I needed to
>> > see
>> > exactly which methods the compiler change affected (just committed as
>> > Kernel-eem.669). In doing this I noticed that recent compiler changes
>> > have
>> > not been applied by recompiling affected methods in the system. If you
>> > look
>> > at Behavior>>#sourceMatchesBytecodeAt: it uses CompiledMethod>>#= to
>> > check
>> > that the compiler's output remains unchanged. In the 4.3 trunk image
>> > with
>> > CompiledMethod>>#= fixed, sourceMatchesBytecodeAt: reports that
>> > some 19654
>> > methods need recompiling:
>> >
>> > (SystemNavigation new allSelect: [:m| (m methodClass
>> > sourceMatchesBytecodeAt: m selector) not]) size 19654
>> >
>> > Being selective in recompiling, via e.g.
>> >
>> > SystemNavigation new allSelect: [:m| (m methodClass
>> > sourceMatchesBytecodeAt:
>> > m selector) ifFalse: [m methodClass recompile: m selector]. false]
>> >
>> > one gets to the desired
>> >
>> > (SystemNavigation new allSelect: [:m| (m methodClass
>> > sourceMatchesBytecodeAt: m selector) not]) size 0
>> >
>> > So the question is should we add an update that does the selective
>> > recompile? I think we should, e.g. by committing a version of Compiler
>> > that
>> > contains the selective recompile as a post-script. Agreed?
>> >
>> > On Mon, Feb 13, 2012 at 11:58 AM, Eliot Miranda
>> > <eliot.miranda at gmail.com>
>> > wrote:
>> >>
>> >> Hi All,
>> >>
>> >> (whitewash alert) I just had occasion to look at the bytecode
>> >> generated by the standard compiler for HashedCollection
>> >> class>>#goodPrimeAtLeast:. Here's the source, with the issue in bold:
>> >>
>> >> goodPrimeAtLeast: lowerLimit
>> >> "Answer the next good prime >= lowerlimit.
>> >> If lowerLimit is larger than the largest known good prime,
>> >> just make it odd."
>> >> | primes low mid high prime |
>> >> primes := self goodPrimes.
>> >> low := 1.
>> >> high := primes size.
>> >> lowerLimit > (primes at: high) ifTrue: [
>> >> ^lowerLimit bitOr: 1 ].
>> >> [ high - low <= 1 ] whileFalse: [
>> >> mid := high + low // 2.
>> >> prime := primes at: mid.
>> >> prime = lowerLimit ifTrue: [ ^prime ].
>> >> prime < lowerLimit
>> >> ifTrue: [ low := mid ]
>> >> ifFalse: [ high := mid ] ].
>> >> (primes at: low) >= lowerLimit ifTrue: [ ^primes at: low ].
>> >> ^primes at: high
>> >>
>> >> The code for this sequence is
>> >> 58 <15> pushTemp: 5
>> >> 59 <10> pushTemp: 0
>> >> 60 <B2> send: <
>> >> 61 <9B> jumpFalse: 66
>> >> 62 <13> pushTemp: 3
>> >> 63 <81 42> storeIntoTemp: 2
>> >> 65 <92> jumpTo: 69
>> >> 66 <13> pushTemp: 3
>> >> 67 <81 44> storeIntoTemp: 4
>> >> 69 <87> pop
>> >>
>> >> where-as the following would be better:
>> >>
>> >> 58 <15> pushTemp: 5
>> >> 59 <10> pushTemp: 0
>> >> 60 <B2> send: <
>> >> 61 <9B> jumpFalse: 66
>> >> 62 <13> pushTemp: 3
>> >> 63 <82 42> popIntoTemp: 2
>> >> 65 <92> jumpTo: 69
>> >> 66 <13> pushTemp: 3
>> >> 67 <82 44> popIntoTemp: 4
>> >>
>> >> The reason is that the code generator favours using a single pop for
>> >> both
>> >> arms of the if:
>> >>
>> >> MessageNode>>sizeCodeForIf: encoder value: forValue
>> >> | thenExpr elseExpr branchSize thenSize elseSize |
>> >> thenExpr := arguments at: 1.
>> >> elseExpr := arguments at: 2.
>> >> (forValue
>> >> or: [(thenExpr isJust: NodeNil)
>> >> or: [elseExpr isJust: NodeNil]]) not
>> >> "(...not ifTrue: avoids using ifFalse: alone during this compile)"
>> >> ifTrue: "Two-armed IFs forEffect share a single pop"
>> >> [^super sizeCodeForEffect: encoder].
>> >> ...
>> >>
>> >> MessageNode>>emitCodeForIf: stack encoder: encoder value: forValue
>> >> | thenExpr thenSize elseExpr elseSize |
>> >> thenSize := sizes at: 1.
>> >> elseSize := sizes at: 2.
>> >> (forValue not and: [elseSize * thenSize > 0]) ifTrue:
>> >> "Two-armed IFs forEffect share a single pop"
>> >> [^super emitCodeForEffect: stack encoder: encoder].
>> >>
>> >> It would be nice if this only happened if doing so actually reduced the
>> >> size of the generated code.
>> >> --
>> >> best,
>> >> Eliot
>> >>
>> >
>> >
>> >
>> > --
>> > best,
>> > Eliot
>> >
>> >
>> >
>> >
>>
>
>
>
> --
> best,
> Eliot
>
>
>
>
More information about the Squeak-dev
mailing list
|