A Register Preservation Macro for Procedures

From: Randall Hyde (spamtrap_at_crayne.org)
Date: 09/16/04


Date: Thu, 16 Sep 2004 16:58:51 +0000 (UTC)

Recently, I received a request on the AoA/HLA Yahoo
group to add a feature to HLA that is found in MASM-
the ability to specify in a procedure's declaration the registers
that should be preserved across that procedure's call (this
would be like the "USE" option in MASM).

While the idea of building this into the HLA language is
intriguing (allowing callee as well as caller preservation
semantics), there really is no need to build this into the
language as it is easy enough to write a macro that
achieves the same result. In this article I will describe how
to create such a macro for HLA procedures.

Before describing the actual macro, it's important to take
a few moments to describe a couple of facilities in HLA
that will make the creation of such a macro possible:
the _initialize_ and _finalize_ compile-time variables.

The _initialize_ and _finalize_ compile-time variables
are strings that HLA initializes to the empty string
every time you declare a procedure. The compiler emits
the contents of the _initialize_ string as the very first HLA statements
in a procedure (immediate after construction of the activation
record), prior to the emission of any explicit statements appearing
after the HLA procedure's begin clause. Similarly, HLA emits
the data held in the _finalize_ string immediately before the
destruction of the activation record, and after all the other
statements in the procedure.

Normally, these two compile-time variables hold the empty string,
so they have no effect on the code generation associated with
an HLA procedure. Their original intent was to hold calls
to an object's constructor and destructor code, so class
declarations in the procedure could automatically call
a constructor and destructor for objects declared in that
procedure. However, these scheme is perfectly general
and can be used to handle *anything* appearing in the
declaration section, not just class objects. The macro I'm
about to present uses this scheme to preserve and restore
a list of register values.

The macro I'm proposing is called "preserve" and its
argument list is a set of 32-bit general purpose registers.
The macro will report an assembly error if the user
specifies an argument that is not a 32-bit register. It will
also report an error if the user specifies the same
register twice. (These two error handling facilities are
easy enough to change if you prefer different semantics.)

You'd use this macro in the following fashion:

procedure YourProc( <parameters); preserve( <register list> )
<other declarations>
begin YourProc;
end YourProc;

The "preserve" macro would adjust the value of the _initialize_
string so that it emits the appropriate pushes, and it would
set the _finalize_ string to emit the appropriate pops.

Without further ado, here's a sample program containing
the preserve macro and a sample invocation:

program t;
#include( "stdlib.hhf" )
?@nodisplay := true;
?@noalignstack := true;

// preserve-
//
// Use this macro as a "procedure option" to specify which
// 32-bit registers you'd like HLA to preserve across the
// procedure call. This macro will set up the _initialize_
// and _finalize_ strings to emit the appropriate set of
// pushes and pops to preserve those registers across the
// call.

#macro preserve( _reglist_[] ):_pushes_, _pops_, _curreg_, _numRegs_;

    #if( @elements( _reglist_ ) > 0 )

        // Check to see if the user has specified a non-register
        // value or has specified the same register twice:

        #for( _i_ := 0 to @elements( _reglist_ ) - 1 )

            #if( !@reg32( _reglist_[_i_] ))

                #error
                (
                    "Argument ",
                    _reglist_[_i_],
                    " must be a 32-bit register"
                )

            #endif

            #for( _j_ := 0 to @elements( _reglist_ ) - 2 )

                #if( _i_ <> _j_ )

                    #if( _reglist_[_i_] = _reglist_[_j_] )

                        #error
                        (
                            "Argument ",
                            _reglist_[_i_],
                            " appears twice in the 'preserve' list"
                        )

                    #endif

                #endif

            #endfor

        #endfor

        // Okay, generate the _initialize_ and _finalize_
        // strings that will preserve the register values
        // across this procedure call:

        ?_pushes_ := "";
        ?_pops_ := "";
        #for( _curreg_ in _reglist_ )

            ?_pushes_ += "push(" + _curreg_ + ");";
            ?_pops_ := "pop(" + _curreg_ + ");" + _pops_;

        #endfor
        ?_initialize_ += _pushes_;
        ?_finalize_ := _pops_ + _finalize_;

    #endif

#endmacro

// Demonstration of the use of the preserve macro.

procedure hasPreserve; preserve( eax, ebx, ecx, edx )
begin hasPreserve;

    aaa;
    exit hasPreserve;
    aaa;

end hasPreserve;

// Sample MASM code that HLA emits for the above procedure:
//
// L806_hasPreserve__hla_ proc near32
// push ebp
// mov ebp, esp
// push eax
// push ebx
// push ecx
// push edx
// aaa
// jmp xL806_hasPreserve__hla___hla_ ;/* hasPreserve*/
// aaa
// xL806_hasPreserve__hla___hla_:
// pop edx
// pop ecx
// pop ebx
// pop eax
// mov esp, ebp
// pop ebp
// ret
// L806_hasPreserve__hla_ endp

procedure noPreserve;
begin noPreserve;

    nop;
    exit noPreserve;
    nop;

end noPreserve;

// Sample MASM code that HLA emits for the above procedure:
//
// L811_noPreserve__hla_ proc near32
// push ebp
// mov ebp, esp
// nop
// jmp xL811_noPreserve__hla___hla_ ;/* noPreserve*/
// nop
// xL811_noPreserve__hla___hla_:
// mov esp, ebp
// pop ebp
// ret
// L811_noPreserve__hla_ endp

begin t;
end t;

Cheers,
Randy Hyde



Relevant Pages

  • Re: HLA and embedded controllers
    ... The fact is that the SEH code I use with HLA *is* portable ... Unless I don't understand what you mean by macro overloading, ... in FASM, you ... Assembly Language Programmers, v2.0, back in 1995. ...
    (alt.lang.asm)
  • A Register Preservation Macro for Procedures
    ... While the idea of building this into the HLA language is ... to create such a macro for HLA procedures. ... the contents of the _initialize_ string as the very first HLA statements ... // mov ebp, esp ...
    (alt.lang.asm)
  • Re: Need help here.
    ... mov(ax, i16); ... HLA ... HLA (High Level Assembler) Parser ... Macro called in file "stdout.hhf" at line 636 ...
    (comp.lang.asm.x86)
  • Re: Need help here.
    ... mov(ax, i16); ... HLA ... HLA (High Level Assembler) Parser ... Macro called in file "stdout.hhf" at line 636 ...
    (comp.lang.asm.x86)
  • Re: Evolution
    ... > If HLA cant handle mov eax, ebx in the correct direction, ... the u32expr macro actually works. ... // leaves this result sitting in the register specified ... use improving his *assembler*. ...
    (alt.lang.asm)