Documentation/TCG/frontend-ops
Frontend Ops
These are the supported operations as implemented by the TCG frontend for the target cpu (what QEMU executes; not where QEMU executes). This information is useful for people who want to port QEMU to emulate a new processor.
You can find all of the frontend helpers in tcg/tcg-op.h. This page covers all them; it does not cover the mechanism for defining port-specific helpersTemplate:Dead link.
Conventions
- When the term register is used unqualified, it most likely is referring to a TCG register.
- The frontend helpers for generating TCG opcodes typically take the form: tcg_gen_<op>[i]_<reg_size>.
- The <op> is the TCG operation that will be generated for its arguments.
- The [i] suffix is used to indicate the TCG operation takes an immediate rather than a normal register.
- The <reg_size> refers to the size of the TCG registers in use. The vast majority of the time, this will match the native size of the emulated target, so rather than force people to type i32 or i64 all the time, the shorthand tl is made available for all helpers. e.g. to perform a 32bit register move for a 32bit target, simply use tcg_gen_mov_tl rather than tcg_gen_mov_i32.
- We won't cover the immediate variants of functions as their usage should be fairly obvious once you grasp the register version.
- To keep things simple, we'll cover the tl variants here. If you need to delve into explicit types, then you should probably know what you're doing anyways, so you're beyond the current scope of this wiki page.
- Similarly, rather than using TCGv_i32 and TCGv_i64 everywhere, people should stick to TCGv and TCGv_ptr.
- The tcg_gen_xxx arguments typically place the return value first followed by the operands. So to add two registers (a = b + c), the code would be tcg_gen_xxx(a, b, c);.
Non-Ops
Some non-ops helpers are also provided.
Registers
Most of the core registers of the emulated processor should have a TCG register equivalent. For processor status flags (or similar oddities), ports will employ tricks to speed things up. This is obviously left up to the discretion of the processor port maintainer.
TCGv reg = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, reg), "reg"); | Declare a named TCG register |
Temporaries
Often times, target insns cannot be broken down into one or two simple RISC insns which means temporary registers might be necessary to store intermediate results. Rather than each frontend maintaining its own static set of temporary scratch registers, helpers are provided to manage these on the fly.
TCGv tmp = tcg_temp_new(); | Create a new temporary register |
TCGv tmpl = tcg_temp_local_new(); | Create a local temporary register. Simple temporary register cannot carry its value across jump/brcond, only local temporary can. |
tcg_temp_free(tmp); | Free a temporary register |
You should not try to "hold on" to temporary registers beyond the target insn you're currently generating. If you need long lived registers, consider allocating a proper one.
Labels
Labels are used to generate conditional code. Here we'll just cover their management; later we'll get into using them in conjunction with opcodes.
int l = gen_new_label(); | Create a new label. |
gen_set_label(l); | Label the current location. |
Ops
These frontend ops expand into backend ops. Consulting that page might be useful for deeper insight into the front/back end relationship.
Math
tcg_gen_add_tl(ret, arg1, arg2); | Add two registers | ret = arg1 + arg2 |
tcg_gen_div_tl(ret, arg1, arg2); | Divide two signed registers and return the result | ret = arg1 / arg2 |
tcg_gen_divu_tl(ret, arg1, arg2); | Divide two unsigned registers and return the result | ret = arg1 / arg2 |
tcg_gen_mov_tl(ret, arg1); | Assign one register to another | ret = arg1 |
tcg_gen_mul_tl(ret, arg1, arg2); | Multiply two signed registers and return the result | ret = arg1 * arg2 |
tcg_gen_mulu_tl(ret, arg1, arg2); | Multiply two unsigned registers and return the result | ret = arg1 * arg2 |
tcg_gen_neg_tl(ret, arg1); | Negate the sign of a register | ret = -arg1 |
tcg_gen_rem_tl(ret, arg1, arg2); | Divide two signed registers and return the remainder | ret = arg1 % arg2 |
tcg_gen_remu_tl(ret, arg1, arg2); | Divide two unsigned registers and return the remainder | ret = arg1 % arg2 |
tcg_gen_sub_tl(ret, arg1, arg2); | Subtract two registers | ret = arg1 - arg2 |
Bit
tcg_gen_and_tl(ret, arg1, arg2); | Logical AND two registers | ret = arg1 & arg2 |
tcg_gen_andc_tl(ret, arg1, arg2); | Logical AND one register with the complement of another | ret = arg1 & ~arg2 |
tcg_gen_eqv_tl(ret, arg1, arg2); | Compute logical equivalent of two registers | ret = !(arg1 ^ arg2) |
tcg_gen_nand_tl(ret, arg1, arg2); | Logical NAND two registers | ret = arg1 ↑ arg2 |
tcg_gen_nor_tl(ret, arg1, arg2); | Logical NOR two registers | ret = arg1 ↓ arg2 |
tcg_gen_not_tl(ret, arg1); | Logical NOT an register | ret = !arg1 |
tcg_gen_or_tl(ret, arg1, arg2); | Logical OR two registers | ret = arg1 | arg2 |
tcg_gen_orc_tl(ret, arg1, arg2); | Logical OR one register with the complement of another | ret = arg1 | ~arg2 |
tcg_gen_rotl_tl(ret, arg1, arg2); | Rotate left one register by magnitude of another | ret = arg1 rotl arg2 |
tcg_gen_rotr_tl(ret, arg1, arg2); | Rotate right one register by magnitude of another | ret = arg1 rotr arg2 |
tcg_gen_sar_tl(ret, arg1, arg2); | Arithmetic shift right one operand by magnitude of another | ret = arg1 >> arg2 /* Sign fills vacant bits */ |
tcg_gen_shl_tl(ret, arg1, arg2); | Logical shift left one registerby magnitude of another | ret = arg1 << arg2 |
tcg_gen_shr_tl(ret, arg1, arg2); | Logical shift right one register by magnitude of another | ret = arg1 >> arg2 |
tcg_gen_xor_tl(ret, arg1, arg2); | Logical XOR two registers | ret = arg1 ^ arg2 |
Byte
tcg_gen_bswap16_tl(ret, arg1); | Byte swap a 16bit register | ret = ((arg1 & 0xff00) >> 8) | ((arg1 & 0xff) << 8) |
tcg_gen_bswap32_tl(ret, arg1); | Byte swap a 32bit register | ret = ...see bswap16 and extend to 32bits... |
tcg_gen_bswap64_tl(ret, arg1); | Byte swap a 64bit register | ret = ...see bswap32 and extend to 64bits... |
tcg_gen_ext8s_tl(ret, arg1); | Sign extend an 8bit register | ret = (int8_t)arg1 |
tcg_gen_ext8u_tl(ret, arg1); | Zero extend an 8bit register | ret = (uint8_t)arg1 |
tcg_gen_ext16s_tl(ret, arg1); | Sign extend an 16bit register | ret = (int16_t)arg1 |
tcg_gen_ext16u_tl(ret, arg1); | Zero extend an 16bit register | ret = (uint16_t)arg1 |
tcg_gen_ext32s_tl(ret, arg1); | Sign extend an 32bit register | ret = (int32_t)arg1 |
tcg_gen_ext32u_tl(ret, arg1); | Zero extend an 32bit register | ret = (uint32_t)arg1 |
Load/Store
These are for moving data between registers and arbitrary host memory. Typically used for funky CPU state that is not represented by dedicated registers already and thus infrequently used. These are not for accessing the target's memory space; see the QEMU_XX helpers below for that.
tcg_gen_ld8s_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load an 8bit quantity from host memory and sign extend |
tcg_gen_ld8u_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load an 8bit quantity from host memory and zero extend |
tcg_gen_ld16s_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load a 16bit quantity from host memory and sign extend |
tcg_gen_ld16u_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load a 16bit quantity from host memory and zero extend |
tcg_gen_ld32s_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load a 32bit quantity from host memory and sign extend |
tcg_gen_ld32u_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load a 32bit quantity from host memory and zero extend |
tcg_gen_ld64_tl(reg, cpu_env, offsetof(CPUState, reg)); | Load a 64bit quantity from host memory |
tcg_gen_ld_tl(reg, cpu_env, offsetof(CPUState, reg)); | Alias to target native sized load |
tcg_gen_st8_tl(reg, cpu_env, offsetof(CPUState, reg)); | Store a 8bit quantity to host memory |
tcg_gen_st16_tl(reg, cpu_env, offsetof(CPUState, reg)); | Store a 16bit quantity to host memory |
tcg_gen_st32_tl(reg, cpu_env, offsetof(CPUState, reg)); | Store a 32bit quantity to host memory |
tcg_gen_st_tl(reg, cpu_env, offsetof(CPUState, reg)); | Alias to target native sized store |
These are for moving data between registers and arbitrary target memory. The address to load/store via is always the second argument while the first argument is always the value to be loaded/stored. The third argument (memory index) only makes sense for system targets; user targets will simply specify 0 all the time.
tcg_gen_qemu_ld8s(ret, addr, mem_idx); | Load an 8bit quantity from target memory and sign extend | ret = *(int8_t *)addr |
tcg_gen_qemu_ld8u(ret, addr, mem_idx); | Load an 8bit quantity from target memory and zero extend | ret = *(uint8_t *)addr |
tcg_gen_qemu_ld16s(ret, addr, mem_idx); | Load a 16bit quantity from target memory and sign extend | ret = *(int8_t *)addr |
tcg_gen_qemu_ld16u(ret, addr, mem_idx); | Load a 16bit quantity from target memory and zero extend | ret = *(uint8_t *)addr |
tcg_gen_qemu_ld32s(ret, addr, mem_idx); | Load a 32bit quantity from target memory and sign extend | ret = *(int8_t *)addr |
tcg_gen_qemu_ld32u(ret, addr, mem_idx); | Load a 32bit quantity from target memory and zero extend | ret = *(uint8_t *)addr |
tcg_gen_qemu_ld64(ret, addr, mem_idx); | Load a 64bit quantity from target memory | ret = *(uint64_t *)addr |
tcg_gen_qemu_st8(arg, addr, mem_idx); | Store an 8bit quantity to target memory | *(uint8_t *)addr = arg |
tcg_gen_qemu_st16(arg, addr, mem_idx); | Store a 16bit quantity to target memory | *(uint16_t *)addr = arg |
tcg_gen_qemu_st32(arg, addr, mem_idx); | Store a 32bit quantity to target memory | *(uint32_t *)addr = arg |
tcg_gen_qemu_st64(arg, addr, mem_idx); | Store a 64bit quantity to target memory | *(uint64_t *)addr = arg |
Code Flow
tcg_gen_brcond_tl(TCG_COND_XXX, arg1, arg2, label); | Test two operands and conditionally branch to a label | if (arg1 <condition> arg2) goto label |
tcg_gen_goto_tb(num); | Goto translation block (TB chaining) | Every TB can goto_tb to max two other different destinations. There are
two jump slots. tcg_gen_goto_tb takes a jump slot index as an arg, 0 or 1. These jumps will only take place if the TB's get chained, you need to tcg_gen_exit_tb with (tb | index) for that to ever happen. tcg_gen_goto_tb may be issued at most once with each slot index per TB. |
tcg_gen_exit_tb(num); | Exit translation block | num may be 0 or TB address ORed with the index of the taken jump slot.
If you tcg_gen_exit_tb(0), chaining will not happen and a new TB will be looked up based on the CPU state. |
tcg_gen_setcond_tl(TCG_COND_XXX, ret, arg1, arg2); | Compare two operands | ret = arg1 <condition> arg2 |