asm/AJx86AssemblerTests.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Fri, 17 Jun 2016 17:25:15 +0100
changeset 26 8eb6716029aa
parent 24 5aace704e3c8
permissions -rw-r--r--
Merge

"
    Copyright (c) 2012-2016 Igor Stasenko
                            Martin McClure
                            Damien Pollet
                            Camillo Bruni
                            Guido Chari
                   2016-now Jan Vrany <jan.vrany [at] fit . cvut . cz>

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the 'Software'), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
"
"{ Package: 'jv:dragonfly/asm' }"

"{ NameSpace: Smalltalk }"

TestCase subclass:#AJx86AssemblerTests
	instanceVariableNames:'asm'
	classVariableNames:''
	poolDictionaries:'AJx86Registers'
	category:'AsmJit-Tests'
!

!AJx86AssemblerTests class methodsFor:'documentation'!

copyright
"
    Copyright (c) 2012-2016 Igor Stasenko
                            Martin McClure
                            Damien Pollet
                            Camillo Bruni
                            Guido Chari
                   2016-now Jan Vrany <jan.vrany [at] fit . cvut . cz>

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the 'Software'), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

"
! !

!AJx86AssemblerTests methodsFor:'running'!

setUp
    super setUp.
    asm := self newAssembler.
! !

!AJx86AssemblerTests methodsFor:'tests'!

testAssembly1
    
    asm 
        push: EBP;
        mov: ESP -> EBP;
        mov: 1024 -> EAX;
        mov: EBP -> ESP;
        pop: EBP;
        ret.
    
    self assert: asm bytes = #(85 139 236 184 0 4 0 0 139 229 93 195) asByteArray
!

