The MASM Forum

64 bit assembler => UASM Assembler Development => Topic started by: Biterider on June 15, 2018, 06:29:57 AM

Title: #IF
Post by: Biterider on June 15, 2018, 06:29:57 AM
Hallo
I'm still working in the translation of header files to asm http://masm32.com/board/index.php?topic=6812.msg76894#msg76894 (http://masm32.com/board/index.php?topic=6812.msg76894#msg76894).
ATM I have a working program that that converts tons of  files, which I'm checking now.
I found a problem with lines like this:
#if defined (_MSC_VER) && (_MSC_VER >= 1020)

The translation looks like
ifndef defined
  defined macro x
    ifdef x
      exitm <-1>
    else
      exitm <0>
    endif
  endm
endif

if defined(_MSC_VER) and (_MSC_VER ge 1020)


This code doesn't work in asm, since if _MSC_VER is not defined, the second condition is still checked producing a compilation error. This behavior is also present in ML, which I think is not correct.

Any chance to change that or to figure out a workaround?

Biterider
Title: Re: #IF
Post by: habran on June 15, 2018, 12:16:46 PM
instead of :if defined(_MSC_VER) and (_MSC_VER ge 1020)
it could be written as:
   if defined(_MSC_VER)
      if _MSC_VER ge 1020
           statement...
      endif
   endif

However, maybe someone comes with better idea.
Title: Re: #IF
Post by: Biterider on June 15, 2018, 03:40:54 PM
Hi Habran
That's right. However, more complex statements that uses #elif or #else can not be written this way.
That is why I asked for support on your side.  ::)

As far as I can see, such a change should not brake existing code...

Biterider
Title: Re: #IF
Post by: jj2007 on June 15, 2018, 05:24:48 PM
Workaround:
_MSC_VER=0  ; somewhere at the top of your source

... code that may set _MSC_VER ...

if _MSC_VER ge 1020
  ...
Title: Re: #IF
Post by: habran on June 15, 2018, 05:41:50 PM
I have tested the first version and it works as expected:
if defined(_MSC_VER) and (_MSC_VER ge 1020)

try to play with it.
I have used :

if defined(_MSC_VER) and (_MSC_VER ge 1020)
  .ERR <There is something wrong with that statement>
endif

and than tried this:

if defined(_MSC_KER) and (_MSC_VER ge 1020)  ; note _MSC_KER is fake
  .ERR <There is something wrong with that statement>
endif


so, in the second case it should throw an error but it doesn't

but with this it does throw an error:

if defined(_MSC_VER) and (_MSC_VER le 1020) ; note I have used le here
  .ERR <Tornero>
endif
Title: Re: #IF
Post by: Biterider on June 15, 2018, 06:03:31 PM
Hi
@JJ: the windows header files is a house of cards that builds heavily upon the existence (definition) of symbols. If we define all of them, the result will not be the same, since we will include things we are not interested in. In case we do, we accept that all generated inc files will need to be corrected by hand (~2000 files for Win10)

@habran
I use the following test case

ifndef defined
  defined macro x
    ifdef x
      exitm <-1>
    else
      exitm <0>
    endif
  endm
endif

;Symbol equ 1

if defined(Symbol) and (Symbol eq 1)
  echo 1
else
  echo 2
endif



If Symbol is commened out, I get a compilation error (Error A2102: Symbol not defined : Symbol)

Biterider
Title: Re: #IF
Post by: habran on June 15, 2018, 06:55:18 PM
I can see now where is the problem ::)
I'll see what I can do about it ;)

However, it will take some time :dazzled:
Title: Re: #IF
Post by: nidud on June 15, 2018, 11:24:57 PM
deleted
Title: Re: #IF
Post by: Biterider on June 16, 2018, 12:17:06 AM
Hi nidud
There are hundreds of such cases in the header files. I chose the first one I found.
You're right with _MSC_VER, but I think there are cases where the undefined symbols are a problem.

