Macros versus Procedures 

Macros are similar to procedures in some respects. Both improve program productivity by aiding in the development of modular source code. There are, however, some significant differences between them.

Parameter passing

Parameter passing in a macro invocation is similar to that in a procedure call of a high-level language. The arguments are listed as part of a macro call. Parameter passing in a procedure call often involves the stack. The number of stack operations in preparation for a procedure call grows in direct proportion to the number of parameters passed. This, in addition to the call/ret overhead, increases the overhead and affects the performance. Macros avoid this overhead by text substitution but increase the space requirement.

Types of parameters

Since a macro is a text substitution mechanism, a variety of parameter types can be passed. For example, we can write a macro
shift	MACRO	opcode, operand, count
          MOV CL, count
          opcode operand, CL
	ENDM
and invoke it as
	shift	SAL, AX, 3
which results in the following macro expansion
	MOV CL, 3
	SAL AX, CL
Here opcode is the instruction mnemonic, which can be an mnemonic in the shift and rotate family of instructions. Thus, the same macro can be used with all of the shift and rotate family of instructions on bytes, words, and doublewords that are either located in a register or memory. Clearly, such parameter types cannot be passed to a procedure.

Invocation mechanism

Macro invocation is done at assembly time by text substitution. However, procedure invocation is done at run time by transferring control to the procedure. This leads to the following tradeoff. Macros tend to increase the length of the executable code due to macro expansions. This leads to increased assembly time. Macro expansion also creates a nuisance at debuuging time--repeatedly looking at part of code (macro expansions) that you know works correctly. Procedures avoid these problems by transferring control to the procedure code. In debugging, the proecdure call can be skipped.

In summary, the tradeoffs are that using macros results in faster execution of the code. However, macros result in increased memory space due to macro expansions. Procedures save space, as only one copy of the procedure is kept. However, procedure invocation overhead (to pass parameters via the stack and for call/ret) increases the execution time. Note that macro invocation causes assembly-time overhead but not run-time overhead. The advantages and disadvantages associated with macros and procedures can be summarized into the following table:

Type of Overhead ProcedureMacro
memory space lower higher
Execution time higher lower
Assembly time lower higher

When are macros better

Given the state of modern technology, this time versus space tradeoff is a major factor in preferring one over the other. The choice between macros and procedures depends on the application requirements. For typical applications, it is recommended to use procedures except in some special situations identified next:

  1. Macros are useful in defining macro-instructions that extend the instruction set of a processor. For example, suppose that we want to write a program module to multiply an operand by 16. We will do this by both a macro and a procedure to see the difference in the overhead.

    Example: Write a macro mult16 to multiply an operand by 16 using shift instructions.
          mult16   MACRO operand
    
                     MOV  CL, 4
    
                     SHL  operand, CL
    
                   ENDM
     

    Example: Write a procedure times16 to multiply an operand by 16. Assume that the operand is pushed on the stack.
     times16    PROC
    
                PUSH BP
    
                MOV  BP, SP
    
                PUSH AX
    
                MOV AX, [BP+4]
    
                MOV CL, 4
    
                SAL AX, CL
    
                MOV [BP+4], AX
    
                POP AX
    
                POP BP
    
                RET 
    
     times16    ENDP
     

    This procedure can be invoked to multiply a word variable count by 16 as

    	PUSH count
    	CALL times16
    	POP count
    
    The overhead involved is substantial. Clearly, this is an impractical proposition.
  2. Macros are useful when text substitution is the only way available. Consider the following example. Suppose that we want to preserve the content of registers BX, CX, DX, SI, DI, and BP across procedure calls. We could use PUSHA and POPA , but these instructions save and restore the AX register as well. But, we want to return a result in AX. We can conveniently do this by the following two macros:

    save_regs MACRO                 restore_regs  MACRO
                PUSH BP                             POP BX
                PUSH DI                             POP CX
                PUSH SI                             POP DX
                PUSH DX                             POP SI
                PUSH CX                             POP DI
                PUSH BX                             POP BP
              ENDM                                ENDM
    
    It is not possible to write a procedure to do the same.