testAssembly3

    " instructions without operands.

    (AJInstructionDescription instructions select: [:each | each group = #emit]) keys asSortedCollection
     "
    | str |
    str :=
#(
#cbw 16r66 16r98
#cdq 16r99
"#cdqe  64 bit "
#clc 16rF8
#cld 16rFC
#cmc 16rF5 
#cpuid 16r0F 16rA2
"#cqo  64 bit "
#cwd 16r66 16r99
#cwde 16r98
#daa 16r27
#das 16r2F
#emms 16r0F 16r77
#f2xm1 16rD9 16rF0
#fabs  16rD9 16rE1
#fchs 16rD9 16rE0
#fclex 16r9B 16rDB 16rE2
#fcompp 16rDE 16rD9
#fcos 16rD9 16rFF
#fdecstp 16rD9 16rF6
#fincstp 16rD9 16rF7
#finit 16r9B 16rDB 16rE3
#fld1 16rD9 16rE8
#fldl2e 16rD9 16rEA
#fldl2t 16rD9 16rE9
#fldlg2 16rD9 16rEC
#fldln2 16rD9 16rED
#fldpi 16rD9 16rEB
#fldz 16rD9 16rEE
#fnclex 16rDB 16rE2
#fninit 16rDB 16rE3
#fnop 16rD9 16rD0
#fpatan 16rD9 16rF3
#fprem 16rD9 16rF8
#fprem1 16rD9 16rF5
#fptan 16rD9 16rF2
#frndint 16rD9 16rFC
#fscale 16rD9 16rFD
#fsin 16rD9 16rFE
#fsincos 16rD9 16rFB
#fsqrt 16rD9 16rFA
#ftst 16rD9 16rE4
#fucompp 16rDA 16rE9
#fwait 16r9B
#fxam 16rD9 16rE5
#fxtract  16rD9 16rF4
#fyl2x  16rD9 16rF1
#fyl2xp1 16rD9 16rF9
#int3 16rCC
#leave  16rC9
#lfence 16r0F 16rAE 16rE8
#lock 16rF0 "prefix"
#mfence 16r0F 16rAE 16rF0
#monitor 16r0F 16r01 16rC8
#mwait 16r0F 16r01 16rC9
#nop 16r90
#pause 16rF3 16r90
#popad 16r61
#popfd 16r9D
" #popfq 16r48 16r9D   - 64 bit "
#pushad 16r60
#pushf 16r66 16r9C
#pushfd 16r9C
" #pushfq -64 bit"
#rdtsc 16r0F 16r31  
#rdtscp 16r0F 16r01 16rF9
#sahf 16r9E
#sfence 16r0F 16rAE 16rF8
#stc 16rF9
#std 16rFD
#ud2 16r0F 16r0B
#std 16rFD "dummy"
) readStream.

[ str atEnd ] whileFalse: [
    | instr tst |
    instr := str next.
    tst := OrderedCollection new.
    [ str peek isInteger ] whileTrue: [ tst add: str next  ].

    asm reset noStackFrame.
    asm perform: instr.
    self assert: (asm bytes = tst asByteArray )
].
!

testAssemblyImmAddr
    "test generating immediate address, 
    note GDB disassembling it to:
        0x1fab <instructions.1862>:     0x8b    0x05    0xef    0xbe    0xad    0xde
        0x00001fab <instructions.1862+0>:       mov    0xdeadbeef,%eax
    which is WRONG!!
    "

    asm
        mov: 16rdeadbeef asUImm ptr32 to: asm EAX. 

    " 8b05efbeadde                   mov         eax, [deadbeef] "
    
    self assert: asm bytes =  #[139 5 239 190 173 222]
    
!

testAssemblyMemBase
    
    asm 
        mov: EAX ptr  -> EAX;
        mov: ESP ptr -> EAX;
        mov: EBP ptr -> EAX.
    
    self assert: asm bytes = #(16r8B 0 16r8B 16r04 16r24 16r8B 16r45 16r00) asByteArray
!

testAssemblyMemBaseDisp

    asm 
        mov: EAX ptr + 1 -> EAX;
        mov: EBX ptr + ECX -> EAX.
    
    self assert: asm bytes = #(16r8B 16r40 16r01 16r8B 16r04 16r0B) asByteArray
!

testAssemblyMemBaseDisp2

    asm 
        mov: EAX ptr - 1 -> EAX;
        mov: EBX ptr + ECX * 2 - 5 -> EAX.
    
    self assert: asm bytes = #(16r8B 16r40 16rFF 16r8B 16r44 16r4B 16rFB) asByteArray
!

testAssemblyMemBytes

    asm 
        mov: ((ESI ptr + ECX) size: 1) -> BL;
        mov: BL -> ((ESI ptr + ECX) size:1).
        
    self assert: asm bytes = #(16r8A 16r1C 16r0E 16r88 16r1C 16r0E ) asByteArray
!

testBitTest

    asm 
        bt: EAX with: 0.

    self assert: asm bytes =  #(16r0F 16rBA 16rE0 16r00) asByteArray
!

testCall

    asm 
        call: EAX;
        call: EAX ptr - 4;
        call: EAX ptr.
    self assert: asm bytes =  #(255 208 255 80 252 255 16) asByteArray
!

testForwardJumps
    
    asm
        jmp: #label1;
        label: #label1.

    self assert: asm bytes =  #(16rEB 0 ) asByteArray.
        
!

testImmLabels

    "test immediates with labels"
    
    | code pos |
    
    asm 
        mov: EAX ptr  -> EAX;
        mov: (16rFFFFFFFF asUImm label: (asm labelNamed: #foo) ) to:  EAX.
    
    code := asm generatedCode.
    pos := code offsetAt: #foo.

    self assert: (code bytes at: pos+1) = 255.
    self assert: (code bytes at: pos+2) = 255.
    self assert: (code bytes at: pos+3) = 255.
    self assert: (code bytes at: pos+4) = 255.
!

testJMPRegister
    
    self 
        assert: [  :assembler |
            assembler jmp: assembler EAX ]
        bytes: #[ 16rFF 2r11100000 ].
        
    self 
        assert: [  :assembler |
            assembler jmp: assembler ECX ]
        bytes: #[ 16rFF 2r11100001 ].
        
    self 
        assert: [  :assembler |
            assembler jmp: assembler EDX ]
        bytes: #[ 16rFF 2r11100010 ]
!

testJumps
    
    asm 
        label: #label1;
        nop;
        nop;
        nop;
        jz: #label1.
        
    self assert: asm bytes =  #(144 144 144 116 251) asByteArray.

    
    asm 
        reset; noStackFrame;
        label: #label1.
        126 timesRepeat: [ asm nop ].
        asm jz: #label1.
    self assert: (asm bytes size = 128).
    
    asm 
        reset; noStackFrame;
        label: #label1;
        nop;
        nop;
        nop;
        jmp: #label1.
        
    self assert: asm bytes =  #(144 144 144 235 251) asByteArray.
    
    asm
        reset; noStackFrame;
        jmp: #label1;
        label: #label1.
        
    self assert: asm bytes =  #(16rEB 0 ) asByteArray.
        
!

testMovSxZx

    asm 
        movsx: asm AX to: asm EAX;
        movzx: asm AX to: asm EAX;
        movsx: asm AL to: asm EAX;
        movzx: asm AH to: asm EAX.

    self assert: asm bytes = 
    #[
        16r0F 16rBF 16rC0
        16r0F 16rB7 16rC0
        16r0F 16rBE 16rC0
        16r0F 16rB6 16rC4 ]
!

testSyscall
    "Syscall instruction is only valid in 64-bit mode"

    self asmShould: [ :a | a syscall ] raise: Error
!

testTest
    "Special RAX opcodes"
    
    "8bit operand  opcode"		
    asm 
        test: AL with: 5.
    self assert: asm bytes = #[16rA8 05].

    
    asm reset;
        test: AX with: 5.
    "16bit operand Prefix byte, 16bit immediate (LSB)"
    self assert: asm bytes = #[16r66 16rA9 05 0].

    "32bit operand "
    asm reset;
        test: EAX with: 1.
    self assert: asm bytes =  #[16rA9 01 00 00 00].
    
    "Need more assert for non-EAX receiver, non-immediate operands"
    
    
    
!

tstRegistersOf: asm

    | numRegs |
    numRegs := asm numGPRegisters.
    
    0 to: numRegs-1 do: [:i |
        self assert: (asm reg8: i) size = 1.
        self assert: (asm reg8: i) index = i.
        
        self assert: (asm reg16: i) size = 2.
        self assert: (asm reg16: i) index = i.
        
        self assert: (asm reg32: i) size = 4.
        self assert: (asm reg32: i) index = i.

        self assert: (asm isGPNRegister: (asm nReg: i)).
    
        asm is64BitMode ifTrue: [
            self assert: (asm reg64: i) size = 8.
            self assert: (asm reg64: i) index = i.
            ]   
    ].

    "Created: / 15-12-2015 / 23:59:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!AJx86AssemblerTests methodsFor:'tests-FPU'!

testFXCH

    self 
        assert: [ :a|
        a fxch "the same as: asm fxch: asm ST1" ]
        bytes: #[	2r11011001 2r11001001 ]
    
!

testFXCHST1

    self 
        assert: [ :a| a fxch: asm ST1 ]
        bytes: #[	2r11011001 2r11001001 ]
    
! !

!AJx86AssemblerTests methodsFor:'tests-data'!

setUpDataBytes
    ^ self setUpDataBytesAlign: 1
!

setUpDataBytesAlign: alignToBytes

    asm nop.
    asm align: alignToBytes.
    ^ asm db: 16r12.
!

testDataBytes
    
    |data|
    data := self setUpDataBytes.
    
    self assert: asm bytes equals: #[144   16r12].
!

testDataBytesAlignDouble
    
    |data|
    data := self setUpDataBytesAlign: 4.
    
    self assert: asm bytes equals: #[144   0 0 0 16r12].
!

testDataBytesAlignQuad
    
    |data|
    data := self setUpDataBytesAlign: 8. 
    self assert: asm bytes equals: #[144   0 0 0   0 0 0 0   16r12].
!

testDataBytesAlignWord
    
    |data|
    data := self setUpDataBytesAlign: 2.
    
    self assert: asm bytes equals: #[144   0 16r12].
!

testDataDouble
    
    | data|
    
    asm nop.
    data := asm dd: #[16r78 16r56 16r34 16r12].
    
    self assert: asm bytes equals: #[144   16r78 16r56 16r34 16r12].
!

testDataWord
    
    | data|
    asm nop.
    data := asm dw: #[16r34 16r12].
    
    self assert: asm bytes equals: #[144   16r34 16r12].
    
    
! !

!AJx86AssemblerTests methodsFor:'utility'!

asmShould: aBlock raise: anError

    self should: [self bytes: aBlock] raise: anError.
!

assert: aBlock bytes: aByteArray

    self assert: (self bytes: aBlock) equals: aByteArray .
!

bytes: aBlock
    asm := self newAssembler.
    aBlock value: asm.
    ^ asm bytes
!

newAssembler 
    ^ AJx86Assembler new
        noStackFrame;
        yourself
! !