Biterider
Title: Re: #IF
Post by: nidud on June 16, 2018, 12:34:10 AM
deleted
Title: Re: #IF
Post by: habran on June 16, 2018, 04:52:09 AM
I agree with Biterider,
even if there are no undefined symbols in those headers it is obvious that assembler behaves inappropriate in such cases. 
Title: Re: #IF
Post by: habran on June 16, 2018, 05:53:56 AM
The problem can be solved with definedmacro
it get computed: if 0 and (_MSC_KER le 1020)
Title: Re: #IF
Post by: Biterider on June 16, 2018, 06:34:28 AM
Hi Habran
Can you be more explicit?


Thanks, Biterider
Title: Re: #IF
Post by: habran on June 16, 2018, 07:32:36 AM
defined macro returns 0 but it doesn't tell assembler not to continue.
In that case defined doesn't return a proper result.
if 0 and (_MSC_KER le 1020)
if 1 and (_MSC_KER le 1020)
these both cases will continue with AND
Title: Re: #IF
Post by: habran on June 16, 2018, 07:42:58 PM
I have succeeded to prevent error for the case that symbol is not defined, it will work only with "if 0 and ...".
The fix is in expreval.c starting with line 1169:

                DebugMsg1(("get_operand(%s): symbol %s not defined, pass > 1, curr proc=>%s<, \n", tokenarray[i].string_ptr, tmp, CurrProc ? CurrProc->sym.name : "NULL" ));
                if ( opnd->type && *opnd->type->name ) {
                    fnEmitErr( MEMBER_NOT_DEFINED, opnd->type->name, tmp );
                } else {
                  if ((0 == _memicmp(tokenarray[0].tokpos, "if", 2)) &&
                      (0 == _memicmp(tokenarray[1].tokpos, "0", 1)) &&
                      (0 == _memicmp(tokenarray[2].tokpos, "and", 3)))
                    ;//skip it
                  else
                    fnEmitErr( SYMBOL_NOT_DEFINED, *(tmp+1) == '&' ? "@@" : tmp );
                }
                return( ERROR );
            }
#if ALIAS_IN_EXPR /* v2.04b: added */

the fix is:

                  if ((0 == _memicmp(tokenarray[0].tokpos, "if", 2)) &&
                      (0 == _memicmp(tokenarray[1].tokpos, "0", 1)) &&
                      (0 == _memicmp(tokenarray[2].tokpos, "and", 3)))
                    ;//skip it
                  else

It is an ugly hack but it works.
Let me know if you are happy with it.
If you can not build UASM I can post it here, because it looks like John is busy lately with his garden.
Title: Re: #IF
Post by: Biterider on June 16, 2018, 08:00:02 PM
Hi habran
If you can provide the exe, I'll test it asap.
Biterider
Title: Re: #IF
Post by: habran on June 16, 2018, 10:00:49 PM
OK, here it is
however, it is just for your testing
The official release will be uploaded if this one is OK.
Title: Re: #IF
Post by: jj2007 on June 16, 2018, 11:45:43 PM
All code written for the new UAsm version will be incompatible with ML, of course...

Here is another workaround:include \masm32\include\masm32rt.inc

defined MACRO arg
  ifdef arg
defret=arg
exitm <1>
  else
defret=0
exitm %0
  endif
endm

.code
start:
  if defined(RECT) and defret GE 16
print "RECT is defined", 10
  else
print "RECT is undefined", 10
  endif
  if defined(bla) and defret GE 16
print "bla is defined", 10
  elseif defret
print "bla is defined, its value is "
print str$(defret), 10
  else
print "bla is undefined", 10
  endif
  bla=1
  if defined(bla) and defret GE 16
print "bla is defined", 10
  elseif defret
print "bla is defined, its value is "
print str$(defret), 10
  else
print "bla is undefined", 10
  endif
  inkey

  exit

end start
Title: Re: #IF
Post by: habran on June 17, 2018, 12:40:16 AM
JJ, I agree with you, even though that change would affect only "if Symbol and" :biggrin:
The problem would appear when headers would work only with UASM.
If Biterider is happy with that macro of yours I am happy to.
It is better to use macro which can be shared with masm.
However, the macro could be maybe done even more sophisticated;)
Title: Re: #IF
Post by: Biterider on June 17, 2018, 06:57:39 AM
Hi
I'll do the testing tomorrow morning.
Biterider

