Reputation: 65006
Consider a nasm macro that is used to inject some assembly whenever it is called, in this case to test whether the passed argument is equal to 42:
%macro special_handler_if_42 1
cmp 42, %1
jne %%skip
; some additional assembly to handle the %1 == 42 case
push %1
push 42
call some_func
%%skip:
%endmacro
In the case it is equal we perform some additional action, otherwise we just continue with the code following the macro. So far, so good.
Now I want to write the macro in a way that's functionally identical, except that the "equals 42" case, which happens to be very rare, is moved "out of line", so that the fall-through (no jump) case is the default one, something like (not shown in macro form now):
cmp 42, rax
je equals_42
jump_back:
; the rest of the code that follows the macro
ret
; somewhere outside the current function
equals_42:
push rax
push 42
call some_func
jmp jump_back
This will be more efficient at execution time and also potentially conserve i-cache space. I'm not sure how to write a macro with a non-local effect like that. Ideas welcome.
Upvotes: 4
Views: 720
Reputation: 44116
If you don't mind splitting the macro into two macros, one performing the test and one handling the assertion, then you can use NASM's context stack.
I imagined a system of macros of the form assert_XXX
that are all pretty similar and perform specific tests.
A single assertions_handler
past the end of the function will generate any handler needed.
Since this system uses the context stack, you should be able to use it multiple times for different functions.
Basically, each assert_XXX
function will push a context on the stack and the assertions_handler
will consume them all.
assert_XXX
will also define context local macros argX
to pass its argument to the handler, so there is no need to hardcode anything.
BITS 64
%macro assert_not_equal 2
;Create and push a new context (The name is optional but goodpractice)
%push assert_equal_ctx
%define %$arg1 %1
%define %$arg2 %2
cmp %1, %2
je %$handler
%$jump_back:
%endmacro
%macro assert_greater 2
%push assert_greater_ctx
%define %$arg1 %1
%define %$arg2 %2
cmp %1, %2
jbe %$handler
%$jump_back:
%endmacro
%macro assertions_handler 0
%rep 1000
%ifctx assert_equal_ctx
%$handler:
push %$arg1
push %$arg2
call somefunc
jmp %$jump_back
%pop assert_equal_ctx
%elifctx assert_greater_ctx
%$handler:
push %$arg1
push %$arg2
call somefunc2
%pop assert_greater_ctx
%else
%exitrep
%endif
%endrep
%endmacro
;
;TEST TEST TEST TEST TEST TEST TEST TEST
;
assert_not_equal rax, 5
nop
nop
nop
assert_greater rax, 8
nop
nop
nop
ret
assertions_handler
;
; Handler functions
;
somefunc:
ret
somefunc2:
ret
The maximum number of assertions per function is set to 1000, you can increment it up to 262.
Upvotes: 3