UML Control Flow Opcodes
Below is a detailed description of all the UML control flow opcodes. For general information about the UML, please see UML Architecture.
CALLC
Usage:
CALLC function,parameter[,condition]
Codegen Shorthand:
UML_CALLC(block, function, parameter); UML_CALLCc(block, condition, function, parameter);
Parameters:
- function — a memory pointer to a C function of the form
void cfunction(void *parameter)
- parameter — a memory pointer that is passed as the argument to the C function
- condition — an optional condition which is used to determine whether or not to execute the call
Flags: undefined
Description: The CALLC opcode is used to execute a C function from within the generated code. A single parameter (evaluated at compile time only) may be passed to the function. More data can be effectively passed by using the parameter to point to a communication block. Upon return from the C function, all registers retain their original values; however, all flags are left in an undefined state.
Like most control flow opcodes, CALLC can be used either unconditionally or flagged with a condition that controls whether or not the target function is called.
Example:
void cfunc_printf_string(void *parameter) { printf("%s", parameter); } void generate_call_to_print_string(drcuml_block *block) { UML_CALLC(block, cfunc_printf_string, "My test string"); }
CALLH
Usage:
CALLH handle[,condition]
Codegen Shorthand:
UML_CALLH(block, handle); UML_CALLHc(block, condition, handle);
Parameters:
- handle — a memory pointer to a previously-allocated code handle
- condition — an optional condition which is used to determine whether or not to execute the call
Flags: undefined
Description: The CALLH function makes a subroutine call to the code referenced by the given handle. Note that although the handle must have already been allocated, it is permitted to pass a handle which has not yet been populated by code. The back-end code generator must support both cases, though it will generally output less efficient code if the handle is not yet populated (since it must fetch the address at runtime from the handle).
Like most control flow opcodes, CALLH can be used either unconditionally or flagged with a condition that controls whether or not the target function is called.
Example:
drcuml_state drcuml; uml::code_handle *mysubroutine; void generate_subroutine_code(drcuml_state *drcuml) { drcuml_block *block; jmp_buf errorbuf; /* allocate a handle */ mysubroutine = drcuml.handle_alloc(drcuml, "my_subroutine_name"); /* handle a fatal error during codegen */ if (setjmp(errorbuf) != 0) fatalerror("Ran out of cache space generating subroutine!"); /* generate a simple subroutine */ block = drcuml_block_begin(drcuml, 10, &errorbuf); UML_HANDLE(block, *mysubroutine); UML_RET(block); /* complete codegen */ drcuml_block_end(block); } void generate_call_to_subroutine(drcuml_block *block) { UML_CALLH(block, *mysubroutine); }
COMMENT
Usage:
COMMENT string
Codegen Shorthand:
UML_COMMENT(block, string);
Parameters:
- string — a memory pointer to character string
Flags: unaffected
Description: The COMMENT opcode exists to provide a means of documenting the UML code. Comments are generally ignored completely by the back-end and typically generate no code. However, they do show up in the disassembly of UML code, and may optionally propogate to disassembly of generated code as well.
Example:
void generate_comment(drcuml_block *block) { UML_COMMENT(block, "This is a comment for disassembly"); }
DEBUG
Usage:
DEBUG pc
Codegen Shorthand:
UML_DEBUG(block, PTYPE(pc));
Parameters:
- pc — a 32-bit integer register, memory location, map variable, or immediate
Flags: undefined
Description: The DEBUG opcode indicates that, if the debugger is present, it should be updated with the provided pc as the current instruction. To properly support the built-in debugger, this opcode should be present before the execution of each instruction. Before issuing a DEBUG opcode, be sure to flush any cached state (including the PC) to its target memory location so that the debugger can properly examine it or modify it. Upon completion of this opcode, all registers retain their original values; however, all flags are left in an undefined state.
Example:
offs_t currentpc; void generate_call_to_debugger(drcuml_block *block) { UML_DEBUG(block, mem(¤tpc)); }
EXH
Usage:
EXH handle,parameter[,cond]
Codegen Shorthand:
UML_EXH(block, handle, PTYPE(parameter)); UML_EXHc(block, condition, handle, PTYPE(parameter));
Parameters:
- handle — a memory pointer to a previously-allocated code handle
- parameter — a 32-bit integer register, memory location, map variable, or immediate
- condition — an optional condition which is used to determine whether or not to generate the exception
Flags: undefined
Description: The EXH function generates an exception, which is effectively a subroutine call with a special parameter. The code to call is referenced by the given handle. As with the CALLH opcode, the handle must have already been allocated, though again it is permitted to pass a handle which has not yet been populated by code. The back-end code generator must support both cases, though it will generally output less efficient code if the handle is not yet populated (since it must fetch the address at runtime from the handle). The parameter is stored in a special internal register called EXP and can be retrieved via the GETEXP opcode.
Like most control flow opcodes, EXH can be used either unconditionally or flagged with a condition that controls whether or not the exception is generated.
Example:
drcuml_state drcuml; uml::code_handle *myexceptionhandler; void generate_exception_handler(drcuml_state *drcuml) { drcuml_block *block; jmp_buf errorbuf; /* allocate a handle */ myexceptionhandler = drcuml.handle_alloc(drcuml, "handle_exception"); /* handle a fatal error during codegen */ if (setjmp(errorbuf) != 0) fatalerror("Ran out of cache space generating exception handler!"); /* generate a simple exception handler */ block = drcuml_block_begin(drcuml, 10, &errorbuf); UML_HANDLE(block, *myexceptionhandler); UML_RET(block); /* complete codegen */ drcuml_block_end(block); } void generate_exception_if_non_zero(drcuml_block *block, int parameter) { UML_EXHc(block, COND_NZ, *myexceptionhandler, parameter); }
EXIT
Usage:
EXIT parameter[,condition]
Codegen Shorthand:
UML_EXIT(block, PTYPE(parameter)); UML_EXITc(block, condition, PTYPE(parameter));
Parameters:
- parameter — a 32-bit integer register, memory location, map variable, or immediate
- condition — an optional condition which is used to determine whether or not to exit
Flags: undefined
Description: The EXIT opcode immediately exits from the generated code and returns control back to the back-end, which ultimately returns control to the dynamic recompiler. The exit can be performed even from within a subroutine or exception handler. The parameter is surfaced as the return value of the execute function.
Like most control flow opcodes, EXIT can be used either unconditionally or flagged with a condition that controls whether or not the exit occurs.
Example:
void exit_with_return_code_in_i0(drcuml_block *block) { UML_EXIT(block, ireg(0)); }
HANDLE
Usage:
HANDLE handle
Codegen Shorthand:
UML_HANDLE(block, handle);
Parameters:
- handle — a memory pointer to previously-allocated handle
Flags: unaffected
Description: The HANDLE opcode connects the current code position to the specified handle. The handle must have been previously explicitly allocated. By definition only one piece of code can connect itself to a handle; attempts to attach a new piece of code to an existing handle are considered errors and will assert in debug builds.
Example:
void attach_code_to_handle(drcuml_block *block, uml::code_handle *handle) { UML_HANDLE(block, *handle); }
HASH
Usage:
HASH mode, pc
Codegen Shorthand:
UML_HASH(block, mode, pc);
Parameters:
- mode — a 32-bit immediate value
- pc — a 32-bit immediate value
Flags: unaffected
Description: The HASH opcode connects the current code position to the back-end's hash table. The back-end is free to implement whatever code lookup mechanism it desires, though a standard one is provided by the back-end utilities module. The available number of modes is specified at dynamic recompiler initialization time; it is an error to specify mode value beyond the number of modes requested at that time.
Note that unlike a handle, a hash for a given mode/pc combination can be replaced at any time.
Example:
void attach_code_to_mode_and_pc(drcuml_block *block, UINT32 mode, UINT32 pc) { UML_HASH(block, mode, pc); }
HASHJMP
Usage:
HASHJMP mode,pc,handle
Codegen Shorthand:
UML_HASHJMP(block, PTYPE(mode), PTYPE(pc), handle);
Parameters:
- mode — a 32-bit integer register, memory location, map variable, or immediate
- pc — a 32-bit integer register, memory location, map variable, or immediate
- handle — memory pointer to allocated handle
Flags: undefined
Description: The HASHJMP opcode transfers control to a block of code that has previously been connected to the provided mode/pc pair. If no code has been registered, an exception is generated by setting the exception parameter EXP to pc and calling the given handle. The expected response to this is to return to the dynamic recompiler and request that it generate new code for the provided mode/pc pair.
Example:
uml::code_handle *nocode_exception_handler; UINT32 current_mode; void jump_to_code_at_pc_in_current_mode(drcuml_block *block, UINT32 pc) { UML_HASHJMP(block, mem(¤tmode), pc, *nocode_exception_handler); }
JMP
Usage:
JMP label[,cond]
Codegen Shorthand:
UML_JMP(block, label); UML_JMPc(block, condition, label);
Parameters:
- label — a 32-bit immediate value
- condition — an optional condition which is used to determine whether or not to jump
Flags: unaffected
Description: The JMP opcode transfers control to another location within the current block. The label parameter specifies a 32-bit integer label that serves as the jump target. Within the block, a LABEL opcode must be present to signify what code the label refers to. In debug builds, attempts to jump to a non-existent label will assert at back-end code generation time (when all the labels are resolved).
Like most control flow opcodes, JMP can be used either unconditionally or flagged with a condition that controls whether or not the jump occurs.
Example:
void conditional_jump_around_code(drcuml_block *block) { uml::code_label curlabel = 1; uml::code_label skip; UML_JMPc(block, COND_NZ, skip = curlabel++); <other code here> UML_LABEL(skip); }
LABEL
Usage:
LABEL label
Codegen Shorthand:
UML_LABEL(block, label);
Parameters:
- label — 32-bit immediate value
Flags: unaffected
Description: The LABEL opcode connects the current code position to a block-local label with the value of label. Only one label per block of UML can claim the same label value, so the dynamic recompiler must set up some conventions to ensure that each label has a unique 32-bit identifier.
Example:
void attach_code_to_label(drcuml_block *block, uml::code_label label) { UML_LABEL(block, label); }
MAPVAR
Usage:
MAPVAR mapvar,value
Codegen Shorthand:
UML_MAPVAR(block, MVAR(mapvar), value);
Parameters:
- mapvar — a map variable
- value — a 32-bit immediate value
Flags: unaffected
Description: The MAPVAR opcode sets the specified map variable mapvar to a new value value starting at the current position within the code. Map variables are encoded into the instruction stream and can be retrieved from within a subroutine or exception handler via the RECOVER opcode. At the start of a block, all map variables default to 0, and hold that value until a MAPVAR opcode overrides it, at which point all subsequent code will take on the new value.
Note that because the RECOVER opcode always operates on the outermost return address, it does not make sense to use MAPVAR within a subroutine.
Example:
void generate_some_code(drcuml_block *block) { UML_MAPVAR(block, mvar(0), 1); /* we are in section 1 of the code */ <section 1 code here> UML_MAPVAR(block, mvar(0), 2); /* now we are in section 2 of the code */ <section 2 code here> }
RECOVER
Usage:
RECOVER dest,mapvar
Codegen Shorthand:
UML_RECOVER(block, PTYPE(dest), MVAR(mapvar));
Parameters:
- dest — a 32-bit integer register or memory location
- mapvar — a map variable
Flags: undefined
Description: The RECOVER opcode retrieves the value of the given mapvar and stores it in dest. It does this by fetching the outermost return address from the stack and using that value to find the value specified by the most recent MAPVAR opcode that preceded the point where the subroutine call or exception generation happened. Because of this behavior, it only makes sense to use RECOVER from within a subroutine or exception handler; using it outside of this context will produce undefined results.
Example:
void generate_recover_code_section_to_i1(drcuml_block *block) { UML_RECOVER(block, ireg(0), mvar(0)); }
RET
Usage:
RET [cond]
Codegen Shorthand:
UML_RET(block); UML_RETc(block, condition);
Parameters:
- condition — an optional condition which is used to determine whether or not to return
Flags: undefined
Description: Returns control from a subroutine call or exception handler to the instruction following the CALLH or EXH opcode.
Like most control flow opcodes, RET can be used either unconditionally or flagged with a condition that controls whether or not the return occurs.
Example:
void generate_return_from_function_if_carry(drcuml_block *block) { UML_RETc(block, COND_C); }