Title: Re: #IF
Post by: Biterider on June 17, 2018, 05:51:00 PM
Hello
@Habran: thanks for the exe. I tested it successfully. I tried other combinations like
if 1 and defined(Symbol) and (Symbol eq 1)   ;Symbol not defined
and confirmed that it is just a hack for only 1 situation.  ;)

If we want to implement something like an "early return of conditional sentences", when the final logical outcome in a given analysis state is known, without having to check the remaining conditions, more work has to be done. BTW, HLLs do it that way.

Another example with "or" is
if 1 or (Symbol eq 1)
This expression should be TRUE, even if symbol is not defined.

The other side of the coin are the arguments of JJ & Habran: compatibility. ML does not work that way. It strictly analyse each condition to finally get the result.
I don't think we will brake existing software, but if we write new one, like the header file translation, ML will not be able to assemble it because of these new features.
ATM, I do not know which way is the best. Maybe someday we'll have to leave ML behind and evolve. Other opinions or ideas?

Biterider
Title: Re: #IF
Post by: LiaoMi on June 17, 2018, 06:19:15 PM
Hello,

ToutEnMasm solved this problem by a simple definition in "Full SDK Include Project"

_WIN32_WINNT equ 00000602h
NTDDI_VERSION equ 06020000h
WINVER equ 06020000h

_WIN64 equ 1
_INTEGRAL_MAX_BITS equ 128 ;-------------
_MSC_VER     equ 1500   ;win xp sp2  reponse vc++
_WIN32_IE equ 800h ;internet explorer 7
_MSC_FULL_VER equ 150021022
__midl equ 0
_WIN32_FUSION equ 0
_WIN32 equ
DBG equ 0 ;ou 1
__GOT_SECURE_LIB__ equ 1 ;a chercher
;_WIN32 equ 1
__RPCNDR_H_VERSION__ equ 100h ;-- ?
IFDEF _WIN64
PPVREGISTER TEXTEQU <R11>
ELSE
PPVREGISTER TEXTEQU <edx>
ENDIF

;----------------------------------------------------------
_X86_ equ 1
__STDC__ equ 1
ISOLATION_AWARE_ENABLED equ 0
_DBG_MEMCPY_INLINE_ equ 0
_MSC_EXTENSIONS equ  1    ; DUMMYUNIONNAME vide
OEMRESOURCE equ 1 ;various resource system IDC_..
_M_IX86 equ 1 ;x86 C compiler target architecture ,no masm
;-----------------------------------
;WINAPI_PARTITION_APP equ 1    ;sdkddkver
;_WIN32_WINNT_WINBLUE equ 603h ;c++
;NTDDI_WINBLUE equ 6030000h ;c++
;----------------------------------
include sdkddkver.sdk ;define _WIN32_WINNT and _WIN32_IE
;----------------- change NTDDI_WINXPSP3 as needed -----------------
NTDDI_WINLH equ NTDDI_WIN2K ;CertSetCertificateContextProperty Windows 2000 Professional minimum system required
_WIN32_WINDOWS equ _WIN32_WINNT_WINXP
_WIN32_WCE equ _WIN32_WINNT_WINXP
WINXP equ NTDDI_WINXP ;minimum xp demandй par rpcdce.h
NTDDI_WXP  equ NTDDI_WINXPSP1
NTDDI_XPSP2 equ NTDDI_WINXPSP2
NTDDI_WIN2003 equ NTDDI_WIN2K ;pas sur
_NT_TARGET_VERSION_WIN7 equ _WIN32_IE_WIN7 ;Windows 7 minimum


This is quite enough to forget about this variable  :biggrin: Or are there any hidden features that I have overlooked?
Title: Re: #IF
Post by: jj2007 on June 17, 2018, 08:25:46 PM
Quote from: LiaoMi on June 17, 2018, 06:19:15 PMToutEnMasm solved this problem by a simple definition in "Full SDK Include Project"

This was my spontaneous reaction, too, but:

