Author Topic: High Level Language in MASM  (Read 61192 times)

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #225 on: May 28, 2016, 09:15:32 AM »
Some new changes:
Code: [Select]
Changes in 2.21 - xx xxx 2016
- added byte index to switch OPTION SWITCH:TABLE
- added error A3022: .CASE redefinition : A(val) : B(val)
- added command line switch /swr - use register [R|E]AX in .SWITCH code
- added OPTION SWITCH:REGAX - use register [R|E]AX in .SWITCH code
- added OPTION SWITCH:NOREGS - use stack in .SWITCH code (default)
- added OPTION SWITCH:NOTEST - skip range test in switch-table jump
- fixed bug in inline macro @CStr()

The jump table code is now recursive so multiple tables may be created in one switch. To simplify table creation an option to use [R|E]AX is added.
Code: [Select]
.switch al
  .case 'A','C','D','E','F','G',
'H','I','J','K','L'
  .case 'M'
  .default
.endsw

Code: [Select]
00000004  3C41              *   cmp al,65
00000006  7CFA              *   jl @C000E
00000008  3C4D              *   cmp al,77
0000000A  7FF6              *   jg @C000E
0000000C  50                *   push eax
0000000D  0FBEC0            *   movsx eax,al
00000010  8B0485FCFEFFFF    *   mov eax,[eax*4+@C0010-(65*4)]
00000017  870424            *   xchg eax,[esp]
0000001A  C3                *   retn

Code: [Select]
OPTION SWITCH:REGAX

Code: [Select]
00000004  3C41              *   cmp al,65
00000006  7CFA              *   jl @C000E
00000008  3C4D              *   cmp al,77
0000000A  7FF6              *   jg @C000E
0000000C  0FBEC0            *   movsx eax,al
0000000F  FF2485FCFEFFFF    *   jmp [eax*4+@C0010-(65*4)]

The NOTEST option is short lived and skips the range-test in the jump code. The option is turned off after usage.

Code: [Select]
.switch eax
  .case 'A','C','D','E','F','G','H','I'
.endsw

Code: [Select]
00000004  83F841            *   cmp eax,65
00000007  7C33              *   jl @C000A
00000009  83F849            *   cmp eax,73
0000000C  7F2E              *   jg @C000A
0000000E  FF2485FCFEFFFF    *   jmp [eax*4+@C000B-(65*4)]

Code: [Select]
OPTION SWITCH:NOTEST

Code: [Select]
00000004  FF2485FCFEFFFF    *   jmp [eax*4+@C000B-(65*4)]

jj2007

  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: High Level Language in MASM
« Reply #226 on: May 28, 2016, 09:27:21 AM »
Code: [Select]
OxPT_Assembler HJWasm32 ; 2.7
OxPT_Assembler HJWasm64 ; 3.0
OPT_Assembler AsmC ; 2.4 secs
:t

Switch testbed is attached - your new code looks pretty fast:
Code: [Select]
Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
Assembled with AsmC
23 ms   case 260, MB Switch_ table
230 ms  case 260, MB Switch_ chain
457 ms  case 260, Masm32 switch
6 ms    case 260, AsmC .Switch

23 ms   case 196, MB Switch_ table
178 ms  case 196, MB Switch_ chain
341 ms  case 196, Masm32 switch
6 ms    case 196, AsmC .Switch

24 ms   case 132, MB Switch_ table
127 ms  case 132, MB Switch_ chain
229 ms  case 132, Masm32 switch
6 ms    case 132, AsmC .Switch

23 ms   case 68, MB Switch_ table
76 ms   case 68, MB Switch_ chain
120 ms  case 68, Masm32 switch
6 ms    case 68, AsmC .Switch

23 ms   case 4, MB Switch_ table
24 ms   case 4, MB Switch_ chain
6 ms    case 4, Masm32 switch
6 ms    case 4, AsmC .Switch

2989    bytes for MbTable
4840    bytes for MbChain
4799    bytes for Masm32
4201    bytes for asmc
:t  :t

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #227 on: June 05, 2016, 01:58:46 AM »
With regards to using a jump table as the default in a switch, this seems to have the same effect as using SSE instructions. The test case improves but no increased performance in real applications.

