Documentation/TCG/frontend-ops

From QEMU

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 helpers.

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

Math operation on a single register:

tcg_gen_mov_tl(ret, arg1); Assign one register to another ret = arg1
tcg_gen_neg_tl(ret, arg1); Negate the sign of a register ret = - arg1

Math operations on two registers:

tcg_gen_add_tl(ret, arg1, arg2); Add two registers ret = arg1 + arg2
tcg_gen_sub_tl(ret, arg1, arg2); Subtract two registers ret = arg1 - arg2
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_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_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

Bit Operations

Logic

Logic operations on a single register:

tcg_gen_not_tl(ret, arg1); Logical NOT an register ret = !arg1

Logic operations on two registers:

tcg_gen_and_tl(ret, arg1, arg2); Logical AND two registers ret = arg1 & arg2
tcg_gen_or_tl(ret, arg1, arg2); Logical OR two registers ret = arg1 | arg2
tcg_gen_xor_tl(ret, arg1, arg2); Logical XOR 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_eqv_tl(ret, arg1, arg2); Compute logical equivalent of 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_orc_tl(ret, arg1, arg2); Logical OR one register with the complement of another ret = arg1 | ~arg2

Shift

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

Rotation

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

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