Lhogho
0.0.027
|
Defines | |
#define | INSTR(format,...) |
#define | INFO(format,...) |
#define | REM(format,...) |
#define | LABEL(format,...) |
#define | ASM (OPTION_ASSEMBLER && ctx->generate) |
Functions | |
void | asm_label (context_t *ctx, chars_t name) |
disassemble label | |
void | asm_prologue (context_t *ctx, atom_t func) |
generates a function prologue | |
void | asm_epilogue (context_t *ctx, atom_t func, int is_macro) |
generates a function epilogue | |
void | asm_preepilogue (context_t *ctx) |
void | asm_external_function (context_t *ctx, atom_t func) |
generates trampoline to external function | |
void | asm_internal_function (context_t *ctx, int static_link, atom_t func) |
generates trampoline to external function | |
void | asm_push_atom (context_t *ctx, atom_t atom) |
genarates a code to push atom | |
void | asm_call_atom (context_t *ctx, atom_t atom, int params) |
genarates a code to call atom | |
void | asm_pop_atom (context_t *ctx) |
genarates a code to pop atom | |
void | asm_pop_result (context_t *ctx) |
generated code for result adjustment | |
void | asm_result_func (context_t *ctx, atom_t source) |
check result of a function | |
void | asm_result_unknown (context_t *ctx, atom_t source) |
check result of expression | |
void | asm_result_proc (context_t *ctx, atom_t source) |
check result of a command | |
void | asm_push_result (context_t *ctx) |
genarates a code to push function's result | |
void | asm_pop_dummy (context_t *ctx) |
genarates a code to pop (and ignore) | |
void | asm_make_direct (context_t *ctx, atom_t var, atom_t source) |
generated code for direct make statement | |
void | asm_reference (context_t *ctx, atom_t var, atom_t source, int check) |
generated code for reference | |
void | asm_push_value_addr (context_t *ctx, atom_t var) |
generated code for address of var's value | |
void | asm_empty_body (context_t *ctx) |
generates the body of an empty function | |
void | asm_output (context_t *ctx, atom_t source, int true_output) |
generated code for OUTPUT statement | |
void | asm_stop (context_t *ctx, atom_t source) |
generated code for STOP statement | |
void | asm_boolean (context_t *ctx, atom_t source) |
generated code for checking boolean values | |
int | asm_if_prologue (context_t *ctx) |
generates prologue for IF command | |
int | asm_if_epilogue (context_t *ctx) |
generates epilogue for IF command | |
void | asm_fix (context_t *ctx, int addr) |
fixes unresolved offset | |
void | asm_dump_source (context_t *ctx, atom_t source) |
dump current source | |
int | asm_repeat_prologue_const (context_t *ctx, int cnt) |
generates const-prologue for REPEAT command | |
int | asm_repeat_prologue_expr (context_t *ctx, atom_t source, int *branch2) |
generates expr-prologue for REPEAT command | |
void | asm_repeat_epilogue (context_t *ctx, int branch, int branch2) |
generates epilogue for REPEAT command | |
int | asm_for_prologue (context_t *ctx, atom_t step_var, atom_t source, int *branch2) |
generates prologue for FOR command | |
void | asm_for_epilogue (context_t *ctx, int branch, int branch2) |
generates epilogue for FOR command | |
int | asm_forever_prologue (context_t *ctx) |
generates prologue for FOREVER command | |
void | asm_forever_epilogue (context_t *ctx, int branch) |
generates epilogue for FOREVER command | |
int | asm_while_prologue (context_t *ctx, int is_while, int is_do) |
generates prologue for WHILE command | |
int | asm_while_inlogue (context_t *ctx, atom_t source, int is_while) |
generates inlogue for WHILE command | |
void | asm_while_epilogue (context_t *ctx, int loop_branch, int skip_branch, int is_while) |
generates epilogue for WHILE/UNTIL command | |
int | asm_catch_prologue (context_t *ctx) |
generates prologue for CATCH command | |
void | asm_catch_epilogue (context_t *ctx) |
generates epilogue for CATCH command | |
void | asm_push_mode (context_t *ctx, int mode) |
generates code to push mode | |
void | asm_push_frame (context_t *ctx) |
generates code to push frame pointer | |
void | asm_pop_frame (context_t *ctx) |
generates code to pop frame pointer | |
void | asm_runtime_reference (context_t *ctx) |
genarates a code to get variable value | |
void | asm_adjust_result (context_t *ctx) |
generated code for result adjustment | |
void | asm_goto (context_t *ctx, atom_t tag) |
generates jump to specific address | |
void | asm_goto_prologue (context_t *ctx, atom_t source) |
generates prologue for GOTO command | |
void | asm_goto_epilogue (context_t *ctx, atom_t source) |
generates epilogue for GOTO command | |
int | asm_test_prologue (context_t *ctx, int criteria) |
generates prologue for IFTRUE/IFFALSE command | |
void | asm_run_epilogue (context_t *ctx) |
generates epilogue for RUN command | |
void | asm_runresult_epilogue (context_t *ctx) |
generates epilogue for RUNRESULT command | |
void | asm_int_3 (context_t *ctx) |
generates Interrupt 3 | |
void | asm_set_output_status (context_t *ctx, int n) |
generates code for output status | |
void | asm_nop (context_t *ctx) |
generates NOP |
#define INSTR | ( | format, | |
... | |||
) |
#define INFO | ( | format, | |
... | |||
) |
#define REM | ( | format, | |
... | |||
) |
#define LABEL | ( | format, | |
... | |||
) |
#define ASM (OPTION_ASSEMBLER && ctx->generate) |
ctx | context |
label | label |
Dumps disassembler's label followed by semicolon
{ #ifdef ADVANCED disasm( ctx, 4, label ); #endif //ADVANCED }
void asm_prologue | ( | context_t * | ctx, |
atom_t | func | ||
) |
ctx | compilation context |
func | function which prologue to generate |
Generates the prologue of a function. This is an initializating sequences of machine instructions needed to setup stack, system data and local vars.
Generated code:
;; initialize stack push ebp ; caller's frame [EBP+0] mov ebp,esp ; EBP=current frame push esi ; parent's frame [EBP-4] push func ; parent atom [EBP-8]
;; initialize system data push repeat_chain ; for root; unbound for non-root call use push 2 ; result of IFTRUE [EBP-16] mov eax,unbound ; default result of the function
;; initialize local variables push empty_list ; runtime locals [EBP-20] push eax ; once for each local variable
{ atom_t a; INFO( "" ); INFO( "=========================" ); INFO( " FUNCTION %a", NAME(func) ); INFO( "=========================" ); INFO( "" ); LABEL( "%a:", NAME(func) ); PUSH_EBP; REM( "function prologue" ); MOV_EBP_ESP; PUSH_ESI; PUSH_ATOM( func ); REM( "parent atom" ); INFO( "" ); INFO( "initialize system data" ); if( func==root ) { MOV_MEM_OFS_EBP( &root_frame ); REM( "store root frame" ); PUSH_ATOMSTR( repeat_chain, TEXT("repeat_chain") ); CALL( use, TEXT("use") ); } else { PUSH_ATOM( unbound ); } PUSH_CONST(2); REM( "IFTRUE status" ); MOV_EAX_ATOM(unbound); REM( "default result of %a", NAME(func) ); INFO( "" ); INFO( "initialize local variables" ); PUSH_ATOM( empty_list ); REM( "runtime locals [EBP-16]" ); int ofs = BASE_OFFSET_LOCALS-4; for( a=LOCALS(func); IS_NOT_EMPTY(a); a=CDR(a) ) if( IS_VARIABLE(CAR(a))&& IS_NORMAL(CAR(a)) ) { // print the name and the address of the local var PUSH_EAX; REM( "local %a [EBP%p]", NAME(CAR(a)), ofs ); ofs-=4; } }
void asm_epilogue | ( | context_t * | ctx, |
atom_t | func, | ||
int | is_macro | ||
) |
ctx | compilation context |
func | function which epilogue to generate |
is_macro | if set then local variables must be copies |
Generates the epilogue of a function. This is a finalizating sequences of machine instructions needed to recover stack and local variables.
Generated code:
mov ebx,0 exit:
push ebp ; push current frame only if is_macro is set call copy_local_vars ; only if is_macro is set
;; free local variables call deuse ; once for each local call deuse ; for runtime locals
;; free system data pop edx ; free result of IFTRUE call deuse ; free repeat chain
;; recover frame pointers pop edx ; free parent atom pop esi mov esp,ebp pop ebp ret
{ atom_t a; INFO( "" ); LABEL( "exit:" ); if( is_macro==COMPILE_AS_MACRO ) { //INT_3; NOP; NOP; NOP; PUSH_EAX; PUSH_EBX; PUSH_EBP; REM("push current frame"); CALL( copy_local_vars, TEXT("copy_local_vars") ); POP_EDX; POP_EBX; POP_EAX; //NOP; NOP; NOP; } INFO( "free local variables" ); for( a=LOCALS(func); IS_NOT_EMPTY(a); a=CDR(a) ) if( IS_VARIABLE(CAR(a)) && IS_NORMAL(CAR(a)) ) CALL( deuse, TEXT("deuse") ); CALL( deuse, TEXT("deuse") ); REM("also free dynamic variables"); INFO( "" ); INFO( "free system data" ); POP_EDX; REM( "free result of IFTRUE" ); CALL( deuse, TEXT("deuse") ); REM( "free repeat chain" ); INFO( "" ); INFO( "recover frame pointers" ); POP_EDX; REM( "delete link to parent" ); POP_ESI; MOV_ESP_EBP; POP_EBP; RET; REM( "end of %a", NAME(func) ); }
void asm_preepilogue | ( | context_t * | ctx | ) |
{ MOV_EBX_CONST(0); }
void asm_external_function | ( | context_t * | ctx, |
atom_t | func | ||
) |
ctx | compilation context |
func | function which trampoline to generate |
Generates a trampoline to call an external function. The trampoline's code converts Logo parameters into C-type data and prepares the stack to be as expected by a sdtcall function.
Generated code:
func: push ebp mov ebp,esp
; clear error flag mov [error_flag],0
; for each parameter do: push [ebp+{ofs}] ; push param call atom_to_xxx ; convert to c-type push edx ; push c-type hi word (if 64-bit) push eax ; push c-type lo word fstp [esp] ; store 32- or 64-bit fp number to stack
call func
; check error flag cmp [error_flag],0 je ok mov eax,[error_flag] jmp exit
; push result ok: push edx ; push c-type hi word (if 64-bit) push eax ; push c-type lo word fstp [esp] ; store 32- or 64-bit fp number to stack call xxx-to_atom ; convert back to atom
exit: mov esp,ebp pop ebp ret
{ //INT_3; INFO( "" ); INFO( "=========================" ); INFO( " EXTERNAL FUNCTION %a", NAME(func) ); INFO( "=========================" ); INFO( "" ); LABEL( "%a:", NAME(func) ); PUSH_EBP; REM( "function prologue" ); MOV_EBP_ESP; INFO( "" ); MOV_MEM_OFS_CONST( &error_flag, 0 ); REM( "clear error flag" ); INFO( "" ); INFO( "convert parameters to C-types" ); atom_t params; for( params=LOCALS(func); IS_NOT_EMPTY(params); params=CDR(params) ) { atom_t param = CAR(params); if( !IS_EXTERNAL(param) ) continue; int c_type = VARCLASS( param ); // push param's atom and convert it to c-type PUSH_MEM_EBP( OFFSET(param) ); REM( "convert %a", NAME(param) ); CALL( c_types[c_type].atom_to_c, c_types[c_type].atom_to_c_name ); // push the result to the stack. If the result is 1..32 bits, then // it is in EAX. If it is 33..64 bits, then it is stored in EDX:EAX. if( c_types[c_type].size>32 ) { PUSH_EDX; } PUSH_EAX; // floating-point numbers are stored in FPU's st0 register. // Pop the value from the register and store it in the stack. if( c_types[c_type].class==C_TYPE_FLOAT ) { if( c_types[c_type].size>32 ) { FSTPD_MEM_ESP; } // store 64-bit float else { FSTPF_MEM_ESP; } // store 32-bit float } } //params=LOCALS(func) INFO( "" ); INFO( "call to external function" ); CALL_ATOM( ADDRESS(func), NAME(func) ); INFO( "" ); INFO( "check error flag" ); CMP_MEM_OFS_CONST_4( &error_flag, 0 ); JE( 0, TEXT("$ok") ); REM( "continue if no error" ); int branch=LOCAL_IP-4; MOV_EAX_MEM_OFS( &error_flag ); JMP( 0, TEXT("exit") ); int branch2=LOCAL_IP-4; // function result must be converted back into atom INFO( "" ); INFO( "convert function result" ); LABEL( "$ok:" ); asm_fix( ctx, branch ); int c_type = VARCLASS( func ); if( c_types[c_type].size>32 ) { PUSH_EDX; } PUSH_EAX; if( c_types[c_type].class==C_TYPE_FLOAT ) { if( c_types[c_type].size>32 ) { FSTPD_MEM_ESP; } // store 64-bit float else { FSTPF_MEM_ESP; } // store 32-bit float } CALL( c_types[c_type].c_to_atom, c_types[c_type].c_to_atom_name ); INFO( "" ); // fix the stack by removing all c-type data // (actually this is not needed, because the // next MOV ESP,EBP command fixes the stack LABEL( "exit:" ); asm_fix( ctx, branch2 ); MOV_ESP_EBP; POP_EBP; RET; REM( "end of %a", NAME(func) ); }
void asm_internal_function | ( | context_t * | ctx, |
int | static_link, | ||
atom_t | func | ||
) |
ctx | compilation context |
static_link | static link from the current frame |
func | function which trampoline to generate |
Generates a trampoline to call an external function. The trampoline's code converts Logo parameters into C-type data and prepares the stack to be as expected by a sdtcall function.
Generated code:
func:
exit: ret
{ //INT_3; INFO( "" ); INFO( "=========================" ); INFO( " INTERNAL FUNCTION %a", NAME(func) ); INFO( "=========================" ); INFO( "" ); asm_prologue( ctx, func ); INFO( "convert parameters to atoms" ); atom_t params; int offset = 8; // this is offset from a C calling code int count = 0; for( params = LOCALS(func); IS_NOT_EMPTY(params); params=CDR(params)) { atom_t param = CAR( params ); if( !IS_INTERNAL(param) ) continue; int c_type = VARCLASS( param ); PUSH_MEM_EBP( offset ); CALL( c_types[c_type].c_to_atom, c_types[c_type].c_to_atom_name ); PUSH_EAX; offset += c_types[c_type].size/8; count++; } PUSH_CONST( count ); //MOV_EBP_CONST( static_link ); //MOV_ESI_CONST( static_link ); MOV_MEM_OFS_EBP( &backup_frame ); MOV_EBP_MEM_OFS( &root_frame ); MOV_ESI_EBP; INFO( "" ); INFO( "call to internal function" ); //INT_3; CALL_ATOM( ADDRESS(func), NAME(func) ); MOV_EBP_MEM_OFS( &backup_frame ); CMP_ID_ERROR; JNE( 0, TEXT("$skip") ); int branch = LOCAL_IP-4; MOV_MEM_OFS_EAX( &error_flag ); REM( "store result in error flag"); asm_fix( ctx, branch ); LABEL( "$skip" ); INFO( "" ); INFO( "convert function result" ); int c_type = VARCLASS( func ); PUSH_EAX; PUSH_EAX; CALL( c_types[c_type].atom_to_c, c_types[c_type].atom_to_c_name ); INFO( "" ); CALL( deuse, TEXT("deuse result") ); POP_EDX; for( params=LOCALS(func); IS_NOT_EMPTY(params); params=CDR(params) ) if( IS_INTERNAL(CAR(params)) ) CALL( deuse, TEXT("deuse") ); //INT_3; asm_epilogue( ctx, func, COMPILE_AS_NON_MACRO ); }
void asm_push_atom | ( | context_t * | ctx, |
atom_t | atom | ||
) |
ctx | compilation context |
atom | atom to push |
Generates machine code which pushes atom in the stack and increases its reference count. The atom reference count is NOT increased if it is UNBOUND or EMPTY LIST.
Generated code:
push {atom} call {use} ; if not UNBOUND or EMPTY LIST
void asm_call_atom | ( | context_t * | ctx, |
atom_t | var, | ||
int | params | ||
) |
ctx | compilation context |
var | atom to call |
params | number of actual inputs |
Generates machine code which calls a logo function. The input atom
contains the name of the function.
Generated code (for functions with fixed number of inputs):
Generated code (for functions with variable number of inputs or not-primitive):
{ int variadic = GET_FLAGS(var,FLAG_INFINITE_ARGS|FLAG_CAN_BE_UNARY|FLAG_MAY_HAVE_EXTRA_ARG); // all variadic functions need one more parameter if( variadic || !GET_FLAGS(var,FLAG_PRIMITIVE) ) { PUSH_CONST( params ); REM( "param count of %a", NAME(var) ); } // test for special system-defined variables-inputs // first test for print-related system inputs if( GET_FLAGS(var,FLAG_PRINT_VARS) ) { compile_system_reference( ctx, NAME(fullprintp) ); compile_system_reference( ctx, NAME(printwidthlimit) ); compile_system_reference( ctx, NAME(printdepthlimit) ); } if( GET_FLAGS(var,FLAG_EQUAL_VARS) ) { compile_system_reference( ctx, NAME(caseignoredp) ); } // test for run-time access to parent if( GET_FLAGS(var,FLAG_PUSH_PARENT) ) { PUSH_CONST( (int)ctx->parent ); REM( "push parent of %a", NAME(var) ); PUSH_EBP; REM( "push current frame" ); } if( IS_PRIMITIVE(var) ) { if( !ADDRESS(var) ) { printf("This primitive is not defined yet -> "); dumpln(NAME(var)); return; } CALL_ATOM( (void*)ADDRESS(var), NAME(var) ); } else { // prepare static link asm_static_link( ctx, var ); CALL_MEM( &ADDRESS(var), NAME(var) ); } // postprocessing of run-time access to parent if( GET_FLAGS(var,FLAG_PUSH_PARENT) ) { POP_EDX; REM("remove static link"); POP_EDX; REM("remove parent"); } // postprocessing of system inputs if( GET_FLAGS(var,FLAG_EQUAL_VARS) ) { asm_pop_atom( ctx ); REM( "remove %a", NAME(caseignoredp) ); } if( GET_FLAGS(var,FLAG_PRINT_VARS) ) { asm_pop_atom( ctx ); REM( "remove %a", NAME(printdepthlimit) ); asm_pop_atom( ctx ); REM( "remove %a", NAME(printwidthlimit) ); asm_pop_atom( ctx ); REM( "remove %a", NAME(fullprintp) ); } // postprocessing of all variadic functions if( variadic || !GET_FLAGS(var,FLAG_PRIMITIVE) ) { POP_EDX; REM("remove param count"); } }
void asm_pop_atom | ( | context_t * | ctx | ) |
void asm_pop_result | ( | context_t * | ctx | ) |
void asm_result_func | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source of the function |
Generates code which check whether the result of a function is acceptible. The source
parameter points to the source where the function is called. It is used to locate the error position in case of errors.
{ INFO( "" ); INFO( "check for errors in func %a", CAR(source) ); PUSH_SOURCE( source ); REM( "source" ); CALL( rt_funchk, TEXT("rt_funchk") ); REM( "check result" ); asm_push_result( ctx ); }
void asm_result_unknown | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source of the function |
Generates code which check whether the result of an expression is acceptible. Actually, any result is acceptible.
{ INFO( "" ); INFO( "check for errors in %a", CAR(source) ); PUSH_SOURCE( source ); CALL( rt_exprchk, TEXT("rt_exprchk") ); REM( "check result" ); }
void asm_result_proc | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source of the function |
Generates code which check whether the result of a command is acceptible. The source
parameter points to the source where the procedure is called. It is used to locate the error position in case of errors.
{ INFO( "" ); INFO( "check for errors in proc %a", CAR(source) ); PUSH_SOURCE( source ); CALL( rt_cmdchk, TEXT("rt_cmdchk") ); REM( "check result, exit if error" ); CMP_ID_ERROR; JE_EXIT; MOV_EBX_CONST( 0 ); }
void asm_push_result | ( | context_t * | ctx | ) |
void asm_pop_dummy | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates machine code which pops data from the the stack and then ignores it.
Generated code: pop edx
{ POP_EDX; }
void asm_make_direct | ( | context_t * | ctx, |
atom_t | var, | ||
atom_t | source | ||
) |
ctx | compilation context |
var | variable which value is changed |
source | source of the make command |
Generates code for direct make
statement. I.e. the var which value is changed is already known.
Before the execution of asm_make_direct() the stack contains the new value.
Primitive variables are accessed directly.
For all user-defined variables the value of depth
determines what code is generated. This variable is the difference between the context where the var is defined and the context where the var is used. The value of delpth
is used to generate code to reach the proper context.
Code generation patterns (assumes the new value is already pushed onto the stack):
(a) primitive (system) variables push [{address}] ; push old value call deuse ; deuse and pop old value mov eax,[esp] ; get new value mov [{address}],eax ; store new value
(b) user-defined var {static} ; calculate base pointer in ESI push [esi+offset] ; push old value call deuse ; deuse and pop old value mov eax,[esp] ; get new value mov [esi+offset],eax ; store new value
{ if( IS_PRIMITIVE(var) || IS_GLOBAL(var) ) { // case (a) int addr = (int)&VALUE(var); // NB! No need to override default segment, // because it is already SS PUSH_MEM( addr ); REM( "free old value of %a", NAME(var) ); CALL( deuse, TEXT("deuse") ); MOV_EAX_MEM_ESP; REM( "set new value of %a", NAME(var) ); MOV_MEM_OFS_EAX( addr ); } else { INFO( "" ); INFO( "assign new value to %a", NAME(var) ); int offset = asm_static_link( ctx, var ); PUSH_MEM_ESI( offset ); REM( "free old value of %a", NAME(var) ); CALL( deuse, TEXT("deuse") ); MOV_EAX_MEM_ESP; REM( "set new value of %a", NAME(var) ); MOV_MEM_ESI_EAX( offset ); } // Generates code which check whether the result // of a make is acceptible. The \c source parameter // points to the source where the procedure is called. // It is used to locate the error position in case // of errors. INFO( "" ); INFO( "check for errors of %a", CAR(source) ); PUSH_SOURCE( source ); CALL( rt_makechk, TEXT("rt_makechk") ); REM( "check result atom" ); CMP_ID_ERROR; REM( "is error atom?" ); JE_EXIT; }
void asm_reference | ( | context_t * | ctx, |
atom_t | var, | ||
atom_t | source, | ||
int | check | ||
) |
ctx | compilation context |
var | variable which value is referenced |
source | source of var reference |
check | if 0 then do not check result |
Generates code for referencing variable's value. The generated code depends on depth
- this is the difference between depths of syntax levels of the current context and the context where the var's value exist.
If the variable is primitive then it is stored in globals and is accessed directly -- i.e. it's value is not stored in the stack.
If check==0 push the value to the stack and exit. Otherwise generate code to check whether the value is not UNBOUND (i.e. to check whether the variable has been assigned a value)
(a) var is primitive push [{address}]
(b) user-defined var {static link} push [esi+offset] ; push value
if check!=0 then also generate:
push {source} call asm_use_var ; use new value cmp [eax+OFFSET_ID],ERROR_ID je exit push eax
{ if( IS_PRIMITIVE(var) ) { // case (a) - primitive variable int addr = (int)&VALUE(var); PUSH_MEM( addr ); } else { // case (b) - user-defined variable int offset = asm_static_link( ctx, var ); PUSH_MEM_ESI( offset ); } REM( "value of %a", NAME(var) ); // generate code to check whether // the variable has a value if( check ) { PUSH_SOURCE( source ); REM( "source" ); CALL( rt_use_var, TEXT("rt_use_var") ); //CMP_ID_ERROR; //JE_EXIT; PUSH_EAX; REM( "still value of %a", NAME(var) ); } }
void asm_push_value_addr | ( | context_t * | ctx, |
atom_t | var | ||
) |
ctx | compilation context |
var | variable which value is looked for |
Generates code that calculates and pushes in the the stack the address of the value of a variable known at compile time.
(a) primitive (system) variables push {address} ; push value's address
(b) user-defined var {static} ; calculate base pointer in ESI add esi,offset ; adjust for offset push esi ; push value's address
{ if( IS_PRIMITIVE(var) ) { // case (a) int addr = (int)&VALUE(var); PUSH_CONST( addr ); REM( "address of %a's value", NAME(var) ); } else { int offset = asm_static_link( ctx, var ); ADD_ESI_CONST_4( offset ); REM( "address of %a's value", NAME(var) ); PUSH_ESI; } }
void asm_empty_body | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates the body of an empty function. This body just sets the result to be unbound atom.
Generated code:
{ INFO( "" ); MOV_EAX_ATOM( unbound ); REM( "there is no result" ); }
void asm_output | ( | context_t * | ctx, |
atom_t | source, | ||
int | true_output | ||
) |
ctx | compilation context |
source | source of the OUTPUT command |
true_output | flag whether OUPUT is real or simulated |
Generates code for output
statement. Before the execution of asm_output() the stack contains the new value to output.
Generated code:
mov ebx,true_output pop eax ; load value in EAX jmp exit
{ INFO( "" ); INFO( "output current result" ); #ifdef ADVANCED if( OPTION_RUNTIME ) { PUSH_SOURCE( source ); REM( "dump source" ); CALL( rt_dump, TEXT("dump") ); } #endif MOV_EBX_CONST( true_output ); POP_EAX; REM( "get current result in EAX" ); JMP_EXIT; }
ctx | compilation context |
source | source of the STOP command |
Generates code for stop
statement.
Generated code:
mov ebx,1 mov eax,{stopped} ; load value in EAX jmp exit
{ INFO( "exit with no result" ); #ifdef ADVANCED if( OPTION_RUNTIME ) { PUSH_SOURCE( source ); REM( "dump source" ); CALL( rt_dump, TEXT("rt_dump") ); } #endif MOV_EBX_CONST( 1 ); MOV_EAX_ATOM( stopped ); REM( "stopped" ); JMP_EXIT; }
void asm_boolean | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source of the expression |
Generates code that checks the top value in the stack is TRUE
or FALSE
. In case of TRUE
sets EAX
= false_true
[1]. In case of FALSE
sets EAX
= false_true
[0]. If the value is neither TRUE
nor FALSE
sets EAX to an error atom
Generated code:
call rt_boolchk cmp [eax+OFFSET_ID],ERROR_ID je exit
{ INFO( "" ); INFO( "check boolean result" ); PUSH_SOURCE( source ); REM( "source" ); CALL( rt_boolchk, TEXT("rt_boolchk") ); REM( "exit if not boolean" ); CMP_ID_ERROR; JE_EXIT; }
int asm_if_prologue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates prologue code for the if
command. Prologue is the code which stands right before the code for then
. It is used to direct execution towards the then
or the else
parts.
It is assumed that the code for the condition is already ready and EAX
contains the address of either false_true
[0] or false_true
[1].
The return value is set to the address of the else
component of the je
instruction. It is used to set the correct offset once the size of then-commands is known.
Generated code: cmp eax,FALSE je else
{ CMP_EAX_ATOM( false_true[0] ); REM( "is it false?" ); JE( 0, TEXT("$else") ); REM( "goto if-else block" ); return LOCAL_IP-4; }
int asm_if_epilogue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates epilogue code for the if
command. Epilogue is the code which stands right after the code for the then-instructions. It contains only a jump over the else-instructions directly to the end of the if
command.
Generated code: jmp ifend
ctx | compilation context |
addr | address of data to fix |
Fixes a relative offset from a given position to the current position as defined by ctx. This is used when there is a forward conditional or inconditional jump, which target is unknown at compile time.
The parameter addr is a 0-based address (i.e. it is an offset relative to START_IP).
void asm_dump_source | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source to dump |
Generates code which dumps the current source. This code is generally placed before calling user-defined function of command.
{ INFO( "" ); PUSH_SOURCE( source ); REM( "dump source" ); CALL( rt_predump, TEXT("rt_predump") ); INFO( "" ); }
int asm_repeat_prologue_const | ( | context_t * | ctx, |
int | cnt | ||
) |
ctx | compilation context |
cnt | number of repetitions |
Generates const-prologue code for the repeat
command. Const-prologue is used when the number of repetitions is known at compile time (currently this means it is a constant).
The prologue initializes the repeat
by calling rt_repeat_enter to set up a new element of the repeat chain.
The return value of the function is the address of the instruction right after the prologue. This address is used by asm_repeat_epilogue() and is absolute address.
Generated code:
push {count} push ebp call rt_repeat_enter
int asm_repeat_prologue_expr | ( | context_t * | ctx, |
atom_t | source, | ||
int * | branch2 | ||
) |
ctx | compilation context |
source | source of the expression |
branch2 | address of branch to skip over the whole repeat |
Generates expr-prologue code for the repeat
command. Expr-prologue is used when the number of repetitions is an expression, i.e. it is not known at compile time.
The prologue generates code to calculate the actual number of repetitions. It checks whether there was an error during calculation. If not, it checks whether the number of repetitions is valid. Finally the prologue initializes the repeat
by calling rt_repeat_enter to set up a new element of the repeat chain.
The return value of the function is the address of the instruction right after the prologue. This address is used by asm_repeat_epilogue() and is absolute address.
Generated code:
{expr} push {source} call rt_repchk cmp [eax+OFFSET_ID],ERROR_ID je exit
push eax mov [eax+REPLIMIT],eax call deuse
cmp eax,0 je skip_repeat ][ ; <-branch2
push eax push ebp call rt_repeat_enter $rep:
{ INFO( "" ); INFO( "check validity of repeat count" ); PUSH_SOURCE( source ); REM( "source" ); CALL( rt_repchk, TEXT("rt_repchk") ); REM( "exit if invalid" ); CMP_ID_ERROR; JE_EXIT; PUSH_EAX; REM( "free repeat count expression" ); MOV_EAX_MEM_EAX( OFFSET_INT ); REM( "get repeat count" ); CALL( deuse, TEXT("deuse") ); CMP_EAX_CONST( 0 ); REM( "exit if it is 0" ); JE( 0, TEXT("skip_repeat") ); *branch2 = LOCAL_IP-4; PUSH_EAX; REM( "repeat count" ); PUSH_EBP; REM( "static link" ); CALL( rt_repeat_enter, TEXT("rt_repeat_enter") ); REM( "initialize repeat" ); asm_label( ctx, TEXT("$rep:") ); return IP; }
void asm_repeat_epilogue | ( | context_t * | ctx, |
int | branch, | ||
int | branch2 | ||
) |
ctx | compilation context |
branch | branch for repeat's loop |
branch2 | branch for skipping repeat |
Generates epilogue code for the repeat
command. There are two prologues for repeat
, but the epilogue is the same for both of them.
The epilogue of repeat
decrements the repeat count and if it is not zero, then loops back to the branch address, which is provided as parameter.
If the value of branch2
is -1, then the repeat with constant number of repetitions and branch2
is ignored. Otherwise it contains the address where a branch is made over the whole body of the repeat when the repetition number is 0.
Generated code: (note: {body} is not a part of the epilogue)
$rep: {body} mov eax,[ebp+REPEATCHAIN] mov eax,[eax+CAR_OFFSET] inc [eax+REPCOUNT] dec [eax+REPLIMIT] jnz $rep
$skip: push ebp call rt_repeat_exit
{ INFO( "" ); INFO( "calculate next loop of repeat" ); MOV_EAX_MEM_EBP( BASE_OFFSET_REPEATCHAIN ); REM( "get repeat chain" ); MOV_EAX_MEM_EAX( CAR_OFFSET ); REM( "get current repeat node" ); INC_EAX_MEM( OFFSET_REPCOUNT ); REM( "increase done repeats" ); DEC_EAX_MEM( OFFSET_REPLIMIT ); REM( "decrease left repeats" ); //PUSH_MEM_EBP(BASE_OFFSET_REPEATCHAIN); //PUSH_MEM_EAX(OFFSET_REPCOUNT); //PUSH_MEM_EAX(OFFSET_REPLIMIT); //CALL( rt_repeat_debug, TEXT("") ); JNZ( branch, TEXT("$rep") ); if( branch2!=-1 ) { INFO( "" ); asm_label( ctx, TEXT("skip_repeat:") ); INFO( "no more loops" ); asm_fix( ctx, branch2 ); } PUSH_EBP; REM( "static link" ); CALL( rt_repeat_exit, TEXT("rt_repeat_exit") ); }
int asm_for_prologue | ( | context_t * | ctx, |
atom_t | step_var, | ||
atom_t | source, | ||
int * | branch2 | ||
) |
ctx | compilation context |
step_var | variable which will hold the value of FOR's step |
source | source of the expression |
branch2 | address of branch to skip over the whole repeat |
Generates prologue code for the for
command.
The prologue generates code to calculate the actual number of repetitions. It checks whether there was an error during calculation. If not, it checks whether the number of repetitions is valid. Finally the prologue initializes the for
by calling rt_repeat_enter to set up a new element of the repeat chain.
The return value of the function is the address of the instruction right after the prologue. This address is used by asm_for_epilogue() and is absolute address.
Generated code:
{expr_from} {expr_to} {expr_step} push {addr of step_var's value} push {source} call rt_forchk cmp [eax+OFFSET_ID],ERROR_ID je exit
push eax mov [eax+REPLIMIT],eax call deuse
cmp eax,0 je skip_for ][ ; <-branch2
push eax push ebp call rt_repeat_enter $for:
{ asm_push_value_addr( ctx, step_var ); INFO( "" ); INFO( "check validity of for count" ); PUSH_SOURCE( source ); REM( "source" ); CALL( rt_forchk, TEXT("rt_forchk") ); REM( "exit if invalid" ); CMP_ID_ERROR; JE_EXIT; PUSH_EAX; REM( "free for count expression" ); MOV_EAX_MEM_EAX( OFFSET_INT ); REM( "get for count" ); CALL( deuse, TEXT("deuse") ); CMP_EAX_CONST( 0 ); REM( "exit if it is 0" ); JE( 0, TEXT("skip_for") ); *branch2 = LOCAL_IP-4; PUSH_EAX; REM( "for count" ); PUSH_EBP; REM( "static link" ); CALL( rt_repeat_enter, TEXT("rt_repeat_enter") ); REM( "initialize for" ); // reuse rt_repeat_enter asm_label( ctx, TEXT("$for:") ); return IP; }
void asm_for_epilogue | ( | context_t * | ctx, |
int | branch, | ||
int | branch2 | ||
) |
ctx | compilation context |
branch | branch for for's loop |
branch2 | branch for skipping for |
Generates epilogue code for the for
command.
The epilogue of for
decrements the repeat count and if it is not zero, then loops back to the branch address, which is provided as parameter.
branch2
contains the address where a branch is made over the whole body of the for when the repetition number is 0.
Generated code: (note: {body} is not a part of the epilogue)
$for: {body} mov eax,[ebp+REPEATCHAIN] mov eax,[eax+CAR_OFFSET] inc [eax+REPCOUNT] dec [eax+REPLIMIT] jnz $for
$skip: push ebp call rt_repeat_exit // reuses repeat_exit
{ INFO( "" ); INFO( "calculate next loop of for" ); MOV_EAX_MEM_EBP( BASE_OFFSET_REPEATCHAIN ); REM( "get repeat chain" ); MOV_EAX_MEM_EAX( CAR_OFFSET ); REM( "get current repeat node" ); INC_EAX_MEM( OFFSET_REPCOUNT ); REM( "increase done repeats" ); DEC_EAX_MEM( OFFSET_REPLIMIT ); REM( "decrease left repeats" ); //PUSH_MEM_EBP(BASE_OFFSET_REPEATCHAIN); //PUSH_MEM_EAX(OFFSET_REPCOUNT); //PUSH_MEM_EAX(OFFSET_REPLIMIT); //CALL( rt_repeat_debug, TEXT("") ); JNZ( branch, TEXT("$for") ); if( branch2!=-1 ) { INFO( "" ); asm_label( ctx, TEXT("skip_for:") ); INFO( "no more loops" ); asm_fix( ctx, branch2 ); } PUSH_EBP; REM( "static link" ); CALL( rt_repeat_exit, TEXT("rt_repeat_exit") ); // same as exit from repeat }
int asm_forever_prologue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates prologue code for the forever
command.
The prologue initializes the forever
by calling rt_forever_enter to set up a new element of the repeat chain.
The return value of the function is the address of the instruction right after the prologue. This address is used by asm_forever_epilogue().
Generated code:
push ebp call rt_forever_enter forever:
void asm_forever_epilogue | ( | context_t * | ctx, |
int | branch | ||
) |
ctx | compilation context |
branch | branch for forever's loop |
Generates epilogue code for the forever
command.
Generated code: (note: {body} is not a part of the epilogue)
forever:: {body} mov eax,[ebp+REPEATCHAIN] mov eax,[eax+CAR_OFFSET] inc [eax+REPCOUNT] jmp forever
{ INFO( "" ); INFO( "calculate next loop of forever" ); MOV_EAX_MEM_EBP( BASE_OFFSET_REPEATCHAIN ); REM( "get current repeat node" ); MOV_EAX_MEM_EAX( CAR_OFFSET ); REM( "get current repeat node" ); INC_EAX_MEM( OFFSET_REPCOUNT ); REM( "increase done repeats" ); JMP( branch, TEXT("forever") ); }
int asm_while_prologue | ( | context_t * | ctx, |
int | is_while, | ||
int | is_do | ||
) |
ctx | compilation context |
is_while | while=1, until=0 |
is_do | do.while&do.until=1, while&until=0 |
Generates prologue code for the while
or the until
command (depending on the value of is_while
parameter). Actually no code is generated, as long as the the prologue is just a label.
Generated code (is_while==1 & is_do==0):
while:
Generated code (is_while==0 & is_do==0):
until:
Generated code (is_while==1 & is_do==1):
do.while:
Generated code (is_while==0 & is_do==1):
do.until:
int asm_while_inlogue | ( | context_t * | ctx, |
atom_t | source, | ||
int | is_while | ||
) |
ctx | compilation context |
source | source of the expression |
is_while | while=1, until=0 |
Generates inlogue code for the while
or the until
command (depending on the value of is_while
parameter). Inlogue is used to calculate whether to continue the execution of while
or until
.
The inlogue generates code to test the condition.
The return value of the function is the address of the instruction where a fix-up is needed.
Generated code:
push {source} call rt_whlchk cmp [eax+OFFSET_ID],ERROR_ID je exit
push eax mov eax,[eax+INT] call deuse cmp eax,0 (for while) or 1 (for until) je skip_while
{ INFO( "" ); INFO( "test condition of while/until" ); PUSH_SOURCE( source ); REM( "source" ); CALL( rt_whlchk, TEXT("rt_whlchk") ); REM( "check condition" ); CMP_ID_ERROR; REM( "exit if error" ); JE_EXIT; PUSH_EAX; REM( "free condition atom" ); MOV_EAX_MEM_EAX( OFFSET_INT ); REM( "get condition result" ); CALL( deuse, TEXT("deuse") ); CMP_EAX_CONST( 1-is_while ); REM( "exit if condition fails" ); JE( 0, is_while?TEXT("skip_while"):TEXT("skip_until") ); return LOCAL_IP-4; }
void asm_while_epilogue | ( | context_t * | ctx, |
int | loop_branch, | ||
int | skip_branch, | ||
int | is_while | ||
) |
ctx | compilation context |
loop_branch | branch for while/until's loop |
skip_branch | branch to skip while/until |
is_while | while=1, until=0 |
Generates epilogue code for the while
or until
command.
Generated code: (note: {body} is not a part of the epilogue)
$whl: ... {body} jmp $whl skip_while: mov eax,(unbound)
int asm_catch_prologue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates prologue code for the catch
command. Prologue is the code which stands right before the code for the body of the catch
. The prologue is a trampoline which temporarily overwrited the default function exit address. Thus, all exits done in the commands of the catch
are directed to this trampoline.
Note! This function changes the exit_addr to point to jump_addr!
Generated code: jmp $1 jump_addr: jmp catch_epilogue $1: {body}
void asm_catch_epilogue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates epilogue code for the catch
command. It fixes unset branch in the prologue and generates code which captures and verifies the result of the monitored block of commands.
Generated code: push {tag} ; already done push {eax} ; already done call rt_catchchk mov ebx,[catch_output_flag] cmp ebx,1 je exit push eax
{ INFO( "" ); INFO( "check result of catch block" ); PUSH_EBX; CALL( rt_catchchk, TEXT("rt_catchchk") ); MOV_EBX_MEM_OFS( &catch_output_flag ); CMP_EBX_CONST(1); JE_EXIT; //__asm__ volatile ("int $3; nop"); PUSH_EAX; }
void asm_push_mode | ( | context_t * | ctx, |
int | mode | ||
) |
ctx | compilation context |
mode | mode to push |
Generates code for pushing the mode of a function call. The mode indicates whether the function is called as a function or as a command. The values of mode should be either COMPILE_AS_FUNC or COMPILE_AS_PROC.
Generated code:
push {mode}
{ PUSH_CONST( mode ); if( mode==COMPILE_AS_FUNC ) REM( "call as function" ); else REM( "call as procedure" ); }
void asm_push_frame | ( | context_t * | ctx | ) |
void asm_pop_frame | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates code for popping the frame pointer previously pushed by asm_push_frame(). The actual value that is popped out is not used for anything.
Generated code:
pop edx
void asm_runtime_reference | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates machine code which finds the value of a variable and pushes it in the stack. The name is in the stack, i.e. it is not processed at compile time.
push {parent} push ebp call : pop edx pop edx
void asm_adjust_result | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates code which adjust the result of a command. This is needed when EAX register is left unchanged from the last expression.
Generated code:
mov eax,{unbound} ; load value in EAX
{ MOV_EAX_ATOM( unbound ); REM( "set void result" ); }
ctx | compilation context |
tag | tag for the target address |
Generates unconditional jump to a given address specified by a tag variable.
Generated code: jmp addr
void asm_goto_prologue | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source of the function |
Generates code to push the source. It will be used by rt_goto().
Generated code:
push {source}
{ PUSH_SOURCE( source ); REM( "source" ); }
void asm_goto_epilogue | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | source of goto command |
Generates epilogue for goto
command which input cannot be resolve at compile time.
The epilogue generates code to find the target address of the goto
command. The code assumes that EAX contains an integer atom with the target address.
Generated code:
{expr} ; already generated cmp [eax+OFFSET_ID],ERROR_ID je exit
push eax ; tag mov eax,[eax+INTEGER] ; EAX=target call deuse ; tag
add eax,{base} jmp eax
{ CMP_ID_ERROR; REM( "exit if error" ); JE_EXIT; PUSH_EAX; REM( "get target relative address" ); MOV_EAX_MEM_EAX( OFFSET_INT ); CALL( deuse, TEXT("deuse") ); ADD_EAX_CONST_4( START_IP ); REM( "make it absolute" ); JMP_EAX( source ); }
int asm_test_prologue | ( | context_t * | ctx, |
int | criteria | ||
) |
ctx | compilation context |
criteria | iftrue=1, iffalse=0 |
Generates prologue code for the iftrue
or the iffalse
command (depending on the value of criteria
parameter). Prologue is used to skip the body if the criyeria is not fulfilled.
The return value of the function is the address of the instruction where a fix-up is needed.
Generated code:
cmp [ebp+OFFSET_TEST],criteria jne skip
{ CMP_MEM_EBP( BASE_OFFSET_TEST, criteria ); REM( "check test result" ); JNE( 0, TEXT("skip") ); return LOCAL_IP-4; }
void asm_run_epilogue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates epilogue for runmacro
command which does the actual execution of commands.
The epilogue generates code to get the address of compiled code and execute it.
Generated code:
{run} ; already generated cmp [eax+OFFSET_ID],ERROR_ID je exit
push eax ; EAX=var atom mov eax,[eax+DESCR1] ; get DESCR1 which contains the address call [eax+ADDRESS] ; code to run, EAX=result of run code call deuse ; deuse var atom (including run code)
cmp ebx,1 je exit
cmp eax,{stopped} je exit
{ INFO( "" ); INFO( "actual execution of run" ); CMP_ID_ERROR; REM( "exit if error" ); JE_EXIT; MOV_ESI_EBP; REM( "static link" ); PUSH_EAX; REM( "EAX=var atom" ); MOV_EAX_MEM_EAX( OFFSET_DESCR1 ); REM( "EAX=var descriptor" ); CALL_MEM_EAX( OFFSET_ADDRESS, TEXT("ADDRESS") ); REM( "get and call var address" ); CALL( deuse, TEXT("deuse") ); CMP_EBX_CONST( 1 ); JE_EXIT; CMP_EAX_ATOM(stopped); REM( "exit if stopped" ); JE_EXIT; }
void asm_runresult_epilogue | ( | context_t * | ctx | ) |
ctx | compilation context |
Generates epilogue for runresult
command which does the actual execution of commands.
The epilogue generates code to get the address of compiled code and execute it. It reuses code generated by asm_run_epilogue().
Generated code:
{run} ; already generated cmp [eax+OFFSET_ID],ERROR_ID je skip
push eax ; EAX=var atom mov eax,[eax+DESCR1] ; get DESCR1 which contains the address call [eax+ADDRESS] ; code to run, EAX=result of run code call deuse ; deuse var atom (including run code) skip: push eax call rt_runresult_fix
{ INFO( "" ); INFO( "actual execution of runresult" ); CMP_ID_ERROR; REM( "skip if error" ); JE( 0, TEXT("$skip") ); int branch=LOCAL_IP-4; MOV_ESI_EBP; REM( "static link" ); PUSH_EAX; REM( "EAX=var atom" ); MOV_EAX_MEM_EAX( OFFSET_DESCR1 ); REM( "EAX=var descriptor" ); CALL_MEM_EAX( OFFSET_ADDRESS, TEXT("ADDRESS") ); REM( "get and call var address" ); CALL( deuse, TEXT("deuse") ); LABEL( "$skip:" ); asm_fix( ctx, branch ); PUSH_EAX; CALL( rt_runresult_fix, TEXT("rt_runresult_fix") ); }
ctx | compilation context |
Generates instruction for trapping debugger.
Generated code:
int 3
{ INT_3; }
void asm_set_output_status | ( | context_t * | ctx, |
int | n | ||
) |
ctx | compilation context |
n | output status |
Generates code to set output/stop status in EBX
Generated code: mov ebx,n
{ MOV_EBX_CONST( n ); }