The reason why the SSE implementation failed is a combination of bad or unrealistic test cases: A test case for strlen() using a million byte long string when the average length is less than 20 byte. The switch implementation is language creation so writing creates the language. This means that all testing so far is based on assumptions.

In Asmc the switch was introduced in version 2.14 and there are now above 300 switches in the source base. Testing Asmc with and without a jump table benefit the latter both in size and speed. Using a more aggressive table creation by allowing larger tables to be created just adds more code and reduces the speed even further.

Code: [Select]
\masm32\m32lib\*.asm

4953 ClockTicks: v2.16
4235 ClockTicks: v2.21A - 300544 byte
4015 ClockTicks: v2.21B - 298496 byte

Code: [Select]
real_math.asm

6828 ClockTicks: v2.16
6578 ClockTicks: v2.21A - 300544 byte
6390 ClockTicks: v2.21B - 298496 byte

The only benefit using a jump table will then be a sequential array of values within a specific range. This range was set to 4 in v2.21A, which means that cases like 1, 4, 7, 10, and so on will fit in a table potentially 4 times larger than the number of cases. The range in v2.21B is set to 2.

Of all the switches used in the test none of them have a "perfect" sequential array of values, and only a few will fit within the 4-range value, and even less in the 2-range value. This means that the speed tests used is more or less irrelevant for normal usage of the switch. For this reason the table switch is now turned off by default.

The reason why the increased usage of tables slows things down may be a combination of different things. Inserting a large table in a relatively small switch in combination with loops will convert most short jumps to long jumps so that may be a reason. A long jump is 5 bytes compare to 2 for a short, so the size increases in addition to the size of the table.

There are however still benefits from using tables if the condition is right but this seems to be an exception rather than a rule.

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #228 on: September 22, 2016, 09:49:13 AM »
Some new changes:
Code: [Select]
Changes in 2.21 - xx xxx 2016
- added 0x prefix for numbers - regress/0x.asm
- added concat of line follow '(' for HLL call(...) - regress/hllconcat.asm
- added OPTION CSTACK (-Cs) to 64-bit - regress/cstack64.asm
- added OPTION LOOPALIGN:0|1|2|4|8|16 - regress/loopalign.asm
- added OPTION CASEALIGN:0|1|2|4|8|16 - regress/casealign.asm
- dllimport:<dll> external proto <no args> (call) error
   call error A2014: cannot define as public or external : <name>
   invoke handle import prefix ([__imp_]<name>), call do not
   if <name>() is dll-import invoke will be used in HLL(call)
   - regress/PE3.ASM
- set OPTION SWITCH:NOTABLE as default
- removed OPTION SWITCH:AUTO
- removed command line switch /swa - Sets jump-table creation to Auto in .SWITCH
- added command line switch /swr - use register [R|E]AX in .SWITCH code
- added OPTION SWITCH:REGAX - use register [R|E]AX in .SWITCH code
- added OPTION SWITCH:NOREGS - use stack in .SWITCH code (default)
- added OPTION SWITCH:NOTEST - skip range test in switch-table jump
- added byte index to switch (64-bit)
- added error A3022: .CASE redefinition : A(val) : B(val)
- fixed bug in inline macro @CStr()

The expansion of the stack using -Cs
Code: [Select]
.x64
.model flat, fastcall
.code

option win64:3, cstack:off ; default is off..

Cs_OFF PROC USES rsi rdi rbx a1, a2, a3, a4
; mov [rsp+8],rcx
; mov [rsp+16],rdx
; mov [rsp+24],r8
; mov [rsp+32],r9
; push rbp
; mov rbp,rsp
; push rsi
; push rdi
; push rbx
; sub rsp,8 + @ReservedStack
sub rsp,rax
mov ecx,a1 ; [rbp+10H]
mov edx,a2 ; [rbp+18H]
mov r8d,a3 ; [rbp+20H]
mov r9d,a4 ; [rbp+28H]
; add rsp,8 + @ReservedStack
; pop rbx
; pop rdi
; pop rsi
; leave
ret
Cs_OFF ENDP

option cstack:on