Quote from: Biterider on June 15, 2018, 06:03:31 PM
@JJ: the windows header files is a house of cards that builds heavily upon the existence (definition) of symbols. If we define all of them, the result will not be the same, since we will include things we are not interested in. In case we do, we accept that all generated inc files will need to be corrected by hand (~2000 files for Win10)
Title: Re: #IF
Post by: LiaoMi on June 17, 2018, 09:14:08 PM
Quote from: jj2007 on June 17, 2018, 08:25:46 PM
Quote from: LiaoMi on June 17, 2018, 06:19:15 PMToutEnMasm solved this problem by a simple definition in "Full SDK Include Project"

This was my spontaneous reaction, too, but:

Quote from: Biterider on June 15, 2018, 06:03:31 PM
@JJ: the windows header files is a house of cards that builds heavily upon the existence (definition) of symbols. If we define all of them, the result will not be the same, since we will include things we are not interested in. In case we do, we accept that all generated inc files will need to be corrected by hand (~2000 files for Win10)

Hi jj2007,

I also thought so, but the question is, do you think a programmer needs all these definitions in assembler? Most structures can be used without configuration conditions. All these flags are needed only for a high-level compiler, in this case, we can get the two strategies - First, we leave some basic definitions, after this we delete all unnecessary definitions. Since I personally dont need these conditional definitions in my code, I decide what I need to add and what not, there is a direct link to the object that I need in my code, nothing else is needed.
Of course my opinion may be wrong, I dont pretend to have absolutely correct understanding of the problem, because I dont know C++ well  :redface:
Title: Re: #IF
Post by: Biterider on June 17, 2018, 09:33:30 PM
Hi LiaoMi
You can go this way, but it will be really hard to keep the set of .inc file updated. With each major Windows update you will need to go through the whole process again ans again. In my opinion, we need an automatic tool that processes the ~2000 header files. Minimal intervention can not be avoided, but not more than that.  :redface:

Biterider
Title: Re: #IF
Post by: Vortex on June 17, 2018, 09:39:21 PM
Sadly, M$ did everything to obfuscate those SDK header files.
Title: Re: #IF
Post by: LiaoMi on June 17, 2018, 10:43:25 PM
Quote from: Biterider on June 17, 2018, 09:33:30 PM
Hi LiaoMi
You can go this way, but it will be really hard to keep the set of .inc file updated. With each major Windows update you will need to go through the whole process again ans again. In my opinion, we need an automatic tool that processes the ~2000 header files. Minimal intervention can not be avoided, but not more than that.  :redface:

Biterider

Hi Biterider,

thats right, I assumed that the basic structure does not change so quickly and I did not consider the idea of a universal solution, It would be convenient to make a parser of such conditions, which shows a table with parameters, similar as it is done in cmake
(https://cmake.org/wp-content/uploads/2014/08/CMakeGUI.jpg)
What to do with these data after processing?  :icon_rolleyes:
Title: Re: #IF
Post by: habran on June 17, 2018, 11:08:29 PM
I totally agree with you Biterider :t
It is not a big deal to implement such features in UASM because it is so flexible and ALIVE, however, headers should be flexible as well and that is why we should use macros for that job, which can be implemented in masm
as well. JJ is on the rite track and maybe some other macros experts could get involved ;)

BTW, correct behaviour of the assembler should be to stop at and after first expression if it is negative and I would like to correct UASM in that matter. That is one of masm "features" which we can avoid.
We can still exclude it with a strict masm compatibility. Opinion about that matter would be appreciated 8)
Title: Re: #IF
Post by: Biterider on June 19, 2018, 06:09:55 AM
Hi
Good news... at least for now :biggrin:
I've listed all the files included, starting with Windows.h, and came up with a total of about 200 files, which is roughly the same number of files supplied by h2incX (v0.99.20).
Much less than the 2000 files located in the header include directories. 200 files can be managed if the logic needs to be changed to meet the requirements of masm.

Nevertheless, I would suggest to keep on the UASM logic optimization.

