<div dir="ltr"><div>I think that my confusion came from the order of specification, like this</div><div><br></div><div>mod operand1:reg operand2:r/m<br></div><div><a href="https://www.felixcloutier.com/x86/lzcnt">https://www.felixcloutier.com/x86/lzcnt</a> (same as Intel manuals)<br></div><div><a class="dO" id=":x9" href="https://mail.google.com/mail/u/0?ui=2&ik=94f5e792ab&attid=0.2&permmsgid=msg-a:r6786606863442654297&view=att&disp=safe&realattid=f_k6smnaem1" target="_blank"><div class="vI">Capture d’écran 2020-02-19 à 02.17.06.png</div> <div class="vJ">(14 Ko)</div></a></div><div><br></div><div><br></div><div>versus mod: modReg RM: r/m RO: reg</div><div>Intel uses such mixture of middle endian in the documentation too...</div><div><a class="dO" id=":wy" href="https://mail.google.com/mail/u/0?ui=2&ik=94f5e792ab&attid=0.1&permmsgid=msg-a:r6786606863442654297&view=att&disp=safe&realattid=f_k6smjvcd0" target="_blank"><div class="vI">Capture d’écran 2020-02-19 à 02.13.57.png</div> <div class="vJ">(23 Ko)</div></a></div><div><br></div><div><br></div><div>I have also further fixed the rexw:r:x:b: for X64 encoding of R8-R15 in LZCNT and BSR (but did not regenerate, that can wait)<br></div><div><a class="dO" id=":xk" href="https://mail.google.com/mail/u/0?ui=2&ik=94f5e792ab&attid=0.3&permmsgid=msg-a:r6786606863442654297&view=att&disp=safe&realattid=f_k6smux4k2" target="_blank"><div class="vI">Capture d’écran 2020-02-19 à 02.22.21.png</div> <div class="vJ">(31 Ko)</div></a></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Le mer. 19 févr. 2020 à 00:51, Nicolas Cellier <<a href="mailto:nicolas.cellier.aka.nice@gmail.com">nicolas.cellier.aka.nice@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>ModR/M is a byte which encodes instruction operands <a href="https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf" target="_blank">https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf</a></div><div>mod is the mod field (2 bits) RM is the r/m field (3 bits), RO is the reg (or opcode) field (3 bits).</div><div>For LZCNT dest is in RO, and mask (source) is in r/m.</div><div>Same for BSR.</div><div> So I had it wrong... will fix ASAP<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Le mar. 18 févr. 2020 à 23:37, Nicolas Cellier <<a href="mailto:nicolas.cellier.aka.nice@gmail.com" target="_blank">nicolas.cellier.aka.nice@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">It seems that I miss-interpreted the order of mod:RM:RO:, it seems like O means output, not operand...<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Le mar. 18 févr. 2020 à 22:53, Nicolas Cellier <<a href="mailto:nicolas.cellier.aka.nice@gmail.com" target="_blank">nicolas.cellier.aka.nice@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Hi all,</div><div>I confirm that generated code for CLZ (LZCNT) is incorrect on IA32 arch.</div><div>The registers are swapped!</div><div><br></div><div>Here is an extract:</div><div><br></div><div>    0x5c26a9c: 83 e0 01        andl   $0x1, %eax<br>    0x5c26a9f: eb 11           jmp    0x5c26ab2<br>    0x5c26aa1: 90              nop    <br>    0x5c26aa2: 90              nop    <br>    0x5c26aa3: 90              nop    <br>    0x5c26aa4: 89 d0           movl   %edx, %eax<br>    0x5c26aa6: 83 e0 03        andl   $0x3, %eax<br>    0x5c26aa9: 75 f1           jne    0x5c26a9c<br>    0x5c26aab: 8b 02           movl   (%edx), %eax<br>    0x5c26aad: 25 ff ff 3f 00  andl   $0x3fffff, %eax           ; imm = 0x3FFFFF <br>    0x5c26ab2: 39 c8           cmpl   %ecx, %eax<br>    0x5c26ab4: 75 e0           jne    0x5c26a96<br>    0x5c26ab6: f3 0f bd d0     lzcntl %eax, %edx<br>    0x5c26aba: 74 0d           je     0x5c26ac9<br>    0x5c26abc: 35 1f 00 00 00  xorl   $0x1f, %eax<br>    0x5c26ac1: 89 c2           movl   %eax, %edx<br>    0x5c26ac3: d1 e2           shll   %edx<br>    0x5c26ac5: 83 c2 01        addl   $0x1, %edx<br>    0x5c26ac8: c3              retl   <br></div><div><br></div><div>What happens is that we count the leading zeros in $eax (TempReg) and store the result in $edx (ReceiverResultReg) ...</div><div><br>    0x5c26ab6: f3 0f bd d0     lzcntl %eax, %edx</div><div><br></div><div>We want the contrary!</div><div><br></div><div>$eax contains 1, presumably because we used it to check for SmallInteger tag bit:</div><div><br></div><div>    0x5c26a9c: 83 e0 01        andl   $0x1, %eax</div><div><br></div><div>So we invariably get 31 leading zeroes in $edx (but we will later overwrite the contents of $edx).</div><div><br></div><div>Then we interpret $eax as the result (thus 1 leading zero), bitInvert, and get 30 as the result for highBit, store that in $edx (shifted and tagged), and we're done... Err!<br></div><div><br></div><div>Obviously the code generation is wrong!</div><div>It did work when I first wrote it, and still work on x64 because we use $eax (TempReg) as both source and dest reg...</div><div><br></div><div>Though, I do not see what we are doing wrong:</div><div><br></div><div>concretizeClzRR</div><div>      <inline: true><br>  | maskReg dest  |<br>    maskReg := operands at: 0.<br>    dest := operands at: 1.<br>       machineCode<br>           at: 0 put: 16rF3;<br>             at: 1 put: 16r0F;<br>             at: 2 put: 16rBD;<br>             at: 3 put: (self mod: ModReg RM: dest RO: maskReg).<br>    ^4</div><div><br></div><div>and we invoke it like that:</div><div>   cogit ClzR: srcReg R: destReg.</div><div><br></div><div>ClzR: reg1 R: reg2<br>      "reg2 := reg1 countLeadingZeros"<br>    <inline: true><br>  <returnTypeC: #'AbstractInstruction *'><br> ^self gen: ClzRR operand: reg1 operand: reg2</div><div><br></div><div>So it seems to me that all is in the correct order...</div><div>cogitIA32 likewise seems perfrectly correct:</div><div><br></div><div>static sqInt<br>genPrimitiveHighBit(void)<br>{<br>    AbstractInstruction *anInstruction11;<br>    AbstractInstruction *anInstruction2;<br>    AbstractInstruction *anInstruction4;<br>    AbstractInstruction *jumpNegativeReceiver;<br>    AbstractInstruction *jumpNegativeReceiver11;<br>    AbstractInstruction *jumpNegativeReceiver3;<br>    sqInt literal1;<br><br><br>        /* remove excess tag bits from the receiver oop */<br><br>        /* and use the abstract cogit facility for case of single tag-bit */<br>        /* begin genHighBitIn:ofSmallIntegerOopWithSingleTagBit: */<br>        if (((ceCheckLZCNT()) & (1U << 5)) != 0) {<br>                /* begin genHighBitClzIn:ofSmallIntegerOopWithSingleTagBit: */<br>                genoperandoperand(ClzRR, ReceiverResultReg, TempReg);<br>                if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) {<br>                        /* begin checkQuickConstant:forInstruction: */<br>                        anInstruction2 = genoperandoperand(CmpCqR, 0, TempReg);<br>                }<br><br>                /* Note the nice bit trick below:<br>                   highBit_1based_of_small_int_value = (BytesPerWord * 8) - leadingZeroCout_of_oop - 1 toAccountForTagBit.<br>                   This is like 2 complements (- reg - 1) on (BytesPerWord * 8) log2 bits, or exactly a bit invert operation... */<br>                jumpNegativeReceiver3 = genConditionalBranchoperand(JumpZero, ((sqInt)0));<br>                /* begin checkLiteral:forInstruction: */<br>                literal1 = (BytesPerWord * 8) - 1;<br>                anInstruction11 = genoperandoperand(XorCwR, (BytesPerWord * 8) - 1, TempReg);<br>                jumpNegativeReceiver = jumpNegativeReceiver3;<br>                goto l10;<br>        }</div><div><br></div><div>which concretize in:</div><div><br></div><div>        case ClzRR:<br>                /* begin concretizeClzRR */<br>                maskReg = ((self_in_dispatchConcretize->operands))[0];<br>                dest = ((self_in_dispatchConcretize->operands))[1];<br>                ((self_in_dispatchConcretize->machineCode))[0] = 243;<br>                ((self_in_dispatchConcretize->machineCode))[1] = 15;<br>                ((self_in_dispatchConcretize->machineCode))[2] = 189;<br>                ((self_in_dispatchConcretize->machineCode))[3] = (modRMRO(self_in_dispatchConcretize, ModReg, dest, maskReg));<br>                return 4;</div><div><br></div><div>The order seems correct all the way down...</div><div>As a workaround, I could revert Eliot's optimization and force a</div><div>    cogit MoveR: ReceiverResultReg R: TempReg.</div><div>But I'd rather want to understand where's the problem...<br></div><div>Another pair of eyes may help!<br></div><div><br></div></div>
</blockquote></div>
</blockquote></div>
</blockquote></div>