Cs_ON PROC USES rsi rdi rbx a1, a2, a3, a4
; mov [rsp+8],rcx
; mov [rsp+16],rdx
; mov [rsp+24],r8
; mov [rsp+32],r9
; push rsi
; push rdi
; push rbx
; push rbp
; mov rbp,rsp
; sub rsp,8 + @ReservedStack
sub rsp,rax
mov ecx,a1 ; [rbp+28H]
mov edx,a2 ; [rbp+30H]
mov r8d,a3 ; [rbp+38H]
mov r9d,a4 ; [rbp+40H]
; leave
; pop rbx
; pop rdi
; pop rsi
ret
Cs_ON ENDP

option stackbase:rsp

Cs_RSP PROC USES rsi rdi rbx a1, a2, a3, a4
; mov [rsp+8],rcx
; mov [rsp+16],rdx
; mov [rsp+24],r8
; mov [rsp+32],r9
; push rsi
; push rdi
; push rbx
; sub rsp,8 + @ReservedStack
sub rsp,rax
mov ecx,a1 ; [rsp+40H]
mov edx,a2 ; [rsp+48H]
mov r8d,a3 ; [rsp+50H]
mov r9d,a4 ; [rsp+58H]
; add rsp,8 + @ReservedStack
; pop rbx
; pop rdi
; pop rsi
ret
Cs_RSP ENDP

END

jj2007

  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: High Level Language in MASM
« Reply #229 on: September 22, 2016, 10:31:25 AM »
What is the purpose of these lines?
Code: [Select]
mov ecx,a1 ; [rbp+10H]
mov edx,a2 ; [rbp+18H]
mov r8d,a3 ; [rbp+20H]
mov r9d,a4 ; [rbp+28H]

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #230 on: September 22, 2016, 10:50:57 AM »
Makes the test case use the arguments to see if the offset is calculated correctly:
Code: [Select]
mov ecx,a1 ; [rbp+28H]
mov edx,a2 ; [rbp+30H]
mov r8d,a3 ; [rbp+38H]
mov r9d,a4 ; [rbp+40H]

jj2007

  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: High Level Language in MASM
« Reply #231 on: September 22, 2016, 05:03:25 PM »
Oh, I see - thanks :P

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #232 on: September 23, 2016, 11:58:06 PM »
Added direct assignment of function call after comma (like the rv(...) macro):
Code: [Select]
.486
.model flat, stdcall
.code

foo proc a
ret
foo endp

mov edi,[ebx + foo(0)]
add esi,foo(0xFFFFFFFF)

end

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #233 on: October 02, 2016, 06:49:19 AM »
Fixed a bug in getenv() where %AsmcDir% was accepted as %Asmc%. The direct assignment of function call also failed in some cases where the local stack was used.

Added directive .ASSERT to simplify testing using an HLL expression as argument. It expand to .IF !( <expression> ) do exit(msg) where the message is <file>(<line>): <expression> stored in the code segment.

Code: [Select]
.ASSERT <assert_expression>
.ASSERT:[<Handler> | ON | OFF | PUSH | POP | PUSHF | POPF | CODE | ENDS]

Handler
  The assert macro calls this routine if the assert expression is true. By placing the assert code in a subroutine instead of within the body of the macro, programs that call assert multiple times will save space. The default handler name is assert_exit.

ON/OFF
  Main switch. Same as OPTION ASMC:0x81/0x01.

PUSH/POP
  Save and restore the ASMC flag. Stack level is 128.

PUSHF/POPF
  Saves flags using PUSHF[D|Q] before calling handler if set.

CODE/ENDS
  Same as <IF (ASSERT EQ ON)> and <ENDIF>.


Assert code:
Code: [Select]
.assert eax == 0
Expands to:
Code: [Select]
.if  !( expression )
assert_exit()
db "TEST(43): "
db "eax == 0"
db 0
.endif
continue:

assert(exp) as a proc
Code: [Select]
.assert:code
assert_exit:
pop rax
PrintAssertMsg( rax, rax )
exit( 1 )
.assert:assert_exit ; the default name is assert_exit
.assert:ends

assert(exp) as a macro
Code: [Select]
assert macro
lea rcx,$
lea rdx,@F
PrintAssertMsg( rcx, rdx )
exit( 1 )
@@:
exitm<>
endm