Biterider
Title: Re: #IF
Post by: habran on June 19, 2018, 08:09:24 AM
Great :t
Does that mean that you want to keep that change in UASM which I implemented?
Title: Re: #IF
Post by: Biterider on June 19, 2018, 03:51:42 PM
Hi Habran
I can not say for sure until I have a working set of files. My best guress is that it will not be needed.
Biterider
Title: Re: #IF
Post by: habran on June 19, 2018, 04:47:18 PM
 :t
Title: Re: #IF
Post by: johnsa on September 17, 2018, 06:43:56 PM
Proposing the following fix:

The core of the issue is that user defined macros are not allowed to received undefined symbols as arguments.
So while IFDEF someUndefinedVar  works, passing that down via a macro will break.

Proposed fix is to allow an undefined symbol to propagate down through the user macro IFF the macro name is "defined"
We can either provide a built-in defined macro, which would be overridden by a user including an INC file such as the Windows headers that define their own defined macro.
Title: Re: #IF
Post by: Biterider on September 29, 2018, 05:51:59 PM
Hello johnsa
Good to hear from you!  8)
I can not say anything about the assembler internals and if your suggestion will fix the problem. BTW, ML behaves in the same way.
I only want to be sure that the problem is well understood.

In the last few weeks I implemented a super lightweight conditional evaluator using the well known "shunting-yard algorithm" in combination with a tristate truth tables (TRUE, FALSE, ERROR). In this way I was able to evaluate such cases as described above. Eventually such an approach could also solve the problem.

Just my 2 cents.  ;)

Biterider

Title: Re: #IF
Post by: johnsa on September 30, 2018, 03:18:46 AM
Hi,

I agree that it is definitely expected behaviour, but we can do better.. why not have a proper "defined" macro that just works and can be combined in other expressions.. it would be backwards compatible and still allow any other approach/method to work too.. so .. we'll just do it :)
Title: Re: #IF
Post by: jj2007 on September 30, 2018, 03:38:34 AM
Quote from: johnsa on September 30, 2018, 03:18:46 AMwhy not have a proper "defined" macro

I would suggest @Defined(somesymbol), for consistency with @SubStr(), @CatStr() etc. ML coders could use a workaround:
include \masm32\include\masm32rt.inc

ifndef @Defined
  @Defined MACRO arg
ifdef arg
EXITM <1>
else
EXITM <0>
endif
  ENDM
endif

.data
MyArray dd 25, 18, 23, 17, 9, 2, 6

.code
start:
  if @Defined(MyArray)
print "MyArray is defined", 13, 10
  else
print "MyArray is UNDEFINED", 13, 10
  endif
  if @Defined(NotMyArray)
print "NotMyArray is defined", 13, 10
  else
print "NotMyArray is UNDEFINED", 13, 10
  endif
  inkey "that was easy"
  exit

end start
Title: Re: #IF
Post by: Biterider on September 30, 2018, 04:26:33 AM
Hi
Sorry, but right now I do not see the point.  ::)
A typical problematic C code line (winerror.h) looks like this

#if defined (_MSC_VER) && (_MSC_VER> = 1020)

The current translation would be

if Defined(_MSC_VER) and (_MSC_VER ge 1020)

If _MSC_VER is not defined here, only the second part of the conditional sentence is problematic because the symbol is undefined and in this case it should not be evaluated because of the use of the logical "and" operator.

I'm using a modified version of Japheth's WinInc defined macro.


  ifndef Defined
    Defined macro x
      ifdef x
        exitm <-1>                      ;-1 to be able to use the 'NOT' operator.
      else                              ;Not(-1) = 0 (FALSE)
        exitm <0>
      endif
    endm
  endif


Maybe you can explain with an example what you want to do.  ;)

Biterider
Title: Re: #IF
Post by: jj2007 on September 30, 2018, 05:51:24 AM
OK, here is a tailored example, with a small modification of the macro:
include \masm32\include\masm32rt.inc

ifndef @Defined
  @Defined MACRO arg
ifdef arg
DefResult equ <arg>
EXITM <arg>
else
DefResult equ <0>
EXITM <0>
endif
  ENDM
endif


.code
start:
  _MSC_VER equ <1020>
  if @Defined(_MSC_VER) ge 1020
print str$(DefResult), 9
print " = _MSC_VER, defined as equal or above 1020", 13, 10, 10
  else
