/* * libbinrec: a recompiling translator for machine code * Copyright (c) 2016 Andrew Church * * This software may be copied and redistributed under certain conditions; * see the file "COPYING" in the source code distribution for details. * NO WARRANTY is provided with this software. */ #ifndef SRC_RTL_H #define SRC_RTL_H #ifndef SRC_COMMON_H #include "src/common.h" #endif /* * This header defines constants and structures for the RTL (register * transfer language) used for translation. The process of translation * consists of generating RTL instructions corresponding to a given * sequence of input (guest) machine instructions, optionally optimizing * the RTL instructions, then translating the RTL into output (host) * machine instructions. * * In a typical RTL, each instruction performs exactly one operation on a * small number of operands (usually one or two, sometimes three), such as * loading a value from memory or adding two values together. With a few * exceptions, every operand to an RTL instruction is a register -- hence * the name "register transfer language" -- though many of these * "registers" are only used for temporary storage of values and will be * optimized out in later stages. This design simplifies the optimization * and code generation stages by reducing the number of distinct operations * that must be handled. * * Note that input machine instructions do not always map directly to RTL * instructions. As a somewhat extreme example, the "movsb" instruction * in the Intel x86 architecture might translate to: * LOAD reg1, (state_reg, #esi_offset) * LOAD_U8 reg2, (reg1) * ADDI reg3, reg1, #1 * STORE (state_reg, #esi_offset), reg3 * LOAD reg4, (state_reg, #edi_offset) * STORE_I8 (reg4), reg2 * ADDI reg5, reg4, #1 * STORE (state_reg, #edi_offset), reg5 * * The RTL used here deviates slightly from a "pure" RTL in that it allows * certain operations involving immediate values (notably memory access * with immediate offset and integer register-immediate operations, as seen * in the example above) to be written as a single instruction. This is * for convenience of translating operations which are common across many * architectures, since otherwise the effort to break them down into * multiple RTL instructions would be effectively wasted when the output * translator converted them back to the original form. * * RTL generated by input parsers must use SSA (static single assignment) * form; attempting to use a register as an output in more than one * instruction is invalid, and will result in an error if operand sanity * checks are enabled. Guest instructions which modify (both read and * write) an operand should create a second RTL register to hold the result * of the instruction. * * "Alias registers" are an exception to the SSA rule. These may only be * used as the source of a GET_ALIAS instruction or the destination of a * SET_ALIAS instruction, but they may be reassigned freely. This allows * them to serve as "aliases" for guest architecture registers, for * example, and allows the input translator to defer some data flow * analysis to the RTL engine, which will then infer phi functions from * the alias assignments. (Currently, there is no interface for explicitly * specifying phi functions.) Note that alias registers have a separate * namespace from ordinary registers, so alias register numbers will not be * unique with respect to ordinary register numbers. * * At a low level, alias registers are effectively aliases for specific * memory locations. These can be specified explicitly by calling * rtl_set_alias_storage(), or they can be left to the host translator, * which will typically allocate a stack frame slot for the alias. In the * latter case, the guest translator is responsible for setting an initial * value with a SET_ALIAS instruction, or subsequent GET_ALIAS instructions * will read undefined data. From the guest translator's point of view, * GET_ALIAS and SET_ALIAS instructions can be thought of as equivalent to * LOAD and STORE, but the host translator can make use of the data flow * information implied by the use of alias registers to omit some of those * loads and stores by keeping the value in a hardware register. * * Within this library, a "unit" or "translation unit" refers to a sequence * of guest instructions translated together into a single host code block; * the RTLUnit structure encapsulates the state associated with a * translation unit. A "basic block" has the usual meaning of a sequence * of instructions which has exactly one entry and one exit point; the * RTLBlock structure encapsulates information about a basic block, with * one RTLUnit containing an arbitrary number of RTLBlocks. Each RTLBlock * contains a sequence of RTL instructions, each of which is represented * internally by an RTLInsn structure. Note that RTLInsns are stored in a * single contiguous array in RTLUnit, and RTLBlocks contain only pointers * to (more precisely, array indices of) the first and last instructions in * the block. */ /*************************************************************************/ /******************** RTL data type and opcode lists *********************/ /*************************************************************************/ /** * RTLDataType: Enumeration of data types for registers used in an RTL * unit. For instructions which take multiple operands, all operands must * be of the same type, except where noted. * * RTLTYPE_ADDRESS is a special integer variant which is the same size as * a memory address (i.e., a pointer) on the host architecture. The * ADDRESS type is treated as distinct from other integer types; to perform * calculations with an address, the second operand must be cast to address * type with an RTLOP_ZCAST or RTLOP_SCAST instruction. * * RTLTYPE_FPSTATE is a special type reserved for holding the result of an * FGETSTATE instruction. This type can only be used with the FGETSTATE, * FSETSTATE, and FTESTEXC instructions, and it cannot be converted to any * other type. */ typedef enum RTLDataType { /* Zero is invalid. */ RTLTYPE_INT32 = 1, // 32-bit integer RTLTYPE_INT64, // 64-bit integer RTLTYPE_ADDRESS, // Host-pointer-sized integer RTLTYPE_FLOAT32, // 32-bit floating point RTLTYPE_FLOAT64, // 64-bit floating point RTLTYPE_V2_FLOAT32, // Vector of 2 x RTLTYPE_FLOAT32 RTLTYPE_V2_FLOAT64, // Vector of 2 x RTLTYPE_FLOAT64 RTLTYPE_FPSTATE, // Floating point state word } RTLDataType; /** * RTLOpcode: Enumeration of operations, used as the value of the * RTLInsn.opcode field. * * Note that in general, computational instructions require that all * operands be of the same type; for example, it is invalid to ADD an * INT32 register and an ADDRESS register, or to add two INT32 registers * and store the result in an ADDRESS register. Use an appropriate * conversion instruction, such as ZCAST or SCAST, to convert between types. */ typedef enum RTLOpcode { /* Zero is invalid. */ /* No operation. For debugging purposes, an immediate value (such as * an instruction address) can be given in the "other" operand, which * will typically be encoded into a no-op instruction in the output. * Registers can also be given in the dest, src1, and src2 fields; * these have no effect on code behavior, but they do extend the live * range of the registers, which can be useful in forcing certain * registers to remain live in tests. If registers are given, they * must obey the usual rules of definedness (dest must be undefined, * src1 and src2 must be defined). Passing a register as the * destination of a NOP and then using that register as the source of * another (non-NOP) instruction results in undefined behavior. */ RTLOP_NOP = 1, /* Alias register operations. */ RTLOP_SET_ALIAS, // alias[other] = src1 // [other is an alias register index] RTLOP_GET_ALIAS, // dest = alias[other] // [other is an alias register index] /* Non-computational operations. */ RTLOP_MOVE, // dest = src1 RTLOP_SELECT, // dest = other ? src1 : src2 // [other must be of integer type] /* Integer operations. */ RTLOP_SCAST, // dest = (typeof(dest))(signed)src1 RTLOP_ZCAST, // dest = (typeof(dest))(unsigned)src1 RTLOP_SEXT8, // dest = (int8_t)src1 RTLOP_SEXT16, // dest = (int16_t)src1 // [signed casts with explicit size since we // don't have 8-bit or 16-bit types] RTLOP_ADD, // dest = src1 + src2 RTLOP_SUB, // dest = src1 - src2 RTLOP_NEG, // dest = -src1 RTLOP_MUL, // dest = src1 * src2 RTLOP_MULHU, // dest = ((unsigned)src1 * (unsigned)src2) >> size // [size = number of bits in value type] RTLOP_MULHS, // dest = ((signed)src1 * (signed)src2) >> size // [size = number of bits in value type] RTLOP_DIVU, // dest = (unsigned)src1 / (unsigned)src2 // [behavior undefined if src2 == 0] RTLOP_DIVS, // dest = (signed)src1 / (signed)src2 // [behavior undefined if src2 == 0] RTLOP_MODU, // dest = (unsigned)src1 % (unsigned)src2 // [behavior undefined if src2 == 0] RTLOP_MODS, // dest = (signed)src1 % (signed)src2 // [behavior undefined if src2 == 0] RTLOP_AND, // dest = src1 & src2 RTLOP_OR, // dest = src1 | src2 RTLOP_XOR, // dest = src1 ^ src2 RTLOP_NOT, // dest = ~src1 RTLOP_SLL, // dest = src1 << src2 // [src2 may be any integer type; result is // undefined when src2 >= #bits(src1)] RTLOP_SRL, // dest = (unsigned)src1 >> src2 // [src2 may be any integer type; result is // undefined when src2 >= #bits(src1)] RTLOP_SRA, // dest = (signed)src1 >> src2 // [src2 may be any integer type; result is // undefined when src2 >= #bits(src1)] RTLOP_ROL, // dest = src1 ROL (src2 % #bits(src1)) // [src2 may be any integer type] RTLOP_ROR, // dest = src1 ROR (src2 % #bits(src1)) // [src2 may be any integer type] RTLOP_CLZ, // dest = [number of leading zeros in src1] // [dest may be any integer type] RTLOP_BSWAP, // dest = [reverse order of bytes in src1] RTLOP_SEQ, // dest = src1 == src2 ? 1 : 0 // [dest may be any integer type] RTLOP_SLTU, // dest = (unsigned)src1 < (unsigned)src2 ? 1 : 0 // [dest may be any integer type] RTLOP_SLTS, // dest = (signed)src1 < (signed)src2 ? 1 : 0 // [dest may be any integer type] RTLOP_SGTU, // dest = (unsigned)src1 > (unsigned)src2 ? 1 : 0 // [dest may be any integer type] RTLOP_SGTS, // dest = (signed)src1 > (signed)src2 ? 1 : 0 // [dest may be any integer type] /* Integer bitfield operations. "start" and "count" are encoded in the * "other" parameter as: other = start | count<<8 */ RTLOP_BFEXT, // dest = (src1 >> start) & ((1<= #bits(src1)] RTLOP_SRLI, // dest = (unsigned)src1 >> IMMEDIATE(other) // [result is undefined when other >= #bits(src1)] RTLOP_SRAI, // dest = (signed)src1 >> IMMEDIATE(other) // [result is undefined when other >= #bits(src1)] RTLOP_RORI, // dest = src1 ROR (IMMEDIATE(other) % #bits(src1)) // [to rotate left, use count = #bits(src1) - count] RTLOP_SEQI, // dest = src1 == src2 ? 1 : 0 // [dest may be any integer type] RTLOP_SLTUI, // dest = (unsigned)src1 < IMMEDIATE(other) ? 1 : 0 // [dest may be any integer type] RTLOP_SLTSI, // dest = (signed)src1 < IMMEDIATE(other) ? 1 : 0 // [dest may be any integer type] RTLOP_SGTUI, // dest = (unsigned)src1 > IMMEDIATE(other) ? 1 : 0 // [dest may be any integer type] RTLOP_SGTSI, // dest = (signed)src1 > IMMEDIATE(other) ? 1 : 0 // [dest may be any integer type] /* Bit-copy operation between floating-point and integer types. * dest and src1 must be of the same size and opposite types: * FLOAT32/INT32, INT64/FLOAT64, etc. */ RTLOP_BITCAST, // bits(dest) = bits(src1) /* Floating-point typecast. This is an arithmetic operation which * can raise floating-point exceptions and quiets SNaN inputs. */ RTLOP_FCVT, // dest = (typeof(dest))src1 // [dest and src1 must be of different types] /* Integer-to-FP cast operations. */ RTLOP_FZCAST, // dest = (typeof(dest))((unsigned)src1) RTLOP_FSCAST, // dest = (typeof(dest))((signed)src1) /* FP-to-integer cast operations. FROUNDI uses the current rounding * mode, while FTRUNCI rounds toward zero. Negative overflow and * NaNs always convert to the minimum signed integer value for the * destination type; it is host-defined whether positive overflow * converts to the minimum or maximum signed integer value. */ RTLOP_FROUNDI, // dest = (signed typeof(dest))round(src1) RTLOP_FTRUNCI, // dest = (signed typeof(dest))trunc(src1) /* Floating-point sign bit manipulation instructions. These do not * raise floating-point exceptions and treat NaNs like other values. * Vector operands are allowed. */ RTLOP_FNEG, // dest = -src1 RTLOP_FABS, // dest = abs(src1) RTLOP_FNABS, // dest = -abs(src1) /* Floating-point arithmetic instructions. If multiple operands are * NaNs, the returned NaN is the first of {src1, src2, src3}; but if * the BINREC_OPT_NATIVE_IEEE_NAN optimization is enabled, any input * NaN may be returned. Vector operands are allowed. */ RTLOP_FADD, // dest = src1 + src2 RTLOP_FSUB, // dest = src1 - src2 RTLOP_FMUL, // dest = src1 * src2 RTLOP_FDIV, // dest = src1 / src2 RTLOP_FSQRT, // dest = sqrt(src1) RTLOP_FMADD, // dest = fma(src1, src2, src3) RTLOP_FMSUB, // dest = fma(src1, src2, -src3) RTLOP_FNMADD, // dest = fma(-src1, src2, src3) RTLOP_FNMSUB, // dest = fma(-src1, src2, -src3) /* Floating-point compare instruction. dest must be of integer type; * other is an immediate value giving the comparison type (RTLFCMP_*). * The result is 1 or 0; for example, "FCMP dest, src1, src2, RTLFCMP_GT" * sets dest to 1 if src1 > src2 and 0 otherwise (including if src1 or * src2 is a NaN). */ RTLOP_FCMP, // dest = fcmp(src1, src2, IMMEDIATE(other)) /* Floating-point state manipulation. State operands must be of type * FPSTATE. */ RTLOP_FGETSTATE, // dest = fpstate() // [reads current host floating-point state] RTLOP_FSETSTATE, // set_fpstate(src1) // [sets host floating-point state to src1] RTLOP_FTESTEXC, // dest = fpstate_has_exception(src1, IMMEDIATE(other)) // [other is one of RTLFEXC_*; dest must be of // integer type and receives 1 or 0] RTLOP_FCLEAREXC, // dest = fpstate_clear_exceptions(src1) RTLOP_FSETROUND, // dest = fpstate_set_round(src1, IMMEDIATE(other)) // [other is one of RTLFROUND_*] RTLOP_FCOPYROUND, // dest = fpstate_copy_round(src1, src2) // [copies rounding mode from src2 into src1] /* Vector manipulation instructions. */ RTLOP_VBUILD2, // dest = {src1, src2} // [dest must be a 2-element vector of typeof(src1)] RTLOP_VBROADCAST, // dest[*] = src1 RTLOP_VEXTRACT, // dest = src1[other] RTLOP_VINSERT, // dest = src1; dest[other] = src2 RTLOP_VFCVT, // dest[*] = (typeof(dest[*]))src1[*] // [dest and src1 must be of different types] /* Vector floating-point compare instruction. dest must be of INT64 * type; other is an immediate value giving the comparison type * (RTLFCMP_*). The low 32 bits of the result are set to all 1 if the * first element of the vector passes the comparison, all 0 otherwise; * the high 32 bits of the result are set similarly for the second * element. */ RTLOP_VFCMP, // dest = -fcmp(src1[0], src2[0], IMMEDIATE(other)) // | -fcmp(src1[1], src2[1], IMMEDIATE(other))<<32 /* Non-memory load operations. If LOAD_ARG instructions are used, * they must be the first instructions in the unit, or the argument * values may be overwritten. */ RTLOP_LOAD_IMM, // dest = IMMEDIATE(other) // [dest must be a scalar type; for floating-point // types, other is assumed to be in IEEE format] RTLOP_LOAD_ARG, // dest = ARG(other) // [other is a function argument index; only // integer arguments are supported] /* Memory load and store operations. Address operands must be of * ADDRESS type, offsets must be in [-32768,+32767], and for 8/16-bit * loads, the value operand must be of INT32 type. For vector load and * store operations, the first element of the vector is loaded from or * stored to the lowest address in memory. Behavior is undefined if * the address is not a multiple of the value size. */ RTLOP_LOAD, // dest = *(typeof(dest) *)(src1 + other) RTLOP_LOAD_U8, // dest = *(uint8_t *)(src1 + other) RTLOP_LOAD_S8, // dest = *(int8_t *)(src1 + other) RTLOP_LOAD_U16, // dest = *(uint16_t *)(src1 + other) RTLOP_LOAD_S16, // dest = *(int16_t *)(src1 + other) RTLOP_STORE, // *(typeof(src2) *)(src1 + other) = src2 RTLOP_STORE_I8, // *(uint8_t *)(src1 + other) = src2 RTLOP_STORE_I16, // *(uint16_t *)(src1 + other) = src2 /* These versions (_BR = "Byte-Reversed") swap the byte order of the * value operand after loading or before storing. Load/store byte * order is interpreted with respect to the host architecture; the * guest translator is responsible for choosing between the normal and * byte-reversed instructions based on the host_little_endian flag in * binrec_t. */ RTLOP_LOAD_BR, // dest = bswap(*(typeof(dest) *)(src1 + other)) RTLOP_LOAD_U16_BR, // dest = bswap16(*(uint16_t *)(src1 + other)) RTLOP_LOAD_S16_BR, // dest = bswap16(*(int16_t *)(src1 + other)) RTLOP_STORE_BR, // *(typeof(src2) *)(src1 + other) = bswap(src2) RTLOP_STORE_I16_BR, // *(uint16_t *)(src1 + other) = bswap16(src2) /* Integer atomic operations. Address operands (src1) must be of * ADDRESS type; other operands may be of any integral type, but (for * CMPXCHG) they must all match. */ RTLOP_ATOMIC_INC, // dest = (*src1)++ RTLOP_CMPXCHG, // dest = (*src1==src2 ? (*src1 = other, src2) : *src1) /* Branch operations ("other" is a label number). */ RTLOP_LABEL, // LABEL(other): RTLOP_GOTO, // goto LABEL(other) RTLOP_GOTO_IF_Z, // if (src1 == 0) goto LABEL(other) RTLOP_GOTO_IF_NZ, // if (src1 != 0) goto LABEL(other) /* Call to host address. src1 must be of ADDRESS type. dest, src2, * and other are optional, but must be integer type if given; if src2 * is omitted, other must also be omitted. */ RTLOP_CALL, // dest = (*src1)(src2, other) /* Call to host address. Functionally identical to CALL, but host * translators should avoid effects on translation state as much as * possible (for example, caller-saved registers should not be avoided * even for registers live across a CALL_TRANSPARENT instruction). * This is intended for the pre- and post-instruction callbacks, so * that using them to debug possible library bugs has only minimal * effect on the generated code. */ RTLOP_CALL_TRANSPARENT, // dest = (*src1)(src2, other) /* Terminator for generated code (returns to caller). The return value * is optional. */ RTLOP_RETURN, // return src1 /* Chain (jump) to another unit of translated code, if available. By * itself, this instruction does nothing, but it provides a point at * which a subsequent CHAIN_RESOLVE instruction can insert code to jump * directly to an address returned by the chain_lookup() callback. * Callers should use rtl_add_chain_insn() to add this instruction, * since that function returns the instruction index needed by the * CHAIN_RESOLVE instruction. */ RTLOP_CHAIN, // if (target) return (*target)(src1, src2) /* Set the target of the given CHAIN instruction to the given pointer * (src1, which must be of type ADDRESS) if it is not NULL. other is * the instruction index of the CHAIN instruction to modify, as * returned from rtl_add_chain_insn(). If the next instruction is not * RETURN, or if src1 is used as an operand to the CHAIN instruction, * incorrect code may be generated. */ RTLOP_CHAIN_RESOLVE, // if (src1) set_chain_target(IMMEDIATE(other), src1) /* Explicit illegal instruction; will trigger an illegal-instruction * exception if executed on the host. */ RTLOP_ILLEGAL, } RTLOpcode; #define RTLOP__FIRST RTLOP_NOP #define RTLOP__LAST RTLOP_ILLEGAL /** * RTLFloatCompare: Enumeration of comparison types for use with the FCMP * instruction. The comparison operand is one of the values from this * enumeration possibly OR'd with the RTLFCMP_INVERT and RTLFCMP_ORDERED * flags. */ typedef enum RTLFloatCompare { RTLFCMP_LT = 0, RTLFCMP_LE = 1, RTLFCMP_GT = 2, RTLFCMP_GE = 3, RTLFCMP_EQ = 4, RTLFCMP_UN = 5, } RTLFloatCompare; enum { RTLFCMP_INVERT = 8, // Invert the sense of the comparison. RTLFCMP_NLT = RTLFCMP_INVERT | RTLFCMP_LT, RTLFCMP_NLE = RTLFCMP_INVERT | RTLFCMP_LE, RTLFCMP_NGT = RTLFCMP_INVERT | RTLFCMP_GT, RTLFCMP_NGE = RTLFCMP_INVERT | RTLFCMP_GE, RTLFCMP_NEQ = RTLFCMP_INVERT | RTLFCMP_EQ, RTLFCMP_NUN = RTLFCMP_INVERT | RTLFCMP_UN, RTLFCMP_ORDERED = 16, // Raise an invalid operation exception on QNaNs. RTLFCMP_OLT = RTLFCMP_ORDERED | RTLFCMP_LT, RTLFCMP_OLE = RTLFCMP_ORDERED | RTLFCMP_LE, RTLFCMP_OGT = RTLFCMP_ORDERED | RTLFCMP_GT, RTLFCMP_OGE = RTLFCMP_ORDERED | RTLFCMP_GE, RTLFCMP_OEQ = RTLFCMP_ORDERED | RTLFCMP_EQ, RTLFCMP_OUN = RTLFCMP_ORDERED | RTLFCMP_UN, RTLFCMP_ONLT = RTLFCMP_ORDERED | RTLFCMP_INVERT | RTLFCMP_LT, RTLFCMP_ONLE = RTLFCMP_ORDERED | RTLFCMP_INVERT | RTLFCMP_LE, RTLFCMP_ONGT = RTLFCMP_ORDERED | RTLFCMP_INVERT | RTLFCMP_GT, RTLFCMP_ONGE = RTLFCMP_ORDERED | RTLFCMP_INVERT | RTLFCMP_GE, RTLFCMP_ONEQ = RTLFCMP_ORDERED | RTLFCMP_INVERT | RTLFCMP_EQ, RTLFCMP_ONUN = RTLFCMP_ORDERED | RTLFCMP_INVERT | RTLFCMP_UN, }; /** * RTLFloatException: Enumeration of floating-point exceptions for use * with the FTESTEXC instruction. */ typedef enum RTLFloatException { RTLFEXC_ANY = 0, // Evaluates to true if any exception has been raised. RTLFEXC_INEXACT, RTLFEXC_INVALID, RTLFEXC_OVERFLOW, RTLFEXC_UNDERFLOW, RTLFEXC_ZERO_DIVIDE, } RTLFloatException; /** * RTLFloatRoundingMode: Enumeration of floating-point rouding modes for * use with the FSETROUND instruction. */ typedef enum RTLFloatRoundingMode { RTLFROUND_NEAREST = 0, // Ties round to even. RTLFROUND_TRUNC, RTLFROUND_FLOOR, RTLFROUND_CEIL, } RTLFloatRoundingMode; /*************************************************************************/ /************************** RTL data structures **************************/ /*************************************************************************/ /** * RTLUnit: State information used in translating a unit of code. * Opaque to callers. */ typedef struct RTLUnit RTLUnit; /*************************************************************************/ /*********************** RTL translation interface ***********************/ /*************************************************************************/ /** * rtl_create_unit: Create a new RTLUnit structure for translation. * * [Parameters] * handle: Translation handle this until will be associated with. * [Return value] * New unit, or NULL on error. */ #define rtl_create_unit INTERNAL(rtl_create_unit) extern RTLUnit *rtl_create_unit(binrec_t *handle); /** * rtl_clear_unit: Clear all data from an RTLUnit structure. After * calling this function, the unit is in the same state as a unit returned * from rtl_create_unit(). * * [Parameters] * unit: RTLUnit to clear. */ #define rtl_clear_unit INTERNAL(rtl_clear_unit) extern void rtl_clear_unit(RTLUnit *unit); /** * rtl_get_error_state: Return whether any previous "add" or "alloc" * operation (rtl_add_insn(), rtl_alloc_register(), etc.) has failed. * This is effectively a NAND operation across the return values of all * such functions, and can be used to avoid individual checks on every * return value. * * [Parameters] * unit: RTLUnit to check. * [Return value] * True if an error has occurred, false if not. */ #define rtl_get_error_state INTERNAL(rtl_get_error_state) extern bool rtl_get_error_state(RTLUnit *unit); /** * rtl_clear_error_state: Clear (reset to false) the state of the error * flag returned by rtl_get_error_state(). * * [Parameters] * unit: RTLUnit to operate on. */ #define rtl_clear_error_state INTERNAL(rtl_clear_error_state) extern void rtl_clear_error_state(RTLUnit *unit); /** * rtl_add_insn: Append an instruction to the given unit. The meaning of * each operand depends on the instruction. * * [Parameters] * unit: RTLUnit to append to. * opcode: Instruction opcode (RTLOP_*). * dest: Destination register for instruction. * src1: First source register for instruction. * src2: Second source register for instruction. * other: Extra register or immediate value for instruction. * [Return value] * True on success, false on error. */ #define rtl_add_insn INTERNAL(rtl_add_insn) extern bool rtl_add_insn(RTLUnit *unit, RTLOpcode opcode, int dest, int src1, int src2, uint64_t other); /** * rtl_add_chain_insn: Append a CHAIN instruction to the given unit, and * return its index for use in a subsequent CHAIN_RESOLVE instruction. * src1 and src2 give the function arguments to pass to the chained code, * and should be the same as the arguments received by the code currently * being generated. * * [Parameters] * unit: RTLUnit to append to. * src1: First function argument for chain target. * src2: Second function argument for chain target. * [Return value] * Index of added instruction, or -1 on error. */ #define rtl_add_chain_insn INTERNAL(rtl_add_chain_insn) extern int rtl_add_chain_insn(RTLUnit *unit, int src1, int src2); /** * rtl_alloc_register: Allocate a new register for use in the given unit. * The register's value is undefined until it has been used as the * destination of an instruction. * * Register numbers will always fit in a uint16_t; they are passed and * returned as int to reduce unnecessary size casts in the compiled code. * * [Parameters] * unit: RTLUnit to allocate a register for. * type: Data type for register (RTLTYPE_*). * [Return value] * Register number (positive) on success, zero on error. */ #define rtl_alloc_register INTERNAL(rtl_alloc_register) extern int rtl_alloc_register(RTLUnit *unit, RTLDataType type); /** * rtl_make_unfoldable: Prevent the given register from being used in * constant folding, even if it would otherwise be foldable. This * function must be called after adding the instruction that sets the * register. * * [Parameters] * unit: RTLUnit containing register to mark. * reg: Register number to mark. */ #define rtl_make_unfoldable INTERNAL(rtl_make_unfoldable) extern void rtl_make_unfoldable(RTLUnit *unit, int reg); /** * rtl_make_unspillable: Prevent the given register from being spilled to * memory. * * Calling this function on too many registers may cause host register * allocation to fail. * * [Parameters] * unit: RTLUnit containing register to mark. * reg: Register number to mark. */ #define rtl_make_unspillable INTERNAL(rtl_make_unspillable) extern void rtl_make_unspillable(RTLUnit *unit, int reg); /** * rtl_make_unique_pointer: Mark the given register as being a "unique * pointer", which points to a region of memory which will never be accessed * except through this register (or another register copied from it). * * [Parameters] * unit: RTLUnit containing register to mark. * reg: Register number to mark. */ #define rtl_make_unique_pointer INTERNAL(rtl_make_unique_pointer) extern void rtl_make_unique_pointer(RTLUnit *unit, int reg); /** * rtl_alloc_alias_register: Allocate a new register which will serve as * an alias for another register. Alias registers may only be used as the * source or target of a MOVE instruction, but they may be reassigned freely. * * Alias register numbers will always fit in a uint16_t; they are passed and * returned as int to reduce unnecessary size casts in the compiled code. * * [Parameters] * unit: RTLUnit to allocate an alias register for. * type: Data type for register (RTLTYPE_*). * [Return value] * Alias register number (positive) on success, zero on error. */ #define rtl_alloc_alias_register INTERNAL(rtl_alloc_alias_register) extern int rtl_alloc_alias_register(RTLUnit *unit, RTLDataType type); /** * rtl_set_alias_storage: Define the storage location for an alias register. * * Binding a memory location to an alias register with this function has * the following implications: * * - Reading the alias with a GET_ALIAS instruction will implicitly load * the value stored at that memory location if no preceding SET_ALIAS * instruction has set the alias's value. * * - Writing the alias with a SET_ALIAS instruction will cause the value to * be stored to that memory location at some point before control leaves * the generated code, though the store may be omitted if a subsequent * SET_ALIAS instruction also modifies the same alias. * * - The base register for the alias will never be spilled to memory (as * if rtl_make_unspillable() had been called on the register). * * If a memory location is not bound to an alias register, reading the * alias before storing a value in it returns undefined data, and any value * stored in the alias when the generated code returns to its caller is * discarded. * * Calling this function on an alias which has already been used in a * GET_ALIAS or SET_ALIAS instruction results in undefined behavior. * * The number of distinct base registers used for aliases should be kept to * a minimum; host code generators may fail at register allocation if too * many base registers are used. * * [Parameters] * unit: RTLUnit to operate on. * alias: Alias register to define the storage location for. * base: RTL register containing base address for memory access (must be * nonzero and of type RTLTYPE_ADDRESS). * offset: Byte offset for memory access (must be within [-32768,+32767]). */ #define rtl_set_alias_storage INTERNAL(rtl_set_alias_storage) extern void rtl_set_alias_storage(RTLUnit *unit, int alias, int base, int16_t offset); /** * rtl_alloc_label: Allocate a new label for use in the given unit. * * Label numbers will always fit in a uint16_t; they are passed and * returned as int to reduce unnecessary size casts in the compiled code. * * [Parameters] * unit: RTLUnit to allocate a label for. * [Return value] * Label number (positive) on success, zero on error. */ #define rtl_alloc_label INTERNAL(rtl_alloc_label) extern int rtl_alloc_label(RTLUnit *unit); /** * rtl_finalize_unit: Perform housekeeping at the end of the given unit's * translation. rtl_add_insn(), rtl_alloc_register(), and rtl_alloc_label() * may not be called for a unit after calling this function on the unit. * * [Parameters] * unit: RTLUnit to finalize. * [Return value] * True on success, false on error. */ #define rtl_finalize_unit INTERNAL(rtl_finalize_unit) extern bool rtl_finalize_unit(RTLUnit *unit); /** * rtl_set_membase_pointer: Mark the given register as holding a pointer * to the base address of the guest memory region. Load operations whose * address register is this register or the result of ADDing another * (constant) register to this one will be eligible for constant folding * if the referenced guest address is in a read-only region. * * [Parameters] * unit: RTLUnit containing register to mark. * reg: Index of register containing the base address of the guest * memory region. */ #define rtl_set_membase_pointer INTERNAL(rtl_set_membase_pointer) extern void rtl_set_membase_pointer(RTLUnit *unit, int reg); /** * rtl_optimize_unit: Perform target-independent optimization on the * given unit. Before calling this function, rtl_finalize_unit() must be * called for the unit. * * [Parameters] * unit: RTLUnit to optimize. * flags: BINREC_OPT_* flags indicating which optimizations to perform. * [Return value] * True on success, false on error. */ #define rtl_optimize_unit INTERNAL(rtl_optimize_unit) extern bool rtl_optimize_unit(RTLUnit *unit, unsigned int flags); /** * rtl_verify_unit: Verify that the given unit is properly constructed * (no uses of undefined registers, etc). * * [Parameters] * unit: RTLUnit to verify. * address: Guest code address associated with unit (for warning and * error messages). * [Return value] * False on error (major code error or out of memory), true otherwise * (including minor code errors). */ #define rtl_verify_unit INTERNAL(rtl_verify_unit) extern bool rtl_verify_unit(RTLUnit *unit, uint32_t address); /** * rtl_disassemble_unit: Return a string containing a disassembled version * of the RTL code in the given unit. The returned string pointer remains * valid until the next call to rtl_disassemble_unit() on the same unit, or * until the unit is destroyed. * * [Parameters] * unit: RTLUnit to disassemble. * verbose: True to include a description of each source register after * each instruction, false to omit register descriptions. * [Return value] * String containing the disassembly, or NULL on error. */ #define rtl_disassemble_unit INTERNAL(rtl_disassemble_unit) extern const char *rtl_disassemble_unit(RTLUnit *unit, bool verbose); /** * rtl_destroy_unit: Destroy the given unit, freeing any resources it used. * * [Parameters] * unit: RTLUnit to destroy (may be NULL). */ #define rtl_destroy_unit INTERNAL(rtl_destroy_unit) extern void rtl_destroy_unit(RTLUnit *unit); /*************************************************************************/ /*************************************************************************/ #endif // RTL_H