.assert:assert ; install new macro handler

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #234 on: October 05, 2016, 10:25:40 PM »
Some return-code-size problems

so this fails:
Code: [Select]
.if GetFileAttributesW( __allocwpath( file ) ) == -1

osmaperr()
.endif

the function returns a DWORD value
Code: [Select]
mov rax,-1 - 0xFFFFFFFFFFFFFFFF
mov eax,-1 - 0x00000000FFFFFFFF
cmp rax,-1 - 0xFFFFFFFFFFFFFFFF

The max imm value for CMP is 32-bit so 0x00000000FFFFFFFF is still -1
Code: [Select]
api proc
mov eax,-1
ret
api endp

.if api() == -1 ; cmp 00000000FFFFFFFF,FFFFFFFFFFFFFFFF

Most return codes are in fact still 32-bit in win64 so .IFD, .IFW, and .IFB is added to downsize the compare. This only apply to 64-bit code where a function call is used in HLL.
Code: [Select]
.ifd api() == -1 ; cmp FFFFFFFF,FFFFFFFF
.ifw api() == -1 ; cmp FFFF,FFFF
.ifb api() == -1 ; cmp FF,FF

Same with .ASSERT
Code: [Select]
.assertd api() == -1
.assertw api() == -1
.assertb api() == -1

jj2007

  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: High Level Language in MASM
« Reply #235 on: October 05, 2016, 11:01:49 PM »
Interesting, HJWasm codes it like this, ML+AsmC choke on both:
Code: [Select]
  int 3
  mov rbx, 1234567812345678h
  cmp rax, 1234567812345678h
; HJWasm32:
; 48 BB 78 56 34 12 78 56 34 12     | movabs rbx, 1234567812345678                  |
; 48 3D 78 56 34 12                 | cmp rax, 12345678                             |

Is that a known issue?

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #236 on: October 06, 2016, 12:03:46 AM »
Yes. It was debated earlier with using .CASE imm64.

It's possible to move/push imm64 but not compare.

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #237 on: October 13, 2016, 08:20:19 AM »
Version 2.21 is now released to the SF site. It include a complete tool-chain with assembler, linker, library manager and a simple make utility. There is also libraries and include files to create simple console applications for 32 and 64-bit. Asmc and Doszip is made using this.

Some of the tools are made using the -pe switch, so they don't use any external libraries. The tools.zip file include two of them plus a 64-bit test.

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #238 on: June 07, 2017, 08:41:31 PM »
Version 2.24J:

Added & as alias for ADDR in proc( &args )

Simple test case:
Code: [Select]
    DialogBoxIndirectParam(rcx, &Dialog, 0, &DialogFunc, NULL);

nidud

  • Member
  • *****
  • Posts: 1408
    • https://github.com/nidud/asmc
Re: High Level Language in MASM
« Reply #239 on: June 08, 2017, 01:00:03 AM »
Latest benchmark result.

\masm32\m32lib\*.asm
Code: [Select]
4852 ClockTicks: \jwasm\jwasm -q @ml.rsp
6396 ClockTicks: \hjwasm\hjwasm32 -q @ml.rsp
4789 ClockTicks: \hjwasm\hjwasm64 -q @ml.rsp
6646 ClockTicks: \hasm\hasm32 -q @ml.rsp
4961 ClockTicks: \hasm\hasm64 -q @ml.rsp
4976 ClockTicks: \uasm\uasm64 -q @ml.rsp
3907 ClockTicks: \asmc\bin\asmc -q @ml.rsp

real_math.asm
Code: [Select]
10311 ClockTicks: \masm32\bin\ml -c -nologo real_math.asm
 8346 ClockTicks: \jwasm\jwasm -q real_math.asm
11903 ClockTicks: \hjwasm\hjwasm32 -q real_math.asm
 8268 ClockTicks: \hjwasm\hjwasm64 -q real_math.asm
12012 ClockTicks: \hasm\hasm32 -q real_math.asm
 8190 ClockTicks: \hasm\hasm64 -q real_math.asm
 8175 ClockTicks: \uasm\uasm64 -q real_math.asm
 6380 ClockTicks: \asmc\bin\asmc -q real_math.asm