print str$(DefResult), 9
print " = _MSC_VER (UNDEFINED or less than 1020)", 13, 10, 10
  endif

  _MSC_VER equ <1000>
if @Defined(_MSC_VER) ge 1020
print str$(DefResult), 9
print " = _MSC_VER, defined as equal or above 1020", 13, 10, 10
  else
print str$(DefResult), 9
print " = _MSC_VER (UNDEFINED or less than 1020)", 13, 10, 10
  endif

if @Defined(_MSC_NOSUCHVER) ge 1020
print str$(DefResult), 9
print " = _MSC_NOSUCHVER, defined as equal or above 1020", 13, 10, 10
  else
print str$(DefResult), 9
print " = _MSC_NOSUCHVER (UNDEFINED or less than 1020)", 13, 10, 10
  endif

  inkey " "
  exit

end start


Output (identical for UAsm and ML 6.15):
1020     = _MSC_VER, defined as equal or above 1020

1000     = _MSC_VER (UNDEFINED or less than 1020)

0        = _MSC_NOSUCHVER (UNDEFINED or less than 1020)
Title: Re: #IF
Post by: Biterider on October 01, 2018, 02:32:20 AM
Hi JJ
Thanks for your proposal which works for this example case  :t , but we don't have to forget about all other possible logical combinations (using NOT, AND, OR, XOR) with more than only 1 argument!
The second issue is the C translation. Using this approach, the original conditional sentence has to be analysed and replaced by a modified/reorganized version. This is a very complex and error prone task.
I'm still convinced that there is only one correct way to solve the original issue adding more robustness and flexibility to the assembler as I described before.

@johnsa: can you elaborate your idea a bit more so I can understand what you have in mind. An example would be very helpful.  :idea:

Biterider
Title: Re: #IF
Post by: johnsa on October 01, 2018, 06:28:41 PM
Sure,

The root of the problem is that internally "IFDEF X" inherently assembles correctly whether X is defined or not, as this is it's purpose. IFDEF only supports a single argument. The problem comes in that if you attempt to create ANY sort of user-level macro like DEFINED(x) the parsing of the macro itself will fail as x is undefined, and undefined symbols are not allowed to be passed to regular macros.
So the idea was to build this macro in, and special case it to allow a direct substitution of the name (ie: x) into the macro body. That way a composite expression should evaluate as planned:

.if defined(x) AND somethingElse EQ 1
.endif

etc.. without any hacks to supports a specific combination or use of AND operator.
Title: Re: #IF
Post by: Biterider on October 01, 2018, 07:41:52 PM
Hi johnsa
Thanks for your answer. I understand what you want to achieve. Strange for me is, that the "@Defined" macro works also with undefined symbols. The following code demonstrates the point. If you define MY_SYMBOL all is fine, but commenting it out, the second conditional sentence can not be compiled (UASM & ML).

ifndef @Defined
  @Defined macro x
    ifdef x
      exitm <-1>                                      ;-1 to be able to use the 'NOT' operator.
    else                                              ;Not(-1) = 0 (FALSE)
      exitm <0>
    endif
  endm
endif

;MY_SYMBOL equ  5

.code                                         
start:                                         
    if @Defined(MY_SYMBOL)
      echo AAAA
    else
      echo BBBB
    endif

    if not @Defined(MY_SYMBOL)
      echo AAAA
    else
      echo BBBB
    endif

    if @Defined(MY_SYMBOL) and MY_SYMBOL ge 10
      echo CCCC
    else
      echo DDDD
    endif
    ret
end start


Biterider
Title: Re: #IF
Post by: johnsa on November 09, 2018, 09:12:03 PM
Just an update on this issue for 2.47..

The issue becomes more complex and the reason the 2nd example doesn't work is the expansion of the macro in a conditional assembly expression.
The only real solution to this problem would be to create a new operator (like le, gt etc) to use in expressions which tests for the defined state of a symbol as opposed to the macro approach.

Alternatively, and I've not tried this.. what is the outcome of using OPATTR against the symbol if defined / undefined, that may already work and be usable in an expression ?