diff options
author | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:49:54 -0700 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:49:54 -0700 |
commit | 68396ea9c0fe4f75ce30b1eba2c44c43c13344bb (patch) | |
tree | 802587d411d9db461e6500c5b635043315f81c27 /tcg | |
download | illumos-kvm-cmd-68396ea9c0fe4f75ce30b1eba2c44c43c13344bb.tar.gz |
Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
Diffstat (limited to 'tcg')
-rw-r--r-- | tcg/LICENSE | 3 | ||||
-rw-r--r-- | tcg/README | 511 | ||||
-rw-r--r-- | tcg/TODO | 14 | ||||
-rw-r--r-- | tcg/arm/tcg-target.c | 1843 | ||||
-rw-r--r-- | tcg/arm/tcg-target.h | 93 | ||||
-rw-r--r-- | tcg/hppa/tcg-target.c | 1683 | ||||
-rw-r--r-- | tcg/hppa/tcg-target.h | 119 | ||||
-rw-r--r-- | tcg/i386/tcg-target.c | 1982 | ||||
-rw-r--r-- | tcg/i386/tcg-target.h | 126 | ||||
-rw-r--r-- | tcg/ia64/tcg-target.c | 2390 | ||||
-rw-r--r-- | tcg/ia64/tcg-target.h | 156 | ||||
-rw-r--r-- | tcg/mips/tcg-target.c | 1533 | ||||
-rw-r--r-- | tcg/mips/tcg-target.h | 110 | ||||
-rw-r--r-- | tcg/ppc/tcg-target.c | 1922 | ||||
-rw-r--r-- | tcg/ppc/tcg-target.h | 98 | ||||
-rw-r--r-- | tcg/ppc64/tcg-target.c | 1699 | ||||
-rw-r--r-- | tcg/ppc64/tcg-target.h | 109 | ||||
-rw-r--r-- | tcg/s390/tcg-target.c | 2324 | ||||
-rw-r--r-- | tcg/s390/tcg-target.h | 109 | ||||
-rw-r--r-- | tcg/sparc/tcg-target.c | 1569 | ||||
-rw-r--r-- | tcg/sparc/tcg-target.h | 150 | ||||
-rw-r--r-- | tcg/tcg-op.h | 2533 | ||||
-rw-r--r-- | tcg/tcg-opc.h | 310 | ||||
-rw-r--r-- | tcg/tcg-runtime.h | 18 | ||||
-rw-r--r-- | tcg/tcg.c | 2175 | ||||
-rw-r--r-- | tcg/tcg.h | 489 |
26 files changed, 24068 insertions, 0 deletions
diff --git a/tcg/LICENSE b/tcg/LICENSE new file mode 100644 index 0000000..be817fa --- /dev/null +++ b/tcg/LICENSE @@ -0,0 +1,3 @@ +All the files in this directory and subdirectories are released under +a BSD like license (see header in each file). No other license is +accepted. diff --git a/tcg/README b/tcg/README new file mode 100644 index 0000000..6600122 --- /dev/null +++ b/tcg/README @@ -0,0 +1,511 @@ +Tiny Code Generator - Fabrice Bellard. + +1) Introduction + +TCG (Tiny Code Generator) began as a generic backend for a C +compiler. It was simplified to be used in QEMU. It also has its roots +in the QOP code generator written by Paul Brook. + +2) Definitions + +The TCG "target" is the architecture for which we generate the +code. It is of course not the same as the "target" of QEMU which is +the emulated architecture. As TCG started as a generic C backend used +for cross compiling, it is assumed that the TCG target is different +from the host, although it is never the case for QEMU. + +A TCG "function" corresponds to a QEMU Translated Block (TB). + +A TCG "temporary" is a variable only live in a basic +block. Temporaries are allocated explicitly in each function. + +A TCG "local temporary" is a variable only live in a function. Local +temporaries are allocated explicitly in each function. + +A TCG "global" is a variable which is live in all the functions +(equivalent of a C global variable). They are defined before the +functions defined. A TCG global can be a memory location (e.g. a QEMU +CPU register), a fixed host register (e.g. the QEMU CPU state pointer) +or a memory location which is stored in a register outside QEMU TBs +(not implemented yet). + +A TCG "basic block" corresponds to a list of instructions terminated +by a branch instruction. + +3) Intermediate representation + +3.1) Introduction + +TCG instructions operate on variables which are temporaries, local +temporaries or globals. TCG instructions and variables are strongly +typed. Two types are supported: 32 bit integers and 64 bit +integers. Pointers are defined as an alias to 32 bit or 64 bit +integers depending on the TCG target word size. + +Each instruction has a fixed number of output variable operands, input +variable operands and always constant operands. + +The notable exception is the call instruction which has a variable +number of outputs and inputs. + +In the textual form, output operands usually come first, followed by +input operands, followed by constant operands. The output type is +included in the instruction name. Constants are prefixed with a '$'. + +add_i32 t0, t1, t2 (t0 <- t1 + t2) + +3.2) Assumptions + +* Basic blocks + +- Basic blocks end after branches (e.g. brcond_i32 instruction), + goto_tb and exit_tb instructions. +- Basic blocks start after the end of a previous basic block, or at a + set_label instruction. + +After the end of a basic block, the content of temporaries is +destroyed, but local temporaries and globals are preserved. + +* Floating point types are not supported yet + +* Pointers: depending on the TCG target, pointer size is 32 bit or 64 + bit. The type TCG_TYPE_PTR is an alias to TCG_TYPE_I32 or + TCG_TYPE_I64. + +* Helpers: + +Using the tcg_gen_helper_x_y it is possible to call any function +taking i32, i64 or pointer types. By default, before calling a helper, +all globals are stored at their canonical location and it is assumed +that the function can modify them. This can be overridden by the +TCG_CALL_CONST function modifier. By default, the helper is allowed to +modify the CPU state or raise an exception. This can be overridden by +the TCG_CALL_PURE function modifier, in which case the call to the +function is removed if the return value is not used. + +On some TCG targets (e.g. x86), several calling conventions are +supported. + +* Branches: + +Use the instruction 'br' to jump to a label. Use 'jmp' to jump to an +explicit address. Conditional branches can only jump to labels. + +3.3) Code Optimizations + +When generating instructions, you can count on at least the following +optimizations: + +- Single instructions are simplified, e.g. + + and_i32 t0, t0, $0xffffffff + + is suppressed. + +- A liveness analysis is done at the basic block level. The + information is used to suppress moves from a dead variable to + another one. It is also used to remove instructions which compute + dead results. The later is especially useful for condition code + optimization in QEMU. + + In the following example: + + add_i32 t0, t1, t2 + add_i32 t0, t0, $1 + mov_i32 t0, $1 + + only the last instruction is kept. + +3.4) Instruction Reference + +********* Function call + +* call <ret> <params> ptr + +call function 'ptr' (pointer type) + +<ret> optional 32 bit or 64 bit return value +<params> optional 32 bit or 64 bit parameters + +********* Jumps/Labels + +* jmp t0 + +Absolute jump to address t0 (pointer type). + +* set_label $label + +Define label 'label' at the current program point. + +* br $label + +Jump to label. + +* brcond_i32/i64 cond, t0, t1, label + +Conditional jump if t0 cond t1 is true. cond can be: + TCG_COND_EQ + TCG_COND_NE + TCG_COND_LT /* signed */ + TCG_COND_GE /* signed */ + TCG_COND_LE /* signed */ + TCG_COND_GT /* signed */ + TCG_COND_LTU /* unsigned */ + TCG_COND_GEU /* unsigned */ + TCG_COND_LEU /* unsigned */ + TCG_COND_GTU /* unsigned */ + +********* Arithmetic + +* add_i32/i64 t0, t1, t2 + +t0=t1+t2 + +* sub_i32/i64 t0, t1, t2 + +t0=t1-t2 + +* neg_i32/i64 t0, t1 + +t0=-t1 (two's complement) + +* mul_i32/i64 t0, t1, t2 + +t0=t1*t2 + +* div_i32/i64 t0, t1, t2 + +t0=t1/t2 (signed). Undefined behavior if division by zero or overflow. + +* divu_i32/i64 t0, t1, t2 + +t0=t1/t2 (unsigned). Undefined behavior if division by zero. + +* rem_i32/i64 t0, t1, t2 + +t0=t1%t2 (signed). Undefined behavior if division by zero or overflow. + +* remu_i32/i64 t0, t1, t2 + +t0=t1%t2 (unsigned). Undefined behavior if division by zero. + +********* Logical + +* and_i32/i64 t0, t1, t2 + +t0=t1&t2 + +* or_i32/i64 t0, t1, t2 + +t0=t1|t2 + +* xor_i32/i64 t0, t1, t2 + +t0=t1^t2 + +* not_i32/i64 t0, t1 + +t0=~t1 + +* andc_i32/i64 t0, t1, t2 + +t0=t1&~t2 + +* eqv_i32/i64 t0, t1, t2 + +t0=~(t1^t2), or equivalently, t0=t1^~t2 + +* nand_i32/i64 t0, t1, t2 + +t0=~(t1&t2) + +* nor_i32/i64 t0, t1, t2 + +t0=~(t1|t2) + +* orc_i32/i64 t0, t1, t2 + +t0=t1|~t2 + +********* Shifts/Rotates + +* shl_i32/i64 t0, t1, t2 + +t0=t1 << t2. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64) + +* shr_i32/i64 t0, t1, t2 + +t0=t1 >> t2 (unsigned). Undefined behavior if t2 < 0 or t2 >= 32 (resp 64) + +* sar_i32/i64 t0, t1, t2 + +t0=t1 >> t2 (signed). Undefined behavior if t2 < 0 or t2 >= 32 (resp 64) + +* rotl_i32/i64 t0, t1, t2 + +Rotation of t2 bits to the left. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64) + +* rotr_i32/i64 t0, t1, t2 + +Rotation of t2 bits to the right. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64) + +********* Misc + +* mov_i32/i64 t0, t1 + +t0 = t1 + +Move t1 to t0 (both operands must have the same type). + +* ext8s_i32/i64 t0, t1 +ext8u_i32/i64 t0, t1 +ext16s_i32/i64 t0, t1 +ext16u_i32/i64 t0, t1 +ext32s_i64 t0, t1 +ext32u_i64 t0, t1 + +8, 16 or 32 bit sign/zero extension (both operands must have the same type) + +* bswap16_i32/i64 t0, t1 + +16 bit byte swap on a 32/64 bit value. It assumes that the two/six high order +bytes are set to zero. + +* bswap32_i32/i64 t0, t1 + +32 bit byte swap on a 32/64 bit value. With a 64 bit value, it assumes that +the four high order bytes are set to zero. + +* bswap64_i64 t0, t1 + +64 bit byte swap + +* discard_i32/i64 t0 + +Indicate that the value of t0 won't be used later. It is useful to +force dead code elimination. + +* deposit_i32/i64 dest, t1, t2, pos, len + +Deposit T2 as a bitfield into T1, placing the result in DEST. +The bitfield is described by POS/LEN, which are immediate values: + + LEN - the length of the bitfield + POS - the position of the first bit, counting from the LSB + +For example, pos=8, len=4 indicates a 4-bit field at bit 8. +This operation would be equivalent to + + dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00) + + +********* Conditional moves + +* setcond_i32/i64 cond, dest, t1, t2 + +dest = (t1 cond t2) + +Set DEST to 1 if (T1 cond T2) is true, otherwise set to 0. + +********* Type conversions + +* ext_i32_i64 t0, t1 +Convert t1 (32 bit) to t0 (64 bit) and does sign extension + +* extu_i32_i64 t0, t1 +Convert t1 (32 bit) to t0 (64 bit) and does zero extension + +* trunc_i64_i32 t0, t1 +Truncate t1 (64 bit) to t0 (32 bit) + +* concat_i32_i64 t0, t1, t2 +Construct t0 (64-bit) taking the low half from t1 (32 bit) and the high half +from t2 (32 bit). + +* concat32_i64 t0, t1, t2 +Construct t0 (64-bit) taking the low half from t1 (64 bit) and the high half +from t2 (64 bit). + +********* Load/Store + +* ld_i32/i64 t0, t1, offset +ld8s_i32/i64 t0, t1, offset +ld8u_i32/i64 t0, t1, offset +ld16s_i32/i64 t0, t1, offset +ld16u_i32/i64 t0, t1, offset +ld32s_i64 t0, t1, offset +ld32u_i64 t0, t1, offset + +t0 = read(t1 + offset) +Load 8, 16, 32 or 64 bits with or without sign extension from host memory. +offset must be a constant. + +* st_i32/i64 t0, t1, offset +st8_i32/i64 t0, t1, offset +st16_i32/i64 t0, t1, offset +st32_i64 t0, t1, offset + +write(t0, t1 + offset) +Write 8, 16, 32 or 64 bits to host memory. + +********* 64-bit target on 32-bit host support + +The following opcodes are internal to TCG. Thus they are to be implemented by +32-bit host code generators, but are not to be emitted by guest translators. +They are emitted as needed by inline functions within "tcg-op.h". + +* brcond2_i32 cond, t0_low, t0_high, t1_low, t1_high, label + +Similar to brcond, except that the 64-bit values T0 and T1 +are formed from two 32-bit arguments. + +* add2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high +* sub2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high + +Similar to add/sub, except that the 64-bit inputs T1 and T2 are +formed from two 32-bit arguments, and the 64-bit output T0 +is returned in two 32-bit outputs. + +* mulu2_i32 t0_low, t0_high, t1, t2 + +Similar to mul, except two 32-bit (unsigned) inputs T1 and T2 yielding +the full 64-bit product T0. The later is returned in two 32-bit outputs. + +* setcond2_i32 cond, dest, t1_low, t1_high, t2_low, t2_high + +Similar to setcond, except that the 64-bit values T1 and T2 are +formed from two 32-bit arguments. The result is a 32-bit value. + +********* QEMU specific operations + +* exit_tb t0 + +Exit the current TB and return the value t0 (word type). + +* goto_tb index + +Exit the current TB and jump to the TB index 'index' (constant) if the +current TB was linked to this TB. Otherwise execute the next +instructions. + +* qemu_ld8u t0, t1, flags +qemu_ld8s t0, t1, flags +qemu_ld16u t0, t1, flags +qemu_ld16s t0, t1, flags +qemu_ld32 t0, t1, flags +qemu_ld32u t0, t1, flags +qemu_ld32s t0, t1, flags +qemu_ld64 t0, t1, flags + +Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU address +type. 'flags' contains the QEMU memory index (selects user or kernel access) +for example. + +Note that "qemu_ld32" implies a 32-bit result, while "qemu_ld32u" and +"qemu_ld32s" imply a 64-bit result appropriately extended from 32 bits. + +* qemu_st8 t0, t1, flags +qemu_st16 t0, t1, flags +qemu_st32 t0, t1, flags +qemu_st64 t0, t1, flags + +Store the data t0 at the QEMU CPU Address t1. t1 has the QEMU CPU +address type. 'flags' contains the QEMU memory index (selects user or +kernel access) for example. + +Note 1: Some shortcuts are defined when the last operand is known to be +a constant (e.g. addi for add, movi for mov). + +Note 2: When using TCG, the opcodes must never be generated directly +as some of them may not be available as "real" opcodes. Always use the +function tcg_gen_xxx(args). + +4) Backend + +tcg-target.h contains the target specific definitions. tcg-target.c +contains the target specific code. + +4.1) Assumptions + +The target word size (TCG_TARGET_REG_BITS) is expected to be 32 bit or +64 bit. It is expected that the pointer has the same size as the word. + +On a 32 bit target, all 64 bit operations are converted to 32 bits. A +few specific operations must be implemented to allow it (see add2_i32, +sub2_i32, brcond2_i32). + +Floating point operations are not supported in this version. A +previous incarnation of the code generator had full support of them, +but it is better to concentrate on integer operations first. + +On a 64 bit target, no assumption is made in TCG about the storage of +the 32 bit values in 64 bit registers. + +4.2) Constraints + +GCC like constraints are used to define the constraints of every +instruction. Memory constraints are not supported in this +version. Aliases are specified in the input operands as for GCC. + +The same register may be used for both an input and an output, even when +they are not explicitly aliased. If an op expands to multiple target +instructions then care must be taken to avoid clobbering input values. +GCC style "early clobber" outputs are not currently supported. + +A target can define specific register or constant constraints. If an +operation uses a constant input constraint which does not allow all +constants, it must also accept registers in order to have a fallback. + +The movi_i32 and movi_i64 operations must accept any constants. + +The mov_i32 and mov_i64 operations must accept any registers of the +same type. + +The ld/st instructions must accept signed 32 bit constant offsets. It +can be implemented by reserving a specific register to compute the +address if the offset is too big. + +The ld/st instructions must accept any destination (ld) or source (st) +register. + +4.3) Function call assumptions + +- The only supported types for parameters and return value are: 32 and + 64 bit integers and pointer. +- The stack grows downwards. +- The first N parameters are passed in registers. +- The next parameters are passed on the stack by storing them as words. +- Some registers are clobbered during the call. +- The function can return 0 or 1 value in registers. On a 32 bit + target, functions must be able to return 2 values in registers for + 64 bit return type. + +5) Recommended coding rules for best performance + +- Use globals to represent the parts of the QEMU CPU state which are + often modified, e.g. the integer registers and the condition + codes. TCG will be able to use host registers to store them. + +- Avoid globals stored in fixed registers. They must be used only to + store the pointer to the CPU state and possibly to store a pointer + to a register window. + +- Use temporaries. Use local temporaries only when really needed, + e.g. when you need to use a value after a jump. Local temporaries + introduce a performance hit in the current TCG implementation: their + content is saved to memory at end of each basic block. + +- Free temporaries and local temporaries when they are no longer used + (tcg_temp_free). Since tcg_const_x() also creates a temporary, you + should free it after it is used. Freeing temporaries does not yield + a better generated code, but it reduces the memory usage of TCG and + the speed of the translation. + +- Don't hesitate to use helpers for complicated or seldom used target + instructions. There is little performance advantage in using TCG to + implement target instructions taking more than about twenty TCG + instructions. + +- Use the 'discard' instruction if you know that TCG won't be able to + prove that a given global is "dead" at a given program point. The + x86 target uses it to improve the condition codes optimisation. diff --git a/tcg/TODO b/tcg/TODO new file mode 100644 index 0000000..0747847 --- /dev/null +++ b/tcg/TODO @@ -0,0 +1,14 @@ +- Add new instructions such as: clz, ctz, popcnt. + +- See if it is worth exporting mul2, mulu2, div2, divu2. + +- Support of globals saved in fixed registers between TBs. + +Ideas: + +- Move the slow part of the qemu_ld/st ops after the end of the TB. + +- Change exception syntax to get closer to QOP system (exception + parameters given with a specific instruction). + +- Add float and vector support. diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c new file mode 100644 index 0000000..918e2f7 --- /dev/null +++ b/tcg/arm/tcg-target.c @@ -0,0 +1,1843 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Andrzej Zaborowski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || \ + defined(__ARM_ARCH_7EM__) || \ + defined(__ARM_ARCH_7M__) || \ + defined(__ARM_ARCH_7R__) +#define USE_ARMV7_INSTRUCTIONS +#endif + +#if defined(USE_ARMV7_INSTRUCTIONS) || \ + defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) +#define USE_ARMV6_INSTRUCTIONS +#endif + +#if defined(USE_ARMV6_INSTRUCTIONS) || \ + defined(__ARM_ARCH_5T__) || \ + defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) +#define USE_ARMV5_INSTRUCTIONS +#endif + +#ifdef USE_ARMV5_INSTRUCTIONS +static const int use_armv5_instructions = 1; +#else +static const int use_armv5_instructions = 0; +#endif +#undef USE_ARMV5_INSTRUCTIONS + +#ifdef USE_ARMV6_INSTRUCTIONS +static const int use_armv6_instructions = 1; +#else +static const int use_armv6_instructions = 0; +#endif +#undef USE_ARMV6_INSTRUCTIONS + +#ifdef USE_ARMV7_INSTRUCTIONS +static const int use_armv7_instructions = 1; +#else +static const int use_armv7_instructions = 0; +#endif +#undef USE_ARMV7_INSTRUCTIONS + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%r0", + "%r1", + "%r2", + "%r3", + "%r4", + "%r5", + "%r6", + "%r7", + "%r8", + "%r9", + "%r10", + "%r11", + "%r12", + "%r13", + "%r14", + "%pc", +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R13, + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R12, + TCG_REG_R14, +}; + +static const int tcg_target_call_iarg_regs[4] = { + TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3 +}; +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_R0, TCG_REG_R1 +}; + +static inline void reloc_abs32(void *code_ptr, tcg_target_long target) +{ + *(uint32_t *) code_ptr = target; +} + +static inline void reloc_pc24(void *code_ptr, tcg_target_long target) +{ + uint32_t offset = ((target - ((tcg_target_long) code_ptr + 8)) >> 2); + + *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & ~0xffffff) + | (offset & 0xffffff); +} + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + switch (type) { + case R_ARM_ABS32: + reloc_abs32(code_ptr, value); + break; + + case R_ARM_CALL: + case R_ARM_JUMP24: + default: + tcg_abort(); + + case R_ARM_PC24: + reloc_pc24(code_ptr, value); + break; + } +} + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + return 4; +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'I': + ct->ct |= TCG_CT_CONST_ARM; + break; + + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); + break; + + /* qemu_ld address */ + case 'l': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); +#ifdef CONFIG_SOFTMMU + /* r0 and r1 will be overwritten when reading the tlb entry, + so don't use these. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#endif + break; + case 'L': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); +#ifdef CONFIG_SOFTMMU + /* r1 is still needed to load data_reg or data_reg2, + so don't use it. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#endif + break; + + /* qemu_st address & data_reg */ + case 's': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); + /* r0 and r1 will be overwritten when reading the tlb entry + (softmmu only) and doing the byte swapping, so don't + use these. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); + break; + /* qemu_st64 data_reg2 */ + case 'S': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); + /* r0 and r1 will be overwritten when reading the tlb entry + (softmmu only) and doing the byte swapping, so don't + use these. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#ifdef CONFIG_SOFTMMU + /* r2 is still needed to load data_reg, so don't use it. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); +#endif + break; + + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + + return 0; +} + +static inline uint32_t rotl(uint32_t val, int n) +{ + return (val << n) | (val >> (32 - n)); +} + +/* ARM immediates for ALU instructions are made of an unsigned 8-bit + right-rotated by an even amount between 0 and 30. */ +static inline int encode_imm(uint32_t imm) +{ + int shift; + + /* simple case, only lower bits */ + if ((imm & ~0xff) == 0) + return 0; + /* then try a simple even shift */ + shift = ctz32(imm) & ~1; + if (((imm >> shift) & ~0xff) == 0) + return 32 - shift; + /* now try harder with rotations */ + if ((rotl(imm, 2) & ~0xff) == 0) + return 2; + if ((rotl(imm, 4) & ~0xff) == 0) + return 4; + if ((rotl(imm, 6) & ~0xff) == 0) + return 6; + /* imm can't be encoded */ + return -1; +} + +static inline int check_fit_imm(uint32_t imm) +{ + return encode_imm(imm) >= 0; +} + +/* Test if a constant matches the constraint. + * TODO: define constraints for: + * + * ldr/str offset: between -0xfff and 0xfff + * ldrh/strh offset: between -0xff and 0xff + * mov operand2: values represented with x << (2 * y), x < 0x100 + * add, sub, eor...: ditto + */ +static inline int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_ARM) && check_fit_imm(val)) + return 1; + else + return 0; +} + +enum arm_data_opc_e { + ARITH_AND = 0x0, + ARITH_EOR = 0x1, + ARITH_SUB = 0x2, + ARITH_RSB = 0x3, + ARITH_ADD = 0x4, + ARITH_ADC = 0x5, + ARITH_SBC = 0x6, + ARITH_RSC = 0x7, + ARITH_TST = 0x8, + ARITH_CMP = 0xa, + ARITH_CMN = 0xb, + ARITH_ORR = 0xc, + ARITH_MOV = 0xd, + ARITH_BIC = 0xe, + ARITH_MVN = 0xf, +}; + +#define TO_CPSR(opc) \ + ((opc == ARITH_CMP || opc == ARITH_CMN || opc == ARITH_TST) << 20) + +#define SHIFT_IMM_LSL(im) (((im) << 7) | 0x00) +#define SHIFT_IMM_LSR(im) (((im) << 7) | 0x20) +#define SHIFT_IMM_ASR(im) (((im) << 7) | 0x40) +#define SHIFT_IMM_ROR(im) (((im) << 7) | 0x60) +#define SHIFT_REG_LSL(rs) (((rs) << 8) | 0x10) +#define SHIFT_REG_LSR(rs) (((rs) << 8) | 0x30) +#define SHIFT_REG_ASR(rs) (((rs) << 8) | 0x50) +#define SHIFT_REG_ROR(rs) (((rs) << 8) | 0x70) + +enum arm_cond_code_e { + COND_EQ = 0x0, + COND_NE = 0x1, + COND_CS = 0x2, /* Unsigned greater or equal */ + COND_CC = 0x3, /* Unsigned less than */ + COND_MI = 0x4, /* Negative */ + COND_PL = 0x5, /* Zero or greater */ + COND_VS = 0x6, /* Overflow */ + COND_VC = 0x7, /* No overflow */ + COND_HI = 0x8, /* Unsigned greater than */ + COND_LS = 0x9, /* Unsigned less or equal */ + COND_GE = 0xa, + COND_LT = 0xb, + COND_GT = 0xc, + COND_LE = 0xd, + COND_AL = 0xe, +}; + +static const uint8_t tcg_cond_to_arm_cond[10] = { + [TCG_COND_EQ] = COND_EQ, + [TCG_COND_NE] = COND_NE, + [TCG_COND_LT] = COND_LT, + [TCG_COND_GE] = COND_GE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_GT, + /* unsigned */ + [TCG_COND_LTU] = COND_CC, + [TCG_COND_GEU] = COND_CS, + [TCG_COND_LEU] = COND_LS, + [TCG_COND_GTU] = COND_HI, +}; + +static inline void tcg_out_bx(TCGContext *s, int cond, int rn) +{ + tcg_out32(s, (cond << 28) | 0x012fff10 | rn); +} + +static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset) +{ + tcg_out32(s, (cond << 28) | 0x0a000000 | + (((offset - 8) >> 2) & 0x00ffffff)); +} + +static inline void tcg_out_b_noaddr(TCGContext *s, int cond) +{ + /* We pay attention here to not modify the branch target by skipping + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ +#ifdef HOST_WORDS_BIGENDIAN + tcg_out8(s, (cond << 4) | 0x0a); + s->code_ptr += 3; +#else + s->code_ptr += 3; + tcg_out8(s, (cond << 4) | 0x0a); +#endif +} + +static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset) +{ + tcg_out32(s, (cond << 28) | 0x0b000000 | + (((offset - 8) >> 2) & 0x00ffffff)); +} + +static inline void tcg_out_blx(TCGContext *s, int cond, int rn) +{ + tcg_out32(s, (cond << 28) | 0x012fff30 | rn); +} + +static inline void tcg_out_dat_reg(TCGContext *s, + int cond, int opc, int rd, int rn, int rm, int shift) +{ + tcg_out32(s, (cond << 28) | (0 << 25) | (opc << 21) | TO_CPSR(opc) | + (rn << 16) | (rd << 12) | shift | rm); +} + +static inline void tcg_out_dat_reg2(TCGContext *s, + int cond, int opc0, int opc1, int rd0, int rd1, + int rn0, int rn1, int rm0, int rm1, int shift) +{ + if (rd0 == rn1 || rd0 == rm1) { + tcg_out32(s, (cond << 28) | (0 << 25) | (opc0 << 21) | (1 << 20) | + (rn0 << 16) | (8 << 12) | shift | rm0); + tcg_out32(s, (cond << 28) | (0 << 25) | (opc1 << 21) | + (rn1 << 16) | (rd1 << 12) | shift | rm1); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd0, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); + } else { + tcg_out32(s, (cond << 28) | (0 << 25) | (opc0 << 21) | (1 << 20) | + (rn0 << 16) | (rd0 << 12) | shift | rm0); + tcg_out32(s, (cond << 28) | (0 << 25) | (opc1 << 21) | + (rn1 << 16) | (rd1 << 12) | shift | rm1); + } +} + +static inline void tcg_out_dat_imm(TCGContext *s, + int cond, int opc, int rd, int rn, int im) +{ + tcg_out32(s, (cond << 28) | (1 << 25) | (opc << 21) | TO_CPSR(opc) | + (rn << 16) | (rd << 12) | im); +} + +static inline void tcg_out_movi32(TCGContext *s, + int cond, int rd, uint32_t arg) +{ + /* TODO: This is very suboptimal, we can easily have a constant + * pool somewhere after all the instructions. */ + if ((int)arg < 0 && (int)arg >= -0x100) { + tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff); + } else if (use_armv7_instructions) { + /* use movw/movt */ + /* movw */ + tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) + | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); + if (arg & 0xffff0000) { + /* movt */ + tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) + | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); + } + } else { + int opc = ARITH_MOV; + int rn = 0; + + do { + int i, rot; + + i = ctz32(arg) & ~1; + rot = ((32 - i) << 7) & 0xf00; + tcg_out_dat_imm(s, cond, opc, rd, rn, ((arg >> i) & 0xff) | rot); + arg &= ~(0xff << i); + + opc = ARITH_ORR; + rn = rd; + } while (arg); + } +} + +static inline void tcg_out_mul32(TCGContext *s, + int cond, int rd, int rs, int rm) +{ + if (rd != rm) + tcg_out32(s, (cond << 28) | (rd << 16) | (0 << 12) | + (rs << 8) | 0x90 | rm); + else if (rd != rs) + tcg_out32(s, (cond << 28) | (rd << 16) | (0 << 12) | + (rm << 8) | 0x90 | rs); + else { + tcg_out32(s, (cond << 28) | ( 8 << 16) | (0 << 12) | + (rs << 8) | 0x90 | rm); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); + } +} + +static inline void tcg_out_umull32(TCGContext *s, + int cond, int rd0, int rd1, int rs, int rm) +{ + if (rd0 != rm && rd1 != rm) + tcg_out32(s, (cond << 28) | 0x800090 | + (rd1 << 16) | (rd0 << 12) | (rs << 8) | rm); + else if (rd0 != rs && rd1 != rs) + tcg_out32(s, (cond << 28) | 0x800090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rs); + else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rm, SHIFT_IMM_LSL(0)); + tcg_out32(s, (cond << 28) | 0x800098 | + (rd1 << 16) | (rd0 << 12) | (rs << 8)); + } +} + +static inline void tcg_out_smull32(TCGContext *s, + int cond, int rd0, int rd1, int rs, int rm) +{ + if (rd0 != rm && rd1 != rm) + tcg_out32(s, (cond << 28) | 0xc00090 | + (rd1 << 16) | (rd0 << 12) | (rs << 8) | rm); + else if (rd0 != rs && rd1 != rs) + tcg_out32(s, (cond << 28) | 0xc00090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rs); + else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rm, SHIFT_IMM_LSL(0)); + tcg_out32(s, (cond << 28) | 0xc00098 | + (rd1 << 16) | (rd0 << 12) | (rs << 8)); + } +} + +static inline void tcg_out_ext8s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxtb */ + tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(24)); + } +} + +static inline void tcg_out_ext8u(TCGContext *s, int cond, + int rd, int rn) +{ + tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff); +} + +static inline void tcg_out_ext16s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxth */ + tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(16)); + } +} + +static inline void tcg_out_ext16u(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_LSR(16)); + } +} + +static inline void tcg_out_bswap16s(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_ASR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap16(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_LSR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev */ + tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_EOR, + TCG_REG_R8, rn, rn, SHIFT_IMM_ROR(16)); + tcg_out_dat_imm(s, cond, ARITH_BIC, + TCG_REG_R8, TCG_REG_R8, 0xff | 0x800); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_ROR(8)); + tcg_out_dat_reg(s, cond, ARITH_EOR, + rd, rd, TCG_REG_R8, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_ld32_12(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x05900000 | + (rn << 16) | (rd << 12) | (im & 0xfff)); + else + tcg_out32(s, (cond << 28) | 0x05100000 | + (rn << 16) | (rd << 12) | ((-im) & 0xfff)); +} + +static inline void tcg_out_st32_12(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x05800000 | + (rn << 16) | (rd << 12) | (im & 0xfff)); + else + tcg_out32(s, (cond << 28) | 0x05000000 | + (rn << 16) | (rd << 12) | ((-im) & 0xfff)); +} + +static inline void tcg_out_ld32_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x07900000 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_st32_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x07800000 | + (rn << 16) | (rd << 12) | rm); +} + +/* Register pre-increment with base writeback. */ +static inline void tcg_out_ld32_rwb(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x07b00000 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_st32_rwb(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x07a00000 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_ld16u_8(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x01d000b0 | + (rn << 16) | (rd << 12) | + ((im & 0xf0) << 4) | (im & 0xf)); + else + tcg_out32(s, (cond << 28) | 0x015000b0 | + (rn << 16) | (rd << 12) | + (((-im) & 0xf0) << 4) | ((-im) & 0xf)); +} + +static inline void tcg_out_st16_8(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x01c000b0 | + (rn << 16) | (rd << 12) | + ((im & 0xf0) << 4) | (im & 0xf)); + else + tcg_out32(s, (cond << 28) | 0x014000b0 | + (rn << 16) | (rd << 12) | + (((-im) & 0xf0) << 4) | ((-im) & 0xf)); +} + +static inline void tcg_out_ld16u_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x019000b0 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_st16_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x018000b0 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_ld16s_8(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x01d000f0 | + (rn << 16) | (rd << 12) | + ((im & 0xf0) << 4) | (im & 0xf)); + else + tcg_out32(s, (cond << 28) | 0x015000f0 | + (rn << 16) | (rd << 12) | + (((-im) & 0xf0) << 4) | ((-im) & 0xf)); +} + +static inline void tcg_out_ld16s_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x019000f0 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_ld8_12(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x05d00000 | + (rn << 16) | (rd << 12) | (im & 0xfff)); + else + tcg_out32(s, (cond << 28) | 0x05500000 | + (rn << 16) | (rd << 12) | ((-im) & 0xfff)); +} + +static inline void tcg_out_st8_12(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x05c00000 | + (rn << 16) | (rd << 12) | (im & 0xfff)); + else + tcg_out32(s, (cond << 28) | 0x05400000 | + (rn << 16) | (rd << 12) | ((-im) & 0xfff)); +} + +static inline void tcg_out_ld8_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x07d00000 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_st8_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x07c00000 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_ld8s_8(TCGContext *s, int cond, + int rd, int rn, tcg_target_long im) +{ + if (im >= 0) + tcg_out32(s, (cond << 28) | 0x01d000d0 | + (rn << 16) | (rd << 12) | + ((im & 0xf0) << 4) | (im & 0xf)); + else + tcg_out32(s, (cond << 28) | 0x015000d0 | + (rn << 16) | (rd << 12) | + (((-im) & 0xf0) << 4) | ((-im) & 0xf)); +} + +static inline void tcg_out_ld8s_r(TCGContext *s, int cond, + int rd, int rn, int rm) +{ + tcg_out32(s, (cond << 28) | 0x019000d0 | + (rn << 16) | (rd << 12) | rm); +} + +static inline void tcg_out_ld32u(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_ld32_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_ld32_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_st32(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_st32_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_st32_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld16u(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_ld16u_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld16s(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_ld16s_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_st16(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_st16_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_st16_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld8u(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_ld8_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_ld8s(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_ld8s_8(s, cond, rd, rn, offset); +} + +static inline void tcg_out_st8(TCGContext *s, int cond, + int rd, int rn, int32_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, cond, TCG_REG_R8, offset); + tcg_out_st8_r(s, cond, rd, rn, TCG_REG_R8); + } else + tcg_out_st8_12(s, cond, rd, rn, offset); +} + +static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr) +{ + int32_t val; + + val = addr - (tcg_target_long) s->code_ptr; + if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd) + tcg_out_b(s, cond, val); + else { +#if 1 + tcg_abort(); +#else + if (cond == COND_AL) { + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); + tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */ + } else { + tcg_out_movi32(s, cond, TCG_REG_R8, val - 8); + tcg_out_dat_reg(s, cond, ARITH_ADD, + TCG_REG_PC, TCG_REG_PC, + TCG_REG_R8, SHIFT_IMM_LSL(0)); + } +#endif + } +} + +static inline void tcg_out_call(TCGContext *s, int cond, uint32_t addr) +{ + int32_t val; + + val = addr - (tcg_target_long) s->code_ptr; + if (val < 0x01fffffd && val > -0x01fffffd) + tcg_out_bl(s, cond, val); + else { +#if 1 + tcg_abort(); +#else + if (cond == COND_AL) { + tcg_out_dat_imm(s, cond, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); + tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */ + } else { + tcg_out_movi32(s, cond, TCG_REG_R9, addr); + tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0, + TCG_REG_PC, SHIFT_IMM_LSL(0)); + tcg_out_bx(s, cond, TCG_REG_R9); + } +#endif + } +} + +static inline void tcg_out_callr(TCGContext *s, int cond, int arg) +{ + if (use_armv5_instructions) { + tcg_out_blx(s, cond, arg); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0, + TCG_REG_PC, SHIFT_IMM_LSL(0)); + tcg_out_bx(s, cond, arg); + } +} + +static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) + tcg_out_goto(s, cond, l->u.value); + else if (cond == COND_AL) { + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); + tcg_out_reloc(s, s->code_ptr, R_ARM_ABS32, label_index, 31337); + s->code_ptr += 4; + } else { + /* Probably this should be preferred even for COND_AL... */ + tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, label_index, 31337); + tcg_out_b_noaddr(s, cond); + } +} + +#ifdef CONFIG_SOFTMMU + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; +#endif + +#define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, data_reg2, bswap; +#ifdef CONFIG_SOFTMMU + int mem_index, s_bits; +# if TARGET_LONG_BITS == 64 + int addr_reg2; +# endif + uint32_t *label_ptr; +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + data_reg = *args++; + if (opc == 3) + data_reg2 = *args++; + else + data_reg2 = 0; /* suppress warning */ + addr_reg = *args++; +#ifdef CONFIG_SOFTMMU +# if TARGET_LONG_BITS == 64 + addr_reg2 = *args++; +# endif + mem_index = *args; + s_bits = opc & 3; + + /* Should generate something like the following: + * shr r8, addr_reg, #TARGET_PAGE_BITS + * and r0, r8, #(CPU_TLB_SIZE - 1) @ Assumption: CPU_TLB_BITS <= 8 + * add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS + */ +# if CPU_TLB_BITS > 8 +# error +# endif + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_R8, + 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + tcg_out_dat_imm(s, COND_AL, ARITH_AND, + TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_AREG0, + TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + /* In the + * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_read))] + * below, the offset is likely to exceed 12 bits if mem_index != 0 and + * not exceed otherwise, so use an + * add r0, r0, #(mem_index * sizeof *CPUState.tlb_table) + * before. + */ + if (mem_index) + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, + (mem_index << (TLB_SHIFT & 1)) | + ((16 - (TLB_SHIFT >> 1)) << 8)); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addr_read)); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1, + TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + /* Check alignment. */ + if (s_bits) + tcg_out_dat_imm(s, COND_EQ, ARITH_TST, + 0, addr_reg, (1 << s_bits) - 1); +# if TARGET_LONG_BITS == 64 + /* XXX: possibly we could use a block data load or writeback in + * the first access. */ + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addr_read) + 4); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0)); +# endif + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addend)); + + switch (opc) { + case 0: + tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + break; + case 0 | 4: + tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + break; + case 1: + tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + if (bswap) { + tcg_out_bswap16(s, COND_EQ, data_reg, data_reg); + } + break; + case 1 | 4: + if (bswap) { + tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + tcg_out_bswap16s(s, COND_EQ, data_reg, data_reg); + } else { + tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } + break; + case 2: + default: + tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, data_reg, data_reg); + } + break; + case 3: + if (bswap) { + tcg_out_ld32_rwb(s, COND_EQ, data_reg2, TCG_REG_R1, addr_reg); + tcg_out_ld32_12(s, COND_EQ, data_reg, TCG_REG_R1, 4); + tcg_out_bswap32(s, COND_EQ, data_reg2, data_reg2); + tcg_out_bswap32(s, COND_EQ, data_reg, data_reg); + } else { + tcg_out_ld32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg); + tcg_out_ld32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4); + } + break; + } + + label_ptr = (void *) s->code_ptr; + tcg_out_b_noaddr(s, COND_EQ); + + /* TODO: move this code to where the constants pool will be */ + if (addr_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0)); + } +# if TARGET_LONG_BITS == 32 + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R1, 0, mem_index); +# else + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); +# endif + tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_ld_helpers[s_bits] - + (tcg_target_long) s->code_ptr); + + switch (opc) { + case 0 | 4: + tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0); + break; + case 1 | 4: + tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0); + break; + case 0: + case 1: + case 2: + default: + if (data_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0)); + } + break; + case 3: + if (data_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R1) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg2, 0, TCG_REG_R1, SHIFT_IMM_LSL(0)); + } + break; + } + + reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr); +#else /* !CONFIG_SOFTMMU */ + if (GUEST_BASE) { + uint32_t offset = GUEST_BASE; + int i; + int rot; + + while (offset) { + i = ctz32(offset) & ~1; + rot = ((32 - i) << 7) & 0xf00; + + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R8, addr_reg, + ((offset >> i) & 0xff) | rot); + addr_reg = TCG_REG_R8; + offset &= ~(0xff << i); + } + } + switch (opc) { + case 0: + tcg_out_ld8_12(s, COND_AL, data_reg, addr_reg, 0); + break; + case 0 | 4: + tcg_out_ld8s_8(s, COND_AL, data_reg, addr_reg, 0); + break; + case 1: + tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap16(s, COND_AL, data_reg, data_reg); + } + break; + case 1 | 4: + if (bswap) { + tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_bswap16s(s, COND_AL, data_reg, data_reg); + } else { + tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0); + } + break; + case 2: + default: + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap32(s, COND_AL, data_reg, data_reg); + } + break; + case 3: + /* TODO: use block load - + * check that data_reg2 > data_reg or the other way */ + if (data_reg == addr_reg) { + tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); + } else { + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); + tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); + } + if (bswap) { + tcg_out_bswap32(s, COND_AL, data_reg, data_reg); + tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2); + } + break; + } +#endif +} + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, data_reg2, bswap; +#ifdef CONFIG_SOFTMMU + int mem_index, s_bits; +# if TARGET_LONG_BITS == 64 + int addr_reg2; +# endif + uint32_t *label_ptr; +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + data_reg = *args++; + if (opc == 3) + data_reg2 = *args++; + else + data_reg2 = 0; /* suppress warning */ + addr_reg = *args++; +#ifdef CONFIG_SOFTMMU +# if TARGET_LONG_BITS == 64 + addr_reg2 = *args++; +# endif + mem_index = *args; + s_bits = opc & 3; + + /* Should generate something like the following: + * shr r8, addr_reg, #TARGET_PAGE_BITS + * and r0, r8, #(CPU_TLB_SIZE - 1) @ Assumption: CPU_TLB_BITS <= 8 + * add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS + */ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + tcg_out_dat_imm(s, COND_AL, ARITH_AND, + TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, + TCG_AREG0, TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + /* In the + * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_write))] + * below, the offset is likely to exceed 12 bits if mem_index != 0 and + * not exceed otherwise, so use an + * add r0, r0, #(mem_index * sizeof *CPUState.tlb_table) + * before. + */ + if (mem_index) + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, + (mem_index << (TLB_SHIFT & 1)) | + ((16 - (TLB_SHIFT >> 1)) << 8)); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addr_write)); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1, + TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + /* Check alignment. */ + if (s_bits) + tcg_out_dat_imm(s, COND_EQ, ARITH_TST, + 0, addr_reg, (1 << s_bits) - 1); +# if TARGET_LONG_BITS == 64 + /* XXX: possibly we could use a block data load or writeback in + * the first access. */ + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addr_write) + 4); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0)); +# endif + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addend)); + + switch (opc) { + case 0: + tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + break; + case 1: + if (bswap) { + tcg_out_bswap16(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st16_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1); + } else { + tcg_out_st16_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } + break; + case 2: + default: + if (bswap) { + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1); + } else { + tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } + break; + case 3: + if (bswap) { + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg2); + tcg_out_st32_rwb(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, addr_reg); + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, 4); + } else { + tcg_out_st32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg); + tcg_out_st32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4); + } + break; + } + + label_ptr = (void *) s->code_ptr; + tcg_out_b_noaddr(s, COND_EQ); + + /* TODO: move this code to where the constants pool will be */ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0)); +# if TARGET_LONG_BITS == 32 + switch (opc) { + case 0: + tcg_out_ext8u(s, COND_AL, TCG_REG_R1, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); + break; + case 1: + tcg_out_ext16u(s, COND_AL, TCG_REG_R1, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); + break; + case 2: + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, data_reg, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); + break; + case 3: + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index); + tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R3) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0)); + } + break; + } +# else + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0)); + switch (opc) { + case 0: + tcg_out_ext8u(s, COND_AL, TCG_REG_R2, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); + break; + case 1: + tcg_out_ext16u(s, COND_AL, TCG_REG_R2, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); + break; + case 2: + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); + break; + case 3: + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index); + tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R3) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0)); + } + break; + } +# endif + + tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_st_helpers[s_bits] - + (tcg_target_long) s->code_ptr); + if (opc == 3) + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R13, TCG_REG_R13, 0x10); + + reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr); +#else /* !CONFIG_SOFTMMU */ + if (GUEST_BASE) { + uint32_t offset = GUEST_BASE; + int i; + int rot; + + while (offset) { + i = ctz32(offset) & ~1; + rot = ((32 - i) << 7) & 0xf00; + + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R1, addr_reg, + ((offset >> i) & 0xff) | rot); + addr_reg = TCG_REG_R1; + offset &= ~(0xff << i); + } + } + switch (opc) { + case 0: + tcg_out_st8_12(s, COND_AL, data_reg, addr_reg, 0); + break; + case 1: + if (bswap) { + tcg_out_bswap16(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st16_8(s, COND_AL, TCG_REG_R0, addr_reg, 0); + } else { + tcg_out_st16_8(s, COND_AL, data_reg, addr_reg, 0); + } + break; + case 2: + default: + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); + } else { + tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + } + break; + case 3: + /* TODO: use block store - + * check that data_reg2 > data_reg or the other way */ + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 4); + } else { + tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4); + } + break; + } +#endif +} + +static uint8_t *tb_ret_addr; + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + int c; + + switch (opc) { + case INDEX_op_exit_tb: + { + uint8_t *ld_ptr = s->code_ptr; + if (args[0] >> 8) + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0); + else + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]); + tcg_out_goto(s, COND_AL, (tcg_target_ulong) tb_ret_addr); + if (args[0] >> 8) { + *ld_ptr = (uint8_t) (s->code_ptr - ld_ptr) - 8; + tcg_out32(s, args[0]); + } + } + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* Direct jump method */ +#if defined(USE_DIRECT_JUMP) + s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; + tcg_out_b_noaddr(s, COND_AL); +#else + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); + s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; + tcg_out32(s, 0); +#endif + } else { + /* Indirect jump method */ +#if 1 + c = (int) (s->tb_next + args[0]) - ((int) s->code_ptr + 8); + if (c > 0xfff || c < -0xfff) { + tcg_out_movi32(s, COND_AL, TCG_REG_R0, + (tcg_target_long) (s->tb_next + args[0])); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0); + } else + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, c); +#else + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0); + tcg_out32(s, (tcg_target_long) (s->tb_next + args[0])); +#endif + } + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + case INDEX_op_call: + if (const_args[0]) + tcg_out_call(s, COND_AL, args[0]); + else + tcg_out_callr(s, COND_AL, args[0]); + break; + case INDEX_op_jmp: + if (const_args[0]) + tcg_out_goto(s, COND_AL, args[0]); + else + tcg_out_bx(s, COND_AL, args[0]); + break; + case INDEX_op_br: + tcg_out_goto_label(s, COND_AL, args[0]); + break; + + case INDEX_op_ld8u_i32: + tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld16u_i32: + tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i32: + tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); + break; + + case INDEX_op_mov_i32: + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + args[0], 0, args[1], SHIFT_IMM_LSL(0)); + break; + case INDEX_op_movi_i32: + tcg_out_movi32(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_add_i32: + c = ARITH_ADD; + goto gen_arith; + case INDEX_op_sub_i32: + c = ARITH_SUB; + goto gen_arith; + case INDEX_op_and_i32: + c = ARITH_AND; + goto gen_arith; + case INDEX_op_andc_i32: + c = ARITH_BIC; + goto gen_arith; + case INDEX_op_or_i32: + c = ARITH_ORR; + goto gen_arith; + case INDEX_op_xor_i32: + c = ARITH_EOR; + /* Fall through. */ + gen_arith: + if (const_args[2]) { + int rot; + rot = encode_imm(args[2]); + tcg_out_dat_imm(s, COND_AL, c, + args[0], args[1], rotl(args[2], rot) | (rot << 7)); + } else + tcg_out_dat_reg(s, COND_AL, c, + args[0], args[1], args[2], SHIFT_IMM_LSL(0)); + break; + case INDEX_op_add2_i32: + tcg_out_dat_reg2(s, COND_AL, ARITH_ADD, ARITH_ADC, + args[0], args[1], args[2], args[3], + args[4], args[5], SHIFT_IMM_LSL(0)); + break; + case INDEX_op_sub2_i32: + tcg_out_dat_reg2(s, COND_AL, ARITH_SUB, ARITH_SBC, + args[0], args[1], args[2], args[3], + args[4], args[5], SHIFT_IMM_LSL(0)); + break; + case INDEX_op_neg_i32: + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0); + break; + case INDEX_op_not_i32: + tcg_out_dat_reg(s, COND_AL, + ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0)); + break; + case INDEX_op_mul_i32: + tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]); + break; + case INDEX_op_mulu2_i32: + tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); + break; + /* XXX: Perhaps args[2] & 0x1f is wrong */ + case INDEX_op_shl_i32: + c = const_args[2] ? + SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]); + goto gen_shift32; + case INDEX_op_shr_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]); + goto gen_shift32; + case INDEX_op_sar_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); + goto gen_shift32; + case INDEX_op_rotr_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); + /* Fall through. */ + gen_shift32: + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); + break; + + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + ((0x20 - args[2]) & 0x1f) ? + SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : + SHIFT_IMM_LSL(0)); + } else { + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_R8, args[1], 0x20); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + SHIFT_REG_ROR(TCG_REG_R8)); + } + break; + + case INDEX_op_brcond_i32: + if (const_args[1]) { + int rot; + rot = encode_imm(args[1]); + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, + args[0], rotl(args[1], rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[0], args[1], SHIFT_IMM_LSL(0)); + } + tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], args[3]); + break; + case INDEX_op_brcond2_i32: + /* The resulting conditions are: + * TCG_COND_EQ --> a0 == a2 && a1 == a3, + * TCG_COND_NE --> (a0 != a2 && a1 == a3) || a1 != a3, + * TCG_COND_LT(U) --> (a0 < a2 && a1 == a3) || a1 < a3, + * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3), + * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3), + * TCG_COND_GT(U) --> (a0 > a2 && a1 == a3) || a1 > a3, + */ + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[1], args[3], SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + args[0], args[2], SHIFT_IMM_LSL(0)); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], args[5]); + break; + case INDEX_op_setcond_i32: + if (const_args[2]) { + int rot; + rot = encode_imm(args[2]); + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, + args[1], rotl(args[2], rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[1], args[2], SHIFT_IMM_LSL(0)); + } + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], + ARITH_MOV, args[0], 0, 0); + break; + case INDEX_op_setcond2_i32: + /* See brcond2_i32 comment */ + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[2], args[4], SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + args[1], args[3], SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])], + ARITH_MOV, args[0], 0, 0); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, COND_AL, args[0], args[1]); + break; + + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tcg_out_ext16u(s, COND_AL, args[0], args[1]); + break; + + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef arm_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + /* TODO: "r", "r", "ri" */ + { INDEX_op_add_i32, { "r", "r", "rI" } }, + { INDEX_op_sub_i32, { "r", "r", "rI" } }, + { INDEX_op_mul_i32, { "r", "r", "r" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_and_i32, { "r", "r", "rI" } }, + { INDEX_op_andc_i32, { "r", "r", "rI" } }, + { INDEX_op_or_i32, { "r", "r", "rI" } }, + { INDEX_op_xor_i32, { "r", "r", "rI" } }, + { INDEX_op_neg_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_brcond_i32, { "r", "rI" } }, + { INDEX_op_setcond_i32, { "r", "r", "rI" } }, + + /* TODO: "r", "r", "r", "r", "ri", "ri" */ + { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } }, + { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } }, + { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "r", "r" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld8u, { "r", "l" } }, + { INDEX_op_qemu_ld8s, { "r", "l" } }, + { INDEX_op_qemu_ld16u, { "r", "l" } }, + { INDEX_op_qemu_ld16s, { "r", "l" } }, + { INDEX_op_qemu_ld32, { "r", "l" } }, + { INDEX_op_qemu_ld64, { "L", "L", "l" } }, + + { INDEX_op_qemu_st8, { "s", "s" } }, + { INDEX_op_qemu_st16, { "s", "s" } }, + { INDEX_op_qemu_st32, { "s", "s" } }, + { INDEX_op_qemu_st64, { "S", "S", "s" } }, +#else + { INDEX_op_qemu_ld8u, { "r", "l", "l" } }, + { INDEX_op_qemu_ld8s, { "r", "l", "l" } }, + { INDEX_op_qemu_ld16u, { "r", "l", "l" } }, + { INDEX_op_qemu_ld16s, { "r", "l", "l" } }, + { INDEX_op_qemu_ld32, { "r", "l", "l" } }, + { INDEX_op_qemu_ld64, { "L", "L", "l", "l" } }, + + { INDEX_op_qemu_st8, { "s", "s", "s" } }, + { INDEX_op_qemu_st16, { "s", "s", "s" } }, + { INDEX_op_qemu_st32, { "s", "s", "s" } }, + { INDEX_op_qemu_st64, { "S", "S", "s", "s" } }, +#endif + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ +#if !defined(CONFIG_USER_ONLY) + /* fail safe */ + if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) + tcg_abort(); +#endif + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_R0) | + (1 << TCG_REG_R1) | + (1 << TCG_REG_R2) | + (1 << TCG_REG_R3) | + (1 << TCG_REG_R12) | + (1 << TCG_REG_R14)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R8); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC); + + tcg_add_target_add_op_defs(arm_op_defs); +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg, + int arg1, tcg_target_long arg2) +{ + tcg_out_ld32u(s, COND_AL, arg, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, + int arg1, tcg_target_long arg2) +{ + tcg_out_st32(s, COND_AL, arg, arg1, arg2); +} + +static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + if (val > 0) + if (val < 0x100) + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, reg, reg, val); + else + tcg_abort(); + else if (val < 0) { + if (val > -0x100) + tcg_out_dat_imm(s, COND_AL, ARITH_SUB, reg, reg, -val); + else + tcg_abort(); + } +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0)); +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + tcg_out_movi32(s, COND_AL, ret, arg); +} + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + /* There is no need to save r7, it is used to store the address + of the env structure and is not modified by GCC. */ + + /* stmdb sp!, { r4 - r6, r8 - r11, lr } */ + tcg_out32(s, (COND_AL << 28) | 0x092d4f70); + + tcg_out_bx(s, COND_AL, TCG_REG_R0); + tb_ret_addr = s->code_ptr; + + /* ldmia sp!, { r4 - r6, r8 - r11, pc } */ + tcg_out32(s, (COND_AL << 28) | 0x08bd8f70); +} diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h new file mode 100644 index 0000000..d8d7d94 --- /dev/null +++ b/tcg/arm/tcg-target.h @@ -0,0 +1,93 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * Copyright (c) 2008 Andrzej Zaborowski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_ARM 1 + +#define TCG_TARGET_REG_BITS 32 +#undef TCG_TARGET_WORDS_BIGENDIAN +#undef TCG_TARGET_STACK_GROWSUP + +enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_PC, +}; + +#define TCG_TARGET_NB_REGS 16 + +#define TCG_CT_CONST_ARM 0x100 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_R13 +#define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 +#define TCG_TARGET_CALL_STACK_OFFSET 0 + +/* optional instructions */ +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#undef TCG_TARGET_HAS_ext8u_i32 /* and r0, r1, #0xff */ +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#define TCG_TARGET_HAS_GUEST_BASE + +enum { + /* Note: must be synced with dyngen-exec.h */ + TCG_AREG0 = TCG_REG_R7, +}; + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ +#if QEMU_GNUC_PREREQ(4, 1) + __builtin___clear_cache((char *) start, (char *) stop); +#else + register unsigned long _beg __asm ("a1") = start; + register unsigned long _end __asm ("a2") = stop; + register unsigned long _flg __asm ("a3") = 0; + __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg)); +#endif +} diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c new file mode 100644 index 0000000..7f4653e --- /dev/null +++ b/tcg/hppa/tcg-target.c @@ -0,0 +1,1683 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%r0", "%r1", "%rp", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", + "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", + "%r24", "%r25", "%r26", "%dp", "%ret0", "%ret1", "%sp", "%r31", +}; +#endif + +/* This is an 8 byte temp slot in the stack frame. */ +#define STACK_TEMP_OFS -16 + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R16 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + + TCG_REG_R17, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + + TCG_REG_R26, + TCG_REG_R25, + TCG_REG_R24, + TCG_REG_R23, + + TCG_REG_RET0, + TCG_REG_RET1, +}; + +static const int tcg_target_call_iarg_regs[4] = { + TCG_REG_R26, + TCG_REG_R25, + TCG_REG_R24, + TCG_REG_R23, +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_RET0, + TCG_REG_RET1, +}; + +/* True iff val fits a signed field of width BITS. */ +static inline int check_fit_tl(tcg_target_long val, unsigned int bits) +{ + return (val << ((sizeof(tcg_target_long) * 8 - bits)) + >> (sizeof(tcg_target_long) * 8 - bits)) == val; +} + +/* True iff depi can be used to compute (reg | MASK). + Accept a bit pattern like: + 0....01....1 + 1....10....0 + 0..01..10..0 + Copied from gcc sources. */ +static inline int or_mask_p(tcg_target_ulong mask) +{ + if (mask == 0 || mask == -1) { + return 0; + } + mask += mask & -mask; + return (mask & (mask - 1)) == 0; +} + +/* True iff depi or extru can be used to compute (reg & mask). + Accept a bit pattern like these: + 0....01....1 + 1....10....0 + 1..10..01..1 + Copied from gcc sources. */ +static inline int and_mask_p(tcg_target_ulong mask) +{ + return or_mask_p(~mask); +} + +static int low_sign_ext(int val, int len) +{ + return (((val << 1) & ~(-1u << len)) | ((val >> (len - 1)) & 1)); +} + +static int reassemble_12(int as12) +{ + return (((as12 & 0x800) >> 11) | + ((as12 & 0x400) >> 8) | + ((as12 & 0x3ff) << 3)); +} + +static int reassemble_17(int as17) +{ + return (((as17 & 0x10000) >> 16) | + ((as17 & 0x0f800) << 5) | + ((as17 & 0x00400) >> 8) | + ((as17 & 0x003ff) << 3)); +} + +static int reassemble_21(int as21) +{ + return (((as21 & 0x100000) >> 20) | + ((as21 & 0x0ffe00) >> 8) | + ((as21 & 0x000180) << 7) | + ((as21 & 0x00007c) << 14) | + ((as21 & 0x000003) << 12)); +} + +/* ??? Bizzarely, there is no PCREL12F relocation type. I guess all + such relocations are simply fully handled by the assembler. */ +#define R_PARISC_PCREL12F R_PARISC_NONE + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + uint32_t *insn_ptr = (uint32_t *)code_ptr; + uint32_t insn = *insn_ptr; + tcg_target_long pcrel; + + value += addend; + pcrel = (value - ((tcg_target_long)code_ptr + 8)) >> 2; + + switch (type) { + case R_PARISC_PCREL12F: + assert(check_fit_tl(pcrel, 12)); + /* ??? We assume all patches are forward. See tcg_out_brcond + re setting the NUL bit on the branch and eliding the nop. */ + assert(pcrel >= 0); + insn &= ~0x1ffdu; + insn |= reassemble_12(pcrel); + break; + case R_PARISC_PCREL17F: + assert(check_fit_tl(pcrel, 17)); + insn &= ~0x1f1ffdu; + insn |= reassemble_17(pcrel); + break; + default: + tcg_abort(); + } + + *insn_ptr = insn; +} + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + return 4; +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; + case 'L': /* qemu_ld/st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R26); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R25); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R24); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R23); + break; + case 'Z': + ct->ct |= TCG_CT_CONST_0; + break; + case 'I': + ct->ct |= TCG_CT_CONST_S11; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S5; + break; + case 'K': + ct->ct |= TCG_CT_CONST_MS11; + break; + case 'M': + ct->ct |= TCG_CT_CONST_AND; + break; + case 'O': + ct->ct |= TCG_CT_CONST_OR; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } else if (ct & TCG_CT_CONST_0) { + return val == 0; + } else if (ct & TCG_CT_CONST_S5) { + return check_fit_tl(val, 5); + } else if (ct & TCG_CT_CONST_S11) { + return check_fit_tl(val, 11); + } else if (ct & TCG_CT_CONST_MS11) { + return check_fit_tl(-val, 11); + } else if (ct & TCG_CT_CONST_AND) { + return and_mask_p(val); + } else if (ct & TCG_CT_CONST_OR) { + return or_mask_p(val); + } + return 0; +} + +#define INSN_OP(x) ((x) << 26) +#define INSN_EXT3BR(x) ((x) << 13) +#define INSN_EXT3SH(x) ((x) << 10) +#define INSN_EXT4(x) ((x) << 6) +#define INSN_EXT5(x) (x) +#define INSN_EXT6(x) ((x) << 6) +#define INSN_EXT7(x) ((x) << 6) +#define INSN_EXT8A(x) ((x) << 6) +#define INSN_EXT8B(x) ((x) << 5) +#define INSN_T(x) (x) +#define INSN_R1(x) ((x) << 16) +#define INSN_R2(x) ((x) << 21) +#define INSN_DEP_LEN(x) (32 - (x)) +#define INSN_SHDEP_CP(x) ((31 - (x)) << 5) +#define INSN_SHDEP_P(x) ((x) << 5) +#define INSN_COND(x) ((x) << 13) +#define INSN_IM11(x) low_sign_ext(x, 11) +#define INSN_IM14(x) low_sign_ext(x, 14) +#define INSN_IM5(x) (low_sign_ext(x, 5) << 16) + +#define COND_NEVER 0 +#define COND_EQ 1 +#define COND_LT 2 +#define COND_LE 3 +#define COND_LTU 4 +#define COND_LEU 5 +#define COND_SV 6 +#define COND_OD 7 +#define COND_FALSE 8 + +#define INSN_ADD (INSN_OP(0x02) | INSN_EXT6(0x18)) +#define INSN_ADDC (INSN_OP(0x02) | INSN_EXT6(0x1c)) +#define INSN_ADDI (INSN_OP(0x2d)) +#define INSN_ADDIL (INSN_OP(0x0a)) +#define INSN_ADDL (INSN_OP(0x02) | INSN_EXT6(0x28)) +#define INSN_AND (INSN_OP(0x02) | INSN_EXT6(0x08)) +#define INSN_ANDCM (INSN_OP(0x02) | INSN_EXT6(0x00)) +#define INSN_COMCLR (INSN_OP(0x02) | INSN_EXT6(0x22)) +#define INSN_COMICLR (INSN_OP(0x24)) +#define INSN_DEP (INSN_OP(0x35) | INSN_EXT3SH(3)) +#define INSN_DEPI (INSN_OP(0x35) | INSN_EXT3SH(7)) +#define INSN_EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7)) +#define INSN_EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6)) +#define INSN_LDIL (INSN_OP(0x08)) +#define INSN_LDO (INSN_OP(0x0d)) +#define INSN_MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2)) +#define INSN_OR (INSN_OP(0x02) | INSN_EXT6(0x09)) +#define INSN_SHD (INSN_OP(0x34) | INSN_EXT3SH(2)) +#define INSN_SUB (INSN_OP(0x02) | INSN_EXT6(0x10)) +#define INSN_SUBB (INSN_OP(0x02) | INSN_EXT6(0x14)) +#define INSN_SUBI (INSN_OP(0x25)) +#define INSN_VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5)) +#define INSN_VEXTRU (INSN_OP(0x34) | INSN_EXT3SH(4)) +#define INSN_VSHD (INSN_OP(0x34) | INSN_EXT3SH(0)) +#define INSN_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a)) +#define INSN_ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2)) +#define INSN_ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0)) + +#define INSN_BL (INSN_OP(0x3a) | INSN_EXT3BR(0)) +#define INSN_BL_N (INSN_OP(0x3a) | INSN_EXT3BR(0) | 2) +#define INSN_BLR (INSN_OP(0x3a) | INSN_EXT3BR(2)) +#define INSN_BV (INSN_OP(0x3a) | INSN_EXT3BR(6)) +#define INSN_BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2) +#define INSN_BLE_SR4 (INSN_OP(0x39) | (1 << 13)) + +#define INSN_LDB (INSN_OP(0x10)) +#define INSN_LDH (INSN_OP(0x11)) +#define INSN_LDW (INSN_OP(0x12)) +#define INSN_LDWM (INSN_OP(0x13)) +#define INSN_FLDDS (INSN_OP(0x0b) | INSN_EXT4(0) | (1 << 12)) + +#define INSN_LDBX (INSN_OP(0x03) | INSN_EXT4(0)) +#define INSN_LDHX (INSN_OP(0x03) | INSN_EXT4(1)) +#define INSN_LDWX (INSN_OP(0x03) | INSN_EXT4(2)) + +#define INSN_STB (INSN_OP(0x18)) +#define INSN_STH (INSN_OP(0x19)) +#define INSN_STW (INSN_OP(0x1a)) +#define INSN_STWM (INSN_OP(0x1b)) +#define INSN_FSTDS (INSN_OP(0x0b) | INSN_EXT4(8) | (1 << 12)) + +#define INSN_COMBT (INSN_OP(0x20)) +#define INSN_COMBF (INSN_OP(0x22)) +#define INSN_COMIBT (INSN_OP(0x21)) +#define INSN_COMIBF (INSN_OP(0x23)) + +/* supplied by libgcc */ +extern void *__canonicalize_funcptr_for_compare(void *); + +static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + /* PA1.1 defines COPY as OR r,0,t; PA2.0 defines COPY as LDO 0(r),t + but hppa-dis.c is unaware of this definition */ + if (ret != arg) { + tcg_out32(s, INSN_OR | INSN_T(ret) | INSN_R1(arg) + | INSN_R2(TCG_REG_R0)); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + if (check_fit_tl(arg, 14)) { + tcg_out32(s, INSN_LDO | INSN_R1(ret) + | INSN_R2(TCG_REG_R0) | INSN_IM14(arg)); + } else { + uint32_t hi, lo; + hi = arg >> 11; + lo = arg & 0x7ff; + + tcg_out32(s, INSN_LDIL | INSN_R2(ret) | reassemble_21(hi)); + if (lo) { + tcg_out32(s, INSN_LDO | INSN_R1(ret) + | INSN_R2(ret) | INSN_IM14(lo)); + } + } +} + +static void tcg_out_ldst(TCGContext *s, int ret, int addr, + tcg_target_long offset, int op) +{ + if (!check_fit_tl(offset, 14)) { + uint32_t hi, lo, op; + + hi = offset >> 11; + lo = offset & 0x7ff; + + if (addr == TCG_REG_R0) { + op = INSN_LDIL | INSN_R2(TCG_REG_R1); + } else { + op = INSN_ADDIL | INSN_R2(addr); + } + tcg_out32(s, op | reassemble_21(hi)); + + addr = TCG_REG_R1; + offset = lo; + } + + if (ret != addr || offset != 0 || op != INSN_LDO) { + tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | INSN_IM14(offset)); + } +} + +/* This function is required by tcg.c. */ +static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, INSN_LDW); +} + +/* This function is required by tcg.c. */ +static inline void tcg_out_st(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, INSN_STW); +} + +static void tcg_out_ldst_index(TCGContext *s, int data, + int base, int index, int op) +{ + tcg_out32(s, op | INSN_T(data) | INSN_R1(index) | INSN_R2(base)); +} + +static inline void tcg_out_addi2(TCGContext *s, int ret, int arg1, + tcg_target_long val) +{ + tcg_out_ldst(s, ret, arg1, val, INSN_LDO); +} + +/* This function is required by tcg.c. */ +static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + tcg_out_addi2(s, reg, reg, val); +} + +static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op) +{ + tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2)); +} + +static inline void tcg_out_arithi(TCGContext *s, int t, int r1, + tcg_target_long val, int op) +{ + assert(check_fit_tl(val, 11)); + tcg_out32(s, op | INSN_R1(t) | INSN_R2(r1) | INSN_IM11(val)); +} + +static inline void tcg_out_nop(TCGContext *s) +{ + tcg_out_arith(s, TCG_REG_R0, TCG_REG_R0, TCG_REG_R0, INSN_OR); +} + +static inline void tcg_out_mtctl_sar(TCGContext *s, int arg) +{ + tcg_out32(s, INSN_MTCTL | INSN_R2(11) | INSN_R1(arg)); +} + +/* Extract LEN bits at position OFS from ARG and place in RET. + Note that here the bit ordering is reversed from the PA-RISC + standard, such that the right-most bit is 0. */ +static inline void tcg_out_extr(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len, int sign) +{ + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, (sign ? INSN_EXTRS : INSN_EXTRU) + | INSN_R1(ret) | INSN_R2(arg) + | INSN_SHDEP_P(31 - ofs) | INSN_DEP_LEN(len)); +} + +/* Likewise with OFS interpreted little-endian. */ +static inline void tcg_out_dep(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len) +{ + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, INSN_DEP | INSN_R2(ret) | INSN_R1(arg) + | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len)); +} + +static inline void tcg_out_shd(TCGContext *s, int ret, int hi, int lo, + unsigned count) +{ + assert(count < 32); + tcg_out32(s, INSN_SHD | INSN_R1(hi) | INSN_R2(lo) | INSN_T(ret) + | INSN_SHDEP_CP(count)); +} + +static void tcg_out_vshd(TCGContext *s, int ret, int hi, int lo, int creg) +{ + tcg_out_mtctl_sar(s, creg); + tcg_out32(s, INSN_VSHD | INSN_T(ret) | INSN_R1(hi) | INSN_R2(lo)); +} + +static void tcg_out_ori(TCGContext *s, int ret, int arg, tcg_target_ulong m) +{ + int bs0, bs1; + + /* Note that the argument is constrained to match or_mask_p. */ + for (bs0 = 0; bs0 < 32; bs0++) { + if ((m & (1u << bs0)) != 0) { + break; + } + } + for (bs1 = bs0; bs1 < 32; bs1++) { + if ((m & (1u << bs1)) == 0) { + break; + } + } + assert(bs1 == 32 || (1ul << bs1) > m); + + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(-1) + | INSN_SHDEP_CP(31 - bs0) | INSN_DEP_LEN(bs1 - bs0)); +} + +static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m) +{ + int ls0, ls1, ms0; + + /* Note that the argument is constrained to match and_mask_p. */ + for (ls0 = 0; ls0 < 32; ls0++) { + if ((m & (1u << ls0)) == 0) { + break; + } + } + for (ls1 = ls0; ls1 < 32; ls1++) { + if ((m & (1u << ls1)) != 0) { + break; + } + } + for (ms0 = ls1; ms0 < 32; ms0++) { + if ((m & (1u << ms0)) == 0) { + break; + } + } + assert (ms0 == 32); + + if (ls1 == 32) { + tcg_out_extr(s, ret, arg, 0, ls0, 0); + } else { + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(0) + | INSN_SHDEP_CP(31 - ls0) | INSN_DEP_LEN(ls1 - ls0)); + } +} + +static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) +{ + tcg_out_extr(s, ret, arg, 0, 8, 1); +} + +static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) +{ + tcg_out_extr(s, ret, arg, 0, 16, 1); +} + +static void tcg_out_shli(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out32(s, INSN_ZDEP | INSN_R2(ret) | INSN_R1(arg) + | INSN_SHDEP_CP(31 - count) | INSN_DEP_LEN(32 - count)); +} + +static void tcg_out_shl(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI); + tcg_out_mtctl_sar(s, TCG_REG_R20); + tcg_out32(s, INSN_ZVDEP | INSN_R2(ret) | INSN_R1(arg) | INSN_DEP_LEN(32)); +} + +static void tcg_out_shri(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out_extr(s, ret, arg, count, 32 - count, 0); +} + +static void tcg_out_shr(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_vshd(s, ret, TCG_REG_R0, arg, creg); +} + +static void tcg_out_sari(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out_extr(s, ret, arg, count, 32 - count, 1); +} + +static void tcg_out_sar(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI); + tcg_out_mtctl_sar(s, TCG_REG_R20); + tcg_out32(s, INSN_VEXTRS | INSN_R1(ret) | INSN_R2(arg) | INSN_DEP_LEN(32)); +} + +static void tcg_out_rotli(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out_shd(s, ret, arg, arg, 32 - count); +} + +static void tcg_out_rotl(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_arithi(s, TCG_REG_R20, creg, 32, INSN_SUBI); + tcg_out_vshd(s, ret, arg, arg, TCG_REG_R20); +} + +static void tcg_out_rotri(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out_shd(s, ret, arg, arg, count); +} + +static void tcg_out_rotr(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_vshd(s, ret, arg, arg, creg); +} + +static void tcg_out_bswap16(TCGContext *s, int ret, int arg, int sign) +{ + if (ret != arg) { + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); /* arg = xxAB */ + } + tcg_out_dep(s, ret, ret, 16, 8); /* ret = xBAB */ + tcg_out_extr(s, ret, ret, 8, 16, sign); /* ret = ..BA */ +} + +static void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) +{ + /* arg = ABCD */ + tcg_out_rotri(s, temp, arg, 16); /* temp = CDAB */ + tcg_out_dep(s, temp, temp, 16, 8); /* temp = CBAB */ + tcg_out_shd(s, ret, arg, temp, 8); /* ret = DCBA */ +} + +static void tcg_out_call(TCGContext *s, void *func) +{ + tcg_target_long val, hi, lo, disp; + + val = (uint32_t)__canonicalize_funcptr_for_compare(func); + disp = (val - ((tcg_target_long)s->code_ptr + 8)) >> 2; + + if (check_fit_tl(disp, 17)) { + tcg_out32(s, INSN_BL_N | INSN_R2(TCG_REG_RP) | reassemble_17(disp)); + } else { + hi = val >> 11; + lo = val & 0x7ff; + + tcg_out32(s, INSN_LDIL | INSN_R2(TCG_REG_R20) | reassemble_21(hi)); + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R20) + | reassemble_17(lo >> 2)); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_RP, TCG_REG_R31); + } +} + +static void tcg_out_xmpyu(TCGContext *s, int retl, int reth, + int arg1, int arg2) +{ + /* Store both words into the stack for copy to the FPU. */ + tcg_out_ldst(s, arg1, TCG_REG_SP, STACK_TEMP_OFS, INSN_STW); + tcg_out_ldst(s, arg2, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_STW); + + /* Load both words into the FPU at the same time. We get away + with this because we can address the left and right half of the + FPU registers individually once loaded. */ + /* fldds stack_temp(sp),fr22 */ + tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_SP) + | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); + + /* xmpyu fr22r,fr22,fr22 */ + tcg_out32(s, 0x3ad64796); + + /* Store the 64-bit result back into the stack. */ + /* fstds stack_temp(sp),fr22 */ + tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_SP) + | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); + + /* Load the pieces of the result that the caller requested. */ + if (reth) { + tcg_out_ldst(s, reth, TCG_REG_SP, STACK_TEMP_OFS, INSN_LDW); + } + if (retl) { + tcg_out_ldst(s, retl, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_LDW); + } +} + +static void tcg_out_add2(TCGContext *s, int destl, int desth, + int al, int ah, int bl, int bh, int blconst) +{ + int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl); + + if (blconst) { + tcg_out_arithi(s, tmp, al, bl, INSN_ADDI); + } else { + tcg_out_arith(s, tmp, al, bl, INSN_ADD); + } + tcg_out_arith(s, desth, ah, bh, INSN_ADDC); + + tcg_out_mov(s, TCG_TYPE_I32, destl, tmp); +} + +static void tcg_out_sub2(TCGContext *s, int destl, int desth, int al, int ah, + int bl, int bh, int alconst, int blconst) +{ + int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl); + + if (alconst) { + if (blconst) { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R20, bl); + bl = TCG_REG_R20; + } + tcg_out_arithi(s, tmp, bl, al, INSN_SUBI); + } else if (blconst) { + tcg_out_arithi(s, tmp, al, -bl, INSN_ADDI); + } else { + tcg_out_arith(s, tmp, al, bl, INSN_SUB); + } + tcg_out_arith(s, desth, ah, bh, INSN_SUBB); + + tcg_out_mov(s, TCG_TYPE_I32, destl, tmp); +} + +static void tcg_out_branch(TCGContext *s, int label_index, int nul) +{ + TCGLabel *l = &s->labels[label_index]; + uint32_t op = nul ? INSN_BL_N : INSN_BL; + + if (l->has_value) { + tcg_target_long val = l->u.value; + + val -= (tcg_target_long)s->code_ptr + 8; + val >>= 2; + assert(check_fit_tl(val, 17)); + + tcg_out32(s, op | reassemble_17(val)); + } else { + /* We need to keep the offset unchanged for retranslation. */ + uint32_t old_insn = *(uint32_t *)s->code_ptr; + + tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL17F, label_index, 0); + tcg_out32(s, op | (old_insn & 0x1f1ffdu)); + } +} + +static const uint8_t tcg_cond_to_cmp_cond[10] = +{ + [TCG_COND_EQ] = COND_EQ, + [TCG_COND_NE] = COND_EQ | COND_FALSE, + [TCG_COND_LT] = COND_LT, + [TCG_COND_GE] = COND_LT | COND_FALSE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_LE | COND_FALSE, + [TCG_COND_LTU] = COND_LTU, + [TCG_COND_GEU] = COND_LTU | COND_FALSE, + [TCG_COND_LEU] = COND_LEU, + [TCG_COND_GTU] = COND_LEU | COND_FALSE, +}; + +static void tcg_out_brcond(TCGContext *s, int cond, TCGArg c1, + TCGArg c2, int c2const, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + int op, pacond; + + /* Note that COMIB operates as if the immediate is the first + operand. We model brcond with the immediate in the second + to better match what targets are likely to give us. For + consistency, model COMB with reversed operands as well. */ + pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)]; + + if (c2const) { + op = (pacond & COND_FALSE ? INSN_COMIBF : INSN_COMIBT); + op |= INSN_IM5(c2); + } else { + op = (pacond & COND_FALSE ? INSN_COMBF : INSN_COMBT); + op |= INSN_R1(c2); + } + op |= INSN_R2(c1); + op |= INSN_COND(pacond & 7); + + if (l->has_value) { + tcg_target_long val = l->u.value; + + val -= (tcg_target_long)s->code_ptr + 8; + val >>= 2; + assert(check_fit_tl(val, 12)); + + /* ??? Assume that all branches to defined labels are backward. + Which means that if the nul bit is set, the delay slot is + executed if the branch is taken, and not executed in fallthru. */ + tcg_out32(s, op | reassemble_12(val)); + tcg_out_nop(s); + } else { + /* We need to keep the offset unchanged for retranslation. */ + uint32_t old_insn = *(uint32_t *)s->code_ptr; + + tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL12F, label_index, 0); + /* ??? Assume that all branches to undefined labels are forward. + Which means that if the nul bit is set, the delay slot is + not executed if the branch is taken, which is what we want. */ + tcg_out32(s, op | 2 | (old_insn & 0x1ffdu)); + } +} + +static void tcg_out_comclr(TCGContext *s, int cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + int op, pacond; + + /* Note that COMICLR operates as if the immediate is the first + operand. We model setcond with the immediate in the second + to better match what targets are likely to give us. For + consistency, model COMCLR with reversed operands as well. */ + pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)]; + + if (c2const) { + op = INSN_COMICLR | INSN_R2(c1) | INSN_R1(ret) | INSN_IM11(c2); + } else { + op = INSN_COMCLR | INSN_R2(c1) | INSN_R1(c2) | INSN_T(ret); + } + op |= INSN_COND(pacond & 7); + op |= pacond & COND_FALSE ? 1 << 12 : 0; + + tcg_out32(s, op); +} + +static void tcg_out_brcond2(TCGContext *s, int cond, TCGArg al, TCGArg ah, + TCGArg bl, int blconst, TCGArg bh, int bhconst, + int label_index) +{ + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tcg_out_comclr(s, tcg_invert_cond(cond), TCG_REG_R0, al, bl, blconst); + tcg_out_brcond(s, cond, ah, bh, bhconst, label_index); + break; + + default: + tcg_out_brcond(s, cond, ah, bh, bhconst, label_index); + tcg_out_comclr(s, TCG_COND_NE, TCG_REG_R0, ah, bh, bhconst); + tcg_out_brcond(s, tcg_unsigned_cond(cond), + al, bl, blconst, label_index); + break; + } +} + +static void tcg_out_setcond(TCGContext *s, int cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_comclr(s, tcg_invert_cond(cond), ret, c1, c2, c2const); + tcg_out_movi(s, TCG_TYPE_I32, ret, 1); +} + +static void tcg_out_setcond2(TCGContext *s, int cond, TCGArg ret, + TCGArg al, TCGArg ah, TCGArg bl, int blconst, + TCGArg bh, int bhconst) +{ + int scratch = TCG_REG_R20; + + if (ret != al && ret != ah + && (blconst || ret != bl) + && (bhconst || ret != bh)) { + scratch = ret; + } + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tcg_out_setcond(s, cond, scratch, al, bl, blconst); + tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, cond == TCG_COND_NE); + break; + + default: + tcg_out_setcond(s, tcg_unsigned_cond(cond), scratch, al, bl, blconst); + tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, 0); + tcg_out_comclr(s, cond, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, 1); + break; + } + + tcg_out_mov(s, TCG_TYPE_I32, ret, scratch); +} + +#if defined(CONFIG_SOFTMMU) +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; + +/* Load and compare a TLB entry, and branch if TLB miss. OFFSET is set to + the offset of the first ADDR_READ or ADDR_WRITE member of the appropriate + TLB for the memory index. The return value is the offset from ENV + contained in R1 afterward (to be used when loading ADDEND); if the + return value is 0, R1 is not used. */ + +static int tcg_out_tlb_read(TCGContext *s, int r0, int r1, int addrlo, + int addrhi, int s_bits, int lab_miss, int offset) +{ + int ret; + + /* Extracting the index into the TLB. The "normal C operation" is + r1 = addr_reg >> TARGET_PAGE_BITS; + r1 &= CPU_TLB_SIZE - 1; + r1 <<= CPU_TLB_ENTRY_BITS; + What this does is extract CPU_TLB_BITS beginning at TARGET_PAGE_BITS + and place them at CPU_TLB_ENTRY_BITS. We can combine the first two + operations with an EXTRU. Unfortunately, the current value of + CPU_TLB_ENTRY_BITS is > 3, so we can't merge that shift with the + add that follows. */ + tcg_out_extr(s, r1, addrlo, TARGET_PAGE_BITS, CPU_TLB_BITS, 0); + tcg_out_shli(s, r1, r1, CPU_TLB_ENTRY_BITS); + tcg_out_arith(s, r1, r1, TCG_AREG0, INSN_ADDL); + + /* Make sure that both the addr_{read,write} and addend can be + read with a 14-bit offset from the same base register. */ + if (check_fit_tl(offset + CPU_TLB_SIZE, 14)) { + ret = 0; + } else { + ret = (offset + 0x400) & ~0x7ff; + offset = ret - offset; + tcg_out_addi2(s, TCG_REG_R1, r1, ret); + r1 = TCG_REG_R1; + } + + /* Load the entry from the computed slot. */ + if (TARGET_LONG_BITS == 64) { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R23, r1, offset); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset + 4); + } else { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset); + } + + /* Compute the value that ought to appear in the TLB for a hit, namely, the page + of the address. We include the low N bits of the address to catch unaligned + accesses and force them onto the slow path. Do this computation after having + issued the load from the TLB slot to give the load time to complete. */ + tcg_out_andi(s, r0, addrlo, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + + /* If not equal, jump to lab_miss. */ + if (TARGET_LONG_BITS == 64) { + tcg_out_brcond2(s, TCG_COND_NE, TCG_REG_R20, TCG_REG_R23, + r0, 0, addrhi, 0, lab_miss); + } else { + tcg_out_brcond(s, TCG_COND_NE, TCG_REG_R20, r0, 0, lab_miss); + } + + return ret; +} +#endif + +static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo_reg, int datahi_reg, + int addr_reg, int addend_reg, int opc) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; +#else + const int bswap = 1; +#endif + + switch (opc) { + case 0: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX); + break; + case 0 | 4: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX); + tcg_out_ext8s(s, datalo_reg, datalo_reg); + break; + case 1: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX); + if (bswap) { + tcg_out_bswap16(s, datalo_reg, datalo_reg, 0); + } + break; + case 1 | 4: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX); + if (bswap) { + tcg_out_bswap16(s, datalo_reg, datalo_reg, 1); + } else { + tcg_out_ext16s(s, datalo_reg, datalo_reg); + } + break; + case 2: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDWX); + if (bswap) { + tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20); + } + break; + case 3: + if (bswap) { + int t = datahi_reg; + datahi_reg = datalo_reg; + datalo_reg = t; + } + /* We can't access the low-part with a reg+reg addressing mode, + so perform the addition now and use reg_ofs addressing mode. */ + if (addend_reg != TCG_REG_R0) { + tcg_out_arith(s, TCG_REG_R20, addr_reg, addend_reg, INSN_ADD); + addr_reg = TCG_REG_R20; + } + /* Make sure not to clobber the base register. */ + if (datahi_reg == addr_reg) { + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW); + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW); + } else { + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW); + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW); + } + if (bswap) { + tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20); + tcg_out_bswap32(s, datahi_reg, datahi_reg, TCG_REG_R20); + } + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + int datalo_reg = *args++; + /* Note that datahi_reg is only used for 64-bit loads. */ + int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0); + int addrlo_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + /* Note that addrhi_reg is only used for 64-bit guests. */ + int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0); + int mem_index = *args; + int lab1, lab2, argreg, offset; + + lab1 = gen_new_label(); + lab2 = gen_new_label(); + + offset = offsetof(CPUState, tlb_table[mem_index][0].addr_read); + offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg, + opc & 3, lab1, offset); + + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25), + offsetof(CPUState, tlb_table[mem_index][0].addend) - offset); + tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, TCG_REG_R20, opc); + tcg_out_branch(s, lab2, 1); + + /* TLB Miss. */ + /* label1: */ + tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr); + + argreg = TCG_REG_R26; + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg); + } + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + + tcg_out_call(s, qemu_ld_helpers[opc & 3]); + + switch (opc) { + case 0: + tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xff); + break; + case 0 | 4: + tcg_out_ext8s(s, datalo_reg, TCG_REG_RET0); + break; + case 1: + tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xffff); + break; + case 1 | 4: + tcg_out_ext16s(s, datalo_reg, TCG_REG_RET0); + break; + case 2: + case 2 | 4: + tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET0); + break; + case 3: + tcg_out_mov(s, TCG_TYPE_I32, datahi_reg, TCG_REG_RET0); + tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET1); + break; + default: + tcg_abort(); + } + + /* label2: */ + tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr); +#else + tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, + (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_R0), opc); +#endif +} + +static void tcg_out_qemu_st_direct(TCGContext *s, int datalo_reg, int datahi_reg, + int addr_reg, int opc) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; +#else + const int bswap = 1; +#endif + + switch (opc) { + case 0: + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STB); + break; + case 1: + if (bswap) { + tcg_out_bswap16(s, TCG_REG_R20, datalo_reg, 0); + datalo_reg = TCG_REG_R20; + } + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STH); + break; + case 2: + if (bswap) { + tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20); + datalo_reg = TCG_REG_R20; + } + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STW); + break; + case 3: + if (bswap) { + tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20); + tcg_out_bswap32(s, TCG_REG_R23, datahi_reg, TCG_REG_R23); + datahi_reg = TCG_REG_R20; + datalo_reg = TCG_REG_R23; + } + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_STW); + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_STW); + break; + default: + tcg_abort(); + } + +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + int datalo_reg = *args++; + /* Note that datahi_reg is only used for 64-bit loads. */ + int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0); + int addrlo_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + /* Note that addrhi_reg is only used for 64-bit guests. */ + int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0); + int mem_index = *args; + int lab1, lab2, argreg, offset; + + lab1 = gen_new_label(); + lab2 = gen_new_label(); + + offset = offsetof(CPUState, tlb_table[mem_index][0].addr_write); + offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg, + opc, lab1, offset); + + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25), + offsetof(CPUState, tlb_table[mem_index][0].addend) - offset); + + /* There are no indexed stores, so we must do this addition explitly. + Careful to avoid R20, which is used for the bswaps to follow. */ + tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_REG_R20, INSN_ADDL); + tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, TCG_REG_R31, opc); + tcg_out_branch(s, lab2, 1); + + /* TLB Miss. */ + /* label1: */ + tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr); + + argreg = TCG_REG_R26; + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg); + } + + switch(opc) { + case 0: + tcg_out_andi(s, argreg--, datalo_reg, 0xff); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 1: + tcg_out_andi(s, argreg--, datalo_reg, 0xffff); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, argreg--, datalo_reg); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 3: + /* Because of the alignment required by the 64-bit data argument, + we will always use R23/R24. Also, we will always run out of + argument registers for storing mem_index, so that will have + to go on the stack. */ + if (mem_index == 0) { + argreg = TCG_REG_R0; + } else { + argreg = TCG_REG_R20; + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + } + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R23, datahi_reg); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R24, datalo_reg); + tcg_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_SP, + TCG_TARGET_CALL_STACK_OFFSET - 4); + break; + default: + tcg_abort(); + } + + tcg_out_call(s, qemu_st_helpers[opc]); + + /* label2: */ + tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr); +#else + /* There are no indexed stores, so if GUEST_BASE is set we must do the add + explicitly. Careful to avoid R20, which is used for the bswaps to follow. */ + if (GUEST_BASE != 0) { + tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_GUEST_BASE_REG, INSN_ADDL); + addrlo_reg = TCG_REG_R31; + } + tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, addrlo_reg, opc); +#endif +} + +static void tcg_out_exit_tb(TCGContext *s, TCGArg arg) +{ + if (!check_fit_tl(arg, 14)) { + uint32_t hi, lo; + hi = arg & ~0x7ff; + lo = arg & 0x7ff; + if (lo) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, hi); + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18)); + tcg_out_addi(s, TCG_REG_RET0, lo); + return; + } + arg = hi; + } + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18)); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, arg); +} + +static void tcg_out_goto_tb(TCGContext *s, TCGArg arg) +{ + if (s->tb_jmp_offset) { + /* direct jump method */ + fprintf(stderr, "goto_tb direct\n"); + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, TCG_REG_R0, + (tcg_target_long)(s->tb_next + arg)); + tcg_out32(s, INSN_BV_N | INSN_R2(TCG_REG_R20)); + } + s->tb_next_offset[arg] = s->code_ptr - s->code_buf; +} + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + switch (opc) { + case INDEX_op_exit_tb: + tcg_out_exit_tb(s, args[0]); + break; + case INDEX_op_goto_tb: + tcg_out_goto_tb(s, args[0]); + break; + + case INDEX_op_call: + if (const_args[0]) { + tcg_out_call(s, (void *)args[0]); + } else { + /* ??? FIXME: the value in the register in args[0] is almost + certainly a procedure descriptor, not a code address. We + probably need to use the millicode $$dyncall routine. */ + tcg_abort(); + } + break; + + case INDEX_op_jmp: + fprintf(stderr, "unimplemented jmp\n"); + tcg_abort(); + break; + + case INDEX_op_br: + tcg_out_branch(s, args[0], 1); + break; + + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]); + break; + + case INDEX_op_ld8u_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB); + break; + case INDEX_op_ld8s_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB); + tcg_out_ext8s(s, args[0], args[0]); + break; + case INDEX_op_ld16u_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH); + break; + case INDEX_op_ld16s_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH); + tcg_out_ext16s(s, args[0], args[0]); + break; + case INDEX_op_ld_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDW); + break; + + case INDEX_op_st8_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STB); + break; + case INDEX_op_st16_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STH); + break; + case INDEX_op_st_i32: + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STW); + break; + + case INDEX_op_add_i32: + if (const_args[2]) { + tcg_out_addi2(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_ADDL); + } + break; + + case INDEX_op_sub_i32: + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1] - args[2]); + } else { + /* Recall that SUBI is a reversed subtract. */ + tcg_out_arithi(s, args[0], args[2], args[1], INSN_SUBI); + } + } else if (const_args[2]) { + tcg_out_addi2(s, args[0], args[1], -args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_SUB); + } + break; + + case INDEX_op_and_i32: + if (const_args[2]) { + tcg_out_andi(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_AND); + } + break; + + case INDEX_op_or_i32: + if (const_args[2]) { + tcg_out_ori(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_OR); + } + break; + + case INDEX_op_xor_i32: + tcg_out_arith(s, args[0], args[1], args[2], INSN_XOR); + break; + + case INDEX_op_andc_i32: + if (const_args[2]) { + tcg_out_andi(s, args[0], args[1], ~args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_ANDCM); + } + break; + + case INDEX_op_shl_i32: + if (const_args[2]) { + tcg_out_shli(s, args[0], args[1], args[2]); + } else { + tcg_out_shl(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_shr_i32: + if (const_args[2]) { + tcg_out_shri(s, args[0], args[1], args[2]); + } else { + tcg_out_shr(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_sar_i32: + if (const_args[2]) { + tcg_out_sari(s, args[0], args[1], args[2]); + } else { + tcg_out_sar(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_rotli(s, args[0], args[1], args[2]); + } else { + tcg_out_rotl(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_rotri(s, args[0], args[1], args[2]); + } else { + tcg_out_rotr(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_mul_i32: + tcg_out_xmpyu(s, args[0], TCG_REG_R0, args[1], args[2]); + break; + case INDEX_op_mulu2_i32: + tcg_out_xmpyu(s, args[0], args[1], args[2], args[3]); + break; + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, args[0], args[1], 0); + break; + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, args[0], args[1], TCG_REG_R20); + break; + + case INDEX_op_not_i32: + tcg_out_arithi(s, args[0], args[1], -1, INSN_SUBI); + break; + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, args[0], args[1]); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args[4], args[0], args[1], + args[2], const_args[2], + args[3], const_args[3], args[5]); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], args[4], const_args[4]); + break; + + case INDEX_op_add2_i32: + tcg_out_add2(s, args[0], args[1], args[2], args[3], + args[4], args[5], const_args[4]); + break; + + case INDEX_op_sub2_i32: + tcg_out_sub2(s, args[0], args[1], args[2], args[3], + args[4], args[5], const_args[2], const_args[4]); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + + default: + fprintf(stderr, "unknown opcode 0x%x\n", opc); + tcg_abort(); + } +} + +static const TCGTargetOpDef hppa_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "r" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rZ", "ri" } }, + { INDEX_op_sub_i32, { "r", "rI", "ri" } }, + { INDEX_op_and_i32, { "r", "rZ", "rM" } }, + { INDEX_op_or_i32, { "r", "rZ", "rO" } }, + { INDEX_op_xor_i32, { "r", "rZ", "rZ" } }, + /* Note that the second argument will be inverted, which means + we want a constant whose inversion matches M, and that O = ~M. + See the implementation of and_mask_p. */ + { INDEX_op_andc_i32, { "r", "rZ", "rO" } }, + + { INDEX_op_mul_i32, { "r", "r", "r" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + + { INDEX_op_brcond_i32, { "rZ", "rJ" } }, + { INDEX_op_brcond2_i32, { "rZ", "rZ", "rJ", "rJ" } }, + + { INDEX_op_setcond_i32, { "r", "rZ", "rI" } }, + { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rI", "rI" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } }, + { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "r", "L" } }, + + { INDEX_op_qemu_st8, { "LZ", "L" } }, + { INDEX_op_qemu_st16, { "LZ", "L" } }, + { INDEX_op_qemu_st32, { "LZ", "L" } }, + { INDEX_op_qemu_st64, { "LZ", "LZ", "L" } }, +#else + { INDEX_op_qemu_ld8u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, + { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } }, + + { INDEX_op_qemu_st8, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st16, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st32, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st64, { "LZ", "LZ", "L", "L" } }, +#endif + { -1 }, +}; + +static int tcg_target_callee_save_regs[] = { + /* R2, the return address register, is saved specially + in the caller's frame. */ + /* R3, the frame pointer, is not currently modified. */ + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + /* R17 is the global env, so no need to save. */ + TCG_REG_R18 +}; + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int frame_size, i; + + /* Allocate space for the fixed frame marker. */ + frame_size = -TCG_TARGET_CALL_STACK_OFFSET; + frame_size += TCG_TARGET_STATIC_CALL_ARGS_SIZE; + + /* Allocate space for the saved registers. */ + frame_size += ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + + /* Align the allocated space. */ + frame_size = ((frame_size + TCG_TARGET_STACK_ALIGN - 1) + & -TCG_TARGET_STACK_ALIGN); + + /* The return address is stored in the caller's frame. */ + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -20); + + /* Allocate stack frame, saving the first register at the same time. */ + tcg_out_ldst(s, tcg_target_callee_save_regs[0], + TCG_REG_SP, frame_size, INSN_STWM); + + /* Save all callee saved registers. */ + for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_st(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], + TCG_REG_SP, -frame_size + i * 4); + } + +#ifdef CONFIG_USE_GUEST_BASE + if (GUEST_BASE != 0) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + /* Jump to TB, and adjust R18 to be the return address. */ + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R26)); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R18, TCG_REG_R31); + + /* Restore callee saved registers. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -frame_size - 20); + for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_ld(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], + TCG_REG_SP, -frame_size + i * 4); + } + + /* Deallocate stack frame and return. */ + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_RP)); + tcg_out_ldst(s, tcg_target_callee_save_regs[0], + TCG_REG_SP, -frame_size, INSN_LDWM); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET1); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* hardwired to zero */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* addil target */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_RP); /* link register */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* frame pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R18); /* return pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R19); /* clobbered w/o pic */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R20); /* reserved */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_DP); /* data pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */ + + tcg_add_target_add_op_defs(hppa_op_defs); +} diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h new file mode 100644 index 0000000..a5cc440 --- /dev/null +++ b/tcg/hppa/tcg-target.h @@ -0,0 +1,119 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define TCG_TARGET_HPPA 1 + +#if defined(_PA_RISC1_1) +#define TCG_TARGET_REG_BITS 32 +#else +#error unsupported +#endif + +#define TCG_TARGET_WORDS_BIGENDIAN + +#define TCG_TARGET_NB_REGS 32 + +enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_RP, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_DP, + TCG_REG_RET0, + TCG_REG_RET1, + TCG_REG_SP, + TCG_REG_R31, +}; + +#define TCG_CT_CONST_0 0x0100 +#define TCG_CT_CONST_S5 0x0200 +#define TCG_CT_CONST_S11 0x0400 +#define TCG_CT_CONST_MS11 0x0800 +#define TCG_CT_CONST_AND 0x1000 +#define TCG_CT_CONST_OR 0x2000 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 64 +#define TCG_TARGET_CALL_STACK_OFFSET -48 +#define TCG_TARGET_STATIC_CALL_ARGS_SIZE 8*4 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 +#define TCG_TARGET_STACK_GROWSUP + +/* optional instructions */ +// #define TCG_TARGET_HAS_div_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 + +/* optional instructions automatically implemented */ +#undef TCG_TARGET_HAS_neg_i32 /* sub rd, 0, rs */ +#undef TCG_TARGET_HAS_ext8u_i32 /* and rd, rs, 0xff */ +#undef TCG_TARGET_HAS_ext16u_i32 /* and rd, rs, 0xffff */ + +#define TCG_TARGET_HAS_GUEST_BASE + +/* Note: must be synced with dyngen-exec.h */ +#define TCG_AREG0 TCG_REG_R17 + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + start &= ~31; + while (start <= stop) { + asm volatile ("fdc 0(%0)\n\t" + "sync\n\t" + "fic 0(%%sr4, %0)\n\t" + "sync" + : : "r"(start) : "memory"); + start += 32; + } +} diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c new file mode 100644 index 0000000..bb19a95 --- /dev/null +++ b/tcg/i386/tcg-target.c @@ -0,0 +1,1982 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { +#if TCG_TARGET_REG_BITS == 64 + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", +#else + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", +#endif +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_RCX, + TCG_REG_RDX, + TCG_REG_RSI, + TCG_REG_RDI, + TCG_REG_RAX, +#else + TCG_REG_EBX, + TCG_REG_ESI, + TCG_REG_EDI, + TCG_REG_EBP, + TCG_REG_ECX, + TCG_REG_EDX, + TCG_REG_EAX, +#endif +}; + +static const int tcg_target_call_iarg_regs[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RDI, + TCG_REG_RSI, + TCG_REG_RDX, + TCG_REG_RCX, + TCG_REG_R8, + TCG_REG_R9, +#else + TCG_REG_EAX, + TCG_REG_EDX, + TCG_REG_ECX +#endif +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_EAX, + TCG_REG_EDX +}; + +static uint8_t *tb_ret_addr; + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch(type) { + case R_386_PC32: + value -= (uintptr_t)code_ptr; + if (value != (int32_t)value) { + tcg_abort(); + } + *(uint32_t *)code_ptr = value; + break; + case R_386_PC8: + value -= (uintptr_t)code_ptr; + if (value != (int8_t)value) { + tcg_abort(); + } + *(uint8_t *)code_ptr = value; + break; + default: + tcg_abort(); + } +} + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + if (TCG_TARGET_REG_BITS == 64) { + return 6; + } + + flags &= TCG_CALL_TYPE_MASK; + switch(flags) { + case TCG_CALL_TYPE_STD: + return 0; + case TCG_CALL_TYPE_REGPARM_1: + case TCG_CALL_TYPE_REGPARM_2: + case TCG_CALL_TYPE_REGPARM: + return flags - TCG_CALL_TYPE_REGPARM_1 + 1; + default: + tcg_abort(); + } +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'a': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EAX); + break; + case 'b': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EBX); + break; + case 'c': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_ECX); + break; + case 'd': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EDX); + break; + case 'S': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_ESI); + break; + case 'D': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, TCG_REG_EDI); + break; + case 'q': + ct->ct |= TCG_CT_REG; + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xf); + } + break; + case 'r': + ct->ct |= TCG_CT_REG; + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + } + break; + + /* qemu_ld/st address constraint */ + case 'L': + ct->ct |= TCG_CT_REG; + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_RSI); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_RDI); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX); + } + break; + + case 'e': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_U32; + break; + + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } + return 0; +} + +#if TCG_TARGET_REG_BITS == 64 +# define LOWREGMASK(x) ((x) & 7) +#else +# define LOWREGMASK(x) (x) +#endif + +#define P_EXT 0x100 /* 0x0f opcode prefix */ +#define P_DATA16 0x200 /* 0x66 opcode prefix */ +#if TCG_TARGET_REG_BITS == 64 +# define P_ADDR32 0x400 /* 0x67 opcode prefix */ +# define P_REXW 0x800 /* Set REX.W = 1 */ +# define P_REXB_R 0x1000 /* REG field as byte register */ +# define P_REXB_RM 0x2000 /* R/M field as byte register */ +#else +# define P_ADDR32 0 +# define P_REXW 0 +# define P_REXB_R 0 +# define P_REXB_RM 0 +#endif + +#define OPC_ARITH_EvIz (0x81) +#define OPC_ARITH_EvIb (0x83) +#define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */ +#define OPC_ADD_GvEv (OPC_ARITH_GvEv | (ARITH_ADD << 3)) +#define OPC_BSWAP (0xc8 | P_EXT) +#define OPC_CALL_Jz (0xe8) +#define OPC_CMP_GvEv (OPC_ARITH_GvEv | (ARITH_CMP << 3)) +#define OPC_DEC_r32 (0x48) +#define OPC_IMUL_GvEv (0xaf | P_EXT) +#define OPC_IMUL_GvEvIb (0x6b) +#define OPC_IMUL_GvEvIz (0x69) +#define OPC_INC_r32 (0x40) +#define OPC_JCC_long (0x80 | P_EXT) /* ... plus condition code */ +#define OPC_JCC_short (0x70) /* ... plus condition code */ +#define OPC_JMP_long (0xe9) +#define OPC_JMP_short (0xeb) +#define OPC_LEA (0x8d) +#define OPC_MOVB_EvGv (0x88) /* stores, more or less */ +#define OPC_MOVL_EvGv (0x89) /* stores, more or less */ +#define OPC_MOVL_GvEv (0x8b) /* loads, more or less */ +#define OPC_MOVL_EvIz (0xc7) +#define OPC_MOVL_Iv (0xb8) +#define OPC_MOVSBL (0xbe | P_EXT) +#define OPC_MOVSWL (0xbf | P_EXT) +#define OPC_MOVSLQ (0x63 | P_REXW) +#define OPC_MOVZBL (0xb6 | P_EXT) +#define OPC_MOVZWL (0xb7 | P_EXT) +#define OPC_POP_r32 (0x58) +#define OPC_PUSH_r32 (0x50) +#define OPC_PUSH_Iv (0x68) +#define OPC_PUSH_Ib (0x6a) +#define OPC_RET (0xc3) +#define OPC_SETCC (0x90 | P_EXT | P_REXB_RM) /* ... plus cc */ +#define OPC_SHIFT_1 (0xd1) +#define OPC_SHIFT_Ib (0xc1) +#define OPC_SHIFT_cl (0xd3) +#define OPC_TESTL (0x85) +#define OPC_XCHG_ax_r32 (0x90) + +#define OPC_GRP3_Ev (0xf7) +#define OPC_GRP5 (0xff) + +/* Group 1 opcode extensions for 0x80-0x83. + These are also used as modifiers for OPC_ARITH. */ +#define ARITH_ADD 0 +#define ARITH_OR 1 +#define ARITH_ADC 2 +#define ARITH_SBB 3 +#define ARITH_AND 4 +#define ARITH_SUB 5 +#define ARITH_XOR 6 +#define ARITH_CMP 7 + +/* Group 2 opcode extensions for 0xc0, 0xc1, 0xd0-0xd3. */ +#define SHIFT_ROL 0 +#define SHIFT_ROR 1 +#define SHIFT_SHL 4 +#define SHIFT_SHR 5 +#define SHIFT_SAR 7 + +/* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */ +#define EXT3_NOT 2 +#define EXT3_NEG 3 +#define EXT3_MUL 4 +#define EXT3_IMUL 5 +#define EXT3_DIV 6 +#define EXT3_IDIV 7 + +/* Group 5 opcode extensions for 0xff. To be used with OPC_GRP5. */ +#define EXT5_INC_Ev 0 +#define EXT5_DEC_Ev 1 +#define EXT5_CALLN_Ev 2 +#define EXT5_JMPN_Ev 4 + +/* Condition codes to be added to OPC_JCC_{long,short}. */ +#define JCC_JMP (-1) +#define JCC_JO 0x0 +#define JCC_JNO 0x1 +#define JCC_JB 0x2 +#define JCC_JAE 0x3 +#define JCC_JE 0x4 +#define JCC_JNE 0x5 +#define JCC_JBE 0x6 +#define JCC_JA 0x7 +#define JCC_JS 0x8 +#define JCC_JNS 0x9 +#define JCC_JP 0xa +#define JCC_JNP 0xb +#define JCC_JL 0xc +#define JCC_JGE 0xd +#define JCC_JLE 0xe +#define JCC_JG 0xf + +static const uint8_t tcg_cond_to_jcc[10] = { + [TCG_COND_EQ] = JCC_JE, + [TCG_COND_NE] = JCC_JNE, + [TCG_COND_LT] = JCC_JL, + [TCG_COND_GE] = JCC_JGE, + [TCG_COND_LE] = JCC_JLE, + [TCG_COND_GT] = JCC_JG, + [TCG_COND_LTU] = JCC_JB, + [TCG_COND_GEU] = JCC_JAE, + [TCG_COND_LEU] = JCC_JBE, + [TCG_COND_GTU] = JCC_JA, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) +{ + int rex; + + if (opc & P_DATA16) { + /* We should never be asking for both 16 and 64-bit operation. */ + assert((opc & P_REXW) == 0); + tcg_out8(s, 0x66); + } + if (opc & P_ADDR32) { + tcg_out8(s, 0x67); + } + + rex = 0; + rex |= (opc & P_REXW) >> 8; /* REX.W */ + rex |= (r & 8) >> 1; /* REX.R */ + rex |= (x & 8) >> 2; /* REX.X */ + rex |= (rm & 8) >> 3; /* REX.B */ + + /* P_REXB_{R,RM} indicates that the given register is the low byte. + For %[abcd]l we need no REX prefix, but for %{si,di,bp,sp}l we do, + as otherwise the encoding indicates %[abcd]h. Note that the values + that are ORed in merely indicate that the REX byte must be present; + those bits get discarded in output. */ + rex |= opc & (r >= 4 ? P_REXB_R : 0); + rex |= opc & (rm >= 4 ? P_REXB_RM : 0); + + if (rex) { + tcg_out8(s, (uint8_t)(rex | 0x40)); + } + + if (opc & P_EXT) { + tcg_out8(s, 0x0f); + } + tcg_out8(s, opc); +} +#else +static void tcg_out_opc(TCGContext *s, int opc) +{ + if (opc & P_DATA16) { + tcg_out8(s, 0x66); + } + if (opc & P_EXT) { + tcg_out8(s, 0x0f); + } + tcg_out8(s, opc); +} +/* Discard the register arguments to tcg_out_opc early, so as not to penalize + the 32-bit compilation paths. This method works with all versions of gcc, + whereas relying on optimization may not be able to exclude them. */ +#define tcg_out_opc(s, opc, r, rm, x) (tcg_out_opc)(s, opc) +#endif + +static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) +{ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); +} + +/* Output an opcode with a full "rm + (index<<shift) + offset" address mode. + We handle either RM and INDEX missing with a negative value. In 64-bit + mode for absolute addresses, ~RM is the size of the immediate operand + that will follow the instruction. */ + +static void tcg_out_modrm_sib_offset(TCGContext *s, int opc, int r, int rm, + int index, int shift, + tcg_target_long offset) +{ + int mod, len; + + if (index < 0 && rm < 0) { + if (TCG_TARGET_REG_BITS == 64) { + /* Try for a rip-relative addressing mode. This has replaced + the 32-bit-mode absolute addressing encoding. */ + tcg_target_long pc = (tcg_target_long)s->code_ptr + 5 + ~rm; + tcg_target_long disp = offset - pc; + if (disp == (int32_t)disp) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 5); + tcg_out32(s, disp); + return; + } + + /* Try for an absolute address encoding. This requires the + use of the MODRM+SIB encoding and is therefore larger than + rip-relative addressing. */ + if (offset == (int32_t)offset) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (4 << 3) | 5); + tcg_out32(s, offset); + return; + } + + /* ??? The memory isn't directly addressable. */ + tcg_abort(); + } else { + /* Absolute address. */ + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (r << 3) | 5); + tcg_out32(s, offset); + return; + } + } + + /* Find the length of the immediate addend. Note that the encoding + that would be used for (%ebp) indicates absolute addressing. */ + if (rm < 0) { + mod = 0, len = 4, rm = 5; + } else if (offset == 0 && LOWREGMASK(rm) != TCG_REG_EBP) { + mod = 0, len = 0; + } else if (offset == (int8_t)offset) { + mod = 0x40, len = 1; + } else { + mod = 0x80, len = 4; + } + + /* Use a single byte MODRM format if possible. Note that the encoding + that would be used for %esp is the escape to the two byte form. */ + if (index < 0 && LOWREGMASK(rm) != TCG_REG_ESP) { + /* Single byte MODRM format. */ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); + } else { + /* Two byte MODRM+SIB format. */ + + /* Note that the encoding that would place %esp into the index + field indicates no index register. In 64-bit mode, the REX.X + bit counts, so %r12 can be used as the index. */ + if (index < 0) { + index = 4; + } else { + assert(index != TCG_REG_ESP); + } + + tcg_out_opc(s, opc, r, rm, index); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (shift << 6) | (LOWREGMASK(index) << 3) | LOWREGMASK(rm)); + } + + if (len == 1) { + tcg_out8(s, offset); + } else if (len == 4) { + tcg_out32(s, offset); + } +} + +/* A simplification of the above with no index or shift. */ +static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, + int rm, tcg_target_long offset) +{ + tcg_out_modrm_sib_offset(s, opc, r, rm, -1, 0, offset); +} + +/* Generate dest op= src. Uses the same ARITH_* codes as tgen_arithi. */ +static inline void tgen_arithr(TCGContext *s, int subop, int dest, int src) +{ + /* Propagate an opcode prefix, such as P_REXW. */ + int ext = subop & ~0x7; + subop &= 0x7; + + tcg_out_modrm(s, OPC_ARITH_GvEv + (subop << 3) + ext, dest, src); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + if (arg != ret) { + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm(s, opc, ret, arg); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + if (arg == 0) { + tgen_arithr(s, ARITH_XOR, ret, ret); + return; + } else if (arg == (uint32_t)arg || type == TCG_TYPE_I32) { + tcg_out_opc(s, OPC_MOVL_Iv + LOWREGMASK(ret), 0, ret, 0); + tcg_out32(s, arg); + } else if (arg == (int32_t)arg) { + tcg_out_modrm(s, OPC_MOVL_EvIz + P_REXW, 0, ret); + tcg_out32(s, arg); + } else { + tcg_out_opc(s, OPC_MOVL_Iv + P_REXW + LOWREGMASK(ret), 0, ret, 0); + tcg_out32(s, arg); + tcg_out32(s, arg >> 31 >> 1); + } +} + +static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) +{ + if (val == (int8_t)val) { + tcg_out_opc(s, OPC_PUSH_Ib, 0, 0, 0); + tcg_out8(s, val); + } else if (val == (int32_t)val) { + tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0); + tcg_out32(s, val); + } else { + tcg_abort(); + } +} + +static inline void tcg_out_push(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_PUSH_r32 + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_pop(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_POP_r32 + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, ret, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, + int arg1, tcg_target_long arg2) +{ + int opc = OPC_MOVL_EvGv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, arg, arg1, arg2); +} + +static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count) +{ + /* Propagate an opcode prefix, such as P_DATA16. */ + int ext = subopc & ~0x7; + subopc &= 0x7; + + if (count == 1) { + tcg_out_modrm(s, OPC_SHIFT_1 + ext, subopc, reg); + } else { + tcg_out_modrm(s, OPC_SHIFT_Ib + ext, subopc, reg); + tcg_out8(s, count); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_rolw_8(TCGContext *s, int reg) +{ + tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8); +} + +static inline void tcg_out_ext8u(TCGContext *s, int dest, int src) +{ + /* movzbl */ + assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); +} + +static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsbl */ + assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); +} + +static inline void tcg_out_ext16u(TCGContext *s, int dest, int src) +{ + /* movzwl */ + tcg_out_modrm(s, OPC_MOVZWL, dest, src); +} + +static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsw[lq] */ + tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src); +} + +static inline void tcg_out_ext32u(TCGContext *s, int dest, int src) +{ + /* 32-bit mov zero extends. */ + tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src); +} + +static inline void tcg_out_ext32s(TCGContext *s, int dest, int src) +{ + tcg_out_modrm(s, OPC_MOVSLQ, dest, src); +} + +static inline void tcg_out_bswap64(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); +} + +static void tgen_arithi(TCGContext *s, int c, int r0, + tcg_target_long val, int cf) +{ + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64) { + rexw = c & -8; + c &= 7; + } + + /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce + partial flags update stalls on Pentium4 and are not recommended + by current Intel optimization manuals. */ + if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) { + int is_inc = (c == ARITH_ADD) ^ (val < 0); + if (TCG_TARGET_REG_BITS == 64) { + /* The single-byte increment encodings are re-tasked as the + REX prefixes. Use the MODRM encoding. */ + tcg_out_modrm(s, OPC_GRP5 + rexw, + (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); + } else { + tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); + } + return; + } + + if (c == ARITH_AND) { + if (TCG_TARGET_REG_BITS == 64) { + if (val == 0xffffffffu) { + tcg_out_ext32u(s, r0, r0); + return; + } + if (val == (uint32_t)val) { + /* AND with no high bits set can use a 32-bit operation. */ + rexw = 0; + } + } + if (val == 0xffu && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) { + tcg_out_ext8u(s, r0, r0); + return; + } + if (val == 0xffffu) { + tcg_out_ext16u(s, r0, r0); + return; + } + } + + if (val == (int8_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0); + tcg_out8(s, val); + return; + } + if (rexw == 0 || val == (int32_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0); + tcg_out32(s, val); + return; + } + + tcg_abort(); +} + +static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + if (val != 0) { + tgen_arithi(s, ARITH_ADD + P_REXW, reg, val, 0); + } +} + +/* Use SMALL != 0 to force a short forward branch. */ +static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small) +{ + int32_t val, val1; + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) { + val = l->u.value - (tcg_target_long)s->code_ptr; + val1 = val - 2; + if ((int8_t)val1 == val1) { + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } + tcg_out8(s, val1); + } else { + if (small) { + tcg_abort(); + } + if (opc == -1) { + tcg_out8(s, OPC_JMP_long); + tcg_out32(s, val - 5); + } else { + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); + tcg_out32(s, val - 6); + } + } + } else if (small) { + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } + tcg_out_reloc(s, s->code_ptr, R_386_PC8, label_index, -1); + s->code_ptr += 1; + } else { + if (opc == -1) { + tcg_out8(s, OPC_JMP_long); + } else { + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); + } + tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4); + s->code_ptr += 4; + } +} + +static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, + int const_arg2, int rexw) +{ + if (const_arg2) { + if (arg2 == 0) { + /* test r, r */ + tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); + } else { + tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0); + } + } else { + tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); + } +} + +static void tcg_out_brcond32(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_brcond64(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); +} +#else +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, + const int *const_args, int small) +{ + int label_next; + label_next = gen_new_label(); + switch(args[4]) { + case TCG_COND_EQ: + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + label_next, 1); + tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], + args[5], small); + break; + case TCG_COND_NE: + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + args[5], small); + tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], + args[5], small); + break; + case TCG_COND_LT: + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_LE: + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_GT: + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_GE: + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_LTU: + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_LEU: + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_GTU: + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + args[5], small); + break; + case TCG_COND_GEU: + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + args[5], small); + break; + default: + tcg_abort(); + } + tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr); +} +#endif + +static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} +#else +static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + TCGArg new_args[6]; + int label_true, label_over; + + memcpy(new_args, args+1, 5*sizeof(TCGArg)); + + if (args[0] == args[1] || args[0] == args[2] + || (!const_args[3] && args[0] == args[3]) + || (!const_args[4] && args[0] == args[4])) { + /* When the destination overlaps with one of the argument + registers, don't do anything tricky. */ + label_true = gen_new_label(); + label_over = gen_new_label(); + + new_args[5] = label_true; + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + tcg_out_jxx(s, JCC_JMP, label_over, 1); + tcg_out_label(s, label_true, (tcg_target_long)s->code_ptr); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); + tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr); + } else { + /* When the destination does not overlap one of the arguments, + clear the destination first, jump if cond false, and emit an + increment in the true case. This results in smaller code. */ + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + + label_over = gen_new_label(); + new_args[4] = tcg_invert_cond(new_args[4]); + new_args[5] = label_over; + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tgen_arithi(s, ARITH_ADD, args[0], 1, 0); + tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr); + } +} +#endif + +static void tcg_out_branch(TCGContext *s, int call, tcg_target_long dest) +{ + tcg_target_long disp = dest - (tcg_target_long)s->code_ptr - 5; + + if (disp == (int32_t)disp) { + tcg_out_opc(s, call ? OPC_CALL_Jz : OPC_JMP_long, 0, 0, 0); + tcg_out32(s, disp); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, dest); + tcg_out_modrm(s, OPC_GRP5, + call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev, TCG_REG_R10); + } +} + +static inline void tcg_out_calli(TCGContext *s, tcg_target_long dest) +{ + tcg_out_branch(s, 1, dest); +} + +static void tcg_out_jmp(TCGContext *s, tcg_target_long dest) +{ + tcg_out_branch(s, 0, dest); +} + +#if defined(CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; + +/* Perform the TLB load and compare. + + Inputs: + ADDRLO_IDX contains the index into ARGS of the low part of the + address; the high part of the address is at ADDR_LOW_IDX+1. + + MEM_INDEX and S_BITS are the memory context and log2 size of the load. + + WHICH is the offset into the CPUTLBEntry structure of the slot to read. + This should be offsetof addr_read or addr_write. + + Outputs: + LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses) + positions of the displacements of forward jumps to the TLB miss case. + + First argument register is loaded with the low part of the address. + In the TLB hit case, it has been adjusted as indicated by the TLB + and so is a host address. In the TLB miss case, it continues to + hold a guest address. + + Second argument register is clobbered. */ + +static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx, + int mem_index, int s_bits, + const TCGArg *args, + uint8_t **label_ptr, int which) +{ + const int addrlo = args[addrlo_idx]; + const int r0 = tcg_target_call_iarg_regs[0]; + const int r1 = tcg_target_call_iarg_regs[1]; + TCGType type = TCG_TYPE_I32; + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64 && TARGET_LONG_BITS == 64) { + type = TCG_TYPE_I64; + rexw = P_REXW; + } + + tcg_out_mov(s, type, r1, addrlo); + tcg_out_mov(s, type, r0, addrlo); + + tcg_out_shifti(s, SHIFT_SHR + rexw, r1, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + tgen_arithi(s, ARITH_AND + rexw, r0, + TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0); + tgen_arithi(s, ARITH_AND + rexw, r1, + (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); + + tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r1, TCG_AREG0, r1, 0, + offsetof(CPUState, tlb_table[mem_index][0]) + + which); + + /* cmp 0(r1), r0 */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r0, r1, 0); + + tcg_out_mov(s, type, r0, addrlo); + + /* jne label1 */ + tcg_out8(s, OPC_JCC_short + JCC_JNE); + label_ptr[0] = s->code_ptr; + s->code_ptr++; + + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + /* cmp 4(r1), addrhi */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv, args[addrlo_idx+1], r1, 4); + + /* jne label1 */ + tcg_out8(s, OPC_JCC_short + JCC_JNE); + label_ptr[1] = s->code_ptr; + s->code_ptr++; + } + + /* TLB Hit. */ + + /* add addend(r1), r0 */ + tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r0, r1, + offsetof(CPUTLBEntry, addend) - which); +} +#endif + +static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi, + int base, tcg_target_long ofs, int sizeop) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 1; +#else + const int bswap = 0; +#endif + switch (sizeop) { + case 0: + tcg_out_modrm_offset(s, OPC_MOVZBL, datalo, base, ofs); + break; + case 0 | 4: + tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW, datalo, base, ofs); + break; + case 1: + tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); + if (bswap) { + tcg_out_rolw_8(s, datalo); + } + break; + case 1 | 4: + if (bswap) { + tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); + tcg_out_rolw_8(s, datalo); + tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); + } else { + tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW, datalo, base, ofs); + } + break; + case 2: + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + if (bswap) { + tcg_out_bswap32(s, datalo); + } + break; +#if TCG_TARGET_REG_BITS == 64 + case 2 | 4: + if (bswap) { + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_bswap32(s, datalo); + tcg_out_ext32s(s, datalo, datalo); + } else { + tcg_out_modrm_offset(s, OPC_MOVSLQ, datalo, base, ofs); + } + break; +#endif + case 3: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_ld(s, TCG_TYPE_I64, datalo, base, ofs); + if (bswap) { + tcg_out_bswap64(s, datalo); + } + } else { + if (bswap) { + int t = datalo; + datalo = datahi; + datahi = t; + } + if (base != datalo) { + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); + } else { + tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + } + if (bswap) { + tcg_out_bswap32(s, datalo); + tcg_out_bswap32(s, datahi); + } + } + break; + default: + tcg_abort(); + } +} + +/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and + EAX. It will be useful once fixed registers globals are less + common. */ +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, + int opc) +{ + int data_reg, data_reg2 = 0; + int addrlo_idx; +#if defined(CONFIG_SOFTMMU) + int mem_index, s_bits, arg_idx; + uint8_t *label_ptr[3]; +#endif + + data_reg = args[0]; + addrlo_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && opc == 3) { + data_reg2 = args[1]; + addrlo_idx = 2; + } + +#if defined(CONFIG_SOFTMMU) + mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)]; + s_bits = opc & 3; + + tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, + label_ptr, offsetof(CPUTLBEntry, addr_read)); + + /* TLB Hit. */ + tcg_out_qemu_ld_direct(s, data_reg, data_reg2, + tcg_target_call_iarg_regs[0], 0, opc); + + /* jmp label2 */ + tcg_out8(s, OPC_JMP_short); + label_ptr[2] = s->code_ptr; + s->code_ptr++; + + /* TLB Miss. */ + + /* label1: */ + *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + *label_ptr[1] = s->code_ptr - label_ptr[1] - 1; + } + + /* XXX: move that code at the end of the TB */ + /* The first argument is already loaded with addrlo. */ + arg_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx++], + args[addrlo_idx + 1]); + } + tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx], + mem_index); + tcg_out_calli(s, (tcg_target_long)qemu_ld_helpers[s_bits]); + + switch(opc) { + case 0 | 4: + tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW); + break; + case 1 | 4: + tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW); + break; + case 0: + tcg_out_ext8u(s, data_reg, TCG_REG_EAX); + break; + case 1: + tcg_out_ext16u(s, data_reg, TCG_REG_EAX); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + break; +#if TCG_TARGET_REG_BITS == 64 + case 2 | 4: + tcg_out_ext32s(s, data_reg, TCG_REG_EAX); + break; +#endif + case 3: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX); + } else if (data_reg == TCG_REG_EDX) { + /* xchg %edx, %eax */ + tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EAX); + } else { + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EDX); + } + break; + default: + tcg_abort(); + } + + /* label2: */ + *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; +#else + { + int32_t offset = GUEST_BASE; + int base = args[addrlo_idx]; + + if (TCG_TARGET_REG_BITS == 64) { + /* ??? We assume all operations have left us with register + contents that are zero extended. So far this appears to + be true. If we want to enforce this, we can either do + an explicit zero-extension here, or (if GUEST_BASE == 0) + use the ADDR32 prefix. For now, do nothing. */ + + if (offset != GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base); + base = TCG_REG_RDI, offset = 0; + } + } + + tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, opc); + } +#endif +} + +static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi, + int base, tcg_target_long ofs, int sizeop) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 1; +#else + const int bswap = 0; +#endif + /* ??? Ideally we wouldn't need a scratch register. For user-only, + we could perform the bswap twice to restore the original value + instead of moving to the scratch. But as it is, the L constraint + means that the second argument reg is definitely free here. */ + int scratch = tcg_target_call_iarg_regs[1]; + + switch (sizeop) { + case 0: + tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R, datalo, base, ofs); + break; + case 1: + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_rolw_8(s, scratch); + datalo = scratch; + } + tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16, datalo, base, ofs); + break; + case 2: + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + datalo = scratch; + } + tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); + break; + case 3: + if (TCG_TARGET_REG_BITS == 64) { + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I64, scratch, datalo); + tcg_out_bswap64(s, scratch); + datalo = scratch; + } + tcg_out_st(s, TCG_TYPE_I64, datalo, base, ofs); + } else if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi); + tcg_out_bswap32(s, scratch); + tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs); + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs + 4); + } else { + tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_st(s, TCG_TYPE_I32, datahi, base, ofs + 4); + } + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, + int opc) +{ + int data_reg, data_reg2 = 0; + int addrlo_idx; +#if defined(CONFIG_SOFTMMU) + int mem_index, s_bits; + int stack_adjust; + uint8_t *label_ptr[3]; +#endif + + data_reg = args[0]; + addrlo_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && opc == 3) { + data_reg2 = args[1]; + addrlo_idx = 2; + } + +#if defined(CONFIG_SOFTMMU) + mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)]; + s_bits = opc; + + tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, + label_ptr, offsetof(CPUTLBEntry, addr_write)); + + /* TLB Hit. */ + tcg_out_qemu_st_direct(s, data_reg, data_reg2, + tcg_target_call_iarg_regs[0], 0, opc); + + /* jmp label2 */ + tcg_out8(s, OPC_JMP_short); + label_ptr[2] = s->code_ptr; + s->code_ptr++; + + /* TLB Miss. */ + + /* label1: */ + *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + *label_ptr[1] = s->code_ptr - label_ptr[1] - 1; + } + + /* XXX: move that code at the end of the TB */ + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, (opc == 3 ? TCG_TYPE_I64 : TCG_TYPE_I32), + TCG_REG_RSI, data_reg); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RDX, mem_index); + stack_adjust = 0; + } else if (TARGET_LONG_BITS == 32) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, data_reg); + if (opc == 3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg2); + tcg_out_pushi(s, mem_index); + stack_adjust = 4; + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); + stack_adjust = 0; + } + } else { + if (opc == 3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]); + tcg_out_pushi(s, mem_index); + tcg_out_push(s, data_reg2); + tcg_out_push(s, data_reg); + stack_adjust = 12; + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]); + switch(opc) { + case 0: + tcg_out_ext8u(s, TCG_REG_ECX, data_reg); + break; + case 1: + tcg_out_ext16u(s, TCG_REG_ECX, data_reg); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg); + break; + } + tcg_out_pushi(s, mem_index); + stack_adjust = 4; + } + } + + tcg_out_calli(s, (tcg_target_long)qemu_st_helpers[s_bits]); + + if (stack_adjust == (TCG_TARGET_REG_BITS / 8)) { + /* Pop and discard. This is 2 bytes smaller than the add. */ + tcg_out_pop(s, TCG_REG_ECX); + } else if (stack_adjust != 0) { + tcg_out_addi(s, TCG_REG_ESP, stack_adjust); + } + + /* label2: */ + *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; +#else + { + int32_t offset = GUEST_BASE; + int base = args[addrlo_idx]; + + if (TCG_TARGET_REG_BITS == 64) { + /* ??? We assume all operations have left us with register + contents that are zero extended. So far this appears to + be true. If we want to enforce this, we can either do + an explicit zero-extension here, or (if GUEST_BASE == 0) + use the ADDR32 prefix. For now, do nothing. */ + + if (offset != GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base); + base = TCG_REG_RDI, offset = 0; + } + } + + tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, opc); + } +#endif +} + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + int c, rexw = 0; + +#if TCG_TARGET_REG_BITS == 64 +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i64): \ + rexw = P_REXW; /* FALLTHRU */ \ + case glue(glue(INDEX_op_, x), _i32) +#else +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i32) +#endif + + switch(opc) { + case INDEX_op_exit_tb: + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, args[0]); + tcg_out_jmp(s, (tcg_target_long) tb_ret_addr); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_out8(s, OPC_JMP_long); /* jmp im */ + s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; + tcg_out32(s, 0); + } else { + /* indirect jump method */ + tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, + (tcg_target_long)(s->tb_next + args[0])); + } + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + case INDEX_op_call: + if (const_args[0]) { + tcg_out_calli(s, args[0]); + } else { + /* call *reg */ + tcg_out_modrm(s, OPC_GRP5, EXT5_CALLN_Ev, args[0]); + } + break; + case INDEX_op_jmp: + if (const_args[0]) { + tcg_out_jmp(s, args[0]); + } else { + /* jmp *reg */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, args[0]); + } + break; + case INDEX_op_br: + tcg_out_jxx(s, JCC_JMP, args[0], 0); + break; + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + OP_32_64(ld8u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZBL, args[0], args[1], args[2]); + break; + OP_32_64(ld8s): + tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, args[0], args[1], args[2]); + break; + OP_32_64(ld16u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZWL, args[0], args[1], args[2]); + break; + OP_32_64(ld16s): + tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, args[0], args[1], args[2]); + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ld32u_i64: +#endif + case INDEX_op_ld_i32: + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + OP_32_64(st8): + tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, + args[0], args[1], args[2]); + break; + OP_32_64(st16): + tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, + args[0], args[1], args[2]); + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_st32_i64: +#endif + case INDEX_op_st_i32: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + OP_32_64(add): + /* For 3-operand addition, use LEA. */ + if (args[0] != args[1]) { + TCGArg a0 = args[0], a1 = args[1], a2 = args[2], c3 = 0; + + if (const_args[2]) { + c3 = a2, a2 = -1; + } else if (a0 == a2) { + /* Watch out for dest = src + dest, since we've removed + the matching constraint on the add. */ + tgen_arithr(s, ARITH_ADD + rexw, a0, a1); + break; + } + + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); + break; + } + c = ARITH_ADD; + goto gen_arith; + OP_32_64(sub): + c = ARITH_SUB; + goto gen_arith; + OP_32_64(and): + c = ARITH_AND; + goto gen_arith; + OP_32_64(or): + c = ARITH_OR; + goto gen_arith; + OP_32_64(xor): + c = ARITH_XOR; + goto gen_arith; + gen_arith: + if (const_args[2]) { + tgen_arithi(s, c + rexw, args[0], args[2], 0); + } else { + tgen_arithr(s, c + rexw, args[0], args[2]); + } + break; + + OP_32_64(mul): + if (const_args[2]) { + int32_t val; + val = args[2]; + if (val == (int8_t)val) { + tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, args[0], args[0]); + tcg_out8(s, val); + } else { + tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, args[0], args[0]); + tcg_out32(s, val); + } + } else { + tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, args[0], args[2]); + } + break; + + OP_32_64(div2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); + break; + OP_32_64(divu2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); + break; + + OP_32_64(shl): + c = SHIFT_SHL; + goto gen_shift; + OP_32_64(shr): + c = SHIFT_SHR; + goto gen_shift; + OP_32_64(sar): + c = SHIFT_SAR; + goto gen_shift; + OP_32_64(rotl): + c = SHIFT_ROL; + goto gen_shift; + OP_32_64(rotr): + c = SHIFT_ROR; + goto gen_shift; + gen_shift: + if (const_args[2]) { + tcg_out_shifti(s, c + rexw, args[0], args[2]); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, args[0]); + } + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], + args[3], 0); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond32(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + OP_32_64(bswap16): + tcg_out_rolw_8(s, args[0]); + break; + OP_32_64(bswap32): + tcg_out_bswap32(s, args[0]); + break; + + OP_32_64(neg): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, args[0]); + break; + OP_32_64(not): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, args[0]); + break; + + OP_32_64(ext8s): + tcg_out_ext8s(s, args[0], args[1], rexw); + break; + OP_32_64(ext16s): + tcg_out_ext16s(s, args[0], args[1], rexw); + break; + OP_32_64(ext8u): + tcg_out_ext8u(s, args[0], args[1]); + break; + OP_32_64(ext16u): + tcg_out_ext16u(s, args[0], args[1]); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_qemu_ld32u: +#endif + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args, const_args, 0); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args, const_args); + break; + case INDEX_op_mulu2_i32: + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_MUL, args[3]); + break; + case INDEX_op_add2_i32: + if (const_args[4]) { + tgen_arithi(s, ARITH_ADD, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_ADD, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_ADC, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_ADC, args[1], args[5]); + } + break; + case INDEX_op_sub2_i32: + if (const_args[4]) { + tgen_arithi(s, ARITH_SUB, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_SUB, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_SBB, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_SBB, args[1], args[5]); + } + break; +#else /* TCG_TARGET_REG_BITS == 64 */ + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ld32s_i64: + tcg_out_modrm_offset(s, OPC_MOVSLQ, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, 2 | 4); + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1], + args[3], 0); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond64(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0]); + break; + case INDEX_op_ext32u_i64: + tcg_out_ext32u(s, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tcg_out_ext32s(s, args[0], args[1]); + break; +#endif + + default: + tcg_abort(); + } + +#undef OP_32_64 +} + +static const TCGTargetOpDef x86_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "q", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "ri" } }, + { INDEX_op_sub_i32, { "r", "0", "ri" } }, + { INDEX_op_mul_i32, { "r", "0", "ri" } }, + { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } }, + { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } }, + { INDEX_op_and_i32, { "r", "0", "ri" } }, + { INDEX_op_or_i32, { "r", "0", "ri" } }, + { INDEX_op_xor_i32, { "r", "0", "ri" } }, + + { INDEX_op_shl_i32, { "r", "0", "ci" } }, + { INDEX_op_shr_i32, { "r", "0", "ci" } }, + { INDEX_op_sar_i32, { "r", "0", "ci" } }, + { INDEX_op_rotl_i32, { "r", "0", "ci" } }, + { INDEX_op_rotr_i32, { "r", "0", "ci" } }, + + { INDEX_op_brcond_i32, { "r", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "0" } }, + { INDEX_op_bswap32_i32, { "r", "0" } }, + + { INDEX_op_neg_i32, { "r", "0" } }, + + { INDEX_op_not_i32, { "r", "0" } }, + + { INDEX_op_ext8s_i32, { "r", "q" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "q" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_setcond_i32, { "q", "r", "ri" } }, + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, + { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, +#else + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "0", "re" } }, + { INDEX_op_mul_i64, { "r", "0", "re" } }, + { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_sub_i64, { "r", "0", "re" } }, + { INDEX_op_and_i64, { "r", "0", "reZ" } }, + { INDEX_op_or_i64, { "r", "0", "re" } }, + { INDEX_op_xor_i64, { "r", "0", "re" } }, + + { INDEX_op_shl_i64, { "r", "0", "ci" } }, + { INDEX_op_shr_i64, { "r", "0", "ci" } }, + { INDEX_op_sar_i64, { "r", "0", "ci" } }, + { INDEX_op_rotl_i64, { "r", "0", "ci" } }, + { INDEX_op_rotr_i64, { "r", "0", "ci" } }, + + { INDEX_op_brcond_i64, { "r", "re" } }, + { INDEX_op_setcond_i64, { "r", "r", "re" } }, + + { INDEX_op_bswap16_i64, { "r", "0" } }, + { INDEX_op_bswap32_i64, { "r", "0" } }, + { INDEX_op_bswap64_i64, { "r", "0" } }, + { INDEX_op_neg_i64, { "r", "0" } }, + { INDEX_op_not_i64, { "r", "0" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, +#endif + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L" } }, + + { INDEX_op_qemu_st8, { "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L" } }, +#elif TARGET_LONG_BITS <= TCG_TARGET_REG_BITS + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "r", "L" } }, + + { INDEX_op_qemu_st8, { "cb", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L", "L" } }, +#else + { INDEX_op_qemu_ld8u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, + { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } }, + + { INDEX_op_qemu_st8, { "cb", "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L", "L", "L" } }, +#endif + { -1 }, +}; + +static int tcg_target_callee_save_regs[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + /* TCG_REG_R14, */ /* Currently used for the global env. */ + TCG_REG_R15, +#else + /* TCG_REG_EBP, */ /* Currently used for the global env. */ + TCG_REG_EBX, + TCG_REG_ESI, + TCG_REG_EDI, +#endif +}; + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i, frame_size, push_size, stack_addend; + + /* TB prologue */ + + /* Save all callee saved registers. */ + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_push(s, tcg_target_callee_save_regs[i]); + } + + /* Reserve some stack space. */ + push_size = 1 + ARRAY_SIZE(tcg_target_callee_save_regs); + push_size *= TCG_TARGET_REG_BITS / 8; + + frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE; + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + stack_addend = frame_size - push_size; + tcg_out_addi(s, TCG_REG_ESP, -stack_addend); + + /* jmp *tb. */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]); + + /* TB epilogue */ + tb_ret_addr = s->code_ptr; + + tcg_out_addi(s, TCG_REG_ESP, stack_addend); + + for (i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { + tcg_out_pop(s, tcg_target_callee_save_regs[i]); + } + tcg_out_opc(s, OPC_RET, 0, 0, 0); +} + +static void tcg_target_init(TCGContext *s) +{ +#if !defined(CONFIG_USER_ONLY) + /* fail safe */ + if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) + tcg_abort(); +#endif + + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + } else { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); + } + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RDI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RSI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + } + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_ESP); + + tcg_add_target_add_op_defs(x86_op_defs); +} diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h new file mode 100644 index 0000000..bfafbfc --- /dev/null +++ b/tcg/i386/tcg-target.h @@ -0,0 +1,126 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_I386 1 + +#if defined(__x86_64__) +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif +//#define TCG_TARGET_WORDS_BIGENDIAN + +#if TCG_TARGET_REG_BITS == 64 +# define TCG_TARGET_NB_REGS 16 +#else +# define TCG_TARGET_NB_REGS 8 +#endif + +enum { + TCG_REG_EAX = 0, + TCG_REG_ECX, + TCG_REG_EDX, + TCG_REG_EBX, + TCG_REG_ESP, + TCG_REG_EBP, + TCG_REG_ESI, + TCG_REG_EDI, + + /* 64-bit registers; always define the symbols to avoid + too much if-deffing. */ + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_RAX = TCG_REG_EAX, + TCG_REG_RCX = TCG_REG_ECX, + TCG_REG_RDX = TCG_REG_EDX, + TCG_REG_RBX = TCG_REG_EBX, + TCG_REG_RSP = TCG_REG_ESP, + TCG_REG_RBP = TCG_REG_EBP, + TCG_REG_RSI = TCG_REG_ESI, + TCG_REG_RDI = TCG_REG_EDI, +}; + +#define TCG_CT_CONST_S32 0x100 +#define TCG_CT_CONST_U32 0x200 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_ESP +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 0 + +/* optional instructions */ +#define TCG_TARGET_HAS_div2_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_not_i32 +// #define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div2_i64 +#define TCG_TARGET_HAS_rot_i64 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_neg_i64 +#define TCG_TARGET_HAS_not_i64 +// #define TCG_TARGET_HAS_andc_i64 +// #define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif + +#define TCG_TARGET_HAS_GUEST_BASE + +/* Note: must be synced with dyngen-exec.h */ +#if TCG_TARGET_REG_BITS == 64 +# define TCG_AREG0 TCG_REG_R14 +#else +# define TCG_AREG0 TCG_REG_EBP +#endif + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ +} diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c new file mode 100644 index 0000000..8dac7f7 --- /dev/null +++ b/tcg/ia64/tcg-target.c @@ -0,0 +1,2390 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009-2010 Aurelien Jarno <aurelien@aurel32.net> + * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Register definitions + */ + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", + "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63", +}; +#endif + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R55 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif +#ifndef GUEST_BASE +#define GUEST_BASE 0 +#endif + +/* Branch registers */ +enum { + TCG_REG_B0 = 0, + TCG_REG_B1, + TCG_REG_B2, + TCG_REG_B3, + TCG_REG_B4, + TCG_REG_B5, + TCG_REG_B6, + TCG_REG_B7, +}; + +/* Floating point registers */ +enum { + TCG_REG_F0 = 0, + TCG_REG_F1, + TCG_REG_F2, + TCG_REG_F3, + TCG_REG_F4, + TCG_REG_F5, + TCG_REG_F6, + TCG_REG_F7, + TCG_REG_F8, + TCG_REG_F9, + TCG_REG_F10, + TCG_REG_F11, + TCG_REG_F12, + TCG_REG_F13, + TCG_REG_F14, + TCG_REG_F15, +}; + +/* Predicate registers */ +enum { + TCG_REG_P0 = 0, + TCG_REG_P1, + TCG_REG_P2, + TCG_REG_P3, + TCG_REG_P4, + TCG_REG_P5, + TCG_REG_P6, + TCG_REG_P7, + TCG_REG_P8, + TCG_REG_P9, + TCG_REG_P10, + TCG_REG_P11, + TCG_REG_P12, + TCG_REG_P13, + TCG_REG_P14, + TCG_REG_P15, +}; + +/* Application registers */ +enum { + TCG_REG_PFS = 64, +}; + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R34, + TCG_REG_R35, + TCG_REG_R36, + TCG_REG_R37, + TCG_REG_R38, + TCG_REG_R39, + TCG_REG_R40, + TCG_REG_R41, + TCG_REG_R42, + TCG_REG_R43, + TCG_REG_R44, + TCG_REG_R45, + TCG_REG_R46, + TCG_REG_R47, + TCG_REG_R48, + TCG_REG_R49, + TCG_REG_R50, + TCG_REG_R51, + TCG_REG_R52, + TCG_REG_R53, + TCG_REG_R54, + TCG_REG_R55, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11 +}; + +static const int tcg_target_call_iarg_regs[8] = { + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_R8, + TCG_REG_R9 +}; + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + return 8; +} + +/* + * opcode formation + */ + +/* bundle templates: stops (double bar in the IA64 manual) are marked with + an uppercase letter. */ +enum { + mii = 0x00, + miI = 0x01, + mIi = 0x02, + mII = 0x03, + mlx = 0x04, + mLX = 0x05, + mmi = 0x08, + mmI = 0x09, + Mmi = 0x0a, + MmI = 0x0b, + mfi = 0x0c, + mfI = 0x0d, + mmf = 0x0e, + mmF = 0x0f, + mib = 0x10, + miB = 0x11, + mbb = 0x12, + mbB = 0x13, + bbb = 0x16, + bbB = 0x17, + mmb = 0x18, + mmB = 0x19, + mfb = 0x1c, + mfB = 0x1d, +}; + +enum { + OPC_ADD_A1 = 0x10000000000ull, + OPC_AND_A1 = 0x10060000000ull, + OPC_AND_A3 = 0x10160000000ull, + OPC_ANDCM_A1 = 0x10068000000ull, + OPC_ANDCM_A3 = 0x10168000000ull, + OPC_ADDS_A4 = 0x10800000000ull, + OPC_ADDL_A5 = 0x12000000000ull, + OPC_ALLOC_M34 = 0x02c00000000ull, + OPC_BR_DPTK_FEW_B1 = 0x08400000000ull, + OPC_BR_SPTK_MANY_B1 = 0x08000001000ull, + OPC_BR_SPTK_MANY_B4 = 0x00100001000ull, + OPC_BR_CALL_SPTK_MANY_B5 = 0x02100001000ull, + OPC_BR_RET_SPTK_MANY_B4 = 0x00108001100ull, + OPC_BRL_SPTK_MANY_X3 = 0x18000001000ull, + OPC_CMP_LT_A6 = 0x18000000000ull, + OPC_CMP_LTU_A6 = 0x1a000000000ull, + OPC_CMP_EQ_A6 = 0x1c000000000ull, + OPC_CMP4_LT_A6 = 0x18400000000ull, + OPC_CMP4_LTU_A6 = 0x1a400000000ull, + OPC_CMP4_EQ_A6 = 0x1c400000000ull, + OPC_DEP_Z_I12 = 0x0a600000000ull, + OPC_EXTR_I11 = 0x0a400002000ull, + OPC_EXTR_U_I11 = 0x0a400000000ull, + OPC_FCVT_FX_TRUNC_S1_F10 = 0x004d0000000ull, + OPC_FCVT_FXU_TRUNC_S1_F10 = 0x004d8000000ull, + OPC_FCVT_XF_F11 = 0x000e0000000ull, + OPC_FMA_S1_F1 = 0x10400000000ull, + OPC_FNMA_S1_F1 = 0x18400000000ull, + OPC_FRCPA_S1_F6 = 0x00600000000ull, + OPC_GETF_SIG_M19 = 0x08708000000ull, + OPC_LD1_M1 = 0x08000000000ull, + OPC_LD1_M3 = 0x0a000000000ull, + OPC_LD2_M1 = 0x08040000000ull, + OPC_LD2_M3 = 0x0a040000000ull, + OPC_LD4_M1 = 0x08080000000ull, + OPC_LD4_M3 = 0x0a080000000ull, + OPC_LD8_M1 = 0x080c0000000ull, + OPC_LD8_M3 = 0x0a0c0000000ull, + OPC_MUX1_I3 = 0x0eca0000000ull, + OPC_NOP_B9 = 0x04008000000ull, + OPC_NOP_F16 = 0x00008000000ull, + OPC_NOP_I18 = 0x00008000000ull, + OPC_NOP_M48 = 0x00008000000ull, + OPC_MOV_I21 = 0x00e00100000ull, + OPC_MOV_RET_I21 = 0x00e00500000ull, + OPC_MOV_I22 = 0x00188000000ull, + OPC_MOV_I_I26 = 0x00150000000ull, + OPC_MOVL_X2 = 0x0c000000000ull, + OPC_OR_A1 = 0x10070000000ull, + OPC_SETF_EXP_M18 = 0x0c748000000ull, + OPC_SETF_SIG_M18 = 0x0c708000000ull, + OPC_SHL_I7 = 0x0f240000000ull, + OPC_SHR_I5 = 0x0f220000000ull, + OPC_SHR_U_I5 = 0x0f200000000ull, + OPC_SHRP_I10 = 0x0ac00000000ull, + OPC_SXT1_I29 = 0x000a0000000ull, + OPC_SXT2_I29 = 0x000a8000000ull, + OPC_SXT4_I29 = 0x000b0000000ull, + OPC_ST1_M4 = 0x08c00000000ull, + OPC_ST2_M4 = 0x08c40000000ull, + OPC_ST4_M4 = 0x08c80000000ull, + OPC_ST8_M4 = 0x08cc0000000ull, + OPC_SUB_A1 = 0x10028000000ull, + OPC_SUB_A3 = 0x10128000000ull, + OPC_UNPACK4_L_I2 = 0x0f860000000ull, + OPC_XMA_L_F2 = 0x1d000000000ull, + OPC_XOR_A1 = 0x10078000000ull, + OPC_ZXT1_I29 = 0x00080000000ull, + OPC_ZXT2_I29 = 0x00088000000ull, + OPC_ZXT4_I29 = 0x00090000000ull, +}; + +static inline uint64_t tcg_opc_a1(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a3(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x80) << 29) /* s */ + | ((imm & 0x7f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a4(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x2000) << 23) /* s */ + | ((imm & 0x1f80) << 20) /* imm6d */ + | ((imm & 0x007f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a5(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x200000) << 15) /* s */ + | ((imm & 0x1f0000) << 6) /* imm5c */ + | ((imm & 0x00ff80) << 20) /* imm9d */ + | ((imm & 0x00007f) << 13) /* imm7b */ + | ((r3 & 0x03) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a6(int qp, uint64_t opc, int p1, + int p2, int r2, int r3) +{ + return opc + | ((p2 & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((p1 & 0x3f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b1(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* s */ + | ((imm & 0x0fffff) << 13) /* imm20b */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b4(int qp, uint64_t opc, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b5(int qp, uint64_t opc, int b1, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + + +static inline uint64_t tcg_opc_b9(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f1(int qp, uint64_t opc, int f1, + int f3, int f4, int f2) +{ + return opc + | ((f4 & 0x7f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f2(int qp, uint64_t opc, int f1, + int f3, int f4, int f2) +{ + return opc + | ((f4 & 0x7f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f6(int qp, uint64_t opc, int f1, + int p2, int f2, int f3) +{ + return opc + | ((p2 & 0x3f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f10(int qp, uint64_t opc, int f1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f11(int qp, uint64_t opc, int f1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f16(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i2(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i3(int qp, uint64_t opc, int r1, + int r2, int mbtype) +{ + return opc + | ((mbtype & 0x0f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i5(int qp, uint64_t opc, int r1, + int r3, int r2) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i7(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i10(int qp, uint64_t opc, int r1, + int r2, int r3, uint64_t count) +{ + return opc + | ((count & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i11(int qp, uint64_t opc, int r1, + int r3, uint64_t pos, uint64_t len) +{ + return opc + | ((len & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((pos & 0x3f) << 14) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i12(int qp, uint64_t opc, int r1, + int r2, uint64_t pos, uint64_t len) +{ + return opc + | ((len & 0x3f) << 27) + | ((pos & 0x3f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i18(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i21(int qp, uint64_t opc, int b1, + int r2, uint64_t imm) +{ + return opc + | ((imm & 0x1ff) << 24) + | ((r2 & 0x7f) << 13) + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i22(int qp, uint64_t opc, int r1, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i26(int qp, uint64_t opc, int ar3, int r2) +{ + return opc + | ((ar3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i29(int qp, uint64_t opc, int r1, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_l2(uint64_t imm) +{ + return (imm & 0x7fffffffffc00000ull) >> 22; +} + +static inline uint64_t tcg_opc_l3(uint64_t imm) +{ + return (imm & 0x07fffffffff00000ull) >> 18; +} + +static inline uint64_t tcg_opc_m1(int qp, uint64_t opc, int r1, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m3(int qp, uint64_t opc, int r1, + int r3, uint64_t imm) +{ + return opc + | ((imm & 0x100) << 28) /* s */ + | ((imm & 0x080) << 20) /* i */ + | ((imm & 0x07f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m4(int qp, uint64_t opc, int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m18(int qp, uint64_t opc, int f1, int r2) +{ + return opc + | ((r2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m19(int qp, uint64_t opc, int r1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m34(int qp, uint64_t opc, int r1, + int sof, int sol, int sor) +{ + return opc + | ((sor & 0x0f) << 27) + | ((sol & 0x7f) << 20) + | ((sof & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m48(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x2(int qp, uint64_t opc, + int r1, uint64_t imm) +{ + return opc + | ((imm & 0x8000000000000000ull) >> 27) /* i */ + | (imm & 0x0000000000200000ull) /* ic */ + | ((imm & 0x00000000001f0000ull) << 6) /* imm5c */ + | ((imm & 0x000000000000ff80ull) << 20) /* imm9d */ + | ((imm & 0x000000000000007full) << 13) /* imm7b */ + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x3(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x0800000000000000ull) >> 23) /* i */ + | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ + | (qp & 0x3f); +} + + +/* + * Relocations + */ + +static inline void reloc_pcrel21b (void *pc, tcg_target_long target) +{ + uint64_t imm; + int64_t disp; + int slot; + + slot = (tcg_target_long) pc & 3; + pc = (void *)((tcg_target_long) pc & ~3); + + disp = target - (tcg_target_long) pc; + imm = (uint64_t) disp >> 4; + + switch(slot) { + case 0: + *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 8) & 0xfffffdc00003ffffull) + | ((imm & 0x100000) << 21) /* s */ + | ((imm & 0x0fffff) << 18); /* imm20b */ + break; + case 1: + *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xfffffffffffb8000ull) + | ((imm & 0x100000) >> 2) /* s */ + | ((imm & 0x0fffe0) >> 5); /* imm20b */ + *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 0) & 0x07ffffffffffffffull) + | ((imm & 0x00001f) << 59); /* imm20b */ + break; + case 2: + *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xf700000fffffffffull) + | ((imm & 0x100000) << 39) /* s */ + | ((imm & 0x0fffff) << 36); /* imm20b */ + break; + } +} + +static inline uint64_t get_reloc_pcrel21b (void *pc) +{ + int64_t low, high; + int slot; + + slot = (tcg_target_long) pc & 3; + pc = (void *)((tcg_target_long) pc & ~3); + + low = (*(uint64_t *)(pc + 0)); + high = (*(uint64_t *)(pc + 8)); + + switch(slot) { + case 0: + return ((low >> 21) & 0x100000) + /* s */ + ((low >> 18) & 0x0fffff); /* imm20b */ + case 1: + return ((high << 2) & 0x100000) + /* s */ + ((high << 5) & 0x0fffe0) + /* imm20b */ + ((low >> 59) & 0x00001f); /* imm20b */ + case 2: + return ((high >> 39) & 0x100000) + /* s */ + ((high >> 36) & 0x0fffff); /* imm20b */ + default: + tcg_abort(); + } +} + +static inline void reloc_pcrel60b (void *pc, tcg_target_long target) +{ + int64_t disp; + uint64_t imm; + + disp = target - (tcg_target_long) pc; + imm = (uint64_t) disp >> 4; + + *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xf700000fff800000ull) + | (imm & 0x0800000000000000ull) /* s */ + | ((imm & 0x07fffff000000000ull) >> 36) /* imm39 */ + | ((imm & 0x00000000000fffffull) << 36); /* imm20b */ + *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 0) & 0x00003fffffffffffull) + | ((imm & 0x0000000ffff00000ull) << 28); /* imm39 */ +} + +static inline uint64_t get_reloc_pcrel60b (void *pc) +{ + int64_t low, high; + + low = (*(uint64_t *)(pc + 0)); + high = (*(uint64_t *)(pc + 8)); + + return ((high) & 0x0800000000000000ull) + /* s */ + ((high >> 36) & 0x00000000000fffffull) + /* imm20b */ + ((high << 36) & 0x07fffff000000000ull) + /* imm39 */ + ((low >> 28) & 0x0000000ffff00000ull); /* imm39 */ +} + + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch (type) { + case R_IA64_PCREL21B: + reloc_pcrel21b(code_ptr, value); + break; + case R_IA64_PCREL60B: + reloc_pcrel60b(code_ptr, value); + default: + tcg_abort(); + } +} + +/* + * Constraints + */ + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); + break; + case 'I': + ct->ct |= TCG_CT_CONST_S22; + break; + case 'S': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); +#if defined(CONFIG_SOFTMMU) + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R56); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R57); +#endif + break; + case 'Z': + /* We are cheating a bit here, using the fact that the register + r0 is also the register number 0. Hence there is no need + to check for const_args in each instruction. */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_ZERO) && val == 0) + return 1; + else if ((ct & TCG_CT_CONST_S22) && val == ((int32_t)val << 10) >> 10) + return 1; + else + return 0; +} + +/* + * Code generation + */ + +static uint8_t *tb_ret_addr; + +static inline void tcg_out_bundle(TCGContext *s, int template, + uint64_t slot0, uint64_t slot1, + uint64_t slot2) +{ + template &= 0x1f; /* 5 bits */ + slot0 &= 0x1ffffffffffull; /* 41 bits */ + slot1 &= 0x1ffffffffffull; /* 41 bits */ + slot2 &= 0x1ffffffffffull; /* 41 bits */ + + *(uint64_t *)(s->code_ptr + 0) = (slot1 << 46) | (slot0 << 5) | template; + *(uint64_t *)(s->code_ptr + 8) = (slot2 << 23) | (slot1 >> 18); + s->code_ptr += 16; +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, 0, arg)); +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + TCGArg reg, tcg_target_long arg) +{ + tcg_out_bundle(s, mLX, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_l2 (arg), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); +} + +static inline void tcg_out_addi(TCGContext *s, TCGArg reg, tcg_target_long val) +{ + if (val == ((int32_t)val << 10) >> 10) { + tcg_out_bundle(s, MmI, + tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, + TCG_REG_R2, val, TCG_REG_R0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, + reg, TCG_REG_R2)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, val); + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, + reg, TCG_REG_R2)); + } +} + +static void tcg_out_br(TCGContext *s, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b1 (TCG_REG_P0, OPC_BR_SPTK_MANY_B1, + get_reloc_pcrel21b(s->code_ptr + 2))); + + if (l->has_value) { + reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value); + } else { + tcg_out_reloc(s, (s->code_ptr - 16) + 2, + R_IA64_PCREL21B, label_index, 0); + } +} + +static inline void tcg_out_call(TCGContext *s, TCGArg addr) +{ + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, addr), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R3, 8, addr), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B6, TCG_REG_R2, 0)); + tcg_out_bundle(s, mmB, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R3), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b5 (TCG_REG_P0, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); +} + +static void tcg_out_exit_tb(TCGContext *s, tcg_target_long arg) +{ + int64_t disp; + uint64_t imm; + + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R8, arg); + + disp = tb_ret_addr - s->code_ptr; + imm = (uint64_t)disp >> 4; + + tcg_out_bundle(s, mLX, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_l3 (imm), + tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, imm)); +} + +static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg) +{ + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, + (tcg_target_long)(s->tb_next + arg)); + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, + TCG_REG_R2, TCG_REG_R2), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R2, 0)); + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, + TCG_REG_B6)); + } + s->tb_next_offset[arg] = s->code_ptr - s->code_buf; +} + +static inline void tcg_out_jmp(TCGContext *s, TCGArg addr) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, addr, 0)); + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b4(TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); +} + +static inline void tcg_out_ld_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (arg2 == ((int16_t)arg2 >> 2) << 2) { + tcg_out_bundle(s, MmI, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R2, arg2, arg1), + tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, arg1), + tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } +} + +static inline void tcg_out_st_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (arg2 == ((int16_t)arg2 >> 2) << 2) { + tcg_out_bundle(s, MmI, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R2, arg2, arg1), + tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, arg1), + tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_ld_rel(s, OPC_LD4_M1, arg, arg1, arg2); + } else { + tcg_out_ld_rel(s, OPC_LD8_M1, arg, arg1, arg2); + } +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_st_rel(s, OPC_ST4_M4, arg, arg1, arg2); + } else { + tcg_out_st_rel(s, OPC_ST8_M4, arg, arg1, arg2); + } +} + +static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + uint64_t opc1, opc2; + + if (const_arg1 && arg1 != 0) { + opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, + TCG_REG_R2, arg1, TCG_REG_R0); + arg1 = TCG_REG_R2; + } else { + opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + } + + if (const_arg2 && arg2 != 0) { + opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, + TCG_REG_R3, arg2, TCG_REG_R0); + arg2 = TCG_REG_R3; + } else { + opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0); + } + + tcg_out_bundle(s, mII, + opc1, + opc2, + tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2)); +} + +static inline void tcg_out_eqv(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_XOR_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_nand(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_nor(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_orc(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, TCG_REG_R2, -1, arg2), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, TCG_REG_R2)); +} + +static inline void tcg_out_mul(TCGContext *s, TCGArg ret, + TCGArg arg1, TCGArg arg2) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F6, arg1), + tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F7, arg2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + tcg_out_bundle(s, mmF, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_f2 (TCG_REG_P0, OPC_XMA_L_F2, TCG_REG_F6, TCG_REG_F6, + TCG_REG_F7, TCG_REG_F0)); + tcg_out_bundle(s, miI, + tcg_opc_m19(TCG_REG_P0, OPC_GETF_SIG_M19, ret, TCG_REG_F6), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); +} + +static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, + ret, arg1, arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, + TCG_REG_R3, 0x1f, arg2), + tcg_opc_i29(TCG_REG_P0, OPC_SXT4_I29, TCG_REG_R2, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_sar_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, + ret, arg1, arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, arg1, arg2)); + } +} + +static inline void tcg_out_shl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, + arg1, 63 - arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R2, + 0x1f, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, + arg1, TCG_REG_R2)); + } +} + +static inline void tcg_out_shl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, + arg1, 63 - arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, + arg1, arg2)); + } +} + +static inline void tcg_out_shr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + arg1, arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_shr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + arg1, arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + arg1, arg2)); + } +} + +static inline void tcg_out_rotl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + TCG_REG_R2, 32 - arg2, 31)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2)); + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R3, + 0x20, TCG_REG_R3), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, + arg1, 0x40 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, + 0x40, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R3, + arg1, arg2), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R2, + arg1, TCG_REG_R2)); + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + TCG_REG_R2, arg2, 31)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, + arg1, arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, + 0x40, arg2), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R3, + arg1, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R2, + arg1, TCG_REG_R2)); + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_ext(TCGContext *s, uint64_t opc_i29, + TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_i29, ret, arg)); +} + +static inline void tcg_out_bswap16(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb)); +} + +static inline void tcg_out_bswap32(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb)); +} + +static inline void tcg_out_bswap64(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, arg, 0xb)); +} + +static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1, + TCGArg arg2, int cmp4) +{ + uint64_t opc_eq_a6, opc_lt_a6, opc_ltu_a6; + + if (cmp4) { + opc_eq_a6 = OPC_CMP4_EQ_A6; + opc_lt_a6 = OPC_CMP4_LT_A6; + opc_ltu_a6 = OPC_CMP4_LTU_A6; + } else { + opc_eq_a6 = OPC_CMP_EQ_A6; + opc_lt_a6 = OPC_CMP_LT_A6; + opc_ltu_a6 = OPC_CMP_LTU_A6; + } + + switch (cond) { + case TCG_COND_EQ: + return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_NE: + return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_LT: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_LTU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_GE: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_GEU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_LE: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); + case TCG_COND_LEU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); + case TCG_COND_GT: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); + case TCG_COND_GTU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); + default: + tcg_abort(); + break; + } +} + +static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1, + int const_arg1, TCGArg arg2, int const_arg2, + int label_index, int cmp4) +{ + TCGLabel *l = &s->labels[label_index]; + uint64_t opc1, opc2; + + if (const_arg1 && arg1 != 0) { + opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2, + arg1, TCG_REG_R0); + arg1 = TCG_REG_R2; + } else { + opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + } + + if (const_arg2 && arg2 != 0) { + opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3, + arg2, TCG_REG_R0); + arg2 = TCG_REG_R3; + } else { + opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0); + } + + tcg_out_bundle(s, mII, + opc1, + opc2, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4)); + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b1 (TCG_REG_P6, OPC_BR_DPTK_FEW_B1, + get_reloc_pcrel21b(s->code_ptr + 2))); + + if (l->has_value) { + reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value); + } else { + tcg_out_reloc(s, (s->code_ptr - 16) + 2, + R_IA64_PCREL21B, label_index, 0); + } +} + +static inline void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg arg1, TCGArg arg2, int cmp4) +{ + tcg_out_bundle(s, MmI, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), + tcg_opc_a5(TCG_REG_P6, OPC_ADDL_A5, ret, 1, TCG_REG_R0), + tcg_opc_a5(TCG_REG_P7, OPC_ADDL_A5, ret, 0, TCG_REG_R0)); +} + +#if defined(CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +/* Load and compare a TLB entry, and return the result in (p6, p7). + R2 is loaded with the address of the addend TLB entry. + R56 is loaded with the address, zero extented on 32-bit targets. */ +static inline void tcg_out_qemu_tlb(TCGContext *s, TCGArg addr_reg, + int s_bits, uint64_t offset_rw, + uint64_t offset_addend) +{ + tcg_out_bundle(s, mII, + tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3, + TARGET_PAGE_MASK | ((1 << s_bits) - 1), + TCG_REG_R0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, TCG_REG_R2, + addr_reg, TARGET_PAGE_BITS, CPU_TLB_BITS - 1), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R2, + TCG_REG_R2, 63 - CPU_TLB_ENTRY_BITS, + 63 - CPU_TLB_ENTRY_BITS)); + tcg_out_bundle(s, mII, + tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2, + offset_rw, TCG_REG_R2), +#if TARGET_LONG_BITS == 32 + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R56, addr_reg), +#else + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R56, + 0, addr_reg), +#endif + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_REG_R2, TCG_AREG0)); + tcg_out_bundle(s, mII, + tcg_opc_m3 (TCG_REG_P0, + (TARGET_LONG_BITS == 32 + ? OPC_LD4_M3 : OPC_LD8_M3), TCG_REG_R57, + TCG_REG_R2, offset_addend - offset_rw), + tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, TCG_REG_R3, + TCG_REG_R3, TCG_REG_R56), + tcg_opc_a6 (TCG_REG_P0, OPC_CMP_EQ_A6, TCG_REG_P6, + TCG_REG_P7, TCG_REG_R3, TCG_REG_R57)); +} + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, mem_index, s_bits, bswap; + uint64_t opc_ld_m1[4] = { OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 }; + uint64_t opc_ext_i29[8] = { OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0, + OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 }; + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + s_bits = opc & 3; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + + /* Read the TLB entry */ + tcg_out_qemu_tlb(s, addr_reg, s_bits, + offsetof(CPUState, tlb_table[mem_index][0].addr_read), + offsetof(CPUState, tlb_table[mem_index][0].addend)); + + /* P6 is the fast path, and P7 the slow path */ + tcg_out_bundle(s, mLX, + tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R57, + mem_index, TCG_REG_R0), + tcg_opc_l2 ((tcg_target_long) qemu_ld_helpers[s_bits]), + tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2, + (tcg_target_long) qemu_ld_helpers[s_bits])); + tcg_out_bundle(s, MmI, + tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3, + TCG_REG_R2, 8), + tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R3, + TCG_REG_R3, TCG_REG_R56), + tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R3, 0)); + if (bswap && s_bits == 1) { + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R3), + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R8, TCG_REG_R8, 15, 15)); + } else if (bswap && s_bits == 2) { + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R3), + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R8, TCG_REG_R8, 31, 31)); + } else { + tcg_out_bundle(s, mmI, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R3), + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + if (!bswap || s_bits == 0) { + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); + } else { + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R8, TCG_REG_R8, 0xb), + tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); + } + + if (opc == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + data_reg, 0, TCG_REG_R8)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_ext_i29[opc], + data_reg, TCG_REG_R8)); + } +} + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, mem_index, bswap; + uint64_t opc_st_m4[4] = { OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 }; + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + + tcg_out_qemu_tlb(s, addr_reg, opc, + offsetof(CPUState, tlb_table[mem_index][0].addr_write), + offsetof(CPUState, tlb_table[mem_index][0].addend)); + + /* P6 is the fast path, and P7 the slow path */ + tcg_out_bundle(s, mLX, + tcg_opc_a4(TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R57, + 0, data_reg), + tcg_opc_l2 ((tcg_target_long) qemu_st_helpers[opc]), + tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2, + (tcg_target_long) qemu_st_helpers[opc])); + tcg_out_bundle(s, MmI, + tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3, + TCG_REG_R2, 8), + tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R3, + TCG_REG_R3, TCG_REG_R56), + tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R3, 0)); + + if (!bswap || opc == 0) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else if (opc == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R2, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R2, TCG_REG_R2, 0xb)); + data_reg = TCG_REG_R2; + } else if (opc == 2) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R2, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R2, TCG_REG_R2, 0xb)); + data_reg = TCG_REG_R2; + } else if (opc == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R2, data_reg, 0xb)); + data_reg = TCG_REG_R2; + } + + tcg_out_bundle(s, miB, + tcg_opc_m4 (TCG_REG_P6, opc_st_m4[opc], + data_reg, TCG_REG_R3), + tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R58, + mem_index, TCG_REG_R0), + tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); +} + +#else /* !CONFIG_SOFTMMU */ + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + static uint64_t const opc_ld_m1[4] = { + OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 + }; + static uint64_t const opc_sxt_i29[4] = { + OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 + }; + int addr_reg, data_reg, s_bits, bswap; + + data_reg = *args++; + addr_reg = *args++; + s_bits = opc & 3; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + +#if TARGET_LONG_BITS == 32 + if (GUEST_BASE != 0) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R3, addr_reg), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, TCG_REG_R3)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R2, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + + if (!bswap || s_bits == 0) { + if (s_bits == opc) { + tcg_out_bundle(s, miI, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], + data_reg, data_reg)); + } + } else if (s_bits == 3) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else { + if (s_bits == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 15, 15)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 31, 31)); + } + if (opc == s_bits) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb), + tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], + data_reg, data_reg)); + } + } +#else + if (GUEST_BASE != 0) { + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, addr_reg), + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + + if (bswap && s_bits == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else if (bswap && s_bits == 2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else if (bswap && s_bits == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } + if (s_bits != opc) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], + data_reg, data_reg)); + } +#endif +} + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + static uint64_t const opc_st_m4[4] = { + OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 + }; + int addr_reg, data_reg, bswap; +#if TARGET_LONG_BITS == 64 + uint64_t add_guest_base; +#endif + + data_reg = *args++; + addr_reg = *args++; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + +#if TARGET_LONG_BITS == 32 + if (GUEST_BASE != 0) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R3, addr_reg), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, TCG_REG_R3)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R2, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + + if (bswap) { + if (opc == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, data_reg, 0xb)); + data_reg = TCG_REG_R3; + } + } + tcg_out_bundle(s, mmI, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + data_reg, TCG_REG_R2), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); +#else + if (GUEST_BASE != 0) { + add_guest_base = tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, addr_reg); + addr_reg = TCG_REG_R2; + } else { + add_guest_base = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + } + + if (!bswap || opc == 0) { + tcg_out_bundle(s, (GUEST_BASE ? MmI : mmI), + add_guest_base, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + data_reg, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + if (opc == 1) { + tcg_out_bundle(s, mII, + add_guest_base, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 2) { + tcg_out_bundle(s, mII, + add_guest_base, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 3) { + tcg_out_bundle(s, miI, + add_guest_base, + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, data_reg, 0xb)); + data_reg = TCG_REG_R3; + } + tcg_out_bundle(s, miI, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + data_reg, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } +#endif +} + +#endif + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + switch(opc) { + case INDEX_op_exit_tb: + tcg_out_exit_tb(s, args[0]); + break; + case INDEX_op_br: + tcg_out_br(s, args[0]); + break; + case INDEX_op_call: + tcg_out_call(s, args[0]); + break; + case INDEX_op_goto_tb: + tcg_out_goto_tb(s, args[0]); + break; + case INDEX_op_jmp: + tcg_out_jmp(s, args[0]); + break; + + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT1_I29, args[0], args[0]); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT2_I29, args[0], args[0]); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT4_I29, args[0], args[0]); + break; + case INDEX_op_ld_i64: + tcg_out_ld_rel(s, OPC_LD8_M1, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_st_rel(s, OPC_ST1_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_st_rel(s, OPC_ST2_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_st_rel(s, OPC_ST4_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st_rel(s, OPC_ST8_M4, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + case INDEX_op_add_i64: + tcg_out_alu(s, OPC_ADD_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_sub_i32: + case INDEX_op_sub_i64: + tcg_out_alu(s, OPC_SUB_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + + case INDEX_op_and_i32: + case INDEX_op_and_i64: + tcg_out_alu(s, OPC_AND_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: + tcg_out_alu(s, OPC_ANDCM_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: + tcg_out_eqv(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + tcg_out_nand(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + tcg_out_nor(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_or_i32: + case INDEX_op_or_i64: + tcg_out_alu(s, OPC_OR_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: + tcg_out_orc(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_xor_i32: + case INDEX_op_xor_i64: + tcg_out_alu(s, OPC_XOR_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + + case INDEX_op_mul_i32: + case INDEX_op_mul_i64: + tcg_out_mul(s, args[0], args[1], args[2]); + break; + + case INDEX_op_sar_i32: + tcg_out_sar_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_sar_i64: + tcg_out_sar_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shl_i32: + tcg_out_shl_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shl_i64: + tcg_out_shl_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shr_i32: + tcg_out_shr_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shr_i64: + tcg_out_shr_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotl_i32: + tcg_out_rotl_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotl_i64: + tcg_out_rotl_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotr_i32: + tcg_out_rotr_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotr_i64: + tcg_out_rotr_i64(s, args[0], args[1], args[2], const_args[2]); + break; + + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + tcg_out_ext(s, OPC_SXT1_I29, args[0], args[1]); + break; + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + tcg_out_ext(s, OPC_ZXT1_I29, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + tcg_out_ext(s, OPC_SXT2_I29, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + tcg_out_ext(s, OPC_ZXT2_I29, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tcg_out_ext(s, OPC_SXT4_I29, args[0], args[1]); + break; + case INDEX_op_ext32u_i64: + tcg_out_ext(s, OPC_ZXT4_I29, args[0], args[1]); + break; + + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + tcg_out_bswap16(s, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + tcg_out_bswap32(s, args[0], args[1]); + break; + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0], args[1]); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, args[2], args[0], const_args[0], + args[1], const_args[1], args[3], 1); + break; + case INDEX_op_brcond_i64: + tcg_out_brcond(s, args[2], args[0], const_args[0], + args[1], const_args[1], args[3], 0); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], 0); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + case INDEX_op_qemu_ld32u: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, 2 | 4); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef ia64_op_defs[] = { + { INDEX_op_br, { } }, + { INDEX_op_call, { "r" } }, + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_jmp, { "r" } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rI", "rI" } }, + { INDEX_op_sub_i32, { "r", "rI", "rI" } }, + + { INDEX_op_and_i32, { "r", "rI", "rI" } }, + { INDEX_op_andc_i32, { "r", "rI", "rI" } }, + { INDEX_op_eqv_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_nand_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_or_i32, { "r", "rI", "rI" } }, + { INDEX_op_orc_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_xor_i32, { "r", "rI", "rI" } }, + + { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, + + { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, + + { INDEX_op_ext8s_i32, { "r", "rZ"} }, + { INDEX_op_ext8u_i32, { "r", "rZ"} }, + { INDEX_op_ext16s_i32, { "r", "rZ"} }, + { INDEX_op_ext16u_i32, { "r", "rZ"} }, + + { INDEX_op_bswap16_i32, { "r", "rZ" } }, + { INDEX_op_bswap32_i32, { "r", "rZ" } }, + + { INDEX_op_brcond_i32, { "rI", "rI" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, + + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "rZ", "r" } }, + { INDEX_op_st16_i64, { "rZ", "r" } }, + { INDEX_op_st32_i64, { "rZ", "r" } }, + { INDEX_op_st_i64, { "rZ", "r" } }, + + { INDEX_op_add_i64, { "r", "rI", "rI" } }, + { INDEX_op_sub_i64, { "r", "rI", "rI" } }, + + { INDEX_op_and_i64, { "r", "rI", "rI" } }, + { INDEX_op_andc_i64, { "r", "rI", "rI" } }, + { INDEX_op_eqv_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_nand_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_nor_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_or_i64, { "r", "rI", "rI" } }, + { INDEX_op_orc_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_xor_i64, { "r", "rI", "rI" } }, + + { INDEX_op_mul_i64, { "r", "rZ", "rZ" } }, + + { INDEX_op_sar_i64, { "r", "rZ", "ri" } }, + { INDEX_op_shl_i64, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i64, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i64, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i64, { "r", "rZ", "ri" } }, + + { INDEX_op_ext8s_i64, { "r", "rZ"} }, + { INDEX_op_ext8u_i64, { "r", "rZ"} }, + { INDEX_op_ext16s_i64, { "r", "rZ"} }, + { INDEX_op_ext16u_i64, { "r", "rZ"} }, + { INDEX_op_ext32s_i64, { "r", "rZ"} }, + { INDEX_op_ext32u_i64, { "r", "rZ"} }, + + { INDEX_op_bswap16_i64, { "r", "rZ" } }, + { INDEX_op_bswap32_i64, { "r", "rZ" } }, + { INDEX_op_bswap64_i64, { "r", "rZ" } }, + + { INDEX_op_brcond_i64, { "rI", "rI" } }, + { INDEX_op_setcond_i64, { "r", "rZ", "rZ" } }, + + { INDEX_op_qemu_ld8u, { "r", "r" } }, + { INDEX_op_qemu_ld8s, { "r", "r" } }, + { INDEX_op_qemu_ld16u, { "r", "r" } }, + { INDEX_op_qemu_ld16s, { "r", "r" } }, + { INDEX_op_qemu_ld32, { "r", "r" } }, + { INDEX_op_qemu_ld32u, { "r", "r" } }, + { INDEX_op_qemu_ld32s, { "r", "r" } }, + { INDEX_op_qemu_ld64, { "r", "r" } }, + + { INDEX_op_qemu_st8, { "SZ", "r" } }, + { INDEX_op_qemu_st16, { "SZ", "r" } }, + { INDEX_op_qemu_st32, { "SZ", "r" } }, + { INDEX_op_qemu_st64, { "SZ", "r" } }, + + { -1 }, +}; + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int frame_size; + + /* reserve some stack space */ + frame_size = TCG_STATIC_CALL_ARGS_SIZE; + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + + /* First emit adhoc function descriptor */ + *(uint64_t *)(s->code_ptr) = (uint64_t)s->code_ptr + 16; /* entry point */ + s->code_ptr += 16; /* skip GP */ + + /* prologue */ + tcg_out_bundle(s, mII, + tcg_opc_m34(TCG_REG_P0, OPC_ALLOC_M34, + TCG_REG_R33, 32, 24, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B6, TCG_REG_R32, 0), + tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, + TCG_REG_R32, TCG_REG_B0)); + + /* ??? If GUEST_BASE < 0x200000, we could load the register via + an ADDL in the M slot of the next bundle. */ + if (GUEST_BASE != 0) { + tcg_out_bundle(s, mlx, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_l2 (GUEST_BASE), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, + TCG_GUEST_BASE_REG, GUEST_BASE)); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } + + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R12, -frame_size, TCG_REG_R12), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); + + /* epilogue */ + tb_ret_addr = s->code_ptr; + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B0, TCG_REG_R32, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R12, frame_size, TCG_REG_R12)); + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i26(TCG_REG_P0, OPC_MOV_I_I26, + TCG_REG_PFS, TCG_REG_R33), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_RET_SPTK_MANY_B4, + TCG_REG_B0)); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], + 0xffffffffffffffffull); + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I64], + 0xffffffffffffffffull); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R15); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R16); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R17); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R18); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R19); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R27); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R28); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R29); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R30); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R31); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R56); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R57); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R58); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R59); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R60); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R61); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R62); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R63); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* zero register */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* global pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R12); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R32); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R33); /* PFS */ + + /* The following 3 are not in use, are call-saved, but *not* saved + by the prologue. Therefore we cannot use them without modifying + the prologue. There doesn't seem to be any good reason to use + these as opposed to the windowed registers. */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R4); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R5); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6); + + tcg_add_target_add_op_defs(ia64_op_defs); +} diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h new file mode 100644 index 0000000..e56e88f --- /dev/null +++ b/tcg/ia64/tcg-target.h @@ -0,0 +1,156 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009-2010 Aurelien Jarno <aurelien@aurel32.net> + * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_IA64 1 + +#define TCG_TARGET_REG_BITS 64 + +/* We only map the first 64 registers */ +#define TCG_TARGET_NB_REGS 64 +enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, + TCG_REG_R32, + TCG_REG_R33, + TCG_REG_R34, + TCG_REG_R35, + TCG_REG_R36, + TCG_REG_R37, + TCG_REG_R38, + TCG_REG_R39, + TCG_REG_R40, + TCG_REG_R41, + TCG_REG_R42, + TCG_REG_R43, + TCG_REG_R44, + TCG_REG_R45, + TCG_REG_R46, + TCG_REG_R47, + TCG_REG_R48, + TCG_REG_R49, + TCG_REG_R50, + TCG_REG_R51, + TCG_REG_R52, + TCG_REG_R53, + TCG_REG_R54, + TCG_REG_R55, + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, +}; + +#define TCG_CT_CONST_ZERO 0x100 +#define TCG_CT_CONST_S22 0x200 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_R12 +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 16 + +/* optional instructions */ +#define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_andc_i64 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_eqv_i32 +#define TCG_TARGET_HAS_eqv_i64 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_nand_i32 +#define TCG_TARGET_HAS_nand_i64 +#define TCG_TARGET_HAS_nor_i32 +#define TCG_TARGET_HAS_nor_i64 +#define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_orc_i64 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_rot_i64 + +/* optional instructions automatically implemented */ +#undef TCG_TARGET_HAS_neg_i32 /* sub r1, r0, r3 */ +#undef TCG_TARGET_HAS_neg_i64 /* sub r1, r0, r3 */ +#undef TCG_TARGET_HAS_not_i32 /* xor r1, -1, r3 */ +#undef TCG_TARGET_HAS_not_i64 /* xor r1, -1, r3 */ + +/* Note: must be synced with dyngen-exec.h */ +#define TCG_AREG0 TCG_REG_R7 + +/* Guest base is supported */ +#define TCG_TARGET_HAS_GUEST_BASE + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + start = start & ~(32UL - 1UL); + stop = (stop + (32UL - 1UL)) & ~(32UL - 1UL); + + for (; start < stop; start += 32UL) { + asm volatile ("fc.i %0" :: "r" (start)); + } + asm volatile (";;sync.i;;srlz.i;;"); +} diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c new file mode 100644 index 0000000..e04b0dc --- /dev/null +++ b/tcg/mips/tcg-target.c @@ -0,0 +1,1533 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org> + * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net> + * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if defined(TCG_TARGET_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) +# define TCG_NEED_BSWAP 0 +#else +# define TCG_NEED_BSWAP 1 +#endif + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "zero", + "at", + "v0", + "v1", + "a0", + "a1", + "a2", + "a3", + "t0", + "t1", + "t2", + "t3", + "t4", + "t5", + "t6", + "t7", + "s0", + "s1", + "s2", + "s3", + "s4", + "s5", + "s6", + "s7", + "t8", + "t9", + "k0", + "k1", + "gp", + "sp", + "fp", + "ra", +}; +#endif + +/* check if we really need so many registers :P */ +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_S0, + TCG_REG_S1, + TCG_REG_S2, + TCG_REG_S3, + TCG_REG_S4, + TCG_REG_S5, + TCG_REG_S6, + TCG_REG_S7, + TCG_REG_T1, + TCG_REG_T2, + TCG_REG_T3, + TCG_REG_T4, + TCG_REG_T5, + TCG_REG_T6, + TCG_REG_T7, + TCG_REG_T8, + TCG_REG_T9, + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_V0, + TCG_REG_V1 +}; + +static const int tcg_target_call_iarg_regs[4] = { + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3 +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_V0, + TCG_REG_V1 +}; + +static uint8_t *tb_ret_addr; + +static inline uint32_t reloc_lo16_val (void *pc, tcg_target_long target) +{ + return target & 0xffff; +} + +static inline void reloc_lo16 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff) + | reloc_lo16_val(pc, target); +} + +static inline uint32_t reloc_hi16_val (void *pc, tcg_target_long target) +{ + return (target >> 16) & 0xffff; +} + +static inline void reloc_hi16 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff) + | reloc_hi16_val(pc, target); +} + +static inline uint32_t reloc_pc16_val (void *pc, tcg_target_long target) +{ + int32_t disp; + + disp = target - (tcg_target_long) pc - 4; + if (disp != (disp << 14) >> 14) { + tcg_abort (); + } + + return (disp >> 2) & 0xffff; +} + +static inline void reloc_pc16 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff) + | reloc_pc16_val(pc, target); +} + +static inline uint32_t reloc_26_val (void *pc, tcg_target_long target) +{ + if ((((tcg_target_long)pc + 4) & 0xf0000000) != (target & 0xf0000000)) { + tcg_abort (); + } + + return (target >> 2) & 0x3ffffff; +} + +static inline void reloc_pc26 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3ffffff) + | reloc_26_val(pc, target); +} + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch(type) { + case R_MIPS_LO16: + reloc_lo16(code_ptr, value); + break; + case R_MIPS_HI16: + reloc_hi16(code_ptr, value); + break; + case R_MIPS_PC16: + reloc_pc16(code_ptr, value); + break; + case R_MIPS_26: + reloc_pc26(code_ptr, value); + break; + default: + tcg_abort(); + } +} + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + return 4; +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + break; + case 'C': + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_T9); + break; + case 'L': /* qemu_ld output arg constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_V0); + break; + case 'l': /* qemu_ld input arg constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); +#if defined(CONFIG_SOFTMMU) + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); +#endif + break; + case 'S': /* qemu_st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); +#if defined(CONFIG_SOFTMMU) +# if TARGET_LONG_BITS == 64 + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1); +# endif + tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2); +#endif + break; + case 'I': + ct->ct |= TCG_CT_CONST_U16; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S16; + break; + case 'Z': + /* We are cheating a bit here, using the fact that the register + ZERO is also the register number 0. Hence there is no need + to check for const_args in each instruction. */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_ZERO) && val == 0) + return 1; + else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) + return 1; + else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) + return 1; + else + return 0; +} + +/* instruction opcodes */ +enum { + OPC_BEQ = 0x04 << 26, + OPC_BNE = 0x05 << 26, + OPC_ADDIU = 0x09 << 26, + OPC_SLTI = 0x0A << 26, + OPC_SLTIU = 0x0B << 26, + OPC_ANDI = 0x0C << 26, + OPC_ORI = 0x0D << 26, + OPC_XORI = 0x0E << 26, + OPC_LUI = 0x0F << 26, + OPC_LB = 0x20 << 26, + OPC_LH = 0x21 << 26, + OPC_LW = 0x23 << 26, + OPC_LBU = 0x24 << 26, + OPC_LHU = 0x25 << 26, + OPC_LWU = 0x27 << 26, + OPC_SB = 0x28 << 26, + OPC_SH = 0x29 << 26, + OPC_SW = 0x2B << 26, + + OPC_SPECIAL = 0x00 << 26, + OPC_SLL = OPC_SPECIAL | 0x00, + OPC_SRL = OPC_SPECIAL | 0x02, + OPC_SRA = OPC_SPECIAL | 0x03, + OPC_SLLV = OPC_SPECIAL | 0x04, + OPC_SRLV = OPC_SPECIAL | 0x06, + OPC_SRAV = OPC_SPECIAL | 0x07, + OPC_JR = OPC_SPECIAL | 0x08, + OPC_JALR = OPC_SPECIAL | 0x09, + OPC_MFHI = OPC_SPECIAL | 0x10, + OPC_MFLO = OPC_SPECIAL | 0x12, + OPC_MULT = OPC_SPECIAL | 0x18, + OPC_MULTU = OPC_SPECIAL | 0x19, + OPC_DIV = OPC_SPECIAL | 0x1A, + OPC_DIVU = OPC_SPECIAL | 0x1B, + OPC_ADDU = OPC_SPECIAL | 0x21, + OPC_SUBU = OPC_SPECIAL | 0x23, + OPC_AND = OPC_SPECIAL | 0x24, + OPC_OR = OPC_SPECIAL | 0x25, + OPC_XOR = OPC_SPECIAL | 0x26, + OPC_NOR = OPC_SPECIAL | 0x27, + OPC_SLT = OPC_SPECIAL | 0x2A, + OPC_SLTU = OPC_SPECIAL | 0x2B, + + OPC_SPECIAL3 = 0x1f << 26, + OPC_SEB = OPC_SPECIAL3 | 0x420, + OPC_SEH = OPC_SPECIAL3 | 0x620, +}; + +/* + * Type reg + */ +static inline void tcg_out_opc_reg(TCGContext *s, int opc, int rd, int rs, int rt) +{ + int32_t inst; + + inst = opc; + inst |= (rs & 0x1F) << 21; + inst |= (rt & 0x1F) << 16; + inst |= (rd & 0x1F) << 11; + tcg_out32(s, inst); +} + +/* + * Type immediate + */ +static inline void tcg_out_opc_imm(TCGContext *s, int opc, int rt, int rs, int imm) +{ + int32_t inst; + + inst = opc; + inst |= (rs & 0x1F) << 21; + inst |= (rt & 0x1F) << 16; + inst |= (imm & 0xffff); + tcg_out32(s, inst); +} + +/* + * Type branch + */ +static inline void tcg_out_opc_br(TCGContext *s, int opc, int rt, int rs) +{ + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + uint16_t offset = (uint16_t)(*(uint32_t *) s->code_ptr); + + tcg_out_opc_imm(s, opc, rt, rs, offset); +} + +/* + * Type sa + */ +static inline void tcg_out_opc_sa(TCGContext *s, int opc, int rd, int rt, int sa) +{ + int32_t inst; + + inst = opc; + inst |= (rt & 0x1F) << 16; + inst |= (rd & 0x1F) << 11; + inst |= (sa & 0x1F) << 6; + tcg_out32(s, inst); + +} + +static inline void tcg_out_nop(TCGContext *s) +{ + tcg_out32(s, 0); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + tcg_out_opc_reg(s, OPC_ADDU, ret, arg, TCG_REG_ZERO); +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + int reg, int32_t arg) +{ + if (arg == (int16_t)arg) { + tcg_out_opc_imm(s, OPC_ADDIU, reg, TCG_REG_ZERO, arg); + } else if (arg == (uint16_t)arg) { + tcg_out_opc_imm(s, OPC_ORI, reg, TCG_REG_ZERO, arg); + } else { + tcg_out_opc_imm(s, OPC_LUI, reg, 0, arg >> 16); + tcg_out_opc_imm(s, OPC_ORI, reg, reg, arg & 0xffff); + } +} + +static inline void tcg_out_bswap16(TCGContext *s, int ret, int arg) +{ + /* ret and arg can't be register at */ + if (ret == TCG_REG_AT || arg == TCG_REG_AT) { + tcg_abort(); + } + + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0x00ff); + + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); +} + +static inline void tcg_out_bswap16s(TCGContext *s, int ret, int arg) +{ + /* ret and arg can't be register at */ + if (ret == TCG_REG_AT || arg == TCG_REG_AT) { + tcg_abort(); + } + + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff); + + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); +} + +static inline void tcg_out_bswap32(TCGContext *s, int ret, int arg) +{ + /* ret and arg must be different and can't be register at */ + if (ret == arg || ret == TCG_REG_AT || arg == TCG_REG_AT) { + tcg_abort(); + } + + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 24); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); + + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, arg, 0xff00); + tcg_out_opc_sa(s, OPC_SLL, TCG_REG_AT, TCG_REG_AT, 8); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); + + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff00); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); +} + +static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) +{ +#ifdef _MIPS_ARCH_MIPS32R2 + tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg); +#else + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24); +#endif +} + +static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) +{ +#ifdef _MIPS_ARCH_MIPS32R2 + tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg); +#else + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); +#endif +} + +static inline void tcg_out_ldst(TCGContext *s, int opc, int arg, + int arg1, tcg_target_long arg2) +{ + if (arg2 == (int16_t) arg2) { + tcg_out_opc_imm(s, opc, arg, arg1, arg2); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, arg2); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_AT, TCG_REG_AT, arg1); + tcg_out_opc_imm(s, opc, arg, TCG_REG_AT, 0); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, OPC_LW, arg, arg1, arg2); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, OPC_SW, arg, arg1, arg2); +} + +static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + if (val == (int16_t)val) { + tcg_out_opc_imm(s, OPC_ADDIU, reg, reg, val); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, val); + tcg_out_opc_reg(s, OPC_ADDU, reg, reg, TCG_REG_AT); + } +} + +static void tcg_out_brcond(TCGContext *s, TCGCond cond, int arg1, + int arg2, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + + switch (cond) { + case TCG_COND_EQ: + tcg_out_opc_br(s, OPC_BEQ, arg1, arg2); + break; + case TCG_COND_NE: + tcg_out_opc_br(s, OPC_BNE, arg1, arg2); + break; + case TCG_COND_LT: + tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg1, arg2); + tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_LTU: + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg1, arg2); + tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_GE: + tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg1, arg2); + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_GEU: + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg1, arg2); + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_LE: + tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg2, arg1); + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_LEU: + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg2, arg1); + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_GT: + tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg2, arg1); + tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO); + break; + case TCG_COND_GTU: + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg2, arg1); + tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO); + break; + default: + tcg_abort(); + break; + } + if (l->has_value) { + reloc_pc16(s->code_ptr - 4, l->u.value); + } else { + tcg_out_reloc(s, s->code_ptr - 4, R_MIPS_PC16, label_index, 0); + } + tcg_out_nop(s); +} + +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_brcond2(TCGContext *s, TCGCond cond, int arg1, + int arg2, int arg3, int arg4, int label_index) +{ + void *label_ptr; + + switch(cond) { + case TCG_COND_NE: + tcg_out_brcond(s, TCG_COND_NE, arg2, arg4, label_index); + tcg_out_brcond(s, TCG_COND_NE, arg1, arg3, label_index); + return; + case TCG_COND_EQ: + break; + case TCG_COND_LT: + case TCG_COND_LE: + tcg_out_brcond(s, TCG_COND_LT, arg2, arg4, label_index); + break; + case TCG_COND_GT: + case TCG_COND_GE: + tcg_out_brcond(s, TCG_COND_GT, arg2, arg4, label_index); + break; + case TCG_COND_LTU: + case TCG_COND_LEU: + tcg_out_brcond(s, TCG_COND_LTU, arg2, arg4, label_index); + break; + case TCG_COND_GTU: + case TCG_COND_GEU: + tcg_out_brcond(s, TCG_COND_GTU, arg2, arg4, label_index); + break; + default: + tcg_abort(); + } + + label_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, arg2, arg4); + tcg_out_nop(s); + + switch(cond) { + case TCG_COND_EQ: + tcg_out_brcond(s, TCG_COND_EQ, arg1, arg3, label_index); + break; + case TCG_COND_LT: + case TCG_COND_LTU: + tcg_out_brcond(s, TCG_COND_LTU, arg1, arg3, label_index); + break; + case TCG_COND_LE: + case TCG_COND_LEU: + tcg_out_brcond(s, TCG_COND_LEU, arg1, arg3, label_index); + break; + case TCG_COND_GT: + case TCG_COND_GTU: + tcg_out_brcond(s, TCG_COND_GTU, arg1, arg3, label_index); + break; + case TCG_COND_GE: + case TCG_COND_GEU: + tcg_out_brcond(s, TCG_COND_GEU, arg1, arg3, label_index); + break; + default: + tcg_abort(); + } + + reloc_pc16(label_ptr, (tcg_target_long) s->code_ptr); +} + +static void tcg_out_setcond(TCGContext *s, TCGCond cond, int ret, + int arg1, int arg2) +{ + switch (cond) { + case TCG_COND_EQ: + if (arg1 == 0) { + tcg_out_opc_imm(s, OPC_SLTIU, ret, arg2, 1); + } else if (arg2 == 0) { + tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1); + } else { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_SLTIU, ret, ret, 1); + } + break; + case TCG_COND_NE: + if (arg1 == 0) { + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg2); + } else if (arg2 == 0) { + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1); + } else { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, ret); + } + break; + case TCG_COND_LT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + break; + case TCG_COND_LTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + break; + case TCG_COND_GE: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_GEU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_LE: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_LEU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_GT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + break; + case TCG_COND_GTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + break; + default: + tcg_abort(); + break; + } +} + +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_setcond2(TCGContext *s, TCGCond cond, int ret, + int arg1, int arg2, int arg3, int arg4) +{ + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_AT, arg2, arg4); + tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg1, arg3); + tcg_out_opc_reg(s, OPC_AND, ret, TCG_REG_AT, TCG_REG_T0); + return; + case TCG_COND_NE: + tcg_out_setcond(s, TCG_COND_NE, TCG_REG_AT, arg2, arg4); + tcg_out_setcond(s, TCG_COND_NE, TCG_REG_T0, arg1, arg3); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_REG_AT, TCG_REG_T0); + return; + case TCG_COND_LT: + case TCG_COND_LE: + tcg_out_setcond(s, TCG_COND_LT, TCG_REG_AT, arg2, arg4); + break; + case TCG_COND_GT: + case TCG_COND_GE: + tcg_out_setcond(s, TCG_COND_GT, TCG_REG_AT, arg2, arg4); + break; + case TCG_COND_LTU: + case TCG_COND_LEU: + tcg_out_setcond(s, TCG_COND_LTU, TCG_REG_AT, arg2, arg4); + break; + case TCG_COND_GTU: + case TCG_COND_GEU: + tcg_out_setcond(s, TCG_COND_GTU, TCG_REG_AT, arg2, arg4); + break; + default: + tcg_abort(); + break; + } + + tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg2, arg4); + + switch(cond) { + case TCG_COND_LT: + case TCG_COND_LTU: + tcg_out_setcond(s, TCG_COND_LTU, ret, arg1, arg3); + break; + case TCG_COND_LE: + case TCG_COND_LEU: + tcg_out_setcond(s, TCG_COND_LEU, ret, arg1, arg3); + break; + case TCG_COND_GT: + case TCG_COND_GTU: + tcg_out_setcond(s, TCG_COND_GTU, ret, arg1, arg3); + break; + case TCG_COND_GE: + case TCG_COND_GEU: + tcg_out_setcond(s, TCG_COND_GEU, ret, arg1, arg3); + break; + default: + tcg_abort(); + } + + tcg_out_opc_reg(s, OPC_AND, ret, ret, TCG_REG_T0); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); +} + +#if defined(CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; +#endif + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, + int opc) +{ + int addr_regl, addr_reg1, addr_meml; + int data_regl, data_regh, data_reg1, data_reg2; + int mem_index, s_bits; +#if defined(CONFIG_SOFTMMU) + void *label1_ptr, *label2_ptr; + int sp_args; +#endif +#if TARGET_LONG_BITS == 64 +# if defined(CONFIG_SOFTMMU) + uint8_t *label3_ptr; +# endif + int addr_regh, addr_reg2, addr_memh; +#endif + data_regl = *args++; + if (opc == 3) + data_regh = *args++; + else + data_regh = 0; + addr_regl = *args++; +#if TARGET_LONG_BITS == 64 + addr_regh = *args++; +#endif + mem_index = *args; + s_bits = opc & 3; + + if (opc == 3) { +#if defined(TCG_TARGET_WORDS_BIGENDIAN) + data_reg1 = data_regh; + data_reg2 = data_regl; +#else + data_reg1 = data_regl; + data_reg2 = data_regh; +#endif + } else { + data_reg1 = data_regl; + data_reg2 = 0; + } +#if TARGET_LONG_BITS == 64 +# if defined(TCG_TARGET_WORDS_BIGENDIAN) + addr_reg1 = addr_regh; + addr_reg2 = addr_regl; + addr_memh = 0; + addr_meml = 4; +# else + addr_reg1 = addr_regl; + addr_reg2 = addr_regh; + addr_memh = 4; + addr_meml = 0; +# endif +#else + addr_reg1 = addr_regl; + addr_meml = 0; +#endif + +#if defined(CONFIG_SOFTMMU) + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0); + tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0, + offsetof(CPUState, tlb_table[mem_index][0].addr_read) + addr_meml); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + tcg_out_opc_reg(s, OPC_AND, TCG_REG_T0, TCG_REG_T0, addr_regl); + +# if TARGET_LONG_BITS == 64 + label3_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, TCG_REG_T0, TCG_REG_AT); + tcg_out_nop(s); + + tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0, + offsetof(CPUState, tlb_table[mem_index][0].addr_read) + addr_memh); + + label1_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BEQ, addr_regh, TCG_REG_AT); + tcg_out_nop(s); + + reloc_pc16(label3_ptr, (tcg_target_long) s->code_ptr); +# else + label1_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_T0, TCG_REG_AT); + tcg_out_nop(s); +# endif + + /* slow path */ + sp_args = TCG_REG_A0; + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg1); +# if TARGET_LONG_BITS == 64 + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg2); +# endif + tcg_out_movi(s, TCG_TYPE_I32, sp_args++, mem_index); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_ld_helpers[s_bits]); + tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0); + tcg_out_nop(s); + + switch(opc) { + case 0: + tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xff); + break; + case 0 | 4: + tcg_out_ext8s(s, data_reg1, TCG_REG_V0); + break; + case 1: + tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xffff); + break; + case 1 | 4: + tcg_out_ext16s(s, data_reg1, TCG_REG_V0); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0); + break; + case 3: + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_V1); + tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0); + break; + default: + tcg_abort(); + } + + label2_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); + tcg_out_nop(s); + + /* label1: fast path */ + reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr); + + tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, + offsetof(CPUState, tlb_table[mem_index][0].addend)); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_A0, addr_regl); +#else + if (GUEST_BASE == (int16_t)GUEST_BASE) { + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_V0, addr_regl, GUEST_BASE); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, GUEST_BASE); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_V0, addr_regl); + } +#endif + + switch(opc) { + case 0: + tcg_out_opc_imm(s, OPC_LBU, data_reg1, TCG_REG_V0, 0); + break; + case 0 | 4: + tcg_out_opc_imm(s, OPC_LB, data_reg1, TCG_REG_V0, 0); + break; + case 1: + if (TCG_NEED_BSWAP) { + tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0); + tcg_out_bswap16(s, data_reg1, TCG_REG_T0); + } else { + tcg_out_opc_imm(s, OPC_LHU, data_reg1, TCG_REG_V0, 0); + } + break; + case 1 | 4: + if (TCG_NEED_BSWAP) { + tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0); + tcg_out_bswap16s(s, data_reg1, TCG_REG_T0); + } else { + tcg_out_opc_imm(s, OPC_LH, data_reg1, TCG_REG_V0, 0); + } + break; + case 2: + if (TCG_NEED_BSWAP) { + tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0); + tcg_out_bswap32(s, data_reg1, TCG_REG_T0); + } else { + tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0); + } + break; + case 3: + if (TCG_NEED_BSWAP) { + tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 4); + tcg_out_bswap32(s, data_reg1, TCG_REG_T0); + tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0); + tcg_out_bswap32(s, data_reg2, TCG_REG_T0); + } else { + tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0); + tcg_out_opc_imm(s, OPC_LW, data_reg2, TCG_REG_V0, 4); + } + break; + default: + tcg_abort(); + } + +#if defined(CONFIG_SOFTMMU) + reloc_pc16(label2_ptr, (tcg_target_long) s->code_ptr); +#endif +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, + int opc) +{ + int addr_regl, addr_reg1, addr_meml; + int data_regl, data_regh, data_reg1, data_reg2; + int mem_index, s_bits; +#if defined(CONFIG_SOFTMMU) + uint8_t *label1_ptr, *label2_ptr; + int sp_args; +#endif +#if TARGET_LONG_BITS == 64 +# if defined(CONFIG_SOFTMMU) + uint8_t *label3_ptr; +# endif + int addr_regh, addr_reg2, addr_memh; +#endif + + data_regl = *args++; + if (opc == 3) { + data_regh = *args++; +#if defined(TCG_TARGET_WORDS_BIGENDIAN) + data_reg1 = data_regh; + data_reg2 = data_regl; +#else + data_reg1 = data_regl; + data_reg2 = data_regh; +#endif + } else { + data_reg1 = data_regl; + data_reg2 = 0; + data_regh = 0; + } + addr_regl = *args++; +#if TARGET_LONG_BITS == 64 + addr_regh = *args++; +# if defined(TCG_TARGET_WORDS_BIGENDIAN) + addr_reg1 = addr_regh; + addr_reg2 = addr_regl; + addr_memh = 0; + addr_meml = 4; +# else + addr_reg1 = addr_regl; + addr_reg2 = addr_regh; + addr_memh = 4; + addr_meml = 0; +# endif +#else + addr_reg1 = addr_regl; + addr_meml = 0; +#endif + mem_index = *args; + s_bits = opc; + +#if defined(CONFIG_SOFTMMU) + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0); + tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0, + offsetof(CPUState, tlb_table[mem_index][0].addr_write) + addr_meml); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + tcg_out_opc_reg(s, OPC_AND, TCG_REG_T0, TCG_REG_T0, addr_regl); + +# if TARGET_LONG_BITS == 64 + label3_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, TCG_REG_T0, TCG_REG_AT); + tcg_out_nop(s); + + tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0, + offsetof(CPUState, tlb_table[mem_index][0].addr_write) + addr_memh); + + label1_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BEQ, addr_regh, TCG_REG_AT); + tcg_out_nop(s); + + reloc_pc16(label3_ptr, (tcg_target_long) s->code_ptr); +# else + label1_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_T0, TCG_REG_AT); + tcg_out_nop(s); +# endif + + /* slow path */ + sp_args = TCG_REG_A0; + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg1); +# if TARGET_LONG_BITS == 64 + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg2); +# endif + switch(opc) { + case 0: + tcg_out_opc_imm(s, OPC_ANDI, sp_args++, data_reg1, 0xff); + break; + case 1: + tcg_out_opc_imm(s, OPC_ANDI, sp_args++, data_reg1, 0xffff); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg1); + break; + case 3: + sp_args = (sp_args + 1) & ~1; + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg1); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg2); + break; + default: + tcg_abort(); + } + if (sp_args > TCG_REG_A3) { + /* Push mem_index on the stack */ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, mem_index); + tcg_out_st(s, TCG_TYPE_I32, TCG_REG_AT, TCG_REG_SP, 16); + } else { + tcg_out_movi(s, TCG_TYPE_I32, sp_args, mem_index); + } + + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_st_helpers[s_bits]); + tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0); + tcg_out_nop(s); + + label2_ptr = s->code_ptr; + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); + tcg_out_nop(s); + + /* label1: fast path */ + reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr); + + tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, + offsetof(CPUState, tlb_table[mem_index][0].addend)); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl); +#else + if (GUEST_BASE == (int16_t)GUEST_BASE) { + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, addr_regl, GUEST_BASE); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, GUEST_BASE); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl); + } + +#endif + + switch(opc) { + case 0: + tcg_out_opc_imm(s, OPC_SB, data_reg1, TCG_REG_A0, 0); + break; + case 1: + if (TCG_NEED_BSWAP) { + tcg_out_bswap16(s, TCG_REG_T0, data_reg1); + tcg_out_opc_imm(s, OPC_SH, TCG_REG_T0, TCG_REG_A0, 0); + } else { + tcg_out_opc_imm(s, OPC_SH, data_reg1, TCG_REG_A0, 0); + } + break; + case 2: + if (TCG_NEED_BSWAP) { + tcg_out_bswap32(s, TCG_REG_T0, data_reg1); + tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0); + } else { + tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0); + } + break; + case 3: + if (TCG_NEED_BSWAP) { + tcg_out_bswap32(s, TCG_REG_T0, data_reg2); + tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0); + tcg_out_bswap32(s, TCG_REG_T0, data_reg1); + tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 4); + } else { + tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0); + tcg_out_opc_imm(s, OPC_SW, data_reg2, TCG_REG_A0, 4); + } + break; + default: + tcg_abort(); + } + +#if defined(CONFIG_SOFTMMU) + reloc_pc16(label2_ptr, (tcg_target_long) s->code_ptr); +#endif +} + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + switch(opc) { + case INDEX_op_exit_tb: + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_V0, args[0]); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, (tcg_target_long)tb_ret_addr); + tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_AT, 0); + tcg_out_nop(s); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, (tcg_target_long)(s->tb_next + args[0])); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_AT, TCG_REG_AT, 0); + tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_AT, 0); + } + tcg_out_nop(s); + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + case INDEX_op_call: + tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, args[0], 0); + tcg_out_nop(s); + break; + case INDEX_op_jmp: + tcg_out_opc_reg(s, OPC_JR, 0, args[0], 0); + tcg_out_nop(s); + break; + case INDEX_op_br: + tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, args[0]); + break; + + case INDEX_op_mov_i32: + tcg_out_mov(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + + case INDEX_op_ld8u_i32: + tcg_out_ldst(s, OPC_LBU, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + tcg_out_ldst(s, OPC_LB, args[0], args[1], args[2]); + break; + case INDEX_op_ld16u_i32: + tcg_out_ldst(s, OPC_LHU, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + tcg_out_ldst(s, OPC_LH, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i32: + tcg_out_ldst(s, OPC_LW, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + tcg_out_ldst(s, OPC_SB, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + tcg_out_ldst(s, OPC_SH, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + tcg_out_ldst(s, OPC_SW, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + if (const_args[2]) { + tcg_out_opc_imm(s, OPC_ADDIU, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_ADDU, args[0], args[1], args[2]); + } + break; + case INDEX_op_add2_i32: + if (const_args[4]) { + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_AT, args[2], args[4]); + } else { + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_AT, args[2], args[4]); + } + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_T0, TCG_REG_AT, args[2]); + if (const_args[5]) { + tcg_out_opc_imm(s, OPC_ADDIU, args[1], args[3], args[5]); + } else { + tcg_out_opc_reg(s, OPC_ADDU, args[1], args[3], args[5]); + } + tcg_out_opc_reg(s, OPC_ADDU, args[1], args[1], TCG_REG_T0); + tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT); + break; + case INDEX_op_sub_i32: + if (const_args[2]) { + tcg_out_opc_imm(s, OPC_ADDIU, args[0], args[1], -args[2]); + } else { + tcg_out_opc_reg(s, OPC_SUBU, args[0], args[1], args[2]); + } + break; + case INDEX_op_sub2_i32: + if (const_args[4]) { + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_AT, args[2], -args[4]); + } else { + tcg_out_opc_reg(s, OPC_SUBU, TCG_REG_AT, args[2], args[4]); + } + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_T0, args[2], TCG_REG_AT); + if (const_args[5]) { + tcg_out_opc_imm(s, OPC_ADDIU, args[1], args[3], -args[5]); + } else { + tcg_out_opc_reg(s, OPC_SUBU, args[1], args[3], args[5]); + } + tcg_out_opc_reg(s, OPC_SUBU, args[1], args[1], TCG_REG_T0); + tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT); + break; + case INDEX_op_mul_i32: + tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]); + tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0); + break; + case INDEX_op_mulu2_i32: + tcg_out_opc_reg(s, OPC_MULTU, 0, args[2], args[3]); + tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0); + tcg_out_opc_reg(s, OPC_MFHI, args[1], 0, 0); + break; + case INDEX_op_div_i32: + tcg_out_opc_reg(s, OPC_DIV, 0, args[1], args[2]); + tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0); + break; + case INDEX_op_divu_i32: + tcg_out_opc_reg(s, OPC_DIVU, 0, args[1], args[2]); + tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0); + break; + case INDEX_op_rem_i32: + tcg_out_opc_reg(s, OPC_DIV, 0, args[1], args[2]); + tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0); + break; + case INDEX_op_remu_i32: + tcg_out_opc_reg(s, OPC_DIVU, 0, args[1], args[2]); + tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0); + break; + + case INDEX_op_and_i32: + if (const_args[2]) { + tcg_out_opc_imm(s, OPC_ANDI, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_AND, args[0], args[1], args[2]); + } + break; + case INDEX_op_or_i32: + if (const_args[2]) { + tcg_out_opc_imm(s, OPC_ORI, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_OR, args[0], args[1], args[2]); + } + break; + case INDEX_op_nor_i32: + tcg_out_opc_reg(s, OPC_NOR, args[0], args[1], args[2]); + break; + case INDEX_op_not_i32: + tcg_out_opc_reg(s, OPC_NOR, args[0], TCG_REG_ZERO, args[1]); + break; + case INDEX_op_xor_i32: + if (const_args[2]) { + tcg_out_opc_imm(s, OPC_XORI, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_XOR, args[0], args[1], args[2]); + } + break; + + case INDEX_op_sar_i32: + if (const_args[2]) { + tcg_out_opc_sa(s, OPC_SRA, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_SRAV, args[0], args[2], args[1]); + } + break; + case INDEX_op_shl_i32: + if (const_args[2]) { + tcg_out_opc_sa(s, OPC_SLL, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_SLLV, args[0], args[2], args[1]); + } + break; + case INDEX_op_shr_i32: + if (const_args[2]) { + tcg_out_opc_sa(s, OPC_SRL, args[0], args[1], args[2]); + } else { + tcg_out_opc_reg(s, OPC_SRLV, args[0], args[2], args[1]); + } + break; + + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, args[0], args[1]); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, args[2], args[0], args[1], args[3]); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args[4], args[0], args[1], args[2], args[3], args[5]); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args[5], args[0], args[1], args[2], args[3], args[4]); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef mips_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "C" } }, + { INDEX_op_jmp, { "r" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rZ", "rJZ" } }, + { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rZ" } }, + { INDEX_op_div_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_divu_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_rem_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_remu_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_sub_i32, { "r", "rZ", "rJZ" } }, + + { INDEX_op_and_i32, { "r", "rZ", "rIZ" } }, + { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_not_i32, { "r", "rZ" } }, + { INDEX_op_or_i32, { "r", "rZ", "rIZ" } }, + { INDEX_op_xor_i32, { "r", "rZ", "rIZ" } }, + + { INDEX_op_shl_i32, { "r", "rZ", "riZ" } }, + { INDEX_op_shr_i32, { "r", "rZ", "riZ" } }, + { INDEX_op_sar_i32, { "r", "rZ", "riZ" } }, + + { INDEX_op_ext8s_i32, { "r", "rZ" } }, + { INDEX_op_ext16s_i32, { "r", "rZ" } }, + + { INDEX_op_brcond_i32, { "rZ", "rZ" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } }, + { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } }, + { INDEX_op_brcond2_i32, { "rZ", "rZ", "rZ", "rZ" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld8u, { "L", "lZ" } }, + { INDEX_op_qemu_ld8s, { "L", "lZ" } }, + { INDEX_op_qemu_ld16u, { "L", "lZ" } }, + { INDEX_op_qemu_ld16s, { "L", "lZ" } }, + { INDEX_op_qemu_ld32, { "L", "lZ" } }, + { INDEX_op_qemu_ld64, { "L", "L", "lZ" } }, + + { INDEX_op_qemu_st8, { "SZ", "SZ" } }, + { INDEX_op_qemu_st16, { "SZ", "SZ" } }, + { INDEX_op_qemu_st32, { "SZ", "SZ" } }, + { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ" } }, +#else + { INDEX_op_qemu_ld8u, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_ld8s, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_ld16u, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_ld16s, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_ld32, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_ld64, { "L", "L", "lZ", "lZ" } }, + + { INDEX_op_qemu_st8, { "SZ", "SZ", "SZ" } }, + { INDEX_op_qemu_st16, { "SZ", "SZ", "SZ" } }, + { INDEX_op_qemu_st32, { "SZ", "SZ", "SZ" } }, + { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ", "SZ" } }, +#endif + { -1 }, +}; + +static int tcg_target_callee_save_regs[] = { +#if 0 /* used for the global env (TCG_AREG0), so no need to save */ + TCG_REG_S0, +#endif + TCG_REG_S1, + TCG_REG_S2, + TCG_REG_S3, + TCG_REG_S4, + TCG_REG_S5, + TCG_REG_S6, + TCG_REG_S7, + TCG_REG_GP, + TCG_REG_FP, + TCG_REG_RA, /* should be last for ABI compliance */ +}; + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i, frame_size; + + /* reserve some stack space */ + frame_size = ARRAY_SIZE(tcg_target_callee_save_regs) * 4 + + TCG_STATIC_CALL_ARGS_SIZE; + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + + /* TB prologue */ + tcg_out_addi(s, TCG_REG_SP, -frame_size); + for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) { + tcg_out_st(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i], + TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4); + } + + /* Call generated code */ + tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_A0, 0); + tcg_out_nop(s); + tb_ret_addr = s->code_ptr; + + /* TB epilogue */ + for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) { + tcg_out_ld(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i], + TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4); + } + + tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_RA, 0); + tcg_out_addi(s, TCG_REG_SP, frame_size); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], 0xffffffff); + tcg_regset_set(tcg_target_call_clobber_regs, + (1 << TCG_REG_V0) | + (1 << TCG_REG_V1) | + (1 << TCG_REG_A0) | + (1 << TCG_REG_A1) | + (1 << TCG_REG_A2) | + (1 << TCG_REG_A3) | + (1 << TCG_REG_T1) | + (1 << TCG_REG_T2) | + (1 << TCG_REG_T3) | + (1 << TCG_REG_T4) | + (1 << TCG_REG_T5) | + (1 << TCG_REG_T6) | + (1 << TCG_REG_T7) | + (1 << TCG_REG_T8) | + (1 << TCG_REG_T9)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); /* zero register */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_K0); /* kernel use only */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_K1); /* kernel use only */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_AT); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_T0); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ + + tcg_add_target_add_op_defs(mips_op_defs); +} diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h new file mode 100644 index 0000000..0028bfa --- /dev/null +++ b/tcg/mips/tcg-target.h @@ -0,0 +1,110 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org> + * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net> + * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_MIPS 1 + +#define TCG_TARGET_REG_BITS 32 +#ifdef __MIPSEB__ +# define TCG_TARGET_WORDS_BIGENDIAN +#endif + +#define TCG_TARGET_NB_REGS 32 + +enum { + TCG_REG_ZERO = 0, + TCG_REG_AT, + TCG_REG_V0, + TCG_REG_V1, + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_T0, + TCG_REG_T1, + TCG_REG_T2, + TCG_REG_T3, + TCG_REG_T4, + TCG_REG_T5, + TCG_REG_T6, + TCG_REG_T7, + TCG_REG_S0, + TCG_REG_S1, + TCG_REG_S2, + TCG_REG_S3, + TCG_REG_S4, + TCG_REG_S5, + TCG_REG_S6, + TCG_REG_S7, + TCG_REG_T8, + TCG_REG_T9, + TCG_REG_K0, + TCG_REG_K1, + TCG_REG_GP, + TCG_REG_SP, + TCG_REG_FP, + TCG_REG_RA, +}; + +#define TCG_CT_CONST_ZERO 0x100 +#define TCG_CT_CONST_U16 0x200 +#define TCG_CT_CONST_S16 0x400 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_STACK_OFFSET 16 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_nor_i32 +#undef TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#undef TCG_TARGET_HAS_bswap32_i32 +#undef TCG_TARGET_HAS_bswap16_i32 +#undef TCG_TARGET_HAS_andc_i32 +#undef TCG_TARGET_HAS_orc_i32 +#undef TCG_TARGET_HAS_eqv_i32 +#undef TCG_TARGET_HAS_nand_i32 + +/* optional instructions automatically implemented */ +#undef TCG_TARGET_HAS_neg_i32 /* sub rd, zero, rt */ +#undef TCG_TARGET_HAS_ext8u_i32 /* andi rt, rs, 0xff */ +#undef TCG_TARGET_HAS_ext16u_i32 /* andi rt, rs, 0xffff */ + +/* Note: must be synced with dyngen-exec.h */ +#define TCG_AREG0 TCG_REG_S0 + +/* guest base is supported */ +#define TCG_TARGET_HAS_GUEST_BASE + +#include <sys/cachectl.h> + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + cacheflush ((void *)start, stop-start, ICACHE); +} diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c new file mode 100644 index 0000000..7970268 --- /dev/null +++ b/tcg/ppc/tcg-target.c @@ -0,0 +1,1922 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +static uint8_t *tb_ret_addr; + +#ifdef _CALL_DARWIN +#define LINKAGE_AREA_SIZE 24 +#define LR_OFFSET 8 +#elif defined _CALL_AIX +#define LINKAGE_AREA_SIZE 52 +#define LR_OFFSET 8 +#else +#define LINKAGE_AREA_SIZE 8 +#define LR_OFFSET 4 +#endif + +#define FAST_PATH + +#ifndef GUEST_BASE +#define GUEST_BASE 0 +#endif + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG 30 +#else +#define TCG_GUEST_BASE_REG 0 +#endif + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "r16", + "r17", + "r18", + "r19", + "r20", + "r21", + "r22", + "r23", + "r24", + "r25", + "r26", + "r27", + "r28", + "r29", + "r30", + "r31" +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, +#ifdef _CALL_DARWIN + TCG_REG_R2, +#endif + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, +#ifndef _CALL_DARWIN + TCG_REG_R11, +#endif + TCG_REG_R12, +#ifndef _CALL_SYSV + TCG_REG_R13, +#endif + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27 +}; + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10 +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_R3, + TCG_REG_R4 +}; + +static const int tcg_target_callee_save_regs[] = { +#ifdef _CALL_DARWIN + TCG_REG_R11, + TCG_REG_R13, +#endif +#ifdef _CALL_AIX + TCG_REG_R13, +#endif + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + /* TCG_REG_R27, */ /* currently used for the global env, so no + need to save */ + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31 +}; + +static uint32_t reloc_pc24_val (void *pc, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) pc; + if ((disp << 6) >> 6 != disp) + tcg_abort (); + + return disp & 0x3fffffc; +} + +static void reloc_pc24 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc) + | reloc_pc24_val (pc, target); +} + +static uint16_t reloc_pc14_val (void *pc, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) pc; + if (disp != (int16_t) disp) + tcg_abort (); + + return disp & 0xfffc; +} + +static void reloc_pc14 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc) + | reloc_pc14_val (pc, target); +} + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch (type) { + case R_PPC_REL14: + reloc_pc14 (code_ptr, value); + break; + case R_PPC_REL24: + reloc_pc24 (code_ptr, value); + break; + default: + tcg_abort(); + } +} + +/* maximum number of register used for input function arguments */ +static int tcg_target_get_call_iarg_regs_count(int flags) +{ + return ARRAY_SIZE (tcg_target_call_iarg_regs); +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'A': case 'B': case 'C': case 'D': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A'); + break; + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; +#ifdef CONFIG_SOFTMMU + case 'L': /* qemu_ld constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + break; + case 'K': /* qemu_st[8..32] constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); +#if TARGET_LONG_BITS == 64 + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); +#endif + break; + case 'M': /* qemu_st64 constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R7); + break; +#else + case 'L': + case 'K': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; + case 'M': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); + break; +#endif + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + return 0; +} + +#define OPCD(opc) ((opc)<<26) +#define XO31(opc) (OPCD(31)|((opc)<<1)) +#define XO19(opc) (OPCD(19)|((opc)<<1)) + +#define B OPCD(18) +#define BC OPCD(16) +#define LBZ OPCD(34) +#define LHZ OPCD(40) +#define LHA OPCD(42) +#define LWZ OPCD(32) +#define STB OPCD(38) +#define STH OPCD(44) +#define STW OPCD(36) + +#define ADDIC OPCD(12) +#define ADDI OPCD(14) +#define ADDIS OPCD(15) +#define ORI OPCD(24) +#define ORIS OPCD(25) +#define XORI OPCD(26) +#define XORIS OPCD(27) +#define ANDI OPCD(28) +#define ANDIS OPCD(29) +#define MULLI OPCD( 7) +#define CMPLI OPCD(10) +#define CMPI OPCD(11) +#define SUBFIC OPCD( 8) + +#define LWZU OPCD(33) +#define STWU OPCD(37) + +#define RLWIMI OPCD(20) +#define RLWINM OPCD(21) +#define RLWNM OPCD(23) + +#define BCLR XO19( 16) +#define BCCTR XO19(528) +#define CRAND XO19(257) +#define CRANDC XO19(129) +#define CRNAND XO19(225) +#define CROR XO19(449) +#define CRNOR XO19( 33) + +#define EXTSB XO31(954) +#define EXTSH XO31(922) +#define ADD XO31(266) +#define ADDE XO31(138) +#define ADDC XO31( 10) +#define AND XO31( 28) +#define SUBF XO31( 40) +#define SUBFC XO31( 8) +#define SUBFE XO31(136) +#define OR XO31(444) +#define XOR XO31(316) +#define MULLW XO31(235) +#define MULHWU XO31( 11) +#define DIVW XO31(491) +#define DIVWU XO31(459) +#define CMP XO31( 0) +#define CMPL XO31( 32) +#define LHBRX XO31(790) +#define LWBRX XO31(534) +#define STHBRX XO31(918) +#define STWBRX XO31(662) +#define MFSPR XO31(339) +#define MTSPR XO31(467) +#define SRAWI XO31(824) +#define NEG XO31(104) +#define MFCR XO31( 19) +#define CNTLZW XO31( 26) +#define NOR XO31(124) +#define ANDC XO31( 60) +#define ORC XO31(412) +#define EQV XO31(284) +#define NAND XO31(476) + +#define LBZX XO31( 87) +#define LHZX XO31(279) +#define LHAX XO31(343) +#define LWZX XO31( 23) +#define STBX XO31(215) +#define STHX XO31(407) +#define STWX XO31(151) + +#define SPR(a,b) ((((a)<<5)|(b))<<11) +#define LR SPR(8, 0) +#define CTR SPR(9, 0) + +#define SLW XO31( 24) +#define SRW XO31(536) +#define SRAW XO31(792) + +#define TW XO31(4) +#define TRAP (TW | TO (31)) + +#define RT(r) ((r)<<21) +#define RS(r) ((r)<<21) +#define RA(r) ((r)<<16) +#define RB(r) ((r)<<11) +#define TO(t) ((t)<<21) +#define SH(s) ((s)<<11) +#define MB(b) ((b)<<6) +#define ME(e) ((e)<<1) +#define BO(o) ((o)<<21) + +#define LK 1 + +#define TAB(t,a,b) (RT(t) | RA(a) | RB(b)) +#define SAB(s,a,b) (RS(s) | RA(a) | RB(b)) + +#define BF(n) ((n)<<23) +#define BI(n, c) (((c)+((n)*4))<<16) +#define BT(n, c) (((c)+((n)*4))<<21) +#define BA(n, c) (((c)+((n)*4))<<16) +#define BB(n, c) (((c)+((n)*4))<<11) + +#define BO_COND_TRUE BO (12) +#define BO_COND_FALSE BO (4) +#define BO_ALWAYS BO (20) + +enum { + CR_LT, + CR_GT, + CR_EQ, + CR_SO +}; + +static const uint32_t tcg_to_bc[10] = { + [TCG_COND_EQ] = BC | BI (7, CR_EQ) | BO_COND_TRUE, + [TCG_COND_NE] = BC | BI (7, CR_EQ) | BO_COND_FALSE, + [TCG_COND_LT] = BC | BI (7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GE] = BC | BI (7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LE] = BC | BI (7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GT] = BC | BI (7, CR_GT) | BO_COND_TRUE, + [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, +}; + +static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + tcg_out32 (s, OR | SAB (arg, ret, arg)); +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + if (arg == (int16_t) arg) + tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff)); + else { + tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff)); + if (arg & 0xffff) + tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff)); + } +} + +static void tcg_out_ldst (TCGContext *s, int ret, int addr, + int offset, int op1, int op2) +{ + if (offset == (int16_t) offset) + tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, offset); + tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0)); + } +} + +static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) s->code_ptr; + if ((disp << 6) >> 6 == disp) + tcg_out32 (s, B | (disp & 0x3fffffc) | mask); + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, (tcg_target_long) target); + tcg_out32 (s, MTSPR | RS (0) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS | mask); + } +} + +static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) +{ +#ifdef _CALL_AIX + int reg; + + if (const_arg) { + reg = 2; + tcg_out_movi (s, TCG_TYPE_I32, reg, arg); + } + else reg = arg; + + tcg_out32 (s, LWZ | RT (0) | RA (reg)); + tcg_out32 (s, MTSPR | RA (0) | CTR); + tcg_out32 (s, LWZ | RT (2) | RA (reg) | 4); + tcg_out32 (s, BCCTR | BO_ALWAYS | LK); +#else + if (const_arg) { + tcg_out_b (s, LK, arg); + } + else { + tcg_out32 (s, MTSPR | RS (arg) | LR); + tcg_out32 (s, BCLR | BO_ALWAYS | LK); + } +#endif +} + +#if defined(CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; +#endif + +static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, data_reg2, r0, r1, rbase, mem_index, s_bits, bswap; +#ifdef CONFIG_SOFTMMU + int r2; + void *label1_ptr, *label2_ptr; +#endif +#if TARGET_LONG_BITS == 64 + int addr_reg2; +#endif + + data_reg = *args++; + if (opc == 3) + data_reg2 = *args++; + else + data_reg2 = 0; + addr_reg = *args++; +#if TARGET_LONG_BITS == 64 + addr_reg2 = *args++; +#endif + mem_index = *args; + s_bits = opc & 3; + +#ifdef CONFIG_SOFTMMU + r0 = 3; + r1 = 4; + r2 = 0; + rbase = 0; + + tcg_out32 (s, (RLWINM + | RA (r0) + | RS (addr_reg) + | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) + | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS)) + | ME (31 - CPU_TLB_ENTRY_BITS) + ) + ); + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0)); + tcg_out32 (s, (LWZU + | RT (r1) + | RA (r0) + | offsetof (CPUState, tlb_table[mem_index][0].addr_read) + ) + ); + tcg_out32 (s, (RLWINM + | RA (r2) + | RS (addr_reg) + | SH (0) + | MB ((32 - s_bits) & 31) + | ME (31 - TARGET_PAGE_BITS) + ) + ); + + tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1)); +#if TARGET_LONG_BITS == 64 + tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4); + tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1)); + tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ)); +#endif + + label1_ptr = s->code_ptr; +#ifdef FAST_PATH + tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE); +#endif + + /* slow path */ +#if TARGET_LONG_BITS == 32 + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg); + tcg_out_movi (s, TCG_TYPE_I32, 4, mem_index); +#else + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2); + tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg); + tcg_out_movi (s, TCG_TYPE_I32, 5, mem_index); +#endif + + tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1); + switch (opc) { + case 0|4: + tcg_out32 (s, EXTSB | RA (data_reg) | RS (3)); + break; + case 1|4: + tcg_out32 (s, EXTSH | RA (data_reg) | RS (3)); + break; + case 0: + case 1: + case 2: + if (data_reg != 3) + tcg_out_mov (s, TCG_TYPE_I32, data_reg, 3); + break; + case 3: + if (data_reg == 3) { + if (data_reg2 == 4) { + tcg_out_mov (s, TCG_TYPE_I32, 0, 4); + tcg_out_mov (s, TCG_TYPE_I32, 4, 3); + tcg_out_mov (s, TCG_TYPE_I32, 3, 0); + } + else { + tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); + tcg_out_mov (s, TCG_TYPE_I32, 3, 4); + } + } + else { + if (data_reg != 4) tcg_out_mov (s, TCG_TYPE_I32, data_reg, 4); + if (data_reg2 != 3) tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); + } + break; + } + label2_ptr = s->code_ptr; + tcg_out32 (s, B); + + /* label1: fast path */ +#ifdef FAST_PATH + reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); +#endif + + /* r0 now contains &env->tlb_table[mem_index][index].addr_read */ + tcg_out32 (s, (LWZ + | RT (r0) + | RA (r0) + | (offsetof (CPUTLBEntry, addend) + - offsetof (CPUTLBEntry, addr_read)) + )); + /* r0 = env->tlb_table[mem_index][index].addend */ + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); + /* r0 = env->tlb_table[mem_index][index].addend + addr */ + +#else /* !CONFIG_SOFTMMU */ + r0 = addr_reg; + r1 = 3; + rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 0; +#else + bswap = 1; +#endif + + switch (opc) { + default: + case 0: + tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); + break; + case 0|4: + tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg)); + break; + case 1: + if (bswap) + tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); + else + tcg_out32 (s, LHZX | TAB (data_reg, rbase, r0)); + break; + case 1|4: + if (bswap) { + tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg)); + } + else tcg_out32 (s, LHAX | TAB (data_reg, rbase, r0)); + break; + case 2: + if (bswap) + tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); + else + tcg_out32 (s, LWZX | TAB (data_reg, rbase, r0)); + break; + case 3: + if (bswap) { + tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); + tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, LWBRX | TAB (data_reg2, rbase, r1)); + } + else { +#ifdef CONFIG_USE_GUEST_BASE + tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); + tcg_out32 (s, LWZX | TAB (data_reg2, rbase, r0)); + tcg_out32 (s, LWZX | TAB (data_reg, rbase, r1)); +#else + if (r0 == data_reg2) { + tcg_out32 (s, LWZ | RT (0) | RA (r0)); + tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4); + tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 0); + } + else { + tcg_out32 (s, LWZ | RT (data_reg2) | RA (r0)); + tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4); + } +#endif + } + break; + } + +#ifdef CONFIG_SOFTMMU + reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr); +#endif +} + +static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, r0, r1, data_reg, data_reg2, mem_index, bswap, rbase; +#ifdef CONFIG_SOFTMMU + int r2, ir; + void *label1_ptr, *label2_ptr; +#endif +#if TARGET_LONG_BITS == 64 + int addr_reg2; +#endif + + data_reg = *args++; + if (opc == 3) + data_reg2 = *args++; + else + data_reg2 = 0; + addr_reg = *args++; +#if TARGET_LONG_BITS == 64 + addr_reg2 = *args++; +#endif + mem_index = *args; + +#ifdef CONFIG_SOFTMMU + r0 = 3; + r1 = 4; + r2 = 0; + rbase = 0; + + tcg_out32 (s, (RLWINM + | RA (r0) + | RS (addr_reg) + | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) + | MB (32 - (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS)) + | ME (31 - CPU_TLB_ENTRY_BITS) + ) + ); + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0)); + tcg_out32 (s, (LWZU + | RT (r1) + | RA (r0) + | offsetof (CPUState, tlb_table[mem_index][0].addr_write) + ) + ); + tcg_out32 (s, (RLWINM + | RA (r2) + | RS (addr_reg) + | SH (0) + | MB ((32 - opc) & 31) + | ME (31 - TARGET_PAGE_BITS) + ) + ); + + tcg_out32 (s, CMP | (7 << 23) | RA (r2) | RB (r1)); +#if TARGET_LONG_BITS == 64 + tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4); + tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1)); + tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ)); +#endif + + label1_ptr = s->code_ptr; +#ifdef FAST_PATH + tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE); +#endif + + /* slow path */ +#if TARGET_LONG_BITS == 32 + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg); + ir = 4; +#else + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2); + tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg); +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + ir = 5; +#else + ir = 4; +#endif +#endif + + switch (opc) { + case 0: + tcg_out32 (s, (RLWINM + | RA (ir) + | RS (data_reg) + | SH (0) + | MB (24) + | ME (31))); + break; + case 1: + tcg_out32 (s, (RLWINM + | RA (ir) + | RS (data_reg) + | SH (0) + | MB (16) + | ME (31))); + break; + case 2: + tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); + break; + case 3: +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + ir = 5; +#endif + tcg_out_mov (s, TCG_TYPE_I32, ir++, data_reg2); + tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); + break; + } + ir++; + + tcg_out_movi (s, TCG_TYPE_I32, ir, mem_index); + tcg_out_call (s, (tcg_target_long) qemu_st_helpers[opc], 1); + label2_ptr = s->code_ptr; + tcg_out32 (s, B); + + /* label1: fast path */ +#ifdef FAST_PATH + reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); +#endif + + tcg_out32 (s, (LWZ + | RT (r0) + | RA (r0) + | (offsetof (CPUTLBEntry, addend) + - offsetof (CPUTLBEntry, addr_write)) + )); + /* r0 = env->tlb_table[mem_index][index].addend */ + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); + /* r0 = env->tlb_table[mem_index][index].addend + addr */ + +#else /* !CONFIG_SOFTMMU */ + r0 = addr_reg; + r1 = 3; + rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 0; +#else + bswap = 1; +#endif + switch (opc) { + case 0: + tcg_out32 (s, STBX | SAB (data_reg, rbase, r0)); + break; + case 1: + if (bswap) + tcg_out32 (s, STHBRX | SAB (data_reg, rbase, r0)); + else + tcg_out32 (s, STHX | SAB (data_reg, rbase, r0)); + break; + case 2: + if (bswap) + tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); + else + tcg_out32 (s, STWX | SAB (data_reg, rbase, r0)); + break; + case 3: + if (bswap) { + tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); + tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); + tcg_out32 (s, STWBRX | SAB (data_reg2, rbase, r1)); + } + else { +#ifdef CONFIG_USE_GUEST_BASE + tcg_out32 (s, STWX | SAB (data_reg2, rbase, r0)); + tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); + tcg_out32 (s, STWX | SAB (data_reg, rbase, r1)); +#else + tcg_out32 (s, STW | RS (data_reg2) | RA (r0)); + tcg_out32 (s, STW | RS (data_reg) | RA (r0) | 4); +#endif + } + break; + } + +#ifdef CONFIG_SOFTMMU + reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr); +#endif +} + +static void tcg_target_qemu_prologue (TCGContext *s) +{ + int i, frame_size; + + frame_size = 0 + + LINKAGE_AREA_SIZE + + TCG_STATIC_CALL_ARGS_SIZE + + ARRAY_SIZE (tcg_target_callee_save_regs) * 4 + ; + frame_size = (frame_size + 15) & ~15; + +#ifdef _CALL_AIX + { + uint32_t addr; + + /* First emit adhoc function descriptor */ + addr = (uint32_t) s->code_ptr + 12; + tcg_out32 (s, addr); /* entry point */ + s->code_ptr += 8; /* skip TOC and environment pointer */ + } +#endif + tcg_out32 (s, MFSPR | RT (0) | LR); + tcg_out32 (s, STWU | RS (1) | RA (1) | (-frame_size & 0xffff)); + for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) + tcg_out32 (s, (STW + | RS (tcg_target_callee_save_regs[i]) + | RA (1) + | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE) + ) + ); + tcg_out32 (s, STW | RS (0) | RA (1) | (frame_size + LR_OFFSET)); + +#ifdef CONFIG_USE_GUEST_BASE + if (GUEST_BASE) { + tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + tcg_out32 (s, MTSPR | RS (3) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS); + tb_ret_addr = s->code_ptr; + + for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) + tcg_out32 (s, (LWZ + | RT (tcg_target_callee_save_regs[i]) + | RA (1) + | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE) + ) + ); + tcg_out32 (s, LWZ | RT (0) | RA (1) | (frame_size + LR_OFFSET)); + tcg_out32 (s, MTSPR | RS (0) | LR); + tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size); + tcg_out32 (s, BCLR | BO_ALWAYS); +} + +static void tcg_out_ld (TCGContext *s, TCGType type, int ret, int arg1, + tcg_target_long arg2) +{ + tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX); +} + +static void tcg_out_st (TCGContext *s, TCGType type, int arg, int arg1, + tcg_target_long arg2) +{ + tcg_out_ldst (s, arg, arg1, arg2, STW, STWX); +} + +static void ppc_addi (TCGContext *s, int rt, int ra, tcg_target_long si) +{ + if (!si && rt == ra) + return; + + if (si == (int16_t) si) + tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff)); + else { + uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15); + tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h); + tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff)); + } +} + +static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + ppc_addi (s, reg, reg, val); +} + +static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, + int const_arg2, int cr) +{ + int imm; + uint32_t op; + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + if (const_arg2) { + if ((int16_t) arg2 == arg2) { + op = CMPI; + imm = 1; + break; + } + else if ((uint16_t) arg2 == arg2) { + op = CMPLI; + imm = 1; + break; + } + } + op = CMPL; + imm = 0; + break; + + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + if (const_arg2) { + if ((int16_t) arg2 == arg2) { + op = CMPI; + imm = 1; + break; + } + } + op = CMP; + imm = 0; + break; + + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + if (const_arg2) { + if ((uint16_t) arg2 == arg2) { + op = CMPLI; + imm = 1; + break; + } + } + op = CMPL; + imm = 0; + break; + + default: + tcg_abort (); + } + op |= BF (cr); + + if (imm) + tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff)); + else { + if (const_arg2) { + tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); + tcg_out32 (s, op | RA (arg1) | RB (0)); + } + else + tcg_out32 (s, op | RA (arg1) | RB (arg2)); + } + +} + +static void tcg_out_bc (TCGContext *s, int bc, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) + tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value)); + else { + uint16_t val = *(uint16_t *) &s->code_ptr[2]; + + /* Thanks to Andrzej Zaborowski */ + tcg_out32 (s, bc | (val & 0xfffc)); + tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0); + } +} + +static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args, + const int *const_args) +{ + TCGCond cond = args[4]; + int op; + struct { int bit1; int bit2; int cond2; } bits[] = { + [TCG_COND_LT ] = { CR_LT, CR_LT, TCG_COND_LT }, + [TCG_COND_LE ] = { CR_LT, CR_GT, TCG_COND_LT }, + [TCG_COND_GT ] = { CR_GT, CR_GT, TCG_COND_GT }, + [TCG_COND_GE ] = { CR_GT, CR_LT, TCG_COND_GT }, + [TCG_COND_LTU] = { CR_LT, CR_LT, TCG_COND_LTU }, + [TCG_COND_LEU] = { CR_LT, CR_GT, TCG_COND_LTU }, + [TCG_COND_GTU] = { CR_GT, CR_GT, TCG_COND_GTU }, + [TCG_COND_GEU] = { CR_GT, CR_LT, TCG_COND_GTU }, + }, *b = &bits[cond]; + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + op = (cond == TCG_COND_EQ) ? CRAND : CRNAND; + tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 6); + tcg_out_cmp (s, cond, args[1], args[3], const_args[3], 7); + tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ)); + break; + case TCG_COND_LT: + case TCG_COND_LE: + case TCG_COND_GT: + case TCG_COND_GE: + case TCG_COND_LTU: + case TCG_COND_LEU: + case TCG_COND_GTU: + case TCG_COND_GEU: + op = (b->bit1 != b->bit2) ? CRANDC : CRAND; + tcg_out_cmp (s, b->cond2, args[1], args[3], const_args[3], 5); + tcg_out_cmp (s, tcg_unsigned_cond (cond), args[0], args[2], + const_args[2], 7); + tcg_out32 (s, op | BT (7, CR_EQ) | BA (5, CR_EQ) | BB (7, b->bit2)); + tcg_out32 (s, CROR | BT (7, CR_EQ) | BA (5, b->bit1) | BB (7, CR_EQ)); + break; + default: + tcg_abort(); + } +} + +static void tcg_out_setcond (TCGContext *s, TCGCond cond, TCGArg arg0, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + int crop, sh, arg; + + switch (cond) { + case TCG_COND_EQ: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + tcg_out32 (s, CNTLZW | RS (arg) | RA (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (27) + | MB (5) + | ME (31) + ) + ); + break; + + case TCG_COND_NE: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + + if (arg == arg1 && arg1 == arg0) { + tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, 0, arg)); + } + else { + tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg)); + } + break; + + case TCG_COND_GT: + case TCG_COND_GTU: + sh = 30; + crop = 0; + goto crtest; + + case TCG_COND_LT: + case TCG_COND_LTU: + sh = 29; + crop = 0; + goto crtest; + + case TCG_COND_GE: + case TCG_COND_GEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT); + goto crtest; + + case TCG_COND_LE: + case TCG_COND_LEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); + crtest: + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); + if (crop) tcg_out32 (s, crop); + tcg_out32 (s, MFCR | RT (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (sh) + | MB (31) + | ME (31) + ) + ); + break; + + default: + tcg_abort (); + } +} + +static void tcg_out_setcond2 (TCGContext *s, const TCGArg *args, + const int *const_args) +{ + tcg_out_cr7eq_from_cond (s, args + 1, const_args + 1); + tcg_out32 (s, MFCR | RT (0)); + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (0) + | SH (31) + | MB (31) + | ME (31) + ) + ); +} + +static void tcg_out_brcond (TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index) +{ + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); + tcg_out_bc (s, tcg_to_bc[cond], label_index); +} + +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, + const int *const_args) +{ + tcg_out_cr7eq_from_cond (s, args, const_args); + tcg_out_bc (s, (BC | BI (7, CR_EQ) | BO_COND_TRUE), args[5]); +} + +void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) +{ + uint32_t *ptr; + long disp = addr - jmp_addr; + unsigned long patch_size; + + ptr = (uint32_t *)jmp_addr; + + if ((disp << 6) >> 6 != disp) { + ptr[0] = 0x3c000000 | (addr >> 16); /* lis 0,addr@ha */ + ptr[1] = 0x60000000 | (addr & 0xffff); /* la 0,addr@l(0) */ + ptr[2] = 0x7c0903a6; /* mtctr 0 */ + ptr[3] = 0x4e800420; /* brctr */ + patch_size = 16; + } else { + /* patch the branch destination */ + if (disp != 16) { + *ptr = 0x48000000 | (disp & 0x03fffffc); /* b disp */ + patch_size = 4; + } else { + ptr[0] = 0x60000000; /* nop */ + ptr[1] = 0x60000000; + ptr[2] = 0x60000000; + ptr[3] = 0x60000000; + patch_size = 16; + } + } + /* flush icache */ + flush_icache_range(jmp_addr, jmp_addr + patch_size); +} + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + switch (opc) { + case INDEX_op_exit_tb: + tcg_out_movi (s, TCG_TYPE_I32, TCG_REG_R3, args[0]); + tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + + s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; + s->code_ptr += 16; + } + else { + tcg_abort (); + } + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + case INDEX_op_br: + { + TCGLabel *l = &s->labels[args[0]]; + + if (l->has_value) { + tcg_out_b (s, 0, l->u.value); + } + else { + uint32_t val = *(uint32_t *) s->code_ptr; + + /* Thanks to Andrzej Zaborowski */ + tcg_out32 (s, B | (val & 0x3fffffc)); + tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0); + } + } + break; + case INDEX_op_call: + tcg_out_call (s, args[0], const_args[0]); + break; + case INDEX_op_jmp: + if (const_args[0]) { + tcg_out_b (s, 0, args[0]); + } + else { + tcg_out32 (s, MTSPR | RS (args[0]) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS); + } + break; + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ld8u_i32: + tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); + break; + case INDEX_op_ld8s_i32: + tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); + tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0])); + break; + case INDEX_op_ld16u_i32: + tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX); + break; + case INDEX_op_ld16s_i32: + tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX); + break; + case INDEX_op_ld_i32: + tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX); + break; + case INDEX_op_st8_i32: + tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX); + break; + case INDEX_op_st16_i32: + tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX); + break; + case INDEX_op_st_i32: + tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX); + break; + + case INDEX_op_add_i32: + if (const_args[2]) + ppc_addi (s, args[0], args[1], args[2]); + else + tcg_out32 (s, ADD | TAB (args[0], args[1], args[2])); + break; + case INDEX_op_sub_i32: + if (const_args[2]) + ppc_addi (s, args[0], args[1], -args[2]); + else + tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1])); + break; + + case INDEX_op_and_i32: + if (const_args[2]) { + uint32_t c; + + c = args[2]; + + if (!c) { + tcg_out_movi (s, TCG_TYPE_I32, args[0], 0); + break; + } +#ifdef __PPU__ + uint32_t t, n; + int mb, me; + + n = c ^ -(c & 1); + t = n + (n & -n); + + if ((t & (t - 1)) == 0) { + int lzc, tzc; + + if ((c & 0x80000001) == 0x80000001) { + lzc = clz32 (n); + tzc = ctz32 (n); + + mb = 32 - tzc; + me = lzc - 1; + } + else { + lzc = clz32 (c); + tzc = ctz32 (c); + + mb = lzc; + me = 31 - tzc; + } + + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (0) + | MB (mb) + | ME (me) + ) + ); + } + else +#endif /* !__PPU__ */ + { + if ((c & 0xffff) == c) + tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | c); + else if ((c & 0xffff0000) == c) + tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0]) + | ((c >> 16) & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, c); + tcg_out32 (s, AND | SAB (args[1], args[0], 0)); + } + } + } + else + tcg_out32 (s, AND | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_or_i32: + if (const_args[2]) { + if (args[2] & 0xffff) { + tcg_out32 (s, ORI | RS (args[1]) | RA (args[0]) + | (args[2] & 0xffff)); + if (args[2] >> 16) + tcg_out32 (s, ORIS | RS (args[0]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + } + else { + tcg_out32 (s, ORIS | RS (args[1]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + } + } + else + tcg_out32 (s, OR | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_xor_i32: + if (const_args[2]) { + if ((args[2] & 0xffff) == args[2]) + tcg_out32 (s, XORI | RS (args[1]) | RA (args[0]) + | (args[2] & 0xffff)); + else if ((args[2] & 0xffff0000) == args[2]) + tcg_out32 (s, XORIS | RS (args[1]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]); + tcg_out32 (s, XOR | SAB (args[1], args[0], 0)); + } + } + else + tcg_out32 (s, XOR | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_andc_i32: + tcg_out32 (s, ANDC | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_orc_i32: + tcg_out32 (s, ORC | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_eqv_i32: + tcg_out32 (s, EQV | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_nand_i32: + tcg_out32 (s, NAND | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_nor_i32: + tcg_out32 (s, NOR | SAB (args[1], args[0], args[2])); + break; + + case INDEX_op_mul_i32: + if (const_args[2]) { + if (args[2] == (int16_t) args[2]) + tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1]) + | (args[2] & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]); + tcg_out32 (s, MULLW | TAB (args[0], args[1], 0)); + } + } + else + tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2])); + break; + + case INDEX_op_div_i32: + tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2])); + break; + + case INDEX_op_divu_i32: + tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2])); + break; + + case INDEX_op_rem_i32: + tcg_out32 (s, DIVW | TAB (0, args[1], args[2])); + tcg_out32 (s, MULLW | TAB (0, 0, args[2])); + tcg_out32 (s, SUBF | TAB (args[0], 0, args[1])); + break; + + case INDEX_op_remu_i32: + tcg_out32 (s, DIVWU | TAB (0, args[1], args[2])); + tcg_out32 (s, MULLW | TAB (0, 0, args[2])); + tcg_out32 (s, SUBF | TAB (args[0], 0, args[1])); + break; + + case INDEX_op_mulu2_i32: + if (args[0] == args[2] || args[0] == args[3]) { + tcg_out32 (s, MULLW | TAB (0, args[2], args[3])); + tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3])); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); + } + else { + tcg_out32 (s, MULLW | TAB (args[0], args[2], args[3])); + tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3])); + } + break; + + case INDEX_op_shl_i32: + if (const_args[2]) { + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (args[2]) + | MB (0) + | ME (31 - args[2]) + ) + ); + } + else + tcg_out32 (s, SLW | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_shr_i32: + if (const_args[2]) { + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (32 - args[2]) + | MB (args[2]) + | ME (31) + ) + ); + } + else + tcg_out32 (s, SRW | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_sar_i32: + if (const_args[2]) + tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2])); + else + tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_rotl_i32: + { + int op = 0 + | RA (args[0]) + | RS (args[1]) + | MB (0) + | ME (31) + | (const_args[2] ? RLWINM | SH (args[2]) + : RLWNM | RB (args[2])) + ; + tcg_out32 (s, op); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + if (!args[2]) { + tcg_out_mov (s, TCG_TYPE_I32, args[0], args[1]); + } + else { + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (32 - args[2]) + | MB (0) + | ME (31) + ); + } + } + else { + tcg_out32 (s, SUBFIC | RT (0) | RA (args[2]) | 32); + tcg_out32 (s, RLWNM + | RA (args[0]) + | RS (args[1]) + | RB (0) + | MB (0) + | ME (31) + ); + } + break; + + case INDEX_op_add2_i32: + if (args[0] == args[3] || args[0] == args[5]) { + tcg_out32 (s, ADDC | TAB (0, args[2], args[4])); + tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5])); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); + } + else { + tcg_out32 (s, ADDC | TAB (args[0], args[2], args[4])); + tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5])); + } + break; + case INDEX_op_sub2_i32: + if (args[0] == args[3] || args[0] == args[5]) { + tcg_out32 (s, SUBFC | TAB (0, args[4], args[2])); + tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3])); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); + } + else { + tcg_out32 (s, SUBFC | TAB (args[0], args[4], args[2])); + tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3])); + } + break; + + case INDEX_op_brcond_i32: + /* + args[0] = r0 + args[1] = r1 + args[2] = cond + args[3] = r1 is const + args[4] = label_index + */ + tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3]); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args, const_args); + break; + + case INDEX_op_neg_i32: + tcg_out32 (s, NEG | RT (args[0]) | RA (args[1])); + break; + + case INDEX_op_not_i32: + tcg_out32 (s, NOR | SAB (args[1], args[0], args[1])); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + + case INDEX_op_ext8s_i32: + tcg_out32 (s, EXTSB | RS (args[1]) | RA (args[0])); + break; + case INDEX_op_ext8u_i32: + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (0) + | MB (24) + | ME (31) + ); + break; + case INDEX_op_ext16s_i32: + tcg_out32 (s, EXTSH | RS (args[1]) | RA (args[0])); + break; + case INDEX_op_ext16u_i32: + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (0) + | MB (16) + | ME (31) + ); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond (s, args[3], args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2 (s, args, const_args); + break; + + case INDEX_op_bswap16_i32: + /* Stolen from gcc's builtin_bswap16 */ + + /* a1 = abcd */ + + /* r0 = (a1 << 8) & 0xff00 # 00d0 */ + tcg_out32 (s, RLWINM + | RA (0) + | RS (args[1]) + | SH (8) + | MB (16) + | ME (23) + ); + + /* a0 = rotate_left (a1, 24) & 0xff # 000c */ + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (24) + | MB (24) + | ME (31) + ); + + /* a0 = a0 | r0 # 00dc */ + tcg_out32 (s, OR | SAB (0, args[0], args[0])); + break; + + case INDEX_op_bswap32_i32: + /* Stolen from gcc's builtin_bswap32 */ + { + int a0 = args[0]; + + /* a1 = args[1] # abcd */ + + if (a0 == args[1]) { + a0 = 0; + } + + /* a0 = rotate_left (a1, 8) # bcda */ + tcg_out32 (s, RLWINM + | RA (a0) + | RS (args[1]) + | SH (8) + | MB (0) + | ME (31) + ); + + /* a0 = (a0 & ~0xff000000) | ((a1 << 24) & 0xff000000) # dcda */ + tcg_out32 (s, RLWIMI + | RA (a0) + | RS (args[1]) + | SH (24) + | MB (0) + | ME (7) + ); + + /* a0 = (a0 & ~0x0000ff00) | ((a1 << 24) & 0x0000ff00) # dcba */ + tcg_out32 (s, RLWIMI + | RA (a0) + | RS (args[1]) + | SH (24) + | MB (16) + | ME (23) + ); + + if (!a0) { + tcg_out_mov (s, TCG_TYPE_I32, args[0], a0); + } + } + break; + + default: + tcg_dump_ops (s, stderr); + tcg_abort (); + } +} + +static const TCGTargetOpDef ppc_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "ri" } }, + { INDEX_op_mul_i32, { "r", "r", "ri" } }, + { INDEX_op_div_i32, { "r", "r", "r" } }, + { INDEX_op_divu_i32, { "r", "r", "r" } }, + { INDEX_op_rem_i32, { "r", "r", "r" } }, + { INDEX_op_remu_i32, { "r", "r", "r" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_sub_i32, { "r", "r", "ri" } }, + { INDEX_op_and_i32, { "r", "r", "ri" } }, + { INDEX_op_or_i32, { "r", "r", "ri" } }, + { INDEX_op_xor_i32, { "r", "r", "ri" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_brcond_i32, { "r", "ri" } }, + + { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } }, + { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } }, + { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_andc_i32, { "r", "r", "r" } }, + { INDEX_op_orc_i32, { "r", "r", "r" } }, + { INDEX_op_eqv_i32, { "r", "r", "r" } }, + { INDEX_op_nand_i32, { "r", "r", "r" } }, + { INDEX_op_nor_i32, { "r", "r", "r" } }, + + { INDEX_op_setcond_i32, { "r", "r", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "r", "L" } }, + + { INDEX_op_qemu_st8, { "K", "K" } }, + { INDEX_op_qemu_st16, { "K", "K" } }, + { INDEX_op_qemu_st32, { "K", "K" } }, + { INDEX_op_qemu_st64, { "M", "M", "M" } }, +#else + { INDEX_op_qemu_ld8u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L", "L", "L" } }, + + { INDEX_op_qemu_st8, { "K", "K", "K" } }, + { INDEX_op_qemu_st16, { "K", "K", "K" } }, + { INDEX_op_qemu_st32, { "K", "K", "K" } }, + { INDEX_op_qemu_st64, { "M", "M", "M", "M" } }, +#endif + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_R0) | +#ifdef _CALL_DARWIN + (1 << TCG_REG_R2) | +#endif + (1 << TCG_REG_R3) | + (1 << TCG_REG_R4) | + (1 << TCG_REG_R5) | + (1 << TCG_REG_R6) | + (1 << TCG_REG_R7) | + (1 << TCG_REG_R8) | + (1 << TCG_REG_R9) | + (1 << TCG_REG_R10) | + (1 << TCG_REG_R11) | + (1 << TCG_REG_R12) + ); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); +#ifndef _CALL_DARWIN + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); +#endif +#ifdef _CALL_SYSV + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); +#endif + + tcg_add_target_add_op_defs(ppc_op_defs); +} diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h new file mode 100644 index 0000000..a1f8599 --- /dev/null +++ b/tcg/ppc/tcg-target.h @@ -0,0 +1,98 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_PPC 1 + +#define TCG_TARGET_REG_BITS 32 +#define TCG_TARGET_WORDS_BIGENDIAN +#define TCG_TARGET_NB_REGS 32 + +enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31 +}; + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_R1 +#define TCG_TARGET_STACK_ALIGN 16 +#if defined _CALL_DARWIN || defined __APPLE__ +#define TCG_TARGET_CALL_STACK_OFFSET 24 +#elif defined _CALL_AIX +#define TCG_TARGET_CALL_STACK_OFFSET 52 +#elif defined _CALL_SYSV +#define TCG_TARGET_CALL_ALIGN_ARGS 1 +#define TCG_TARGET_CALL_STACK_OFFSET 8 +#else +#error Unsupported system +#endif + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_eqv_i32 +#define TCG_TARGET_HAS_nand_i32 +#define TCG_TARGET_HAS_nor_i32 + +#define TCG_AREG0 TCG_REG_R27 + +#define TCG_TARGET_HAS_GUEST_BASE diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c new file mode 100644 index 0000000..ebbee34 --- /dev/null +++ b/tcg/ppc64/tcg-target.c @@ -0,0 +1,1699 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define TCG_CT_CONST_U32 0x100 + +static uint8_t *tb_ret_addr; + +#define FAST_PATH + +#if TARGET_LONG_BITS == 32 +#define LD_ADDR LWZU +#define CMP_L 0 +#else +#define LD_ADDR LDU +#define CMP_L (1<<21) +#endif + +#ifndef GUEST_BASE +#define GUEST_BASE 0 +#endif + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG 30 +#else +#define TCG_GUEST_BASE_REG 0 +#endif + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "r16", + "r17", + "r18", + "r19", + "r20", + "r21", + "r22", + "r23", + "r24", + "r25", + "r26", + "r27", + "r28", + "r29", + "r30", + "r31" +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, +#ifdef __APPLE__ + TCG_REG_R2, +#endif + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, +#ifndef __APPLE__ + TCG_REG_R11, +#endif + TCG_REG_R12, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27 +}; + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10 +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_R3 +}; + +static const int tcg_target_callee_save_regs[] = { +#ifdef __APPLE__ + TCG_REG_R11, +#endif + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + /* TCG_REG_R27, */ /* currently used for the global env, so no + need to save */ + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31 +}; + +static uint32_t reloc_pc24_val (void *pc, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) pc; + if ((disp << 38) >> 38 != disp) + tcg_abort (); + + return disp & 0x3fffffc; +} + +static void reloc_pc24 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc) + | reloc_pc24_val (pc, target); +} + +static uint16_t reloc_pc14_val (void *pc, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) pc; + if (disp != (int16_t) disp) + tcg_abort (); + + return disp & 0xfffc; +} + +static void reloc_pc14 (void *pc, tcg_target_long target) +{ + *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc) + | reloc_pc14_val (pc, target); +} + +static void patch_reloc (uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch (type) { + case R_PPC_REL14: + reloc_pc14 (code_ptr, value); + break; + case R_PPC_REL24: + reloc_pc24 (code_ptr, value); + break; + default: + tcg_abort (); + } +} + +/* maximum number of register used for input function arguments */ +static int tcg_target_get_call_iarg_regs_count (int flags) +{ + return ARRAY_SIZE (tcg_target_call_iarg_regs); +} + +/* parse target specific constraints */ +static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'A': case 'B': case 'C': case 'D': + ct->ct |= TCG_CT_REG; + tcg_regset_set_reg (ct->u.regs, 3 + ct_str[0] - 'A'); + break; + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32 (ct->u.regs, 0, 0xffffffff); + break; + case 'L': /* qemu_ld constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32 (ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); +#ifdef CONFIG_SOFTMMU + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4); +#endif + break; + case 'S': /* qemu_st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32 (ct->u.regs, 0, 0xffffffff); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); +#ifdef CONFIG_SOFTMMU + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R5); +#endif + break; + case 'Z': + ct->ct |= TCG_CT_CONST_U32; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static int tcg_target_const_match (tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_U32) && (val == (uint32_t) val)) + return 1; + return 0; +} + +#define OPCD(opc) ((opc)<<26) +#define XO19(opc) (OPCD(19)|((opc)<<1)) +#define XO30(opc) (OPCD(30)|((opc)<<2)) +#define XO31(opc) (OPCD(31)|((opc)<<1)) +#define XO58(opc) (OPCD(58)|(opc)) +#define XO62(opc) (OPCD(62)|(opc)) + +#define B OPCD( 18) +#define BC OPCD( 16) +#define LBZ OPCD( 34) +#define LHZ OPCD( 40) +#define LHA OPCD( 42) +#define LWZ OPCD( 32) +#define STB OPCD( 38) +#define STH OPCD( 44) +#define STW OPCD( 36) + +#define STD XO62( 0) +#define STDU XO62( 1) +#define STDX XO31(149) + +#define LD XO58( 0) +#define LDX XO31( 21) +#define LDU XO58( 1) +#define LWA XO58( 2) +#define LWAX XO31(341) + +#define ADDIC OPCD( 12) +#define ADDI OPCD( 14) +#define ADDIS OPCD( 15) +#define ORI OPCD( 24) +#define ORIS OPCD( 25) +#define XORI OPCD( 26) +#define XORIS OPCD( 27) +#define ANDI OPCD( 28) +#define ANDIS OPCD( 29) +#define MULLI OPCD( 7) +#define CMPLI OPCD( 10) +#define CMPI OPCD( 11) + +#define LWZU OPCD( 33) +#define STWU OPCD( 37) + +#define RLWINM OPCD( 21) + +#define RLDICL XO30( 0) +#define RLDICR XO30( 1) +#define RLDIMI XO30( 3) + +#define BCLR XO19( 16) +#define BCCTR XO19(528) +#define CRAND XO19(257) +#define CRANDC XO19(129) +#define CRNAND XO19(225) +#define CROR XO19(449) +#define CRNOR XO19( 33) + +#define EXTSB XO31(954) +#define EXTSH XO31(922) +#define EXTSW XO31(986) +#define ADD XO31(266) +#define ADDE XO31(138) +#define ADDC XO31( 10) +#define AND XO31( 28) +#define SUBF XO31( 40) +#define SUBFC XO31( 8) +#define SUBFE XO31(136) +#define OR XO31(444) +#define XOR XO31(316) +#define MULLW XO31(235) +#define MULHWU XO31( 11) +#define DIVW XO31(491) +#define DIVWU XO31(459) +#define CMP XO31( 0) +#define CMPL XO31( 32) +#define LHBRX XO31(790) +#define LWBRX XO31(534) +#define STHBRX XO31(918) +#define STWBRX XO31(662) +#define MFSPR XO31(339) +#define MTSPR XO31(467) +#define SRAWI XO31(824) +#define NEG XO31(104) +#define MFCR XO31( 19) +#define CNTLZW XO31( 26) +#define CNTLZD XO31( 58) + +#define MULLD XO31(233) +#define MULHD XO31( 73) +#define MULHDU XO31( 9) +#define DIVD XO31(489) +#define DIVDU XO31(457) + +#define LBZX XO31( 87) +#define LHZX XO31(279) +#define LHAX XO31(343) +#define LWZX XO31( 23) +#define STBX XO31(215) +#define STHX XO31(407) +#define STWX XO31(151) + +#define SPR(a,b) ((((a)<<5)|(b))<<11) +#define LR SPR(8, 0) +#define CTR SPR(9, 0) + +#define SLW XO31( 24) +#define SRW XO31(536) +#define SRAW XO31(792) + +#define SLD XO31( 27) +#define SRD XO31(539) +#define SRAD XO31(794) +#define SRADI XO31(413<<1) + +#define TW XO31( 4) +#define TRAP (TW | TO (31)) + +#define RT(r) ((r)<<21) +#define RS(r) ((r)<<21) +#define RA(r) ((r)<<16) +#define RB(r) ((r)<<11) +#define TO(t) ((t)<<21) +#define SH(s) ((s)<<11) +#define MB(b) ((b)<<6) +#define ME(e) ((e)<<1) +#define BO(o) ((o)<<21) +#define MB64(b) ((b)<<5) + +#define LK 1 + +#define TAB(t,a,b) (RT(t) | RA(a) | RB(b)) +#define SAB(s,a,b) (RS(s) | RA(a) | RB(b)) + +#define BF(n) ((n)<<23) +#define BI(n, c) (((c)+((n)*4))<<16) +#define BT(n, c) (((c)+((n)*4))<<21) +#define BA(n, c) (((c)+((n)*4))<<16) +#define BB(n, c) (((c)+((n)*4))<<11) + +#define BO_COND_TRUE BO (12) +#define BO_COND_FALSE BO ( 4) +#define BO_ALWAYS BO (20) + +enum { + CR_LT, + CR_GT, + CR_EQ, + CR_SO +}; + +static const uint32_t tcg_to_bc[10] = { + [TCG_COND_EQ] = BC | BI (7, CR_EQ) | BO_COND_TRUE, + [TCG_COND_NE] = BC | BI (7, CR_EQ) | BO_COND_FALSE, + [TCG_COND_LT] = BC | BI (7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GE] = BC | BI (7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LE] = BC | BI (7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GT] = BC | BI (7, CR_GT) | BO_COND_TRUE, + [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, +}; + +static void tcg_out_mov (TCGContext *s, TCGType type, int ret, int arg) +{ + tcg_out32 (s, OR | SAB (arg, ret, arg)); +} + +static void tcg_out_rld (TCGContext *s, int op, int ra, int rs, int sh, int mb) +{ + sh = SH (sh & 0x1f) | (((sh >> 5) & 1) << 1); + mb = MB64 ((mb >> 5) | ((mb << 1) & 0x3f)); + tcg_out32 (s, op | RA (ra) | RS (rs) | sh | mb); +} + +static void tcg_out_movi32 (TCGContext *s, int ret, int32_t arg) +{ + if (arg == (int16_t) arg) + tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff)); + else { + tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff)); + if (arg & 0xffff) + tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff)); + } +} + +static void tcg_out_movi (TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + int32_t arg32 = arg; + arg = type == TCG_TYPE_I32 ? arg & 0xffffffff : arg; + + if (arg == arg32) { + tcg_out_movi32 (s, ret, arg32); + } + else { + if ((uint64_t) arg >> 32) { + uint16_t h16 = arg >> 16; + uint16_t l16 = arg; + + tcg_out_movi32 (s, ret, arg >> 32); + tcg_out_rld (s, RLDICR, ret, ret, 32, 31); + if (h16) tcg_out32 (s, ORIS | RS (ret) | RA (ret) | h16); + if (l16) tcg_out32 (s, ORI | RS (ret) | RA (ret) | l16); + } + else { + tcg_out_movi32 (s, ret, arg32); + if (arg32 < 0) + tcg_out_rld (s, RLDICL, ret, ret, 0, 32); + } + } +} + +static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) s->code_ptr; + if ((disp << 38) >> 38 == disp) + tcg_out32 (s, B | (disp & 0x3fffffc) | mask); + else { + tcg_out_movi (s, TCG_TYPE_I64, 0, (tcg_target_long) target); + tcg_out32 (s, MTSPR | RS (0) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS | mask); + } +} + +static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) +{ +#ifdef __APPLE__ + if (const_arg) { + tcg_out_b (s, LK, arg); + } + else { + tcg_out32 (s, MTSPR | RS (arg) | LR); + tcg_out32 (s, BCLR | BO_ALWAYS | LK); + } +#else + int reg; + + if (const_arg) { + reg = 2; + tcg_out_movi (s, TCG_TYPE_I64, reg, arg); + } + else reg = arg; + + tcg_out32 (s, LD | RT (0) | RA (reg)); + tcg_out32 (s, MTSPR | RA (0) | CTR); + tcg_out32 (s, LD | RT (11) | RA (reg) | 16); + tcg_out32 (s, LD | RT (2) | RA (reg) | 8); + tcg_out32 (s, BCCTR | BO_ALWAYS | LK); +#endif +} + +static void tcg_out_ldst (TCGContext *s, int ret, int addr, + int offset, int op1, int op2) +{ + if (offset == (int16_t) offset) + tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I64, 0, offset); + tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0)); + } +} + +static void tcg_out_ldsta (TCGContext *s, int ret, int addr, + int offset, int op1, int op2) +{ + if (offset == (int16_t) (offset & ~3)) + tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I64, 0, offset); + tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0)); + } +} + +#if defined (CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; + +static void tcg_out_tlb_read (TCGContext *s, int r0, int r1, int r2, + int addr_reg, int s_bits, int offset) +{ +#if TARGET_LONG_BITS == 32 + tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32); + + tcg_out32 (s, (RLWINM + | RA (r0) + | RS (addr_reg) + | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) + | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS)) + | ME (31 - CPU_TLB_ENTRY_BITS) + ) + ); + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0)); + tcg_out32 (s, (LWZU | RT (r1) | RA (r0) | offset)); + tcg_out32 (s, (RLWINM + | RA (r2) + | RS (addr_reg) + | SH (0) + | MB ((32 - s_bits) & 31) + | ME (31 - TARGET_PAGE_BITS) + ) + ); +#else + tcg_out_rld (s, RLDICL, r0, addr_reg, + 64 - TARGET_PAGE_BITS, + 64 - CPU_TLB_BITS); + tcg_out_rld (s, RLDICR, r0, r0, + CPU_TLB_ENTRY_BITS, + 63 - CPU_TLB_ENTRY_BITS); + + tcg_out32 (s, ADD | TAB (r0, r0, TCG_AREG0)); + tcg_out32 (s, LD_ADDR | RT (r1) | RA (r0) | offset); + + if (!s_bits) { + tcg_out_rld (s, RLDICR, r2, addr_reg, 0, 63 - TARGET_PAGE_BITS); + } + else { + tcg_out_rld (s, RLDICL, r2, addr_reg, + 64 - TARGET_PAGE_BITS, + TARGET_PAGE_BITS - s_bits); + tcg_out_rld (s, RLDICL, r2, r2, TARGET_PAGE_BITS, 0); + } +#endif +} +#endif + +static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, r0, r1, rbase, mem_index, s_bits, bswap; +#ifdef CONFIG_SOFTMMU + int r2; + void *label1_ptr, *label2_ptr; +#endif + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + s_bits = opc & 3; + +#ifdef CONFIG_SOFTMMU + r0 = 3; + r1 = 4; + r2 = 0; + rbase = 0; + + tcg_out_tlb_read (s, r0, r1, r2, addr_reg, s_bits, + offsetof (CPUState, tlb_table[mem_index][0].addr_read)); + + tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L); + + label1_ptr = s->code_ptr; +#ifdef FAST_PATH + tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE); +#endif + + /* slow path */ + tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg); + tcg_out_movi (s, TCG_TYPE_I64, 4, mem_index); + + tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1); + + switch (opc) { + case 0|4: + tcg_out32 (s, EXTSB | RA (data_reg) | RS (3)); + break; + case 1|4: + tcg_out32 (s, EXTSH | RA (data_reg) | RS (3)); + break; + case 2|4: + tcg_out32 (s, EXTSW | RA (data_reg) | RS (3)); + break; + case 0: + case 1: + case 2: + case 3: + if (data_reg != 3) + tcg_out_mov (s, TCG_TYPE_I64, data_reg, 3); + break; + } + label2_ptr = s->code_ptr; + tcg_out32 (s, B); + + /* label1: fast path */ +#ifdef FAST_PATH + reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); +#endif + + /* r0 now contains &env->tlb_table[mem_index][index].addr_read */ + tcg_out32 (s, (LD + | RT (r0) + | RA (r0) + | (offsetof (CPUTLBEntry, addend) + - offsetof (CPUTLBEntry, addr_read)) + )); + /* r0 = env->tlb_table[mem_index][index].addend */ + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); + /* r0 = env->tlb_table[mem_index][index].addend + addr */ + +#else /* !CONFIG_SOFTMMU */ +#if TARGET_LONG_BITS == 32 + tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32); +#endif + r0 = addr_reg; + r1 = 3; + rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 0; +#else + bswap = 1; +#endif + switch (opc) { + default: + case 0: + tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); + break; + case 0|4: + tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg)); + break; + case 1: + if (bswap) + tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); + else + tcg_out32 (s, LHZX | TAB (data_reg, rbase, r0)); + break; + case 1|4: + if (bswap) { + tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg)); + } + else tcg_out32 (s, LHAX | TAB (data_reg, rbase, r0)); + break; + case 2: + if (bswap) + tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); + else + tcg_out32 (s, LWZX | TAB (data_reg, rbase, r0)); + break; + case 2|4: + if (bswap) { + tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, EXTSW | RA (data_reg) | RS (data_reg)); + } + else tcg_out32 (s, LWAX | TAB (data_reg, rbase, r0)); + break; + case 3: +#ifdef CONFIG_USE_GUEST_BASE + if (bswap) { + tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); + tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); + tcg_out32 (s, LWBRX | TAB ( r1, rbase, r1)); + tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0); + } + else tcg_out32 (s, LDX | TAB (data_reg, rbase, r0)); +#else + if (bswap) { + tcg_out_movi32 (s, 0, 4); + tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0)); + tcg_out32 (s, LWBRX | RT ( r1) | RA (r0)); + tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0); + } + else tcg_out32 (s, LD | RT (data_reg) | RA (r0)); +#endif + break; + } + +#ifdef CONFIG_SOFTMMU + reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr); +#endif +} + +static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, r0, r1, rbase, data_reg, mem_index, bswap; +#ifdef CONFIG_SOFTMMU + int r2; + void *label1_ptr, *label2_ptr; +#endif + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + +#ifdef CONFIG_SOFTMMU + r0 = 3; + r1 = 4; + r2 = 0; + rbase = 0; + + tcg_out_tlb_read (s, r0, r1, r2, addr_reg, opc, + offsetof (CPUState, tlb_table[mem_index][0].addr_write)); + + tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L); + + label1_ptr = s->code_ptr; +#ifdef FAST_PATH + tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE); +#endif + + /* slow path */ + tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg); + tcg_out_rld (s, RLDICL, 4, data_reg, 0, 64 - (1 << (3 + opc))); + tcg_out_movi (s, TCG_TYPE_I64, 5, mem_index); + + tcg_out_call (s, (tcg_target_long) qemu_st_helpers[opc], 1); + + label2_ptr = s->code_ptr; + tcg_out32 (s, B); + + /* label1: fast path */ +#ifdef FAST_PATH + reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); +#endif + + tcg_out32 (s, (LD + | RT (r0) + | RA (r0) + | (offsetof (CPUTLBEntry, addend) + - offsetof (CPUTLBEntry, addr_write)) + )); + /* r0 = env->tlb_table[mem_index][index].addend */ + tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); + /* r0 = env->tlb_table[mem_index][index].addend + addr */ + +#else /* !CONFIG_SOFTMMU */ +#if TARGET_LONG_BITS == 32 + tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32); +#endif + r1 = 3; + r0 = addr_reg; + rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 0; +#else + bswap = 1; +#endif + switch (opc) { + case 0: + tcg_out32 (s, STBX | SAB (data_reg, rbase, r0)); + break; + case 1: + if (bswap) + tcg_out32 (s, STHBRX | SAB (data_reg, rbase, r0)); + else + tcg_out32 (s, STHX | SAB (data_reg, rbase, r0)); + break; + case 2: + if (bswap) + tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); + else + tcg_out32 (s, STWX | SAB (data_reg, rbase, r0)); + break; + case 3: + if (bswap) { + tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); + tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); + tcg_out_rld (s, RLDICL, 0, data_reg, 32, 0); + tcg_out32 (s, STWBRX | SAB (0, rbase, r1)); + } + else tcg_out32 (s, STDX | SAB (data_reg, rbase, r0)); + break; + } + +#ifdef CONFIG_SOFTMMU + reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr); +#endif +} + +static void tcg_target_qemu_prologue (TCGContext *s) +{ + int i, frame_size; +#ifndef __APPLE__ + uint64_t addr; +#endif + + frame_size = 0 + + 8 /* back chain */ + + 8 /* CR */ + + 8 /* LR */ + + 8 /* compiler doubleword */ + + 8 /* link editor doubleword */ + + 8 /* TOC save area */ + + TCG_STATIC_CALL_ARGS_SIZE + + ARRAY_SIZE (tcg_target_callee_save_regs) * 8 + ; + frame_size = (frame_size + 15) & ~15; + +#ifndef __APPLE__ + /* First emit adhoc function descriptor */ + addr = (uint64_t) s->code_ptr + 24; + tcg_out32 (s, addr >> 32); tcg_out32 (s, addr); /* entry point */ + s->code_ptr += 16; /* skip TOC and environment pointer */ +#endif + + /* Prologue */ + tcg_out32 (s, MFSPR | RT (0) | LR); + tcg_out32 (s, STDU | RS (1) | RA (1) | (-frame_size & 0xffff)); + for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) + tcg_out32 (s, (STD + | RS (tcg_target_callee_save_regs[i]) + | RA (1) + | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE) + ) + ); + tcg_out32 (s, STD | RS (0) | RA (1) | (frame_size + 16)); + +#ifdef CONFIG_USE_GUEST_BASE + if (GUEST_BASE) { + tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + tcg_out32 (s, MTSPR | RS (3) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS); + + /* Epilogue */ + tb_ret_addr = s->code_ptr; + + for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) + tcg_out32 (s, (LD + | RT (tcg_target_callee_save_regs[i]) + | RA (1) + | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE) + ) + ); + tcg_out32 (s, LD | RT (0) | RA (1) | (frame_size + 16)); + tcg_out32 (s, MTSPR | RS (0) | LR); + tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size); + tcg_out32 (s, BCLR | BO_ALWAYS); +} + +static void tcg_out_ld (TCGContext *s, TCGType type, int ret, int arg1, + tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) + tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX); + else + tcg_out_ldsta (s, ret, arg1, arg2, LD, LDX); +} + +static void tcg_out_st (TCGContext *s, TCGType type, int arg, int arg1, + tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) + tcg_out_ldst (s, arg, arg1, arg2, STW, STWX); + else + tcg_out_ldsta (s, arg, arg1, arg2, STD, STDX); +} + +static void ppc_addi32 (TCGContext *s, int rt, int ra, tcg_target_long si) +{ + if (!si && rt == ra) + return; + + if (si == (int16_t) si) + tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff)); + else { + uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15); + tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h); + tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff)); + } +} + +static void ppc_addi64 (TCGContext *s, int rt, int ra, tcg_target_long si) +{ + /* XXX: suboptimal */ + if (si == (int16_t) si + || ((((uint64_t) si >> 31) == 0) && (si & 0x8000) == 0)) + ppc_addi32 (s, rt, ra, si); + else { + tcg_out_movi (s, TCG_TYPE_I64, 0, si); + tcg_out32 (s, ADD | RT (rt) | RA (ra)); + } +} + +static void tcg_out_addi (TCGContext *s, int reg, tcg_target_long val) +{ + ppc_addi64 (s, reg, reg, val); +} + +static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, + int const_arg2, int cr, int arch64) +{ + int imm; + uint32_t op; + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + if (const_arg2) { + if ((int16_t) arg2 == arg2) { + op = CMPI; + imm = 1; + break; + } + else if ((uint16_t) arg2 == arg2) { + op = CMPLI; + imm = 1; + break; + } + } + op = CMPL; + imm = 0; + break; + + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + if (const_arg2) { + if ((int16_t) arg2 == arg2) { + op = CMPI; + imm = 1; + break; + } + } + op = CMP; + imm = 0; + break; + + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + if (const_arg2) { + if ((uint16_t) arg2 == arg2) { + op = CMPLI; + imm = 1; + break; + } + } + op = CMPL; + imm = 0; + break; + + default: + tcg_abort (); + } + op |= BF (cr) | (arch64 << 21); + + if (imm) + tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff)); + else { + if (const_arg2) { + tcg_out_movi (s, TCG_TYPE_I64, 0, arg2); + tcg_out32 (s, op | RA (arg1) | RB (0)); + } + else + tcg_out32 (s, op | RA (arg1) | RB (arg2)); + } + +} + +static void tcg_out_setcond (TCGContext *s, TCGType type, TCGCond cond, + TCGArg arg0, TCGArg arg1, TCGArg arg2, + int const_arg2) +{ + int crop, sh, arg; + + switch (cond) { + case TCG_COND_EQ: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, type, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + + if (type == TCG_TYPE_I64) { + tcg_out32 (s, CNTLZD | RS (arg) | RA (0)); + tcg_out_rld (s, RLDICL, arg0, 0, 58, 6); + } + else { + tcg_out32 (s, CNTLZW | RS (arg) | RA (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (27) + | MB (5) + | ME (31) + ) + ); + } + break; + + case TCG_COND_NE: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, type, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + + if (arg == arg1 && arg1 == arg0) { + tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, 0, arg)); + } + else { + tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg)); + } + break; + + case TCG_COND_GT: + case TCG_COND_GTU: + sh = 30; + crop = 0; + goto crtest; + + case TCG_COND_LT: + case TCG_COND_LTU: + sh = 29; + crop = 0; + goto crtest; + + case TCG_COND_GE: + case TCG_COND_GEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT); + goto crtest; + + case TCG_COND_LE: + case TCG_COND_LEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); + crtest: + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, type == TCG_TYPE_I64); + if (crop) tcg_out32 (s, crop); + tcg_out32 (s, MFCR | RT (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (sh) + | MB (31) + | ME (31) + ) + ); + break; + + default: + tcg_abort (); + } +} + +static void tcg_out_bc (TCGContext *s, int bc, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) + tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value)); + else { + uint16_t val = *(uint16_t *) &s->code_ptr[2]; + + /* Thanks to Andrzej Zaborowski */ + tcg_out32 (s, bc | (val & 0xfffc)); + tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0); + } +} + +static void tcg_out_brcond (TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int arch64) +{ + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, arch64); + tcg_out_bc (s, tcg_to_bc[cond], label_index); +} + +void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) +{ + TCGContext s; + unsigned long patch_size; + + s.code_ptr = (uint8_t *) jmp_addr; + tcg_out_b (&s, 0, addr); + patch_size = s.code_ptr - (uint8_t *) jmp_addr; + flush_icache_range (jmp_addr, jmp_addr + patch_size); +} + +static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + int c; + + switch (opc) { + case INDEX_op_exit_tb: + tcg_out_movi (s, TCG_TYPE_I64, TCG_REG_R3, args[0]); + tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + + s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; + s->code_ptr += 28; + } + else { + tcg_abort (); + } + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + case INDEX_op_br: + { + TCGLabel *l = &s->labels[args[0]]; + + if (l->has_value) { + tcg_out_b (s, 0, l->u.value); + } + else { + uint32_t val = *(uint32_t *) s->code_ptr; + + /* Thanks to Andrzej Zaborowski */ + tcg_out32 (s, B | (val & 0x3fffffc)); + tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0); + } + } + break; + case INDEX_op_call: + tcg_out_call (s, args[0], const_args[0]); + break; + case INDEX_op_jmp: + if (const_args[0]) { + tcg_out_b (s, 0, args[0]); + } + else { + tcg_out32 (s, MTSPR | RS (args[0]) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS); + } + break; + case INDEX_op_movi_i32: + tcg_out_movi (s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_movi_i64: + tcg_out_movi (s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); + break; + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); + tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0])); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX); + break; + case INDEX_op_ld32s_i64: + tcg_out_ldsta (s, args[0], args[1], args[2], LWA, LWAX); + break; + case INDEX_op_ld_i64: + tcg_out_ldsta (s, args[0], args[1], args[2], LD, LDX); + break; + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX); + break; + case INDEX_op_st_i64: + tcg_out_ldsta (s, args[0], args[1], args[2], STD, STDX); + break; + + case INDEX_op_add_i32: + if (const_args[2]) + ppc_addi32 (s, args[0], args[1], args[2]); + else + tcg_out32 (s, ADD | TAB (args[0], args[1], args[2])); + break; + case INDEX_op_sub_i32: + if (const_args[2]) + ppc_addi32 (s, args[0], args[1], -args[2]); + else + tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1])); + break; + + case INDEX_op_and_i64: + case INDEX_op_and_i32: + if (const_args[2]) { + if ((args[2] & 0xffff) == args[2]) + tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | args[2]); + else if ((args[2] & 0xffff0000) == args[2]) + tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + else { + tcg_out_movi (s, (opc == INDEX_op_and_i32 + ? TCG_TYPE_I32 + : TCG_TYPE_I64), + 0, args[2]); + tcg_out32 (s, AND | SAB (args[1], args[0], 0)); + } + } + else + tcg_out32 (s, AND | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_or_i64: + case INDEX_op_or_i32: + if (const_args[2]) { + if (args[2] & 0xffff) { + tcg_out32 (s, ORI | RS (args[1]) | RA (args[0]) + | (args[2] & 0xffff)); + if (args[2] >> 16) + tcg_out32 (s, ORIS | RS (args[0]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + } + else { + tcg_out32 (s, ORIS | RS (args[1]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + } + } + else + tcg_out32 (s, OR | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_xor_i64: + case INDEX_op_xor_i32: + if (const_args[2]) { + if ((args[2] & 0xffff) == args[2]) + tcg_out32 (s, XORI | RS (args[1]) | RA (args[0]) + | (args[2] & 0xffff)); + else if ((args[2] & 0xffff0000) == args[2]) + tcg_out32 (s, XORIS | RS (args[1]) | RA (args[0]) + | ((args[2] >> 16) & 0xffff)); + else { + tcg_out_movi (s, (opc == INDEX_op_and_i32 + ? TCG_TYPE_I32 + : TCG_TYPE_I64), + 0, args[2]); + tcg_out32 (s, XOR | SAB (args[1], args[0], 0)); + } + } + else + tcg_out32 (s, XOR | SAB (args[1], args[0], args[2])); + break; + + case INDEX_op_mul_i32: + if (const_args[2]) { + if (args[2] == (int16_t) args[2]) + tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1]) + | (args[2] & 0xffff)); + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]); + tcg_out32 (s, MULLW | TAB (args[0], args[1], 0)); + } + } + else + tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2])); + break; + + case INDEX_op_div_i32: + tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2])); + break; + + case INDEX_op_divu_i32: + tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2])); + break; + + case INDEX_op_rem_i32: + tcg_out32 (s, DIVW | TAB (0, args[1], args[2])); + tcg_out32 (s, MULLW | TAB (0, 0, args[2])); + tcg_out32 (s, SUBF | TAB (args[0], 0, args[1])); + break; + + case INDEX_op_remu_i32: + tcg_out32 (s, DIVWU | TAB (0, args[1], args[2])); + tcg_out32 (s, MULLW | TAB (0, 0, args[2])); + tcg_out32 (s, SUBF | TAB (args[0], 0, args[1])); + break; + + case INDEX_op_shl_i32: + if (const_args[2]) { + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (args[2]) + | MB (0) + | ME (31 - args[2]) + ) + ); + } + else + tcg_out32 (s, SLW | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_shr_i32: + if (const_args[2]) { + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (32 - args[2]) + | MB (args[2]) + | ME (31) + ) + ); + } + else + tcg_out32 (s, SRW | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_sar_i32: + if (const_args[2]) + tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2])); + else + tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2])); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 0); + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 1); + break; + + case INDEX_op_neg_i32: + case INDEX_op_neg_i64: + tcg_out32 (s, NEG | RT (args[0]) | RA (args[1])); + break; + + case INDEX_op_add_i64: + if (const_args[2]) + ppc_addi64 (s, args[0], args[1], args[2]); + else + tcg_out32 (s, ADD | TAB (args[0], args[1], args[2])); + break; + case INDEX_op_sub_i64: + if (const_args[2]) + ppc_addi64 (s, args[0], args[1], -args[2]); + else + tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1])); + break; + + case INDEX_op_shl_i64: + if (const_args[2]) + tcg_out_rld (s, RLDICR, args[0], args[1], args[2], 63 - args[2]); + else + tcg_out32 (s, SLD | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_shr_i64: + if (const_args[2]) + tcg_out_rld (s, RLDICL, args[0], args[1], 64 - args[2], args[2]); + else + tcg_out32 (s, SRD | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_sar_i64: + if (const_args[2]) { + int sh = SH (args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); + tcg_out32 (s, SRADI | RA (args[0]) | RS (args[1]) | sh); + } + else + tcg_out32 (s, SRAD | SAB (args[1], args[0], args[2])); + break; + + case INDEX_op_mul_i64: + tcg_out32 (s, MULLD | TAB (args[0], args[1], args[2])); + break; + case INDEX_op_div_i64: + tcg_out32 (s, DIVD | TAB (args[0], args[1], args[2])); + break; + case INDEX_op_divu_i64: + tcg_out32 (s, DIVDU | TAB (args[0], args[1], args[2])); + break; + case INDEX_op_rem_i64: + tcg_out32 (s, DIVD | TAB (0, args[1], args[2])); + tcg_out32 (s, MULLD | TAB (0, 0, args[2])); + tcg_out32 (s, SUBF | TAB (args[0], 0, args[1])); + break; + case INDEX_op_remu_i64: + tcg_out32 (s, DIVDU | TAB (0, args[1], args[2])); + tcg_out32 (s, MULLD | TAB (0, 0, args[2])); + tcg_out32 (s, SUBF | TAB (args[0], 0, args[1])); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld (s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld (s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld (s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld (s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + case INDEX_op_qemu_ld32u: + tcg_out_qemu_ld (s, args, 2); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld (s, args, 2 | 4); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld (s, args, 3); + break; + case INDEX_op_qemu_st8: + tcg_out_qemu_st (s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st (s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st (s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st (s, args, 3); + break; + + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + c = EXTSB; + goto gen_ext; + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + c = EXTSH; + goto gen_ext; + case INDEX_op_ext32s_i64: + c = EXTSW; + goto gen_ext; + gen_ext: + tcg_out32 (s, c | RS (args[1]) | RA (args[0])); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond (s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], + const_args[2]); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond (s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], + const_args[2]); + break; + + default: + tcg_dump_ops (s, stderr); + tcg_abort (); + } +} + +static const TCGTargetOpDef ppc_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_movi_i64, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "ri" } }, + { INDEX_op_mul_i32, { "r", "r", "ri" } }, + { INDEX_op_div_i32, { "r", "r", "r" } }, + { INDEX_op_divu_i32, { "r", "r", "r" } }, + { INDEX_op_rem_i32, { "r", "r", "r" } }, + { INDEX_op_remu_i32, { "r", "r", "r" } }, + { INDEX_op_sub_i32, { "r", "r", "ri" } }, + { INDEX_op_and_i32, { "r", "r", "ri" } }, + { INDEX_op_or_i32, { "r", "r", "ri" } }, + { INDEX_op_xor_i32, { "r", "r", "ri" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + + { INDEX_op_brcond_i32, { "r", "ri" } }, + { INDEX_op_brcond_i64, { "r", "ri" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "r", "ri" } }, + { INDEX_op_sub_i64, { "r", "r", "ri" } }, + { INDEX_op_and_i64, { "r", "r", "rZ" } }, + { INDEX_op_or_i64, { "r", "r", "rZ" } }, + { INDEX_op_xor_i64, { "r", "r", "rZ" } }, + + { INDEX_op_shl_i64, { "r", "r", "ri" } }, + { INDEX_op_shr_i64, { "r", "r", "ri" } }, + { INDEX_op_sar_i64, { "r", "r", "ri" } }, + + { INDEX_op_mul_i64, { "r", "r", "r" } }, + { INDEX_op_div_i64, { "r", "r", "r" } }, + { INDEX_op_divu_i64, { "r", "r", "r" } }, + { INDEX_op_rem_i64, { "r", "r", "r" } }, + { INDEX_op_remu_i64, { "r", "r", "r" } }, + + { INDEX_op_neg_i64, { "r", "r" } }, + + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L" } }, + + { INDEX_op_qemu_st8, { "S", "S" } }, + { INDEX_op_qemu_st16, { "S", "S" } }, + { INDEX_op_qemu_st32, { "S", "S" } }, + { INDEX_op_qemu_st64, { "S", "S" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + + { INDEX_op_setcond_i32, { "r", "r", "ri" } }, + { INDEX_op_setcond_i64, { "r", "r", "ri" } }, + + { -1 }, +}; + +static void tcg_target_init (TCGContext *s) +{ + tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); + tcg_regset_set32 (tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_R0) | +#ifdef __APPLE__ + (1 << TCG_REG_R2) | +#endif + (1 << TCG_REG_R3) | + (1 << TCG_REG_R4) | + (1 << TCG_REG_R5) | + (1 << TCG_REG_R6) | + (1 << TCG_REG_R7) | + (1 << TCG_REG_R8) | + (1 << TCG_REG_R9) | + (1 << TCG_REG_R10) | + (1 << TCG_REG_R11) | + (1 << TCG_REG_R12) + ); + + tcg_regset_clear (s->reserved_regs); + tcg_regset_set_reg (s->reserved_regs, TCG_REG_R0); + tcg_regset_set_reg (s->reserved_regs, TCG_REG_R1); +#ifndef __APPLE__ + tcg_regset_set_reg (s->reserved_regs, TCG_REG_R2); +#endif + tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13); + + tcg_add_target_add_op_defs (ppc_op_defs); +} diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h new file mode 100644 index 0000000..8a6db11 --- /dev/null +++ b/tcg/ppc64/tcg-target.h @@ -0,0 +1,109 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_PPC64 1 + +#define TCG_TARGET_REG_BITS 64 +#define TCG_TARGET_WORDS_BIGENDIAN +#define TCG_TARGET_NB_REGS 32 + +enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31 +}; + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_R1 +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 48 + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 +/* #define TCG_TARGET_HAS_rot_i32 */ +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +/* #define TCG_TARGET_HAS_ext8u_i32 */ +/* #define TCG_TARGET_HAS_ext16u_i32 */ +/* #define TCG_TARGET_HAS_bswap16_i32 */ +/* #define TCG_TARGET_HAS_bswap32_i32 */ +/* #define TCG_TARGET_HAS_not_i32 */ +#define TCG_TARGET_HAS_neg_i32 +/* #define TCG_TARGET_HAS_andc_i32 */ +/* #define TCG_TARGET_HAS_orc_i32 */ +/* #define TCG_TARGET_HAS_eqv_i32 */ +/* #define TCG_TARGET_HAS_nand_i32 */ +/* #define TCG_TARGET_HAS_nor_i32 */ + +#define TCG_TARGET_HAS_div_i64 +/* #define TCG_TARGET_HAS_rot_i64 */ +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +/* #define TCG_TARGET_HAS_ext8u_i64 */ +/* #define TCG_TARGET_HAS_ext16u_i64 */ +/* #define TCG_TARGET_HAS_ext32u_i64 */ +/* #define TCG_TARGET_HAS_bswap16_i64 */ +/* #define TCG_TARGET_HAS_bswap32_i64 */ +/* #define TCG_TARGET_HAS_bswap64_i64 */ +/* #define TCG_TARGET_HAS_not_i64 */ +#define TCG_TARGET_HAS_neg_i64 +/* #define TCG_TARGET_HAS_andc_i64 */ +/* #define TCG_TARGET_HAS_orc_i64 */ +/* #define TCG_TARGET_HAS_eqv_i64 */ +/* #define TCG_TARGET_HAS_nand_i64 */ +/* #define TCG_TARGET_HAS_nor_i64 */ + +#define TCG_AREG0 TCG_REG_R27 + +#define TCG_TARGET_HAS_GUEST_BASE +#define TCG_TARGET_EXTEND_ARGS 1 diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c new file mode 100644 index 0000000..450fcab --- /dev/null +++ b/tcg/s390/tcg-target.c @@ -0,0 +1,2324 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009 Ulrich Hecht <uli@suse.de> + * Copyright (c) 2009 Alexander Graf <agraf@suse.de> + * Copyright (c) 2010 Richard Henderson <rth@twiddle.net> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* ??? The translation blocks produced by TCG are generally small enough to + be entirely reachable with a 16-bit displacement. Leaving the option for + a 32-bit displacement here Just In Case. */ +#define USE_LONG_BRANCHES 0 + +#define TCG_CT_CONST_32 0x0100 +#define TCG_CT_CONST_NEG 0x0200 +#define TCG_CT_CONST_ADDI 0x0400 +#define TCG_CT_CONST_MULI 0x0800 +#define TCG_CT_CONST_ANDI 0x1000 +#define TCG_CT_CONST_ORI 0x2000 +#define TCG_CT_CONST_XORI 0x4000 +#define TCG_CT_CONST_CMPI 0x8000 + +/* Several places within the instruction set 0 means "no register" + rather than TCG_REG_R0. */ +#define TCG_REG_NONE 0 + +/* A scratch register that may be be used throughout the backend. */ +#define TCG_TMP0 TCG_REG_R14 + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R13 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif + +#ifndef GUEST_BASE +#define GUEST_BASE 0 +#endif + + +/* All of the following instructions are prefixed with their instruction + format, and are defined as 8- or 16-bit quantities, even when the two + halves of the 16-bit quantity may appear 32 bits apart in the insn. + This makes it easy to copy the values from the tables in Appendix B. */ +typedef enum S390Opcode { + RIL_AFI = 0xc209, + RIL_AGFI = 0xc208, + RIL_ALGFI = 0xc20a, + RIL_BRASL = 0xc005, + RIL_BRCL = 0xc004, + RIL_CFI = 0xc20d, + RIL_CGFI = 0xc20c, + RIL_CLFI = 0xc20f, + RIL_CLGFI = 0xc20e, + RIL_IIHF = 0xc008, + RIL_IILF = 0xc009, + RIL_LARL = 0xc000, + RIL_LGFI = 0xc001, + RIL_LGRL = 0xc408, + RIL_LLIHF = 0xc00e, + RIL_LLILF = 0xc00f, + RIL_LRL = 0xc40d, + RIL_MSFI = 0xc201, + RIL_MSGFI = 0xc200, + RIL_NIHF = 0xc00a, + RIL_NILF = 0xc00b, + RIL_OIHF = 0xc00c, + RIL_OILF = 0xc00d, + RIL_XIHF = 0xc006, + RIL_XILF = 0xc007, + + RI_AGHI = 0xa70b, + RI_AHI = 0xa70a, + RI_BRC = 0xa704, + RI_IIHH = 0xa500, + RI_IIHL = 0xa501, + RI_IILH = 0xa502, + RI_IILL = 0xa503, + RI_LGHI = 0xa709, + RI_LLIHH = 0xa50c, + RI_LLIHL = 0xa50d, + RI_LLILH = 0xa50e, + RI_LLILL = 0xa50f, + RI_MGHI = 0xa70d, + RI_MHI = 0xa70c, + RI_NIHH = 0xa504, + RI_NIHL = 0xa505, + RI_NILH = 0xa506, + RI_NILL = 0xa507, + RI_OIHH = 0xa508, + RI_OIHL = 0xa509, + RI_OILH = 0xa50a, + RI_OILL = 0xa50b, + + RIE_CGIJ = 0xec7c, + RIE_CGRJ = 0xec64, + RIE_CIJ = 0xec7e, + RIE_CLGRJ = 0xec65, + RIE_CLIJ = 0xec7f, + RIE_CLGIJ = 0xec7d, + RIE_CLRJ = 0xec77, + RIE_CRJ = 0xec76, + + RRE_AGR = 0xb908, + RRE_CGR = 0xb920, + RRE_CLGR = 0xb921, + RRE_DLGR = 0xb987, + RRE_DLR = 0xb997, + RRE_DSGFR = 0xb91d, + RRE_DSGR = 0xb90d, + RRE_LGBR = 0xb906, + RRE_LCGR = 0xb903, + RRE_LGFR = 0xb914, + RRE_LGHR = 0xb907, + RRE_LGR = 0xb904, + RRE_LLGCR = 0xb984, + RRE_LLGFR = 0xb916, + RRE_LLGHR = 0xb985, + RRE_LRVR = 0xb91f, + RRE_LRVGR = 0xb90f, + RRE_LTGR = 0xb902, + RRE_MSGR = 0xb90c, + RRE_MSR = 0xb252, + RRE_NGR = 0xb980, + RRE_OGR = 0xb981, + RRE_SGR = 0xb909, + RRE_XGR = 0xb982, + + RR_AR = 0x1a, + RR_BASR = 0x0d, + RR_BCR = 0x07, + RR_CLR = 0x15, + RR_CR = 0x19, + RR_DR = 0x1d, + RR_LCR = 0x13, + RR_LR = 0x18, + RR_LTR = 0x12, + RR_NR = 0x14, + RR_OR = 0x16, + RR_SR = 0x1b, + RR_XR = 0x17, + + RSY_RLL = 0xeb1d, + RSY_RLLG = 0xeb1c, + RSY_SLLG = 0xeb0d, + RSY_SRAG = 0xeb0a, + RSY_SRLG = 0xeb0c, + + RS_SLL = 0x89, + RS_SRA = 0x8a, + RS_SRL = 0x88, + + RXY_AG = 0xe308, + RXY_AY = 0xe35a, + RXY_CG = 0xe320, + RXY_CY = 0xe359, + RXY_LB = 0xe376, + RXY_LG = 0xe304, + RXY_LGB = 0xe377, + RXY_LGF = 0xe314, + RXY_LGH = 0xe315, + RXY_LHY = 0xe378, + RXY_LLGC = 0xe390, + RXY_LLGF = 0xe316, + RXY_LLGH = 0xe391, + RXY_LMG = 0xeb04, + RXY_LRV = 0xe31e, + RXY_LRVG = 0xe30f, + RXY_LRVH = 0xe31f, + RXY_LY = 0xe358, + RXY_STCY = 0xe372, + RXY_STG = 0xe324, + RXY_STHY = 0xe370, + RXY_STMG = 0xeb24, + RXY_STRV = 0xe33e, + RXY_STRVG = 0xe32f, + RXY_STRVH = 0xe33f, + RXY_STY = 0xe350, + + RX_A = 0x5a, + RX_C = 0x59, + RX_L = 0x58, + RX_LH = 0x48, + RX_ST = 0x50, + RX_STC = 0x42, + RX_STH = 0x40, +} S390Opcode; + +#define LD_SIGNED 0x04 +#define LD_UINT8 0x00 +#define LD_INT8 (LD_UINT8 | LD_SIGNED) +#define LD_UINT16 0x01 +#define LD_INT16 (LD_UINT16 | LD_SIGNED) +#define LD_UINT32 0x02 +#define LD_INT32 (LD_UINT32 | LD_SIGNED) +#define LD_UINT64 0x03 +#define LD_INT64 (LD_UINT64 | LD_SIGNED) + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10" "%r11" "%r12" "%r13" "%r14" "%r15" +}; +#endif + +/* Since R6 is a potential argument register, choose it last of the + call-saved registers. Likewise prefer the call-clobbered registers + in reverse order to maximize the chance of avoiding the arguments. */ +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R13, + TCG_REG_R12, + TCG_REG_R11, + TCG_REG_R10, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_R7, + TCG_REG_R6, + TCG_REG_R14, + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R5, + TCG_REG_R4, + TCG_REG_R3, + TCG_REG_R2, +}; + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R2, + TCG_REG_R3, +}; + +#define S390_CC_EQ 8 +#define S390_CC_LT 4 +#define S390_CC_GT 2 +#define S390_CC_OV 1 +#define S390_CC_NE (S390_CC_LT | S390_CC_GT) +#define S390_CC_LE (S390_CC_LT | S390_CC_EQ) +#define S390_CC_GE (S390_CC_GT | S390_CC_EQ) +#define S390_CC_NEVER 0 +#define S390_CC_ALWAYS 15 + +/* Condition codes that result from a COMPARE and COMPARE LOGICAL. */ +static const uint8_t tcg_cond_to_s390_cond[10] = { + [TCG_COND_EQ] = S390_CC_EQ, + [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_LT] = S390_CC_LT, + [TCG_COND_LE] = S390_CC_LE, + [TCG_COND_GT] = S390_CC_GT, + [TCG_COND_GE] = S390_CC_GE, + [TCG_COND_LTU] = S390_CC_LT, + [TCG_COND_LEU] = S390_CC_LE, + [TCG_COND_GTU] = S390_CC_GT, + [TCG_COND_GEU] = S390_CC_GE, +}; + +/* Condition codes that result from a LOAD AND TEST. Here, we have no + unsigned instruction variation, however since the test is vs zero we + can re-map the outcomes appropriately. */ +static const uint8_t tcg_cond_to_ltr_cond[10] = { + [TCG_COND_EQ] = S390_CC_EQ, + [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_LT] = S390_CC_LT, + [TCG_COND_LE] = S390_CC_LE, + [TCG_COND_GT] = S390_CC_GT, + [TCG_COND_GE] = S390_CC_GE, + [TCG_COND_LTU] = S390_CC_NEVER, + [TCG_COND_LEU] = S390_CC_EQ, + [TCG_COND_GTU] = S390_CC_NE, + [TCG_COND_GEU] = S390_CC_ALWAYS, +}; + +#ifdef CONFIG_SOFTMMU + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; +#endif + +static uint8_t *tb_ret_addr; + +/* A list of relevant facilities used by this translator. Some of these + are required for proper operation, and these are checked at startup. */ + +#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2)) +#define FACILITY_LONG_DISP (1ULL << (63 - 18)) +#define FACILITY_EXT_IMM (1ULL << (63 - 21)) +#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34)) + +static uint64_t facilities; + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + tcg_target_long code_ptr_tl = (tcg_target_long)code_ptr; + tcg_target_long pcrel2; + + /* ??? Not the usual definition of "addend". */ + pcrel2 = (value - (code_ptr_tl + addend)) >> 1; + + switch (type) { + case R_390_PC16DBL: + assert(pcrel2 == (int16_t)pcrel2); + *(int16_t *)code_ptr = pcrel2; + break; + case R_390_PC32DBL: + assert(pcrel2 == (int32_t)pcrel2); + *(int32_t *)code_ptr = pcrel2; + break; + default: + tcg_abort(); + break; + } +} + +static int tcg_target_get_call_iarg_regs_count(int flags) +{ + return sizeof(tcg_target_call_iarg_regs) / sizeof(int); +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str = *pct_str; + + switch (ct_str[0]) { + case 'r': /* all registers */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + break; + case 'R': /* not R0 */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + break; + case 'L': /* qemu_ld/st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R2); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); + break; + case 'a': /* force R2 for division */ + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_R2); + break; + case 'b': /* force R3 for division */ + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_R3); + break; + case 'N': /* force immediate negate */ + ct->ct |= TCG_CT_CONST_NEG; + break; + case 'W': /* force 32-bit ("word") immediate */ + ct->ct |= TCG_CT_CONST_32; + break; + case 'I': + ct->ct |= TCG_CT_CONST_ADDI; + break; + case 'K': + ct->ct |= TCG_CT_CONST_MULI; + break; + case 'A': + ct->ct |= TCG_CT_CONST_ANDI; + break; + case 'O': + ct->ct |= TCG_CT_CONST_ORI; + break; + case 'X': + ct->ct |= TCG_CT_CONST_XORI; + break; + case 'C': + ct->ct |= TCG_CT_CONST_CMPI; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + + return 0; +} + +/* Immediates to be used with logical AND. This is an optimization only, + since a full 64-bit immediate AND can always be performed with 4 sequential + NI[LH][LH] instructions. What we're looking for is immediates that we + can load efficiently, and the immediate load plus the reg-reg AND is + smaller than the sequential NI's. */ + +static int tcg_match_andi(int ct, tcg_target_ulong val) +{ + int i; + + if (facilities & FACILITY_EXT_IMM) { + if (ct & TCG_CT_CONST_32) { + /* All 32-bit ANDs can be performed with 1 48-bit insn. */ + return 1; + } + + /* Zero-extensions. */ + if (val == 0xff || val == 0xffff || val == 0xffffffff) { + return 1; + } + } else { + if (ct & TCG_CT_CONST_32) { + val = (uint32_t)val; + } else if (val == 0xffffffff) { + return 1; + } + } + + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = ~(0xffffull << i*16); + if ((val & mask) == mask) { + return 1; + } + } + + /* Look for 16-bit values performing the mask. These are better + to load with LLI[LH][LH]. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = 0xffffull << i*16; + if ((val & mask) == val) { + return 0; + } + } + + /* Look for 32-bit values performing the 64-bit mask. These + are better to load with LLI[LH]F, or if extended immediates + not available, with a pair of LLI insns. */ + if ((ct & TCG_CT_CONST_32) == 0) { + if (val <= 0xffffffff || (val & 0xffffffff) == 0) { + return 0; + } + } + + return 1; +} + +/* Immediates to be used with logical OR. This is an optimization only, + since a full 64-bit immediate OR can always be performed with 4 sequential + OI[LH][LH] instructions. What we're looking for is immediates that we + can load efficiently, and the immediate load plus the reg-reg OR is + smaller than the sequential OI's. */ + +static int tcg_match_ori(int ct, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + if (ct & TCG_CT_CONST_32) { + /* All 32-bit ORs can be performed with 1 48-bit insn. */ + return 1; + } + } + + /* Look for negative values. These are best to load with LGHI. */ + if (val < 0) { + if (val == (int16_t)val) { + return 0; + } + if (facilities & FACILITY_EXT_IMM) { + if (val == (int32_t)val) { + return 0; + } + } + } + + return 1; +} + +/* Immediates to be used with logical XOR. This is almost, but not quite, + only an optimization. XOR with immediate is only supported with the + extended-immediate facility. That said, there are a few patterns for + which it is better to load the value into a register first. */ + +static int tcg_match_xori(int ct, tcg_target_long val) +{ + if ((facilities & FACILITY_EXT_IMM) == 0) { + return 0; + } + + if (ct & TCG_CT_CONST_32) { + /* All 32-bit XORs can be performed with 1 48-bit insn. */ + return 1; + } + + /* Look for negative values. These are best to load with LGHI. */ + if (val < 0 && val == (int32_t)val) { + return 0; + } + + return 1; +} + +/* Imediates to be used with comparisons. */ + +static int tcg_match_cmpi(int ct, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + /* The COMPARE IMMEDIATE instruction is available. */ + if (ct & TCG_CT_CONST_32) { + /* We have a 32-bit immediate and can compare against anything. */ + return 1; + } else { + /* ??? We have no insight here into whether the comparison is + signed or unsigned. The COMPARE IMMEDIATE insn uses a 32-bit + signed immediate, and the COMPARE LOGICAL IMMEDIATE insn uses + a 32-bit unsigned immediate. If we were to use the (semi) + obvious "val == (int32_t)val" we would be enabling unsigned + comparisons vs very large numbers. The only solution is to + take the intersection of the ranges. */ + /* ??? Another possible solution is to simply lie and allow all + constants here and force the out-of-range values into a temp + register in tgen_cmp when we have knowledge of the actual + comparison code in use. */ + return val >= 0 && val <= 0x7fffffff; + } + } else { + /* Only the LOAD AND TEST instruction is available. */ + return val == 0; + } +} + +/* Test if a constant matches the constraint. */ +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct = arg_ct->ct; + + if (ct & TCG_CT_CONST) { + return 1; + } + + /* Handle the modifiers. */ + if (ct & TCG_CT_CONST_NEG) { + val = -val; + } + if (ct & TCG_CT_CONST_32) { + val = (int32_t)val; + } + + /* The following are mutually exclusive. */ + if (ct & TCG_CT_CONST_ADDI) { + /* Immediates that may be used with add. If we have the + extended-immediates facility then we have ADD IMMEDIATE + with signed and unsigned 32-bit, otherwise we have only + ADD HALFWORD IMMEDIATE with a signed 16-bit. */ + if (facilities & FACILITY_EXT_IMM) { + return val == (int32_t)val || val == (uint32_t)val; + } else { + return val == (int16_t)val; + } + } else if (ct & TCG_CT_CONST_MULI) { + /* Immediates that may be used with multiply. If we have the + general-instruction-extensions, then we have MULTIPLY SINGLE + IMMEDIATE with a signed 32-bit, otherwise we have only + MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */ + if (facilities & FACILITY_GEN_INST_EXT) { + return val == (int32_t)val; + } else { + return val == (int16_t)val; + } + } else if (ct & TCG_CT_CONST_ANDI) { + return tcg_match_andi(ct, val); + } else if (ct & TCG_CT_CONST_ORI) { + return tcg_match_ori(ct, val); + } else if (ct & TCG_CT_CONST_XORI) { + return tcg_match_xori(ct, val); + } else if (ct & TCG_CT_CONST_CMPI) { + return tcg_match_cmpi(ct, val); + } + + return 0; +} + +/* Emit instructions according to the given instruction format. */ + +static void tcg_out_insn_RR(TCGContext *s, S390Opcode op, TCGReg r1, TCGReg r2) +{ + tcg_out16(s, (op << 8) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RRE(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2) +{ + tcg_out32(s, (op << 16) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2) +{ + tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff)); +} + +static void tcg_out_insn_RIL(TCGContext *s, S390Opcode op, TCGReg r1, int i2) +{ + tcg_out16(s, op | (r1 << 4)); + tcg_out32(s, i2); +} + +static void tcg_out_insn_RS(TCGContext *s, S390Opcode op, TCGReg r1, + TCGReg b2, TCGReg r3, int disp) +{ + tcg_out32(s, (op << 24) | (r1 << 20) | (r3 << 16) | (b2 << 12) + | (disp & 0xfff)); +} + +static void tcg_out_insn_RSY(TCGContext *s, S390Opcode op, TCGReg r1, + TCGReg b2, TCGReg r3, int disp) +{ + tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3); + tcg_out32(s, (op & 0xff) | (b2 << 28) + | ((disp & 0xfff) << 16) | ((disp & 0xff000) >> 4)); +} + +#define tcg_out_insn_RX tcg_out_insn_RS +#define tcg_out_insn_RXY tcg_out_insn_RSY + +/* Emit an opcode with "type-checking" of the format. */ +#define tcg_out_insn(S, FMT, OP, ...) \ + glue(tcg_out_insn_,FMT)(S, glue(glue(FMT,_),OP), ## __VA_ARGS__) + + +/* emit 64-bit shifts */ +static void tcg_out_sh64(TCGContext* s, S390Opcode op, TCGReg dest, + TCGReg src, TCGReg sh_reg, int sh_imm) +{ + tcg_out_insn_RSY(s, op, dest, sh_reg, src, sh_imm); +} + +/* emit 32-bit shifts */ +static void tcg_out_sh32(TCGContext* s, S390Opcode op, TCGReg dest, + TCGReg sh_reg, int sh_imm) +{ + tcg_out_insn_RS(s, op, dest, sh_reg, 0, sh_imm); +} + +static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) +{ + if (src != dst) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LR, dst, src); + } else { + tcg_out_insn(s, RRE, LGR, dst, src); + } + } +} + +/* load a register with an immediate value */ +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long sval) +{ + static const S390Opcode lli_insns[4] = { + RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH + }; + + tcg_target_ulong uval = sval; + int i; + + if (type == TCG_TYPE_I32) { + uval = (uint32_t)sval; + sval = (int32_t)sval; + } + + /* Try all 32-bit insns that can load it in one go. */ + if (sval >= -0x8000 && sval < 0x8000) { + tcg_out_insn(s, RI, LGHI, ret, sval); + return; + } + + for (i = 0; i < 4; i++) { + tcg_target_long mask = 0xffffull << i*16; + if ((uval & mask) == uval) { + tcg_out_insn_RI(s, lli_insns[i], ret, uval >> i*16); + return; + } + } + + /* Try all 48-bit insns that can load it in one go. */ + if (facilities & FACILITY_EXT_IMM) { + if (sval == (int32_t)sval) { + tcg_out_insn(s, RIL, LGFI, ret, sval); + return; + } + if (uval <= 0xffffffff) { + tcg_out_insn(s, RIL, LLILF, ret, uval); + return; + } + if ((uval & 0xffffffff) == 0) { + tcg_out_insn(s, RIL, LLIHF, ret, uval >> 31 >> 1); + return; + } + } + + /* Try for PC-relative address load. */ + if ((sval & 1) == 0) { + intptr_t off = (sval - (intptr_t)s->code_ptr) >> 1; + if (off == (int32_t)off) { + tcg_out_insn(s, RIL, LARL, ret, off); + return; + } + } + + /* If extended immediates are not present, then we may have to issue + several instructions to load the low 32 bits. */ + if (!(facilities & FACILITY_EXT_IMM)) { + /* A 32-bit unsigned value can be loaded in 2 insns. And given + that the lli_insns loop above did not succeed, we know that + both insns are required. */ + if (uval <= 0xffffffff) { + tcg_out_insn(s, RI, LLILL, ret, uval); + tcg_out_insn(s, RI, IILH, ret, uval >> 16); + return; + } + + /* If all high bits are set, the value can be loaded in 2 or 3 insns. + We first want to make sure that all the high bits get set. With + luck the low 16-bits can be considered negative to perform that for + free, otherwise we load an explicit -1. */ + if (sval >> 31 >> 1 == -1) { + if (uval & 0x8000) { + tcg_out_insn(s, RI, LGHI, ret, uval); + } else { + tcg_out_insn(s, RI, LGHI, ret, -1); + tcg_out_insn(s, RI, IILL, ret, uval); + } + tcg_out_insn(s, RI, IILH, ret, uval >> 16); + return; + } + } + + /* If we get here, both the high and low parts have non-zero bits. */ + + /* Recurse to load the lower 32-bits. */ + tcg_out_movi(s, TCG_TYPE_I32, ret, sval); + + /* Insert data into the high 32-bits. */ + uval = uval >> 31 >> 1; + if (facilities & FACILITY_EXT_IMM) { + if (uval < 0x10000) { + tcg_out_insn(s, RI, IIHL, ret, uval); + } else if ((uval & 0xffff) == 0) { + tcg_out_insn(s, RI, IIHH, ret, uval >> 16); + } else { + tcg_out_insn(s, RIL, IIHF, ret, uval); + } + } else { + if (uval & 0xffff) { + tcg_out_insn(s, RI, IIHL, ret, uval); + } + if (uval & 0xffff0000) { + tcg_out_insn(s, RI, IIHH, ret, uval >> 16); + } + } +} + + +/* Emit a load/store type instruction. Inputs are: + DATA: The register to be loaded or stored. + BASE+OFS: The effective address. + OPC_RX: If the operation has an RX format opcode (e.g. STC), otherwise 0. + OPC_RXY: The RXY format opcode for the operation (e.g. STCY). */ + +static void tcg_out_mem(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy, + TCGReg data, TCGReg base, TCGReg index, + tcg_target_long ofs) +{ + if (ofs < -0x80000 || ofs >= 0x80000) { + /* Combine the low 16 bits of the offset with the actual load insn; + the high 48 bits must come from an immediate load. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs & ~0xffff); + ofs &= 0xffff; + + /* If we were already given an index register, add it in. */ + if (index != TCG_REG_NONE) { + tcg_out_insn(s, RRE, AGR, TCG_TMP0, index); + } + index = TCG_TMP0; + } + + if (opc_rx && ofs >= 0 && ofs < 0x1000) { + tcg_out_insn_RX(s, opc_rx, data, base, index, ofs); + } else { + tcg_out_insn_RXY(s, opc_rxy, data, base, index, ofs); + } +} + + +/* load data without address translation or endianness conversion */ +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, tcg_target_long ofs) +{ + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs); + } +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, tcg_target_long ofs) +{ + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs); + } +} + +/* load data from an absolute host address */ +static void tcg_out_ld_abs(TCGContext *s, TCGType type, TCGReg dest, void *abs) +{ + tcg_target_long addr = (tcg_target_long)abs; + + if (facilities & FACILITY_GEN_INST_EXT) { + tcg_target_long disp = (addr - (tcg_target_long)s->code_ptr) >> 1; + if (disp == (int32_t)disp) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, LRL, dest, disp); + } else { + tcg_out_insn(s, RIL, LGRL, dest, disp); + } + return; + } + } + + tcg_out_movi(s, TCG_TYPE_PTR, dest, addr & ~0xffff); + tcg_out_ld(s, type, dest, dest, addr & 0xffff); +} + +static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LGBR, dest, src); + return; + } + + if (type == TCG_TYPE_I32) { + if (dest == src) { + tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 24); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 24); + } + tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 24); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 56); + tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 56); + } +} + +static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LLGCR, dest, src); + return; + } + + if (dest == src) { + tcg_out_movi(s, type, TCG_TMP0, 0xff); + src = TCG_TMP0; + } else { + tcg_out_movi(s, type, dest, 0xff); + } + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, src); + } else { + tcg_out_insn(s, RRE, NGR, dest, src); + } +} + +static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LGHR, dest, src); + return; + } + + if (type == TCG_TYPE_I32) { + if (dest == src) { + tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 16); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 16); + } + tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 16); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 48); + tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 48); + } +} + +static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LLGHR, dest, src); + return; + } + + if (dest == src) { + tcg_out_movi(s, type, TCG_TMP0, 0xffff); + src = TCG_TMP0; + } else { + tcg_out_movi(s, type, dest, 0xffff); + } + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, src); + } else { + tcg_out_insn(s, RRE, NGR, dest, src); + } +} + +static inline void tgen_ext32s(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_insn(s, RRE, LGFR, dest, src); +} + +static inline void tgen_ext32u(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_insn(s, RRE, LLGFR, dest, src); +} + +static inline void tgen32_addi(TCGContext *s, TCGReg dest, int32_t val) +{ + if (val == (int16_t)val) { + tcg_out_insn(s, RI, AHI, dest, val); + } else { + tcg_out_insn(s, RIL, AFI, dest, val); + } +} + +static inline void tgen64_addi(TCGContext *s, TCGReg dest, int64_t val) +{ + if (val == (int16_t)val) { + tcg_out_insn(s, RI, AGHI, dest, val); + } else if (val == (int32_t)val) { + tcg_out_insn(s, RIL, AGFI, dest, val); + } else if (val == (uint32_t)val) { + tcg_out_insn(s, RIL, ALGFI, dest, val); + } else { + tcg_abort(); + } + +} + +static void tgen64_andi(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + static const S390Opcode ni_insns[4] = { + RI_NILL, RI_NILH, RI_NIHL, RI_NIHH + }; + static const S390Opcode nif_insns[2] = { + RIL_NILF, RIL_NIHF + }; + + int i; + + /* Look for no-op. */ + if (val == -1) { + return; + } + + /* Look for the zero-extensions. */ + if (val == 0xffffffff) { + tgen_ext32u(s, dest, dest); + return; + } + + if (facilities & FACILITY_EXT_IMM) { + if (val == 0xff) { + tgen_ext8u(s, TCG_TYPE_I64, dest, dest); + return; + } + if (val == 0xffff) { + tgen_ext16u(s, TCG_TYPE_I64, dest, dest); + return; + } + + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = ~(0xffffull << i*16); + if ((val & mask) == mask) { + tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); + return; + } + } + + /* Try all 48-bit insns that can perform it in one go. */ + if (facilities & FACILITY_EXT_IMM) { + for (i = 0; i < 2; i++) { + tcg_target_ulong mask = ~(0xffffffffull << i*32); + if ((val & mask) == mask) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); + return; + } + } + } + + /* Perform the AND via sequential modifications to the high and low + parts. Do this via recursion to handle 16-bit vs 32-bit masks in + each half. */ + tgen64_andi(s, dest, val | 0xffffffff00000000ull); + tgen64_andi(s, dest, val | 0x00000000ffffffffull); + } else { + /* With no extended-immediate facility, just emit the sequence. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = 0xffffull << i*16; + if ((val & mask) != mask) { + tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); + } + } + } +} + +static void tgen64_ori(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + static const S390Opcode oi_insns[4] = { + RI_OILL, RI_OILH, RI_OIHL, RI_OIHH + }; + static const S390Opcode nif_insns[2] = { + RIL_OILF, RIL_OIHF + }; + + int i; + + /* Look for no-op. */ + if (val == 0) { + return; + } + + if (facilities & FACILITY_EXT_IMM) { + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = (0xffffull << i*16); + if ((val & mask) != 0 && (val & ~mask) == 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); + return; + } + } + + /* Try all 48-bit insns that can perform it in one go. */ + for (i = 0; i < 2; i++) { + tcg_target_ulong mask = (0xffffffffull << i*32); + if ((val & mask) != 0 && (val & ~mask) == 0) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); + return; + } + } + + /* Perform the OR via sequential modifications to the high and + low parts. Do this via recursion to handle 16-bit vs 32-bit + masks in each half. */ + tgen64_ori(s, dest, val & 0x00000000ffffffffull); + tgen64_ori(s, dest, val & 0xffffffff00000000ull); + } else { + /* With no extended-immediate facility, we don't need to be so + clever. Just iterate over the insns and mask in the constant. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = (0xffffull << i*16); + if ((val & mask) != 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); + } + } + } +} + +static void tgen64_xori(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + /* Perform the xor by parts. */ + if (val & 0xffffffff) { + tcg_out_insn(s, RIL, XILF, dest, val); + } + if (val > 0xffffffff) { + tcg_out_insn(s, RIL, XIHF, dest, val >> 31 >> 1); + } +} + +static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, + TCGArg c2, int c2const) +{ + bool is_unsigned = (c > TCG_COND_GT); + if (c2const) { + if (c2 == 0) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LTR, r1, r1); + } else { + tcg_out_insn(s, RRE, LTGR, r1, r1); + } + return tcg_cond_to_ltr_cond[c]; + } else { + if (is_unsigned) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, CLFI, r1, c2); + } else { + tcg_out_insn(s, RIL, CLGFI, r1, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, CFI, r1, c2); + } else { + tcg_out_insn(s, RIL, CGFI, r1, c2); + } + } + } + } else { + if (is_unsigned) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, CLR, r1, c2); + } else { + tcg_out_insn(s, RRE, CLGR, r1, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, CR, r1, c2); + } else { + tcg_out_insn(s, RRE, CGR, r1, c2); + } + } + } + return tcg_cond_to_s390_cond[c]; +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg dest, TCGReg r1, TCGArg c2, int c2const) +{ + int cc = tgen_cmp(s, type, c, r1, c2, c2const); + + /* Emit: r1 = 1; if (cc) goto over; r1 = 0; over: */ + tcg_out_movi(s, type, dest, 1); + tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); + tcg_out_movi(s, type, dest, 0); +} + +static void tgen_gotoi(TCGContext *s, int cc, tcg_target_long dest) +{ + tcg_target_long off = (dest - (tcg_target_long)s->code_ptr) >> 1; + if (off > -0x8000 && off < 0x7fff) { + tcg_out_insn(s, RI, BRC, cc, off); + } else if (off == (int32_t)off) { + tcg_out_insn(s, RIL, BRCL, cc, off); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, dest); + tcg_out_insn(s, RR, BCR, cc, TCG_TMP0); + } +} + +static void tgen_branch(TCGContext *s, int cc, int labelno) +{ + TCGLabel* l = &s->labels[labelno]; + if (l->has_value) { + tgen_gotoi(s, cc, l->u.value); + } else if (USE_LONG_BRANCHES) { + tcg_out16(s, RIL_BRCL | (cc << 4)); + tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, labelno, -2); + s->code_ptr += 4; + } else { + tcg_out16(s, RI_BRC | (cc << 4)); + tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, labelno, -2); + s->code_ptr += 2; + } +} + +static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, + TCGReg r1, TCGReg r2, int labelno) +{ + TCGLabel* l = &s->labels[labelno]; + tcg_target_long off; + + if (l->has_value) { + off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1; + } else { + /* We need to keep the offset unchanged for retranslation. */ + off = ((int16_t *)s->code_ptr)[1]; + tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2); + } + + tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2); + tcg_out16(s, off); + tcg_out16(s, cc << 12 | (opc & 0xff)); +} + +static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, + TCGReg r1, int i2, int labelno) +{ + TCGLabel* l = &s->labels[labelno]; + tcg_target_long off; + + if (l->has_value) { + off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1; + } else { + /* We need to keep the offset unchanged for retranslation. */ + off = ((int16_t *)s->code_ptr)[1]; + tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2); + } + + tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc); + tcg_out16(s, off); + tcg_out16(s, (i2 << 8) | (opc & 0xff)); +} + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg r1, TCGArg c2, int c2const, int labelno) +{ + int cc; + + if (facilities & FACILITY_GEN_INST_EXT) { + bool is_unsigned = (c > TCG_COND_GT); + bool in_range; + S390Opcode opc; + + cc = tcg_cond_to_s390_cond[c]; + + if (!c2const) { + opc = (type == TCG_TYPE_I32 + ? (is_unsigned ? RIE_CLRJ : RIE_CRJ) + : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ)); + tgen_compare_branch(s, opc, cc, r1, c2, labelno); + return; + } + + /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field. + If the immediate we've been given does not fit that range, we'll + fall back to separate compare and branch instructions using the + larger comparison range afforded by COMPARE IMMEDIATE. */ + if (type == TCG_TYPE_I32) { + if (is_unsigned) { + opc = RIE_CLIJ; + in_range = (uint32_t)c2 == (uint8_t)c2; + } else { + opc = RIE_CIJ; + in_range = (int32_t)c2 == (int8_t)c2; + } + } else { + if (is_unsigned) { + opc = RIE_CLGIJ; + in_range = (uint64_t)c2 == (uint8_t)c2; + } else { + opc = RIE_CGIJ; + in_range = (int64_t)c2 == (int8_t)c2; + } + } + if (in_range) { + tgen_compare_imm_branch(s, opc, cc, r1, c2, labelno); + return; + } + } + + cc = tgen_cmp(s, type, c, r1, c2, c2const); + tgen_branch(s, cc, labelno); +} + +static void tgen_calli(TCGContext *s, tcg_target_long dest) +{ + tcg_target_long off = (dest - (tcg_target_long)s->code_ptr) >> 1; + if (off == (int32_t)off) { + tcg_out_insn(s, RIL, BRASL, TCG_REG_R14, off); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, dest); + tcg_out_insn(s, RR, BASR, TCG_REG_R14, TCG_TMP0); + } +} + +static void tcg_out_qemu_ld_direct(TCGContext *s, int opc, TCGReg data, + TCGReg base, TCGReg index, int disp) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; +#else + const int bswap = 1; +#endif + switch (opc) { + case LD_UINT8: + tcg_out_insn(s, RXY, LLGC, data, base, index, disp); + break; + case LD_INT8: + tcg_out_insn(s, RXY, LGB, data, base, index, disp); + break; + case LD_UINT16: + if (bswap) { + /* swapped unsigned halfword load with upper bits zeroed */ + tcg_out_insn(s, RXY, LRVH, data, base, index, disp); + tgen_ext16u(s, TCG_TYPE_I64, data, data); + } else { + tcg_out_insn(s, RXY, LLGH, data, base, index, disp); + } + break; + case LD_INT16: + if (bswap) { + /* swapped sign-extended halfword load */ + tcg_out_insn(s, RXY, LRVH, data, base, index, disp); + tgen_ext16s(s, TCG_TYPE_I64, data, data); + } else { + tcg_out_insn(s, RXY, LGH, data, base, index, disp); + } + break; + case LD_UINT32: + if (bswap) { + /* swapped unsigned int load with upper bits zeroed */ + tcg_out_insn(s, RXY, LRV, data, base, index, disp); + tgen_ext32u(s, data, data); + } else { + tcg_out_insn(s, RXY, LLGF, data, base, index, disp); + } + break; + case LD_INT32: + if (bswap) { + /* swapped sign-extended int load */ + tcg_out_insn(s, RXY, LRV, data, base, index, disp); + tgen_ext32s(s, data, data); + } else { + tcg_out_insn(s, RXY, LGF, data, base, index, disp); + } + break; + case LD_UINT64: + if (bswap) { + tcg_out_insn(s, RXY, LRVG, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, LG, data, base, index, disp); + } + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st_direct(TCGContext *s, int opc, TCGReg data, + TCGReg base, TCGReg index, int disp) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; +#else + const int bswap = 1; +#endif + switch (opc) { + case LD_UINT8: + if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, STC, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STCY, data, base, index, disp); + } + break; + case LD_UINT16: + if (bswap) { + tcg_out_insn(s, RXY, STRVH, data, base, index, disp); + } else if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, STH, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STHY, data, base, index, disp); + } + break; + case LD_UINT32: + if (bswap) { + tcg_out_insn(s, RXY, STRV, data, base, index, disp); + } else if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, ST, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STY, data, base, index, disp); + } + break; + case LD_UINT64: + if (bswap) { + tcg_out_insn(s, RXY, STRVG, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STG, data, base, index, disp); + } + break; + default: + tcg_abort(); + } +} + +#if defined(CONFIG_SOFTMMU) +static void tgen64_andi_tmp(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + if (tcg_match_andi(0, val)) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, val); + tcg_out_insn(s, RRE, NGR, dest, TCG_TMP0); + } else { + tgen64_andi(s, dest, val); + } +} + +static void tcg_prepare_qemu_ldst(TCGContext* s, TCGReg data_reg, + TCGReg addr_reg, int mem_index, int opc, + uint16_t **label2_ptr_p, int is_store) +{ + const TCGReg arg0 = TCG_REG_R2; + const TCGReg arg1 = TCG_REG_R3; + int s_bits = opc & 3; + uint16_t *label1_ptr; + tcg_target_long ofs; + + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, arg0, addr_reg); + } else { + tcg_out_mov(s, TCG_TYPE_I64, arg0, addr_reg); + } + + tcg_out_sh64(s, RSY_SRLG, arg1, addr_reg, TCG_REG_NONE, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + tgen64_andi_tmp(s, arg0, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + tgen64_andi_tmp(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + + if (is_store) { + ofs = offsetof(CPUState, tlb_table[mem_index][0].addr_write); + } else { + ofs = offsetof(CPUState, tlb_table[mem_index][0].addr_read); + } + assert(ofs < 0x80000); + + if (TARGET_LONG_BITS == 32) { + tcg_out_mem(s, RX_C, RXY_CY, arg0, arg1, TCG_AREG0, ofs); + } else { + tcg_out_mem(s, 0, RXY_CG, arg0, arg1, TCG_AREG0, ofs); + } + + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, arg0, addr_reg); + } else { + tcg_out_mov(s, TCG_TYPE_I64, arg0, addr_reg); + } + + label1_ptr = (uint16_t*)s->code_ptr; + + /* je label1 (offset will be patched in later) */ + tcg_out_insn(s, RI, BRC, S390_CC_EQ, 0); + + /* call load/store helper */ + if (is_store) { + /* Make sure to zero-extend the value to the full register + for the calling convention. */ + switch (opc) { + case LD_UINT8: + tgen_ext8u(s, TCG_TYPE_I64, arg1, data_reg); + break; + case LD_UINT16: + tgen_ext16u(s, TCG_TYPE_I64, arg1, data_reg); + break; + case LD_UINT32: + tgen_ext32u(s, arg1, data_reg); + break; + case LD_UINT64: + tcg_out_mov(s, TCG_TYPE_I64, arg1, data_reg); + break; + default: + tcg_abort(); + } + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, mem_index); + tgen_calli(s, (tcg_target_ulong)qemu_st_helpers[s_bits]); + } else { + tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index); + tgen_calli(s, (tcg_target_ulong)qemu_ld_helpers[s_bits]); + + /* sign extension */ + switch (opc) { + case LD_INT8: + tgen_ext8s(s, TCG_TYPE_I64, data_reg, arg0); + break; + case LD_INT16: + tgen_ext16s(s, TCG_TYPE_I64, data_reg, arg0); + break; + case LD_INT32: + tgen_ext32s(s, data_reg, arg0); + break; + default: + /* unsigned -> just copy */ + tcg_out_mov(s, TCG_TYPE_I64, data_reg, arg0); + break; + } + } + + /* jump to label2 (end) */ + *label2_ptr_p = (uint16_t*)s->code_ptr; + + tcg_out_insn(s, RI, BRC, S390_CC_ALWAYS, 0); + + /* this is label1, patch branch */ + *(label1_ptr + 1) = ((unsigned long)s->code_ptr - + (unsigned long)label1_ptr) >> 1; + + ofs = offsetof(CPUState, tlb_table[mem_index][0].addend); + assert(ofs < 0x80000); + + tcg_out_mem(s, 0, RXY_AG, arg0, arg1, TCG_AREG0, ofs); +} + +static void tcg_finish_qemu_ldst(TCGContext* s, uint16_t *label2_ptr) +{ + /* patch branch */ + *(label2_ptr + 1) = ((unsigned long)s->code_ptr - + (unsigned long)label2_ptr) >> 1; +} +#else +static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, + TCGReg *index_reg, tcg_target_long *disp) +{ + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, TCG_TMP0, *addr_reg); + *addr_reg = TCG_TMP0; + } + if (GUEST_BASE < 0x80000) { + *index_reg = TCG_REG_NONE; + *disp = GUEST_BASE; + } else { + *index_reg = TCG_GUEST_BASE_REG; + *disp = 0; + } +} +#endif /* CONFIG_SOFTMMU */ + +/* load data with address translation (if applicable) + and endianness conversion */ +static void tcg_out_qemu_ld(TCGContext* s, const TCGArg* args, int opc) +{ + TCGReg addr_reg, data_reg; +#if defined(CONFIG_SOFTMMU) + int mem_index; + uint16_t *label2_ptr; +#else + TCGReg index_reg; + tcg_target_long disp; +#endif + + data_reg = *args++; + addr_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + mem_index = *args; + + tcg_prepare_qemu_ldst(s, data_reg, addr_reg, mem_index, + opc, &label2_ptr, 0); + + tcg_out_qemu_ld_direct(s, opc, data_reg, TCG_REG_R2, TCG_REG_NONE, 0); + + tcg_finish_qemu_ldst(s, label2_ptr); +#else + tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); + tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp); +#endif +} + +static void tcg_out_qemu_st(TCGContext* s, const TCGArg* args, int opc) +{ + TCGReg addr_reg, data_reg; +#if defined(CONFIG_SOFTMMU) + int mem_index; + uint16_t *label2_ptr; +#else + TCGReg index_reg; + tcg_target_long disp; +#endif + + data_reg = *args++; + addr_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + mem_index = *args; + + tcg_prepare_qemu_ldst(s, data_reg, addr_reg, mem_index, + opc, &label2_ptr, 1); + + tcg_out_qemu_st_direct(s, opc, data_reg, TCG_REG_R2, TCG_REG_NONE, 0); + + tcg_finish_qemu_ldst(s, label2_ptr); +#else + tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); + tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp); +#endif +} + +#if TCG_TARGET_REG_BITS == 64 +# define OP_32_64(x) \ + case glue(glue(INDEX_op_,x),_i32): \ + case glue(glue(INDEX_op_,x),_i64) +#else +# define OP_32_64(x) \ + case glue(glue(INDEX_op_,x),_i32) +#endif + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + S390Opcode op; + + switch (opc) { + case INDEX_op_exit_tb: + /* return value */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, args[0]); + tgen_gotoi(s, S390_CC_ALWAYS, (unsigned long)tb_ret_addr); + break; + + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + tcg_abort(); + } else { + /* load address stored at s->tb_next + args[0] */ + tcg_out_ld_abs(s, TCG_TYPE_PTR, TCG_TMP0, s->tb_next + args[0]); + /* and go there */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_TMP0); + } + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + + case INDEX_op_call: + if (const_args[0]) { + tgen_calli(s, args[0]); + } else { + tcg_out_insn(s, RR, BASR, TCG_REG_R14, args[0]); + } + break; + + case INDEX_op_mov_i32: + tcg_out_mov(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + + OP_32_64(ld8u): + /* ??? LLC (RXY format) is only present with the extended-immediate + facility, whereas LLGC is always present. */ + tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + OP_32_64(ld8s): + /* ??? LB is no smaller than LGB, so no point to using it. */ + tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + OP_32_64(ld16u): + /* ??? LLH (RXY format) is only present with the extended-immediate + facility, whereas LLGH is always present. */ + tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + case INDEX_op_ld16s_i32: + tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + case INDEX_op_ld_i32: + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + OP_32_64(st8): + tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], + TCG_REG_NONE, args[2]); + break; + + OP_32_64(st16): + tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1], + TCG_REG_NONE, args[2]); + break; + + case INDEX_op_st_i32: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + if (const_args[2]) { + tgen32_addi(s, args[0], args[2]); + } else { + tcg_out_insn(s, RR, AR, args[0], args[2]); + } + break; + case INDEX_op_sub_i32: + if (const_args[2]) { + tgen32_addi(s, args[0], -args[2]); + } else { + tcg_out_insn(s, RR, SR, args[0], args[2]); + } + break; + + case INDEX_op_and_i32: + if (const_args[2]) { + tgen64_andi(s, args[0], args[2] | 0xffffffff00000000ull); + } else { + tcg_out_insn(s, RR, NR, args[0], args[2]); + } + break; + case INDEX_op_or_i32: + if (const_args[2]) { + tgen64_ori(s, args[0], args[2] & 0xffffffff); + } else { + tcg_out_insn(s, RR, OR, args[0], args[2]); + } + break; + case INDEX_op_xor_i32: + if (const_args[2]) { + tgen64_xori(s, args[0], args[2] & 0xffffffff); + } else { + tcg_out_insn(s, RR, XR, args[0], args[2]); + } + break; + + case INDEX_op_neg_i32: + tcg_out_insn(s, RR, LCR, args[0], args[1]); + break; + + case INDEX_op_mul_i32: + if (const_args[2]) { + if ((int32_t)args[2] == (int16_t)args[2]) { + tcg_out_insn(s, RI, MHI, args[0], args[2]); + } else { + tcg_out_insn(s, RIL, MSFI, args[0], args[2]); + } + } else { + tcg_out_insn(s, RRE, MSR, args[0], args[2]); + } + break; + + case INDEX_op_div2_i32: + tcg_out_insn(s, RR, DR, TCG_REG_R2, args[4]); + break; + case INDEX_op_divu2_i32: + tcg_out_insn(s, RRE, DLR, TCG_REG_R2, args[4]); + break; + + case INDEX_op_shl_i32: + op = RS_SLL; + do_shift32: + if (const_args[2]) { + tcg_out_sh32(s, op, args[0], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh32(s, op, args[0], args[2], 0); + } + break; + case INDEX_op_shr_i32: + op = RS_SRL; + goto do_shift32; + case INDEX_op_sar_i32: + op = RS_SRA; + goto do_shift32; + + case INDEX_op_rotl_i32: + /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], + TCG_REG_NONE, (32 - args[2]) & 31); + } else { + tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); + tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0); + } + break; + + case INDEX_op_ext8s_i32: + tgen_ext8s(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tgen_ext16s(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext8u_i32: + tgen_ext8u(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tgen_ext16u(s, TCG_TYPE_I32, args[0], args[1]); + break; + + OP_32_64(bswap16): + /* The TCG bswap definition requires bits 0-47 already be zero. + Thus we don't need the G-type insns to implement bswap16_i64. */ + tcg_out_insn(s, RRE, LRVR, args[0], args[1]); + tcg_out_sh32(s, RS_SRL, args[0], TCG_REG_NONE, 16); + break; + OP_32_64(bswap32): + tcg_out_insn(s, RRE, LRVR, args[0], args[1]); + break; + + case INDEX_op_br: + tgen_branch(s, S390_CC_ALWAYS, args[0]); + break; + + case INDEX_op_brcond_i32: + tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], + args[1], const_args[1], args[3]); + break; + case INDEX_op_setcond_i32: + tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, LD_UINT8); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, LD_INT8); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, LD_UINT16); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, LD_INT16); + break; + case INDEX_op_qemu_ld32: + /* ??? Technically we can use a non-extending instruction. */ + tcg_out_qemu_ld(s, args, LD_UINT32); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, LD_UINT64); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, LD_UINT8); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, LD_UINT16); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, LD_UINT32); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, LD_UINT64); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_mov_i64: + tcg_out_mov(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + + case INDEX_op_ld16s_i64: + tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld32u_i64: + tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + + case INDEX_op_st32_i64: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i64: + if (const_args[2]) { + tgen64_addi(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, AGR, args[0], args[2]); + } + break; + case INDEX_op_sub_i64: + if (const_args[2]) { + tgen64_addi(s, args[0], -args[2]); + } else { + tcg_out_insn(s, RRE, SGR, args[0], args[2]); + } + break; + + case INDEX_op_and_i64: + if (const_args[2]) { + tgen64_andi(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, NGR, args[0], args[2]); + } + break; + case INDEX_op_or_i64: + if (const_args[2]) { + tgen64_ori(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, OGR, args[0], args[2]); + } + break; + case INDEX_op_xor_i64: + if (const_args[2]) { + tgen64_xori(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, XGR, args[0], args[2]); + } + break; + + case INDEX_op_neg_i64: + tcg_out_insn(s, RRE, LCGR, args[0], args[1]); + break; + case INDEX_op_bswap64_i64: + tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); + break; + + case INDEX_op_mul_i64: + if (const_args[2]) { + if (args[2] == (int16_t)args[2]) { + tcg_out_insn(s, RI, MGHI, args[0], args[2]); + } else { + tcg_out_insn(s, RIL, MSGFI, args[0], args[2]); + } + } else { + tcg_out_insn(s, RRE, MSGR, args[0], args[2]); + } + break; + + case INDEX_op_div2_i64: + /* ??? We get an unnecessary sign-extension of the dividend + into R3 with this definition, but as we do in fact always + produce both quotient and remainder using INDEX_op_div_i64 + instead requires jumping through even more hoops. */ + tcg_out_insn(s, RRE, DSGR, TCG_REG_R2, args[4]); + break; + case INDEX_op_divu2_i64: + tcg_out_insn(s, RRE, DLGR, TCG_REG_R2, args[4]); + break; + + case INDEX_op_shl_i64: + op = RSY_SLLG; + do_shift64: + if (const_args[2]) { + tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, op, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_shr_i64: + op = RSY_SRLG; + goto do_shift64; + case INDEX_op_sar_i64: + op = RSY_SRAG; + goto do_shift64; + + case INDEX_op_rotl_i64: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], + TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_rotr_i64: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], + TCG_REG_NONE, (64 - args[2]) & 63); + } else { + /* We can use the smaller 32-bit negate because only the + low 6 bits are examined for the rotate. */ + tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0); + } + break; + + case INDEX_op_ext8s_i64: + tgen_ext8s(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext16s_i64: + tgen_ext16s(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tgen_ext32s(s, args[0], args[1]); + break; + case INDEX_op_ext8u_i64: + tgen_ext8u(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext16u_i64: + tgen_ext16u(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext32u_i64: + tgen_ext32u(s, args[0], args[1]); + break; + + case INDEX_op_brcond_i64: + tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], + args[1], const_args[1], args[3]); + break; + case INDEX_op_setcond_i64: + tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_qemu_ld32u: + tcg_out_qemu_ld(s, args, LD_UINT32); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, LD_INT32); + break; +#endif /* TCG_TARGET_REG_BITS == 64 */ + + case INDEX_op_jmp: + /* This one is obsolete and never emitted. */ + tcg_abort(); + break; + + default: + fprintf(stderr,"unimplemented opc 0x%x\n",opc); + tcg_abort(); + } +} + +static const TCGTargetOpDef s390_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "0", "rWI" } }, + { INDEX_op_sub_i32, { "r", "0", "rWNI" } }, + { INDEX_op_mul_i32, { "r", "0", "rK" } }, + + { INDEX_op_div2_i32, { "b", "a", "0", "1", "r" } }, + { INDEX_op_divu2_i32, { "b", "a", "0", "1", "r" } }, + + { INDEX_op_and_i32, { "r", "0", "rWA" } }, + { INDEX_op_or_i32, { "r", "0", "rWO" } }, + { INDEX_op_xor_i32, { "r", "0", "rWX" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "0", "Ri" } }, + { INDEX_op_shr_i32, { "r", "0", "Ri" } }, + { INDEX_op_sar_i32, { "r", "0", "Ri" } }, + + { INDEX_op_rotl_i32, { "r", "r", "Ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "Ri" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_brcond_i32, { "r", "rWC" } }, + { INDEX_op_setcond_i32, { "r", "r", "rWC" } }, + + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L" } }, + + { INDEX_op_qemu_st8, { "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L" } }, + +#if defined(__s390x__) + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "0", "rI" } }, + { INDEX_op_sub_i64, { "r", "0", "rNI" } }, + { INDEX_op_mul_i64, { "r", "0", "rK" } }, + + { INDEX_op_div2_i64, { "b", "a", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "b", "a", "0", "1", "r" } }, + + { INDEX_op_and_i64, { "r", "0", "rA" } }, + { INDEX_op_or_i64, { "r", "0", "rO" } }, + { INDEX_op_xor_i64, { "r", "0", "rX" } }, + + { INDEX_op_neg_i64, { "r", "r" } }, + + { INDEX_op_shl_i64, { "r", "r", "Ri" } }, + { INDEX_op_shr_i64, { "r", "r", "Ri" } }, + { INDEX_op_sar_i64, { "r", "r", "Ri" } }, + + { INDEX_op_rotl_i64, { "r", "r", "Ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "Ri" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, + + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_brcond_i64, { "r", "rC" } }, + { INDEX_op_setcond_i64, { "r", "r", "rC" } }, + + { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, +#endif + + { -1 }, +}; + +/* ??? Linux kernels provide an AUXV entry AT_HWCAP that provides most of + this information. However, getting at that entry is not easy this far + away from main. Our options are: start searching from environ, but + that fails as soon as someone does a setenv in between. Read the data + from /proc/self/auxv. Or do the probing ourselves. The only thing + extra that AT_HWCAP gives us is HWCAP_S390_HIGH_GPRS, which indicates + that the kernel saves all 64-bits of the registers around traps while + in 31-bit mode. But this is true of all "recent" kernels (ought to dig + back and see from when this might not be true). */ + +#include <signal.h> + +static volatile sig_atomic_t got_sigill; + +static void sigill_handler(int sig) +{ + got_sigill = 1; +} + +static void query_facilities(void) +{ + struct sigaction sa_old, sa_new; + register int r0 __asm__("0"); + register void *r1 __asm__("1"); + int fail; + + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = sigill_handler; + sigaction(SIGILL, &sa_new, &sa_old); + + /* First, try STORE FACILITY LIST EXTENDED. If this is present, then + we need not do any more probing. Unfortunately, this itself is an + extension and the original STORE FACILITY LIST instruction is + kernel-only, storing its results at absolute address 200. */ + /* stfle 0(%r1) */ + r1 = &facilities; + asm volatile(".word 0xb2b0,0x1000" + : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); + + if (got_sigill) { + /* STORE FACILITY EXTENDED is not available. Probe for one of each + kind of instruction that we're interested in. */ + /* ??? Possibly some of these are in practice never present unless + the store-facility-extended facility is also present. But since + that isn't documented it's just better to probe for each. */ + + /* Test for z/Architecture. Required even in 31-bit mode. */ + got_sigill = 0; + /* agr %r0,%r0 */ + asm volatile(".word 0xb908,0x0000" : "=r"(r0) : : "cc"); + if (!got_sigill) { + facilities |= FACILITY_ZARCH_ACTIVE; + } + + /* Test for long displacement. */ + got_sigill = 0; + /* ly %r0,0(%r1) */ + r1 = &facilities; + asm volatile(".word 0xe300,0x1000,0x0058" + : "=r"(r0) : "r"(r1) : "cc"); + if (!got_sigill) { + facilities |= FACILITY_LONG_DISP; + } + + /* Test for extended immediates. */ + got_sigill = 0; + /* afi %r0,0 */ + asm volatile(".word 0xc209,0x0000,0x0000" : : : "cc"); + if (!got_sigill) { + facilities |= FACILITY_EXT_IMM; + } + + /* Test for general-instructions-extension. */ + got_sigill = 0; + /* msfi %r0,1 */ + asm volatile(".word 0xc201,0x0000,0x0001"); + if (!got_sigill) { + facilities |= FACILITY_GEN_INST_EXT; + } + } + + sigaction(SIGILL, &sa_old, NULL); + + /* The translator currently uses these extensions unconditionally. + Pruning this back to the base ESA/390 architecture doesn't seem + worthwhile, since even the KVM target requires z/Arch. */ + fail = 0; + if ((facilities & FACILITY_ZARCH_ACTIVE) == 0) { + fprintf(stderr, "TCG: z/Arch facility is required.\n"); + fprintf(stderr, "TCG: Boot with a 64-bit enabled kernel.\n"); + fail = 1; + } + if ((facilities & FACILITY_LONG_DISP) == 0) { + fprintf(stderr, "TCG: long-displacement facility is required.\n"); + fail = 1; + } + + /* So far there's just enough support for 31-bit mode to let the + compile succeed. This is good enough to run QEMU with KVM. */ + if (sizeof(void *) != 8) { + fprintf(stderr, "TCG: 31-bit mode is not supported.\n"); + fail = 1; + } + + if (fail) { + exit(-1); + } +} + +static void tcg_target_init(TCGContext *s) +{ +#if !defined(CONFIG_USER_ONLY) + /* fail safe */ + if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) { + tcg_abort(); + } +#endif + + query_facilities(); + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R1); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R2); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R3); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R4); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R5); + /* The return register can be considered call-clobbered. */ + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); + /* XXX many insns can't be used with R0, so we better avoid it for now */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + + tcg_add_target_add_op_defs(s390_op_defs); +} + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + /* stmg %r6,%r15,48(%r15) (save registers) */ + tcg_out_insn(s, RXY, STMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 48); + + /* aghi %r15,-160 (stack frame) */ + tcg_out_insn(s, RI, AGHI, TCG_REG_R15, -160); + + if (GUEST_BASE >= 0x80000) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } + + /* br %r2 (go to TB) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R2); + + tb_ret_addr = s->code_ptr; + + /* lmg %r6,%r15,208(%r15) (restore registers) */ + tcg_out_insn(s, RXY, LMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 208); + + /* br %r14 (return) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); +} + +static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + tcg_abort(); +} diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h new file mode 100644 index 0000000..4e45cf3 --- /dev/null +++ b/tcg/s390/tcg-target.h @@ -0,0 +1,109 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009 Ulrich Hecht <uli@suse.de> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_S390 1 + +#ifdef __s390x__ +#define TCG_TARGET_REG_BITS 64 +#else +#define TCG_TARGET_REG_BITS 32 +#endif + +#define TCG_TARGET_WORDS_BIGENDIAN + +typedef enum TCGReg { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15 +} TCGReg; + +#define TCG_TARGET_NB_REGS 16 + +/* optional instructions */ +#define TCG_TARGET_HAS_div2_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +// #define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +// #define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div2_i64 +#define TCG_TARGET_HAS_rot_i64 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +// #define TCG_TARGET_HAS_not_i64 +#define TCG_TARGET_HAS_neg_i64 +// #define TCG_TARGET_HAS_andc_i64 +// #define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif + +#define TCG_TARGET_HAS_GUEST_BASE + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_R15 +#define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_STACK_OFFSET 0 + +#define TCG_TARGET_EXTEND_ARGS 1 + +enum { + /* Note: must be synced with dyngen-exec.h */ + TCG_AREG0 = TCG_REG_R10, +}; + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ +} diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c new file mode 100644 index 0000000..5f1353a --- /dev/null +++ b/tcg/sparc/tcg-target.c @@ -0,0 +1,1569 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%g0", + "%g1", + "%g2", + "%g3", + "%g4", + "%g5", + "%g6", + "%g7", + "%o0", + "%o1", + "%o2", + "%o3", + "%o4", + "%o5", + "%o6", + "%o7", + "%l0", + "%l1", + "%l2", + "%l3", + "%l4", + "%l5", + "%l6", + "%l7", + "%i0", + "%i1", + "%i2", + "%i3", + "%i4", + "%i5", + "%i6", + "%i7", +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_L0, + TCG_REG_L1, + TCG_REG_L2, + TCG_REG_L3, + TCG_REG_L4, + TCG_REG_L5, + TCG_REG_L6, + TCG_REG_L7, + TCG_REG_I0, + TCG_REG_I1, + TCG_REG_I2, + TCG_REG_I3, + TCG_REG_I4, +}; + +static const int tcg_target_call_iarg_regs[6] = { + TCG_REG_O0, + TCG_REG_O1, + TCG_REG_O2, + TCG_REG_O3, + TCG_REG_O4, + TCG_REG_O5, +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_O0, + TCG_REG_O1, +}; + +static inline int check_fit_tl(tcg_target_long val, unsigned int bits) +{ + return (val << ((sizeof(tcg_target_long) * 8 - bits)) + >> (sizeof(tcg_target_long) * 8 - bits)) == val; +} + +static inline int check_fit_i32(uint32_t val, unsigned int bits) +{ + return ((val << (32 - bits)) >> (32 - bits)) == val; +} + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch (type) { + case R_SPARC_32: + if (value != (uint32_t)value) + tcg_abort(); + *(uint32_t *)code_ptr = value; + break; + case R_SPARC_WDISP22: + value -= (long)code_ptr; + value >>= 2; + if (!check_fit_tl(value, 22)) + tcg_abort(); + *(uint32_t *)code_ptr = ((*(uint32_t *)code_ptr) & ~0x3fffff) | value; + break; + case R_SPARC_WDISP19: + value -= (long)code_ptr; + value >>= 2; + if (!check_fit_tl(value, 19)) + tcg_abort(); + *(uint32_t *)code_ptr = ((*(uint32_t *)code_ptr) & ~0x7ffff) | value; + break; + default: + tcg_abort(); + } +} + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + return 6; +} + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch (ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; + case 'L': /* qemu_ld/st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + // Helper args + tcg_regset_reset_reg(ct->u.regs, TCG_REG_O0); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_O1); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_O2); + break; + case 'I': + ct->ct |= TCG_CT_CONST_S11; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S13; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) + return 1; + else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13)) + return 1; + else + return 0; +} + +#define INSN_OP(x) ((x) << 30) +#define INSN_OP2(x) ((x) << 22) +#define INSN_OP3(x) ((x) << 19) +#define INSN_OPF(x) ((x) << 5) +#define INSN_RD(x) ((x) << 25) +#define INSN_RS1(x) ((x) << 14) +#define INSN_RS2(x) (x) +#define INSN_ASI(x) ((x) << 5) + +#define INSN_IMM11(x) ((1 << 13) | ((x) & 0x7ff)) +#define INSN_IMM13(x) ((1 << 13) | ((x) & 0x1fff)) +#define INSN_OFF19(x) (((x) >> 2) & 0x07ffff) +#define INSN_OFF22(x) (((x) >> 2) & 0x3fffff) + +#define INSN_COND(x, a) (((x) << 25) | ((a) << 29)) +#define COND_N 0x0 +#define COND_E 0x1 +#define COND_LE 0x2 +#define COND_L 0x3 +#define COND_LEU 0x4 +#define COND_CS 0x5 +#define COND_NEG 0x6 +#define COND_VS 0x7 +#define COND_A 0x8 +#define COND_NE 0x9 +#define COND_G 0xa +#define COND_GE 0xb +#define COND_GU 0xc +#define COND_CC 0xd +#define COND_POS 0xe +#define COND_VC 0xf +#define BA (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2)) + +#define MOVCC_ICC (1 << 18) +#define MOVCC_XCC (1 << 18 | 1 << 12) + +#define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00)) +#define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10)) +#define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01)) +#define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05)) +#define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02)) +#define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12)) +#define ARITH_ORN (INSN_OP(2) | INSN_OP3(0x06)) +#define ARITH_XOR (INSN_OP(2) | INSN_OP3(0x03)) +#define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04)) +#define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14)) +#define ARITH_ADDX (INSN_OP(2) | INSN_OP3(0x10)) +#define ARITH_SUBX (INSN_OP(2) | INSN_OP3(0x0c)) +#define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a)) +#define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e)) +#define ARITH_SDIV (INSN_OP(2) | INSN_OP3(0x0f)) +#define ARITH_MULX (INSN_OP(2) | INSN_OP3(0x09)) +#define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d)) +#define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d)) +#define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c)) + +#define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25)) +#define SHIFT_SRL (INSN_OP(2) | INSN_OP3(0x26)) +#define SHIFT_SRA (INSN_OP(2) | INSN_OP3(0x27)) + +#define SHIFT_SLLX (INSN_OP(2) | INSN_OP3(0x25) | (1 << 12)) +#define SHIFT_SRLX (INSN_OP(2) | INSN_OP3(0x26) | (1 << 12)) +#define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12)) + +#define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0)) +#define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0)) +#define JMPL (INSN_OP(2) | INSN_OP3(0x38)) +#define SAVE (INSN_OP(2) | INSN_OP3(0x3c)) +#define RESTORE (INSN_OP(2) | INSN_OP3(0x3d)) +#define SETHI (INSN_OP(0) | INSN_OP2(0x4)) +#define CALL INSN_OP(1) +#define LDUB (INSN_OP(3) | INSN_OP3(0x01)) +#define LDSB (INSN_OP(3) | INSN_OP3(0x09)) +#define LDUH (INSN_OP(3) | INSN_OP3(0x02)) +#define LDSH (INSN_OP(3) | INSN_OP3(0x0a)) +#define LDUW (INSN_OP(3) | INSN_OP3(0x00)) +#define LDSW (INSN_OP(3) | INSN_OP3(0x08)) +#define LDX (INSN_OP(3) | INSN_OP3(0x0b)) +#define STB (INSN_OP(3) | INSN_OP3(0x05)) +#define STH (INSN_OP(3) | INSN_OP3(0x06)) +#define STW (INSN_OP(3) | INSN_OP3(0x04)) +#define STX (INSN_OP(3) | INSN_OP3(0x0e)) +#define LDUBA (INSN_OP(3) | INSN_OP3(0x11)) +#define LDSBA (INSN_OP(3) | INSN_OP3(0x19)) +#define LDUHA (INSN_OP(3) | INSN_OP3(0x12)) +#define LDSHA (INSN_OP(3) | INSN_OP3(0x1a)) +#define LDUWA (INSN_OP(3) | INSN_OP3(0x10)) +#define LDSWA (INSN_OP(3) | INSN_OP3(0x18)) +#define LDXA (INSN_OP(3) | INSN_OP3(0x1b)) +#define STBA (INSN_OP(3) | INSN_OP3(0x15)) +#define STHA (INSN_OP(3) | INSN_OP3(0x16)) +#define STWA (INSN_OP(3) | INSN_OP3(0x14)) +#define STXA (INSN_OP(3) | INSN_OP3(0x1e)) + +#ifndef ASI_PRIMARY_LITTLE +#define ASI_PRIMARY_LITTLE 0x88 +#endif + +static inline void tcg_out_arith(TCGContext *s, int rd, int rs1, int rs2, + int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | + INSN_RS2(rs2)); +} + +static inline void tcg_out_arithi(TCGContext *s, int rd, int rs1, + uint32_t offset, int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | + INSN_IMM13(offset)); +} + +static void tcg_out_arithc(TCGContext *s, int rd, int rs1, + int val2, int val2const, int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) + | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR); +} + +static inline void tcg_out_sethi(TCGContext *s, int ret, uint32_t arg) +{ + tcg_out32(s, SETHI | INSN_RD(ret) | ((arg & 0xfffffc00) >> 10)); +} + +static inline void tcg_out_movi_imm13(TCGContext *s, int ret, uint32_t arg) +{ + tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR); +} + +static inline void tcg_out_movi_imm32(TCGContext *s, int ret, uint32_t arg) +{ + if (check_fit_tl(arg, 13)) + tcg_out_movi_imm13(s, ret, arg); + else { + tcg_out_sethi(s, ret, arg); + if (arg & 0x3ff) + tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); + } +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + /* All 32-bit constants, as well as 64-bit constants with + no high bits set go through movi_imm32. */ + if (TCG_TARGET_REG_BITS == 32 + || type == TCG_TYPE_I32 + || (arg & ~(tcg_target_long)0xffffffff) == 0) { + tcg_out_movi_imm32(s, ret, arg); + } else if (check_fit_tl(arg, 13)) { + /* A 13-bit constant sign-extended to 64-bits. */ + tcg_out_movi_imm13(s, ret, arg); + } else if (check_fit_tl(arg, 32)) { + /* A 32-bit constant sign-extended to 64-bits. */ + tcg_out_sethi(s, ret, ~arg); + tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR); + } else { + tcg_out_movi_imm32(s, TCG_REG_I4, arg >> (TCG_TARGET_REG_BITS / 2)); + tcg_out_arithi(s, TCG_REG_I4, TCG_REG_I4, 32, SHIFT_SLLX); + tcg_out_movi_imm32(s, ret, arg); + tcg_out_arith(s, ret, ret, TCG_REG_I4, ARITH_OR); + } +} + +static inline void tcg_out_ld_raw(TCGContext *s, int ret, + tcg_target_long arg) +{ + tcg_out_sethi(s, ret, arg); + tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) | + INSN_IMM13(arg & 0x3ff)); +} + +static inline void tcg_out_ld_ptr(TCGContext *s, int ret, + tcg_target_long arg) +{ + if (!check_fit_tl(arg, 10)) + tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ffULL); + if (TCG_TARGET_REG_BITS == 64) { + tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(ret) | + INSN_IMM13(arg & 0x3ff)); + } else { + tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) | + INSN_IMM13(arg & 0x3ff)); + } +} + +static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset, int op) +{ + if (check_fit_tl(offset, 13)) + tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(addr) | + INSN_IMM13(offset)); + else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, offset); + tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(TCG_REG_I5) | + INSN_RS2(addr)); + } +} + +static inline void tcg_out_ldst_asi(TCGContext *s, int ret, int addr, + int offset, int op, int asi) +{ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, offset); + tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(TCG_REG_I5) | + INSN_ASI(asi) | INSN_RS2(addr)); +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) + tcg_out_ldst(s, ret, arg1, arg2, LDUW); + else + tcg_out_ldst(s, ret, arg1, arg2, LDX); +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, + int arg1, tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) + tcg_out_ldst(s, arg, arg1, arg2, STW); + else + tcg_out_ldst(s, arg, arg1, arg2, STX); +} + +static inline void tcg_out_sety(TCGContext *s, int rs) +{ + tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); +} + +static inline void tcg_out_rdy(TCGContext *s, int rd) +{ + tcg_out32(s, RDY | INSN_RD(rd)); +} + +static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + if (val != 0) { + if (check_fit_tl(val, 13)) + tcg_out_arithi(s, reg, reg, val, ARITH_ADD); + else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, val); + tcg_out_arith(s, reg, reg, TCG_REG_I5, ARITH_ADD); + } + } +} + +static inline void tcg_out_andi(TCGContext *s, int reg, tcg_target_long val) +{ + if (val != 0) { + if (check_fit_tl(val, 13)) + tcg_out_arithi(s, reg, reg, val, ARITH_AND); + else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, val); + tcg_out_arith(s, reg, reg, TCG_REG_I5, ARITH_AND); + } + } +} + +static void tcg_out_div32(TCGContext *s, int rd, int rs1, + int val2, int val2const, int uns) +{ + /* Load Y with the sign/zero extension of RS1 to 64-bits. */ + if (uns) { + tcg_out_sety(s, TCG_REG_G0); + } else { + tcg_out_arithi(s, TCG_REG_I5, rs1, 31, SHIFT_SRA); + tcg_out_sety(s, TCG_REG_I5); + } + + tcg_out_arithc(s, rd, rs1, val2, val2const, + uns ? ARITH_UDIV : ARITH_SDIV); +} + +static inline void tcg_out_nop(TCGContext *s) +{ + tcg_out_sethi(s, TCG_REG_G0, 0); +} + +static void tcg_out_branch_i32(TCGContext *s, int opc, int label_index) +{ + int32_t val; + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) { + val = l->u.value - (tcg_target_long)s->code_ptr; + tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x2) + | INSN_OFF22(l->u.value - (unsigned long)s->code_ptr))); + } else { + tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP22, label_index, 0); + tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x2) | 0)); + } +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_branch_i64(TCGContext *s, int opc, int label_index) +{ + int32_t val; + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) { + val = l->u.value - (tcg_target_long)s->code_ptr; + tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x1) | + (0x5 << 19) | + INSN_OFF19(l->u.value - (unsigned long)s->code_ptr))); + } else { + tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP19, label_index, 0); + tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x1) | + (0x5 << 19) | 0)); + } +} +#endif + +static const uint8_t tcg_cond_to_bcond[10] = { + [TCG_COND_EQ] = COND_E, + [TCG_COND_NE] = COND_NE, + [TCG_COND_LT] = COND_L, + [TCG_COND_GE] = COND_GE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_G, + [TCG_COND_LTU] = COND_CS, + [TCG_COND_GEU] = COND_CC, + [TCG_COND_LEU] = COND_LEU, + [TCG_COND_GTU] = COND_GU, +}; + +static void tcg_out_cmp(TCGContext *s, TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); +} + +static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2); + tcg_out_branch_i32(s, tcg_cond_to_bcond[cond], label_index); + tcg_out_nop(s); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2); + tcg_out_branch_i64(s, tcg_cond_to_bcond[cond], label_index); + tcg_out_nop(s); +} +#else +static void tcg_out_brcond2_i32(TCGContext *s, TCGCond cond, + TCGArg al, TCGArg ah, + TCGArg bl, int blconst, + TCGArg bh, int bhconst, int label_dest) +{ + int cc, label_next = gen_new_label(); + + tcg_out_cmp(s, ah, bh, bhconst); + + /* Note that we fill one of the delay slots with the second compare. */ + switch (cond) { + case TCG_COND_EQ: + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0); + tcg_out_branch_i32(s, cc, label_next); + tcg_out_cmp(s, al, bl, blconst); + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_EQ], 0); + tcg_out_branch_i32(s, cc, label_dest); + break; + + case TCG_COND_NE: + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0); + tcg_out_branch_i32(s, cc, label_dest); + tcg_out_cmp(s, al, bl, blconst); + tcg_out_branch_i32(s, cc, label_dest); + break; + + default: + /* ??? One could fairly easily special-case 64-bit unsigned + compares against 32-bit zero-extended constants. For instance, + we know that (unsigned)AH < 0 is false and need not emit it. + Similarly, (unsigned)AH > 0 being true implies AH != 0, so the + second branch will never be taken. */ + cc = INSN_COND(tcg_cond_to_bcond[cond], 0); + tcg_out_branch_i32(s, cc, label_dest); + tcg_out_nop(s); + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0); + tcg_out_branch_i32(s, cc, label_next); + tcg_out_cmp(s, al, bl, blconst); + cc = INSN_COND(tcg_cond_to_bcond[tcg_unsigned_cond(cond)], 0); + tcg_out_branch_i32(s, cc, label_dest); + break; + } + tcg_out_nop(s); + + tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr); +} +#endif + +static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + TCGArg t; + + /* For 32-bit comparisons, we can play games with ADDX/SUBX. */ + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + if (c2 != 0) { + tcg_out_arithc(s, ret, c1, c2, c2const, ARITH_XOR); + } + c1 = TCG_REG_G0, c2 = ret, c2const = 0; + cond = (cond == TCG_COND_EQ ? TCG_COND_LEU : TCG_COND_LTU); + break; + + case TCG_COND_GTU: + case TCG_COND_GEU: + if (c2const && c2 != 0) { + tcg_out_movi_imm13(s, TCG_REG_I5, c2); + c2 = TCG_REG_I5; + } + t = c1, c1 = c2, c2 = t, c2const = 0; + cond = tcg_swap_cond(cond); + break; + + case TCG_COND_LTU: + case TCG_COND_LEU: + break; + + default: + tcg_out_cmp(s, c1, c2, c2const); +#if defined(__sparc_v9__) || defined(__sparc_v8plus__) + tcg_out_movi_imm13(s, ret, 0); + tcg_out32 (s, ARITH_MOVCC | INSN_RD(ret) + | INSN_RS1(tcg_cond_to_bcond[cond]) + | MOVCC_ICC | INSN_IMM11(1)); +#else + t = gen_new_label(); + tcg_out_branch_i32(s, INSN_COND(tcg_cond_to_bcond[cond], 1), t); + tcg_out_movi_imm13(s, ret, 1); + tcg_out_movi_imm13(s, ret, 0); + tcg_out_label(s, t, (tcg_target_long)s->code_ptr); +#endif + return; + } + + tcg_out_cmp(s, c1, c2, c2const); + if (cond == TCG_COND_LTU) { + tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDX); + } else { + tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBX); + } +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_movi_imm13(s, ret, 0); + tcg_out32 (s, ARITH_MOVCC | INSN_RD(ret) + | INSN_RS1(tcg_cond_to_bcond[cond]) + | MOVCC_XCC | INSN_IMM11(1)); +} +#else +static void tcg_out_setcond2_i32(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg al, TCGArg ah, + TCGArg bl, int blconst, + TCGArg bh, int bhconst) +{ + int lab; + + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond_i32(s, TCG_COND_EQ, TCG_REG_I5, al, bl, blconst); + tcg_out_setcond_i32(s, TCG_COND_EQ, ret, ah, bh, bhconst); + tcg_out_arith(s, ret, ret, TCG_REG_I5, ARITH_AND); + break; + + case TCG_COND_NE: + tcg_out_setcond_i32(s, TCG_COND_NE, TCG_REG_I5, al, al, blconst); + tcg_out_setcond_i32(s, TCG_COND_NE, ret, ah, bh, bhconst); + tcg_out_arith(s, ret, ret, TCG_REG_I5, ARITH_OR); + break; + + default: + lab = gen_new_label(); + + tcg_out_cmp(s, ah, bh, bhconst); + tcg_out_branch_i32(s, INSN_COND(tcg_cond_to_bcond[cond], 1), lab); + tcg_out_movi_imm13(s, ret, 1); + tcg_out_branch_i32(s, INSN_COND(COND_NE, 1), lab); + tcg_out_movi_imm13(s, ret, 0); + + tcg_out_setcond_i32(s, tcg_unsigned_cond(cond), ret, al, bl, blconst); + + tcg_out_label(s, lab, (tcg_target_long)s->code_ptr); + break; + } +} +#endif + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) | + INSN_IMM13(-TCG_TARGET_STACK_MINFRAME)); + tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I0) | + INSN_RS2(TCG_REG_G0)); + tcg_out_nop(s); +} + +#if defined(CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +static const void * const qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static const void * const qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; +#endif + +#if TARGET_LONG_BITS == 32 +#define TARGET_LD_OP LDUW +#else +#define TARGET_LD_OP LDX +#endif + +#if defined(CONFIG_SOFTMMU) +#if HOST_LONG_BITS == 32 +#define TARGET_ADDEND_LD_OP LDUW +#else +#define TARGET_ADDEND_LD_OP LDX +#endif +#endif + +#ifdef __arch64__ +#define HOST_LD_OP LDX +#define HOST_ST_OP STX +#define HOST_SLL_OP SHIFT_SLLX +#define HOST_SRA_OP SHIFT_SRAX +#else +#define HOST_LD_OP LDUW +#define HOST_ST_OP STW +#define HOST_SLL_OP SHIFT_SLL +#define HOST_SRA_OP SHIFT_SRA +#endif + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, + int opc) +{ + int addr_reg, data_reg, arg0, arg1, arg2, mem_index, s_bits; +#if defined(CONFIG_SOFTMMU) + uint32_t *label1_ptr, *label2_ptr; +#endif + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + s_bits = opc & 3; + + arg0 = TCG_REG_O0; + arg1 = TCG_REG_O1; + arg2 = TCG_REG_O2; + +#if defined(CONFIG_SOFTMMU) + /* srl addr_reg, x, arg1 */ + tcg_out_arithi(s, arg1, addr_reg, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS, + SHIFT_SRL); + /* and addr_reg, x, arg0 */ + tcg_out_arithi(s, arg0, addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1), + ARITH_AND); + + /* and arg1, x, arg1 */ + tcg_out_andi(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + + /* add arg1, x, arg1 */ + tcg_out_addi(s, arg1, offsetof(CPUState, + tlb_table[mem_index][0].addr_read)); + + /* add env, arg1, arg1 */ + tcg_out_arith(s, arg1, TCG_AREG0, arg1, ARITH_ADD); + + /* ld [arg1], arg2 */ + tcg_out32(s, TARGET_LD_OP | INSN_RD(arg2) | INSN_RS1(arg1) | + INSN_RS2(TCG_REG_G0)); + + /* subcc arg0, arg2, %g0 */ + tcg_out_arith(s, TCG_REG_G0, arg0, arg2, ARITH_SUBCC); + + /* will become: + be label1 + or + be,pt %xcc label1 */ + label1_ptr = (uint32_t *)s->code_ptr; + tcg_out32(s, 0); + + /* mov (delay slot) */ + tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg); + + /* mov */ + tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index); + + /* XXX: move that code at the end of the TB */ + /* qemu_ld_helper[s_bits](arg0, arg1) */ + tcg_out32(s, CALL | ((((tcg_target_ulong)qemu_ld_helpers[s_bits] + - (tcg_target_ulong)s->code_ptr) >> 2) + & 0x3fffffff)); + /* Store AREG0 in stack to avoid ugly glibc bugs that mangle + global registers */ + // delay slot + tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE - + sizeof(long), HOST_ST_OP); + tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE - + sizeof(long), HOST_LD_OP); + + /* data_reg = sign_extend(arg0) */ + switch(opc) { + case 0 | 4: + /* sll arg0, 24/56, data_reg */ + tcg_out_arithi(s, data_reg, arg0, (int)sizeof(tcg_target_long) * 8 - 8, + HOST_SLL_OP); + /* sra data_reg, 24/56, data_reg */ + tcg_out_arithi(s, data_reg, data_reg, + (int)sizeof(tcg_target_long) * 8 - 8, HOST_SRA_OP); + break; + case 1 | 4: + /* sll arg0, 16/48, data_reg */ + tcg_out_arithi(s, data_reg, arg0, + (int)sizeof(tcg_target_long) * 8 - 16, HOST_SLL_OP); + /* sra data_reg, 16/48, data_reg */ + tcg_out_arithi(s, data_reg, data_reg, + (int)sizeof(tcg_target_long) * 8 - 16, HOST_SRA_OP); + break; + case 2 | 4: + /* sll arg0, 32, data_reg */ + tcg_out_arithi(s, data_reg, arg0, 32, HOST_SLL_OP); + /* sra data_reg, 32, data_reg */ + tcg_out_arithi(s, data_reg, data_reg, 32, HOST_SRA_OP); + break; + case 0: + case 1: + case 2: + case 3: + default: + /* mov */ + tcg_out_mov(s, TCG_TYPE_REG, data_reg, arg0); + break; + } + + /* will become: + ba label2 */ + label2_ptr = (uint32_t *)s->code_ptr; + tcg_out32(s, 0); + + /* nop (delay slot */ + tcg_out_nop(s); + + /* label1: */ +#if TARGET_LONG_BITS == 32 + /* be label1 */ + *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x2) | + INSN_OFF22((unsigned long)s->code_ptr - + (unsigned long)label1_ptr)); +#else + /* be,pt %xcc label1 */ + *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x1) | + (0x5 << 19) | INSN_OFF19((unsigned long)s->code_ptr - + (unsigned long)label1_ptr)); +#endif + + /* ld [arg1 + x], arg1 */ + tcg_out_ldst(s, arg1, arg1, offsetof(CPUTLBEntry, addend) - + offsetof(CPUTLBEntry, addr_read), TARGET_ADDEND_LD_OP); + +#if TARGET_LONG_BITS == 32 + /* and addr_reg, x, arg0 */ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, 0xffffffff); + tcg_out_arith(s, arg0, addr_reg, TCG_REG_I5, ARITH_AND); + /* add arg0, arg1, arg0 */ + tcg_out_arith(s, arg0, arg0, arg1, ARITH_ADD); +#else + /* add addr_reg, arg1, arg0 */ + tcg_out_arith(s, arg0, addr_reg, arg1, ARITH_ADD); +#endif + +#else + arg0 = addr_reg; +#endif + + switch(opc) { + case 0: + /* ldub [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDUB); + break; + case 0 | 4: + /* ldsb [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDSB); + break; + case 1: +#ifdef TARGET_WORDS_BIGENDIAN + /* lduh [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDUH); +#else + /* lduha [arg0] ASI_PRIMARY_LITTLE, data_reg */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, LDUHA, ASI_PRIMARY_LITTLE); +#endif + break; + case 1 | 4: +#ifdef TARGET_WORDS_BIGENDIAN + /* ldsh [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDSH); +#else + /* ldsha [arg0] ASI_PRIMARY_LITTLE, data_reg */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, LDSHA, ASI_PRIMARY_LITTLE); +#endif + break; + case 2: +#ifdef TARGET_WORDS_BIGENDIAN + /* lduw [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDUW); +#else + /* lduwa [arg0] ASI_PRIMARY_LITTLE, data_reg */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, LDUWA, ASI_PRIMARY_LITTLE); +#endif + break; + case 2 | 4: +#ifdef TARGET_WORDS_BIGENDIAN + /* ldsw [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDSW); +#else + /* ldswa [arg0] ASI_PRIMARY_LITTLE, data_reg */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, LDSWA, ASI_PRIMARY_LITTLE); +#endif + break; + case 3: +#ifdef TARGET_WORDS_BIGENDIAN + /* ldx [arg0], data_reg */ + tcg_out_ldst(s, data_reg, arg0, 0, LDX); +#else + /* ldxa [arg0] ASI_PRIMARY_LITTLE, data_reg */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, LDXA, ASI_PRIMARY_LITTLE); +#endif + break; + default: + tcg_abort(); + } + +#if defined(CONFIG_SOFTMMU) + /* label2: */ + *label2_ptr = (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2) | + INSN_OFF22((unsigned long)s->code_ptr - + (unsigned long)label2_ptr)); +#endif +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, + int opc) +{ + int addr_reg, data_reg, arg0, arg1, arg2, mem_index, s_bits; +#if defined(CONFIG_SOFTMMU) + uint32_t *label1_ptr, *label2_ptr; +#endif + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + + s_bits = opc; + + arg0 = TCG_REG_O0; + arg1 = TCG_REG_O1; + arg2 = TCG_REG_O2; + +#if defined(CONFIG_SOFTMMU) + /* srl addr_reg, x, arg1 */ + tcg_out_arithi(s, arg1, addr_reg, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS, + SHIFT_SRL); + + /* and addr_reg, x, arg0 */ + tcg_out_arithi(s, arg0, addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1), + ARITH_AND); + + /* and arg1, x, arg1 */ + tcg_out_andi(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + + /* add arg1, x, arg1 */ + tcg_out_addi(s, arg1, offsetof(CPUState, + tlb_table[mem_index][0].addr_write)); + + /* add env, arg1, arg1 */ + tcg_out_arith(s, arg1, TCG_AREG0, arg1, ARITH_ADD); + + /* ld [arg1], arg2 */ + tcg_out32(s, TARGET_LD_OP | INSN_RD(arg2) | INSN_RS1(arg1) | + INSN_RS2(TCG_REG_G0)); + + /* subcc arg0, arg2, %g0 */ + tcg_out_arith(s, TCG_REG_G0, arg0, arg2, ARITH_SUBCC); + + /* will become: + be label1 + or + be,pt %xcc label1 */ + label1_ptr = (uint32_t *)s->code_ptr; + tcg_out32(s, 0); + + /* mov (delay slot) */ + tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg); + + /* mov */ + tcg_out_mov(s, TCG_TYPE_REG, arg1, data_reg); + + /* mov */ + tcg_out_movi(s, TCG_TYPE_I32, arg2, mem_index); + + /* XXX: move that code at the end of the TB */ + /* qemu_st_helper[s_bits](arg0, arg1, arg2) */ + tcg_out32(s, CALL | ((((tcg_target_ulong)qemu_st_helpers[s_bits] + - (tcg_target_ulong)s->code_ptr) >> 2) + & 0x3fffffff)); + /* Store AREG0 in stack to avoid ugly glibc bugs that mangle + global registers */ + // delay slot + tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE - + sizeof(long), HOST_ST_OP); + tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE - + sizeof(long), HOST_LD_OP); + + /* will become: + ba label2 */ + label2_ptr = (uint32_t *)s->code_ptr; + tcg_out32(s, 0); + + /* nop (delay slot) */ + tcg_out_nop(s); + +#if TARGET_LONG_BITS == 32 + /* be label1 */ + *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x2) | + INSN_OFF22((unsigned long)s->code_ptr - + (unsigned long)label1_ptr)); +#else + /* be,pt %xcc label1 */ + *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x1) | + (0x5 << 19) | INSN_OFF19((unsigned long)s->code_ptr - + (unsigned long)label1_ptr)); +#endif + + /* ld [arg1 + x], arg1 */ + tcg_out_ldst(s, arg1, arg1, offsetof(CPUTLBEntry, addend) - + offsetof(CPUTLBEntry, addr_write), TARGET_ADDEND_LD_OP); + +#if TARGET_LONG_BITS == 32 + /* and addr_reg, x, arg0 */ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, 0xffffffff); + tcg_out_arith(s, arg0, addr_reg, TCG_REG_I5, ARITH_AND); + /* add arg0, arg1, arg0 */ + tcg_out_arith(s, arg0, arg0, arg1, ARITH_ADD); +#else + /* add addr_reg, arg1, arg0 */ + tcg_out_arith(s, arg0, addr_reg, arg1, ARITH_ADD); +#endif + +#else + arg0 = addr_reg; +#endif + + switch(opc) { + case 0: + /* stb data_reg, [arg0] */ + tcg_out_ldst(s, data_reg, arg0, 0, STB); + break; + case 1: +#ifdef TARGET_WORDS_BIGENDIAN + /* sth data_reg, [arg0] */ + tcg_out_ldst(s, data_reg, arg0, 0, STH); +#else + /* stha data_reg, [arg0] ASI_PRIMARY_LITTLE */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, STHA, ASI_PRIMARY_LITTLE); +#endif + break; + case 2: +#ifdef TARGET_WORDS_BIGENDIAN + /* stw data_reg, [arg0] */ + tcg_out_ldst(s, data_reg, arg0, 0, STW); +#else + /* stwa data_reg, [arg0] ASI_PRIMARY_LITTLE */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, STWA, ASI_PRIMARY_LITTLE); +#endif + break; + case 3: +#ifdef TARGET_WORDS_BIGENDIAN + /* stx data_reg, [arg0] */ + tcg_out_ldst(s, data_reg, arg0, 0, STX); +#else + /* stxa data_reg, [arg0] ASI_PRIMARY_LITTLE */ + tcg_out_ldst_asi(s, data_reg, arg0, 0, STXA, ASI_PRIMARY_LITTLE); +#endif + break; + default: + tcg_abort(); + } + +#if defined(CONFIG_SOFTMMU) + /* label2: */ + *label2_ptr = (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2) | + INSN_OFF22((unsigned long)s->code_ptr - + (unsigned long)label2_ptr)); +#endif +} + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ + int c; + + switch (opc) { + case INDEX_op_exit_tb: + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, args[0]); + tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I7) | + INSN_IMM13(8)); + tcg_out32(s, RESTORE | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_G0) | + INSN_RS2(TCG_REG_G0)); + break; + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_out_sethi(s, TCG_REG_I5, args[0] & 0xffffe000); + tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I5) | + INSN_IMM13((args[0] & 0x1fff))); + s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; + } else { + /* indirect jump method */ + tcg_out_ld_ptr(s, TCG_REG_I5, (tcg_target_long)(s->tb_next + args[0])); + tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I5) | + INSN_RS2(TCG_REG_G0)); + } + tcg_out_nop(s); + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + case INDEX_op_call: + if (const_args[0]) + tcg_out32(s, CALL | ((((tcg_target_ulong)args[0] + - (tcg_target_ulong)s->code_ptr) >> 2) + & 0x3fffffff)); + else { + tcg_out_ld_ptr(s, TCG_REG_I5, + (tcg_target_long)(s->tb_next + args[0])); + tcg_out32(s, JMPL | INSN_RD(TCG_REG_O7) | INSN_RS1(TCG_REG_I5) | + INSN_RS2(TCG_REG_G0)); + } + /* Store AREG0 in stack to avoid ugly glibc bugs that mangle + global registers */ + // delay slot + tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE - + sizeof(long), HOST_ST_OP); + tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK, + TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE - + sizeof(long), HOST_LD_OP); + break; + case INDEX_op_jmp: + case INDEX_op_br: + tcg_out_branch_i32(s, COND_A, args[0]); + tcg_out_nop(s); + break; + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]); + break; + +#if TCG_TARGET_REG_BITS == 64 +#define OP_32_64(x) \ + glue(glue(case INDEX_op_, x), _i32): \ + glue(glue(case INDEX_op_, x), _i64) +#else +#define OP_32_64(x) \ + glue(glue(case INDEX_op_, x), _i32) +#endif + OP_32_64(ld8u): + tcg_out_ldst(s, args[0], args[1], args[2], LDUB); + break; + OP_32_64(ld8s): + tcg_out_ldst(s, args[0], args[1], args[2], LDSB); + break; + OP_32_64(ld16u): + tcg_out_ldst(s, args[0], args[1], args[2], LDUH); + break; + OP_32_64(ld16s): + tcg_out_ldst(s, args[0], args[1], args[2], LDSH); + break; + case INDEX_op_ld_i32: +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ld32u_i64: +#endif + tcg_out_ldst(s, args[0], args[1], args[2], LDUW); + break; + OP_32_64(st8): + tcg_out_ldst(s, args[0], args[1], args[2], STB); + break; + OP_32_64(st16): + tcg_out_ldst(s, args[0], args[1], args[2], STH); + break; + case INDEX_op_st_i32: +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_st32_i64: +#endif + tcg_out_ldst(s, args[0], args[1], args[2], STW); + break; + OP_32_64(add): + c = ARITH_ADD; + goto gen_arith; + OP_32_64(sub): + c = ARITH_SUB; + goto gen_arith; + OP_32_64(and): + c = ARITH_AND; + goto gen_arith; + OP_32_64(andc): + c = ARITH_ANDN; + goto gen_arith; + OP_32_64(or): + c = ARITH_OR; + goto gen_arith; + OP_32_64(orc): + c = ARITH_ORN; + goto gen_arith; + OP_32_64(xor): + c = ARITH_XOR; + goto gen_arith; + case INDEX_op_shl_i32: + c = SHIFT_SLL; + goto gen_arith; + case INDEX_op_shr_i32: + c = SHIFT_SRL; + goto gen_arith; + case INDEX_op_sar_i32: + c = SHIFT_SRA; + goto gen_arith; + case INDEX_op_mul_i32: + c = ARITH_UMUL; + goto gen_arith; + + OP_32_64(neg): + c = ARITH_SUB; + goto gen_arith1; + OP_32_64(not): + c = ARITH_ORN; + goto gen_arith1; + + case INDEX_op_div_i32: + tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 0); + break; + case INDEX_op_divu_i32: + tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 1); + break; + + case INDEX_op_rem_i32: + case INDEX_op_remu_i32: + tcg_out_div32(s, TCG_REG_I5, args[1], args[2], const_args[2], + opc == INDEX_op_remu_i32); + tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2], + ARITH_UMUL); + tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond_i32(s, args[2], args[0], args[1], const_args[1], + args[3]); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond_i32(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + tcg_out_brcond2_i32(s, args[4], args[0], args[1], + args[2], const_args[2], + args[3], const_args[3], args[5]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2_i32(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], + args[4], const_args[4]); + break; + case INDEX_op_add2_i32: + tcg_out_arithc(s, args[0], args[2], args[4], const_args[4], + ARITH_ADDCC); + tcg_out_arithc(s, args[1], args[3], args[5], const_args[5], + ARITH_ADDX); + break; + case INDEX_op_sub2_i32: + tcg_out_arithc(s, args[0], args[2], args[4], const_args[4], + ARITH_SUBCC); + tcg_out_arithc(s, args[1], args[3], args[5], const_args[5], + ARITH_SUBX); + break; + case INDEX_op_mulu2_i32: + tcg_out_arithc(s, args[0], args[2], args[3], const_args[3], + ARITH_UMUL); + tcg_out_rdy(s, args[1]); + break; +#endif + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_qemu_ld32u: +#endif + tcg_out_qemu_ld(s, args, 2); + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, 2 | 4); + break; +#endif + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ld32s_i64: + tcg_out_ldst(s, args[0], args[1], args[2], LDSW); + break; + case INDEX_op_ld_i64: + tcg_out_ldst(s, args[0], args[1], args[2], LDX); + break; + case INDEX_op_st_i64: + tcg_out_ldst(s, args[0], args[1], args[2], STX); + break; + case INDEX_op_shl_i64: + c = SHIFT_SLLX; + goto gen_arith; + case INDEX_op_shr_i64: + c = SHIFT_SRLX; + goto gen_arith; + case INDEX_op_sar_i64: + c = SHIFT_SRAX; + goto gen_arith; + case INDEX_op_mul_i64: + c = ARITH_MULX; + goto gen_arith; + case INDEX_op_div_i64: + c = ARITH_SDIVX; + goto gen_arith; + case INDEX_op_divu_i64: + c = ARITH_UDIVX; + goto gen_arith; + case INDEX_op_rem_i64: + case INDEX_op_remu_i64: + tcg_out_arithc(s, TCG_REG_I5, args[1], args[2], const_args[2], + opc == INDEX_op_rem_i64 ? ARITH_SDIVX : ARITH_UDIVX); + tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2], + ARITH_MULX); + tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB); + break; + case INDEX_op_ext32s_i64: + if (const_args[1]) { + tcg_out_movi(s, TCG_TYPE_I64, args[0], (int32_t)args[1]); + } else { + tcg_out_arithi(s, args[0], args[1], 0, SHIFT_SRA); + } + break; + case INDEX_op_ext32u_i64: + if (const_args[1]) { + tcg_out_movi_imm32(s, args[0], args[1]); + } else { + tcg_out_arithi(s, args[0], args[1], 0, SHIFT_SRL); + } + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond_i64(s, args[2], args[0], args[1], const_args[1], + args[3]); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond_i64(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + +#endif + gen_arith: + tcg_out_arithc(s, args[0], args[1], args[2], const_args[2], c); + break; + + gen_arith1: + tcg_out_arithc(s, args[0], TCG_REG_G0, args[1], const_args[1], c); + break; + + default: + fprintf(stderr, "unknown opcode 0x%x\n", opc); + tcg_abort(); + } +} + +static const TCGTargetOpDef sparc_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "r", "rJ" } }, + { INDEX_op_mul_i32, { "r", "r", "rJ" } }, + { INDEX_op_div_i32, { "r", "r", "rJ" } }, + { INDEX_op_divu_i32, { "r", "r", "rJ" } }, + { INDEX_op_rem_i32, { "r", "r", "rJ" } }, + { INDEX_op_remu_i32, { "r", "r", "rJ" } }, + { INDEX_op_sub_i32, { "r", "r", "rJ" } }, + { INDEX_op_and_i32, { "r", "r", "rJ" } }, + { INDEX_op_andc_i32, { "r", "r", "rJ" } }, + { INDEX_op_or_i32, { "r", "r", "rJ" } }, + { INDEX_op_orc_i32, { "r", "r", "rJ" } }, + { INDEX_op_xor_i32, { "r", "r", "rJ" } }, + + { INDEX_op_shl_i32, { "r", "r", "rJ" } }, + { INDEX_op_shr_i32, { "r", "r", "rJ" } }, + { INDEX_op_sar_i32, { "r", "r", "rJ" } }, + + { INDEX_op_neg_i32, { "r", "rJ" } }, + { INDEX_op_not_i32, { "r", "rJ" } }, + + { INDEX_op_brcond_i32, { "r", "rJ" } }, + { INDEX_op_setcond_i32, { "r", "r", "rJ" } }, + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_brcond2_i32, { "r", "r", "rJ", "rJ" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "rJ", "rJ" } }, + { INDEX_op_add2_i32, { "r", "r", "r", "r", "rJ", "rJ" } }, + { INDEX_op_sub2_i32, { "r", "r", "r", "r", "rJ", "rJ" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "rJ" } }, +#endif + + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, +#endif + + { INDEX_op_qemu_st8, { "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + { INDEX_op_qemu_ld64, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L" } }, + + { INDEX_op_add_i64, { "r", "r", "rJ" } }, + { INDEX_op_mul_i64, { "r", "r", "rJ" } }, + { INDEX_op_div_i64, { "r", "r", "rJ" } }, + { INDEX_op_divu_i64, { "r", "r", "rJ" } }, + { INDEX_op_rem_i64, { "r", "r", "rJ" } }, + { INDEX_op_remu_i64, { "r", "r", "rJ" } }, + { INDEX_op_sub_i64, { "r", "r", "rJ" } }, + { INDEX_op_and_i64, { "r", "r", "rJ" } }, + { INDEX_op_andc_i64, { "r", "r", "rJ" } }, + { INDEX_op_or_i64, { "r", "r", "rJ" } }, + { INDEX_op_orc_i64, { "r", "r", "rJ" } }, + { INDEX_op_xor_i64, { "r", "r", "rJ" } }, + + { INDEX_op_shl_i64, { "r", "r", "rJ" } }, + { INDEX_op_shr_i64, { "r", "r", "rJ" } }, + { INDEX_op_sar_i64, { "r", "r", "rJ" } }, + + { INDEX_op_neg_i64, { "r", "rJ" } }, + { INDEX_op_not_i64, { "r", "rJ" } }, + + { INDEX_op_ext32s_i64, { "r", "ri" } }, + { INDEX_op_ext32u_i64, { "r", "ri" } }, + + { INDEX_op_brcond_i64, { "r", "rJ" } }, + { INDEX_op_setcond_i64, { "r", "r", "rJ" } }, +#endif + { -1 }, +}; + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); +#if TCG_TARGET_REG_BITS == 64 + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); +#endif + tcg_regset_set32(tcg_target_call_clobber_regs, 0, + (1 << TCG_REG_G1) | + (1 << TCG_REG_G2) | + (1 << TCG_REG_G3) | + (1 << TCG_REG_G4) | + (1 << TCG_REG_G5) | + (1 << TCG_REG_G6) | + (1 << TCG_REG_G7) | + (1 << TCG_REG_O0) | + (1 << TCG_REG_O1) | + (1 << TCG_REG_O2) | + (1 << TCG_REG_O3) | + (1 << TCG_REG_O4) | + (1 << TCG_REG_O5) | + (1 << TCG_REG_O7)); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_G0); +#if TCG_TARGET_REG_BITS == 64 + tcg_regset_set_reg(s->reserved_regs, TCG_REG_I4); // for internal use +#endif + tcg_regset_set_reg(s->reserved_regs, TCG_REG_I5); // for internal use + tcg_regset_set_reg(s->reserved_regs, TCG_REG_I6); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_I7); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_O6); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_O7); + tcg_add_target_add_op_defs(sparc_op_defs); +} diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h new file mode 100644 index 0000000..df0785e --- /dev/null +++ b/tcg/sparc/tcg-target.h @@ -0,0 +1,150 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define TCG_TARGET_SPARC 1 + +#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#define TCG_TARGET_REG_BITS 64 +#else +#define TCG_TARGET_REG_BITS 32 +#endif + +#define TCG_TARGET_WORDS_BIGENDIAN + +#define TCG_TARGET_NB_REGS 32 + +enum { + TCG_REG_G0 = 0, + TCG_REG_G1, + TCG_REG_G2, + TCG_REG_G3, + TCG_REG_G4, + TCG_REG_G5, + TCG_REG_G6, + TCG_REG_G7, + TCG_REG_O0, + TCG_REG_O1, + TCG_REG_O2, + TCG_REG_O3, + TCG_REG_O4, + TCG_REG_O5, + TCG_REG_O6, + TCG_REG_O7, + TCG_REG_L0, + TCG_REG_L1, + TCG_REG_L2, + TCG_REG_L3, + TCG_REG_L4, + TCG_REG_L5, + TCG_REG_L6, + TCG_REG_L7, + TCG_REG_I0, + TCG_REG_I1, + TCG_REG_I2, + TCG_REG_I3, + TCG_REG_I4, + TCG_REG_I5, + TCG_REG_I6, + TCG_REG_I7, +}; + +#define TCG_CT_CONST_S11 0x100 +#define TCG_CT_CONST_S13 0x200 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_I6 +#ifdef __arch64__ +// Reserve space for AREG0 +#define TCG_TARGET_STACK_MINFRAME (176 + 4 * (int)sizeof(long) + \ + TCG_STATIC_CALL_ARGS_SIZE) +#define TCG_TARGET_CALL_STACK_OFFSET (2047 - 16) +#define TCG_TARGET_STACK_ALIGN 16 +#else +// AREG0 + one word for alignment +#define TCG_TARGET_STACK_MINFRAME (92 + (2 + 1) * (int)sizeof(long) + \ + TCG_STATIC_CALL_ARGS_SIZE) +#define TCG_TARGET_CALL_STACK_OFFSET TCG_TARGET_STACK_MINFRAME +#define TCG_TARGET_STACK_ALIGN 8 +#endif + +#ifdef __arch64__ +#define TCG_TARGET_EXTEND_ARGS 1 +#endif + +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 +// #define TCG_TARGET_HAS_rot_i32 +// #define TCG_TARGET_HAS_ext8s_i32 +// #define TCG_TARGET_HAS_ext16s_i32 +// #define TCG_TARGET_HAS_ext8u_i32 +// #define TCG_TARGET_HAS_ext16u_i32 +// #define TCG_TARGET_HAS_bswap16_i32 +// #define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div_i64 +// #define TCG_TARGET_HAS_rot_i64 +// #define TCG_TARGET_HAS_ext8s_i64 +// #define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +// #define TCG_TARGET_HAS_ext8u_i64 +// #define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +// #define TCG_TARGET_HAS_bswap16_i64 +// #define TCG_TARGET_HAS_bswap32_i64 +// #define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_neg_i64 +#define TCG_TARGET_HAS_not_i64 +#define TCG_TARGET_HAS_andc_i64 +#define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif + +/* Note: must be synced with dyngen-exec.h */ +#ifdef CONFIG_SOLARIS +#define TCG_AREG0 TCG_REG_G2 +#elif defined(__sparc_v9__) +#define TCG_AREG0 TCG_REG_G5 +#else +#define TCG_AREG0 TCG_REG_G6 +#endif + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + unsigned long p; + + p = start & ~(8UL - 1UL); + stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL); + + for (; p < stop; p += 8) + __asm__ __volatile__("flush\t%0" : : "r" (p)); +} diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h new file mode 100644 index 0000000..207a89f --- /dev/null +++ b/tcg/tcg-op.h @@ -0,0 +1,2533 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "tcg.h" + +int gen_new_label(void); + +static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 arg1) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); +} + +static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 arg1) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); +} + +static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg arg1) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = arg1; +} + +static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); +} + +static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); +} + +static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGArg arg2) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = arg2; +} + +static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGArg arg2) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = arg2; +} + +static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg arg1, TCGArg arg2) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = arg1; + *gen_opparam_ptr++ = arg2; +} + +static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); +} + +static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); +} + +static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGArg arg3) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = arg3; +} + +static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGArg arg3) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = arg3; +} + +static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(val); + *gen_opparam_ptr++ = GET_TCGV_PTR(base); + *gen_opparam_ptr++ = offset; +} + +static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(val); + *gen_opparam_ptr++ = GET_TCGV_PTR(base); + *gen_opparam_ptr++ = offset; +} + +static inline void tcg_gen_qemu_ldst_op_i64_i32(TCGOpcode opc, TCGv_i64 val, + TCGv_i32 addr, TCGArg mem_index) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(val); + *gen_opparam_ptr++ = GET_TCGV_I32(addr); + *gen_opparam_ptr++ = mem_index; +} + +static inline void tcg_gen_qemu_ldst_op_i64_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_i64 addr, TCGArg mem_index) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(val); + *gen_opparam_ptr++ = GET_TCGV_I64(addr); + *gen_opparam_ptr++ = mem_index; +} + +static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGv_i32 arg4) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); +} + +static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGv_i64 arg4) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); +} + +static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGArg arg4) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = arg4; +} + +static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGArg arg4) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = arg4; +} + +static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGArg arg3, TCGArg arg4) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = arg3; + *gen_opparam_ptr++ = arg4; +} + +static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGArg arg3, TCGArg arg4) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = arg3; + *gen_opparam_ptr++ = arg4; +} + +static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); + *gen_opparam_ptr++ = GET_TCGV_I32(arg5); +} + +static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); + *gen_opparam_ptr++ = GET_TCGV_I64(arg5); +} + +static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5, + TCGv_i32 arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); + *gen_opparam_ptr++ = GET_TCGV_I32(arg5); + *gen_opparam_ptr++ = GET_TCGV_I32(arg6); +} + +static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5, + TCGv_i64 arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); + *gen_opparam_ptr++ = GET_TCGV_I64(arg5); + *gen_opparam_ptr++ = GET_TCGV_I64(arg6); +} + +static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGv_i32 arg4, + TCGv_i32 arg5, TCGArg arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); + *gen_opparam_ptr++ = GET_TCGV_I32(arg5); + *gen_opparam_ptr++ = arg6; +} + +static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGv_i64 arg4, + TCGv_i64 arg5, TCGArg arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); + *gen_opparam_ptr++ = GET_TCGV_I64(arg5); + *gen_opparam_ptr++ = arg6; +} + +static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGv_i32 arg4, TCGArg arg5, TCGArg arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); + *gen_opparam_ptr++ = arg5; + *gen_opparam_ptr++ = arg6; +} + +static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGv_i64 arg4, TCGArg arg5, TCGArg arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); + *gen_opparam_ptr++ = arg5; + *gen_opparam_ptr++ = arg6; +} + +static inline void gen_set_label(int n) +{ + tcg_gen_op1i(INDEX_op_set_label, n); +} + +static inline void tcg_gen_br(int label) +{ + tcg_gen_op1i(INDEX_op_br, label); +} + +static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (!TCGV_EQUAL_I32(ret, arg)) + tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); +} + +static inline void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) +{ + tcg_gen_op2i_i32(INDEX_op_movi_i32, ret, arg); +} + +/* A version of dh_sizemask from def-helper.h that doesn't rely on + preprocessor magic. */ +static inline int tcg_gen_sizemask(int n, int is_64bit, int is_signed) +{ + return (is_64bit << n*2) | (is_signed << (n*2 + 1)); +} + +/* helper calls */ +static inline void tcg_gen_helperN(void *func, int flags, int sizemask, + TCGArg ret, int nargs, TCGArg *args) +{ + TCGv_ptr fn; + fn = tcg_const_ptr((tcg_target_long)func); + tcg_gen_callN(&tcg_ctx, fn, flags, sizemask, ret, + nargs, args); + tcg_temp_free_ptr(fn); +} + +/* Note: Both tcg_gen_helper32() and tcg_gen_helper64() are currently + reserved for helpers in tcg-runtime.c. These helpers are all const + and pure, hence the call to tcg_gen_callN() with TCG_CALL_CONST | + TCG_CALL_PURE. This may need to be adjusted if these functions + start to be used with other helpers. */ +static inline void tcg_gen_helper32(void *func, int sizemask, TCGv_i32 ret, + TCGv_i32 a, TCGv_i32 b) +{ + TCGv_ptr fn; + TCGArg args[2]; + fn = tcg_const_ptr((tcg_target_long)func); + args[0] = GET_TCGV_I32(a); + args[1] = GET_TCGV_I32(b); + tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask, + GET_TCGV_I32(ret), 2, args); + tcg_temp_free_ptr(fn); +} + +static inline void tcg_gen_helper64(void *func, int sizemask, TCGv_i64 ret, + TCGv_i64 a, TCGv_i64 b) +{ + TCGv_ptr fn; + TCGArg args[2]; + fn = tcg_const_ptr((tcg_target_long)func); + args[0] = GET_TCGV_I64(a); + args[1] = GET_TCGV_I64(b); + tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask, + GET_TCGV_I64(ret), 2, args); + tcg_temp_free_ptr(fn); +} + +/* 32 bit ops */ + +static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); +} + +static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); +} + +static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); +} + +static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); +} + +static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); +} + +static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_add_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) +{ + TCGv_i32 t0 = tcg_const_i32(arg1); + tcg_gen_sub_i32(ret, t0, arg2); + tcg_temp_free_i32(t0); +} + +static inline void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_sub_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCGV_EQUAL_I32(arg1, arg2)) { + tcg_gen_mov_i32(ret, arg1); + } else { + tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); + } +} + +static inline void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_movi_i32(ret, 0); + } else if (arg2 == 0xffffffff) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_and_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCGV_EQUAL_I32(arg1, arg2)) { + tcg_gen_mov_i32(ret, arg1); + } else { + tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); + } +} + +static inline void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0xffffffff) { + tcg_gen_movi_i32(ret, 0xffffffff); + } else if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_or_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCGV_EQUAL_I32(arg1, arg2)) { + tcg_gen_movi_i32(ret, 0); + } else { + tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); + } +} + +static inline void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_xor_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_shl_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_shr_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_sar_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +static inline void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, + TCGv_i32 arg2, int label_index) +{ + tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_index); +} + +static inline void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, + int32_t arg2, int label_index) +{ + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_brcond_i32(cond, arg1, t0, label_index); + tcg_temp_free_i32(t0); +} + +static inline void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); +} + +static inline void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2) +{ + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_setcond_i32(cond, ret, arg1, t0); + tcg_temp_free_i32(t0); +} + +static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_mul_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +} + +#ifdef TCG_TARGET_HAS_div_i32 +static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); +} +#elif defined(TCG_TARGET_HAS_div2_i32) +static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t0, arg1, 31); + tcg_gen_op5_i32(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2); + tcg_temp_free_i32(t0); +} + +static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t0, arg1, 31); + tcg_gen_op5_i32(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2); + tcg_temp_free_i32(t0); +} + +static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, 0); + tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2); + tcg_temp_free_i32(t0); +} + +static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, 0); + tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); + tcg_temp_free_i32(t0); +} +#else +static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 0, 1); + sizemask |= tcg_gen_sizemask(1, 0, 1); + sizemask |= tcg_gen_sizemask(2, 0, 1); + + tcg_gen_helper32(tcg_helper_div_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 0, 1); + sizemask |= tcg_gen_sizemask(1, 0, 1); + sizemask |= tcg_gen_sizemask(2, 0, 1); + + tcg_gen_helper32(tcg_helper_rem_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 0, 0); + sizemask |= tcg_gen_sizemask(1, 0, 0); + sizemask |= tcg_gen_sizemask(2, 0, 0); + + tcg_gen_helper32(tcg_helper_divu_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 0, 0); + sizemask |= tcg_gen_sizemask(1, 0, 0); + sizemask |= tcg_gen_sizemask(2, 0, 0); + + tcg_gen_helper32(tcg_helper_remu_i32, sizemask, ret, arg1, arg2); +} +#endif + +#if TCG_TARGET_REG_BITS == 32 + +static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (!TCGV_EQUAL_I64(ret, arg)) { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + } +} + +static inline void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) +{ + tcg_gen_movi_i32(TCGV_LOW(ret), arg); + tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); +} + +static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), 31); +} + +static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, + tcg_target_long offset) +{ + /* since arg2 and ret have different types, they cannot be the + same temporary */ +#ifdef TCG_TARGET_WORDS_BIGENDIAN + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); +#else + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); +#endif +} + +static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); +} + +static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); +} + +static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); +} + +static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) +{ +#ifdef TCG_TARGET_WORDS_BIGENDIAN + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); +#else + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); +#endif +} + +static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op6_i32(INDEX_op_add2_i32, TCGV_LOW(ret), TCGV_HIGH(ret), + TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), + TCGV_HIGH(arg2)); +} + +static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op6_i32(INDEX_op_sub2_i32, TCGV_LOW(ret), TCGV_HIGH(ret), + TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), + TCGV_HIGH(arg2)); +} + +static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +} + +static inline void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_andi_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); + tcg_gen_andi_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); +} + +static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +} + +static inline void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_ori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); + tcg_gen_ori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); +} + +static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +} + +static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_xori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); + tcg_gen_xori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); +} + +/* XXX: use generic code when basic block handling is OK or CPU + specific code (x86) */ +static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_shl_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_shifti_i64(ret, arg1, arg2, 0, 0); +} + +static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_shr_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_shifti_i64(ret, arg1, arg2, 1, 0); +} + +static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_sar_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1); +} + +static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, + TCGv_i64 arg2, int label_index) +{ + tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, + TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), + TCGV_HIGH(arg2), cond, label_index); +} + +static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), + TCGV_LOW(arg1), TCGV_HIGH(arg1), + TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0; + TCGv_i32 t1; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i32(); + + tcg_gen_op4_i32(INDEX_op_mulu2_i32, TCGV_LOW(t0), TCGV_HIGH(t0), + TCGV_LOW(arg1), TCGV_LOW(arg2)); + + tcg_gen_mul_i32(t1, TCGV_LOW(arg1), TCGV_HIGH(arg2)); + tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1); + tcg_gen_mul_i32(t1, TCGV_HIGH(arg1), TCGV_LOW(arg2)); + tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1); + + tcg_gen_mov_i64(ret, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i32(t1); +} + +static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2); +} + +#else + +static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (!TCGV_EQUAL_I64(ret, arg)) + tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); +} + +static inline void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) +{ + tcg_gen_op2i_i64(INDEX_op_movi_i64, ret, arg); +} + +static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); +} + +static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_i64 arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); +} + +static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_i64 arg2, + tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_i64 arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); +} + +static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCGV_EQUAL_I64(arg1, arg2)) { + tcg_gen_mov_i64(ret, arg1); + } else { + tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); + } +} + +static inline void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_and_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCGV_EQUAL_I64(arg1, arg2)) { + tcg_gen_mov_i64(ret, arg1); + } else { + tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); + } +} + +static inline void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_or_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCGV_EQUAL_I64(arg1, arg2)) { + tcg_gen_movi_i64(ret, 0); + } else { + tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); + } +} + +static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_xor_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_shl_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_shr_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_sar_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, + TCGv_i64 arg2, int label_index) +{ + tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, label_index); +} + +static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); +} + +static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); +} + +#ifdef TCG_TARGET_HAS_div_i64 +static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); +} +#elif defined(TCG_TARGET_HAS_div2_i64) +static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t0, arg1, 63); + tcg_gen_op5_i64(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t0, arg1, 63); + tcg_gen_op5_i64(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t0, 0); + tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t0, 0); + tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); + tcg_temp_free_i64(t0); +} +#else +static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2); +} +#endif + +#endif + +static inline void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_add_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +static inline void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg1); + tcg_gen_sub_i64(ret, t0, arg2); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_sub_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} +static inline void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, + int64_t arg2, int label_index) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_brcond_i64(cond, arg1, t0, label_index); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_setcond_i64(cond, ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +static inline void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_mul_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +} + + +/***************************************/ +/* optional operations */ + +static inline void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_ext8s_i32 + tcg_gen_op2_i32(INDEX_op_ext8s_i32, ret, arg); +#else + tcg_gen_shli_i32(ret, arg, 24); + tcg_gen_sari_i32(ret, ret, 24); +#endif +} + +static inline void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_ext16s_i32 + tcg_gen_op2_i32(INDEX_op_ext16s_i32, ret, arg); +#else + tcg_gen_shli_i32(ret, arg, 16); + tcg_gen_sari_i32(ret, ret, 16); +#endif +} + +static inline void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_ext8u_i32 + tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg); +#else + tcg_gen_andi_i32(ret, arg, 0xffu); +#endif +} + +static inline void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_ext16u_i32 + tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg); +#else + tcg_gen_andi_i32(ret, arg, 0xffffu); +#endif +} + +/* Note: we assume the two high bytes are set to zero */ +static inline void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_bswap16_i32 + tcg_gen_op2_i32(INDEX_op_bswap16_i32, ret, arg); +#else + TCGv_i32 t0 = tcg_temp_new_i32(); + + tcg_gen_ext8u_i32(t0, arg); + tcg_gen_shli_i32(t0, t0, 8); + tcg_gen_shri_i32(ret, arg, 8); + tcg_gen_or_i32(ret, ret, t0); + tcg_temp_free_i32(t0); +#endif +} + +static inline void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_bswap32_i32 + tcg_gen_op2_i32(INDEX_op_bswap32_i32, ret, arg); +#else + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + + tcg_gen_shli_i32(t0, arg, 24); + + tcg_gen_andi_i32(t1, arg, 0x0000ff00); + tcg_gen_shli_i32(t1, t1, 8); + tcg_gen_or_i32(t0, t0, t1); + + tcg_gen_shri_i32(t1, arg, 8); + tcg_gen_andi_i32(t1, t1, 0x0000ff00); + tcg_gen_or_i32(t0, t0, t1); + + tcg_gen_shri_i32(t1, arg, 24); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +#endif +} + +#if TCG_TARGET_REG_BITS == 32 +static inline void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_ext8s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +static inline void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_ext16s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +static inline void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +static inline void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_ext8u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_ext16u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(ret, TCGV_LOW(arg)); +} + +static inline void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) +{ + tcg_gen_mov_i32(TCGV_LOW(ret), arg); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +static inline void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) +{ + tcg_gen_mov_i32(TCGV_LOW(ret), arg); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +/* Note: we assume the six high bytes are set to zero */ +static inline void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + tcg_gen_bswap16_i32(TCGV_LOW(ret), TCGV_LOW(arg)); +} + +/* Note: we assume the four high bytes are set to zero */ +static inline void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + tcg_gen_bswap32_i32(TCGV_LOW(ret), TCGV_LOW(arg)); +} + +static inline void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + + tcg_gen_bswap32_i32(t0, TCGV_LOW(arg)); + tcg_gen_bswap32_i32(t1, TCGV_HIGH(arg)); + tcg_gen_mov_i32(TCGV_LOW(ret), t1); + tcg_gen_mov_i32(TCGV_HIGH(ret), t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +} +#else + +static inline void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_ext8s_i64 + tcg_gen_op2_i64(INDEX_op_ext8s_i64, ret, arg); +#else + tcg_gen_shli_i64(ret, arg, 56); + tcg_gen_sari_i64(ret, ret, 56); +#endif +} + +static inline void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_ext16s_i64 + tcg_gen_op2_i64(INDEX_op_ext16s_i64, ret, arg); +#else + tcg_gen_shli_i64(ret, arg, 48); + tcg_gen_sari_i64(ret, ret, 48); +#endif +} + +static inline void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_ext32s_i64 + tcg_gen_op2_i64(INDEX_op_ext32s_i64, ret, arg); +#else + tcg_gen_shli_i64(ret, arg, 32); + tcg_gen_sari_i64(ret, ret, 32); +#endif +} + +static inline void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_ext8u_i64 + tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg); +#else + tcg_gen_andi_i64(ret, arg, 0xffu); +#endif +} + +static inline void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_ext16u_i64 + tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg); +#else + tcg_gen_andi_i64(ret, arg, 0xffffu); +#endif +} + +static inline void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_ext32u_i64 + tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg); +#else + tcg_gen_andi_i64(ret, arg, 0xffffffffu); +#endif +} + +/* Note: we assume the target supports move between 32 and 64 bit + registers. This will probably break MIPS64 targets. */ +static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(arg))); +} + +/* Note: we assume the target supports move between 32 and 64 bit + registers */ +static inline void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) +{ + tcg_gen_ext32u_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); +} + +/* Note: we assume the target supports move between 32 and 64 bit + registers */ +static inline void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) +{ + tcg_gen_ext32s_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); +} + +/* Note: we assume the six high bytes are set to zero */ +static inline void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_bswap16_i64 + tcg_gen_op2_i64(INDEX_op_bswap16_i64, ret, arg); +#else + TCGv_i64 t0 = tcg_temp_new_i64(); + + tcg_gen_ext8u_i64(t0, arg); + tcg_gen_shli_i64(t0, t0, 8); + tcg_gen_shri_i64(ret, arg, 8); + tcg_gen_or_i64(ret, ret, t0); + tcg_temp_free_i64(t0); +#endif +} + +/* Note: we assume the four high bytes are set to zero */ +static inline void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_bswap32_i64 + tcg_gen_op2_i64(INDEX_op_bswap32_i64, ret, arg); +#else + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t0, arg, 24); + tcg_gen_ext32u_i64(t0, t0); + + tcg_gen_andi_i64(t1, arg, 0x0000ff00); + tcg_gen_shli_i64(t1, t1, 8); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 8); + tcg_gen_andi_i64(t1, t1, 0x0000ff00); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 24); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +#endif +} + +static inline void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_bswap64_i64 + tcg_gen_op2_i64(INDEX_op_bswap64_i64, ret, arg); +#else + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t0, arg, 56); + + tcg_gen_andi_i64(t1, arg, 0x0000ff00); + tcg_gen_shli_i64(t1, t1, 40); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_andi_i64(t1, arg, 0x00ff0000); + tcg_gen_shli_i64(t1, t1, 24); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_andi_i64(t1, arg, 0xff000000); + tcg_gen_shli_i64(t1, t1, 8); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 8); + tcg_gen_andi_i64(t1, t1, 0xff000000); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 24); + tcg_gen_andi_i64(t1, t1, 0x00ff0000); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 40); + tcg_gen_andi_i64(t1, t1, 0x0000ff00); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 56); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +#endif +} + +#endif + +static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_neg_i32 + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); +#else + TCGv_i32 t0 = tcg_const_i32(0); + tcg_gen_sub_i32(ret, t0, arg); + tcg_temp_free_i32(t0); +#endif +} + +static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_neg_i64 + tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); +#else + TCGv_i64 t0 = tcg_const_i64(0); + tcg_gen_sub_i64(ret, t0, arg); + tcg_temp_free_i64(t0); +#endif +} + +static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) +{ +#ifdef TCG_TARGET_HAS_not_i32 + tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); +#else + tcg_gen_xori_i32(ret, arg, -1); +#endif +} + +static inline void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) +{ +#ifdef TCG_TARGET_HAS_not_i64 + tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); +#elif defined(TCG_TARGET_HAS_not_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); +#else + tcg_gen_xori_i64(ret, arg, -1); +#endif +} + +static inline void tcg_gen_discard_i32(TCGv_i32 arg) +{ + tcg_gen_op1_i32(INDEX_op_discard, arg); +} + +#if TCG_TARGET_REG_BITS == 32 +static inline void tcg_gen_discard_i64(TCGv_i64 arg) +{ + tcg_gen_discard_i32(TCGV_LOW(arg)); + tcg_gen_discard_i32(TCGV_HIGH(arg)); +} +#else +static inline void tcg_gen_discard_i64(TCGv_i64 arg) +{ + tcg_gen_op1_i64(INDEX_op_discard, arg); +} +#endif + +static inline void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high) +{ +#if TCG_TARGET_REG_BITS == 32 + tcg_gen_mov_i32(TCGV_LOW(dest), low); + tcg_gen_mov_i32(TCGV_HIGH(dest), high); +#else + TCGv_i64 tmp = tcg_temp_new_i64(); + /* This extension is only needed for type correctness. + We may be able to do better given target specific information. */ + tcg_gen_extu_i32_i64(tmp, high); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_extu_i32_i64(dest, low); + tcg_gen_or_i64(dest, dest, tmp); + tcg_temp_free_i64(tmp); +#endif +} + +static inline void tcg_gen_concat32_i64(TCGv_i64 dest, TCGv_i64 low, TCGv_i64 high) +{ +#if TCG_TARGET_REG_BITS == 32 + tcg_gen_concat_i32_i64(dest, TCGV_LOW(low), TCGV_LOW(high)); +#else + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(dest, low); + tcg_gen_shli_i64(tmp, high, 32); + tcg_gen_or_i64(dest, dest, tmp); + tcg_temp_free_i64(tmp); +#endif +} + +static inline void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_andc_i32 + tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); +#else + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_not_i32(t0, arg2); + tcg_gen_and_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +#endif +} + +static inline void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_andc_i64 + tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_andc_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, arg2); + tcg_gen_and_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +#endif +} + +static inline void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_eqv_i32 + tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); +#else + tcg_gen_xor_i32(ret, arg1, arg2); + tcg_gen_not_i32(ret, ret); +#endif +} + +static inline void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_eqv_i64 + tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_eqv_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else + tcg_gen_xor_i64(ret, arg1, arg2); + tcg_gen_not_i64(ret, ret); +#endif +} + +static inline void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_nand_i32 + tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); +#else + tcg_gen_and_i32(ret, arg1, arg2); + tcg_gen_not_i32(ret, ret); +#endif +} + +static inline void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_nand_i64 + tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_nand_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else + tcg_gen_and_i64(ret, arg1, arg2); + tcg_gen_not_i64(ret, ret); +#endif +} + +static inline void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_nor_i32 + tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); +#else + tcg_gen_or_i32(ret, arg1, arg2); + tcg_gen_not_i32(ret, ret); +#endif +} + +static inline void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_nor_i64 + tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_nor_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else + tcg_gen_or_i64(ret, arg1, arg2); + tcg_gen_not_i64(ret, ret); +#endif +} + +static inline void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_orc_i32 + tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); +#else + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_not_i32(t0, arg2); + tcg_gen_or_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +#endif +} + +static inline void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_orc_i64 + tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_orc_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, arg2); + tcg_gen_or_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +#endif +} + +static inline void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_rot_i32 + tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2); +#else + TCGv_i32 t0, t1; + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shl_i32(t0, arg1, arg2); + tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_shr_i32(t1, arg1, t1); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +#endif +} + +static inline void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_rot_i64 + tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); +#else + TCGv_i64 t0, t1; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_shl_i64(t0, arg1, arg2); + tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_shr_i64(t1, arg1, t1); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +#endif +} + +static inline void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { +#ifdef TCG_TARGET_HAS_rot_i32 + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_rotl_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +#else + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shli_i32(t0, arg1, arg2); + tcg_gen_shri_i32(t1, arg1, 32 - arg2); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +#endif + } +} + +static inline void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { +#ifdef TCG_TARGET_HAS_rot_i64 + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_rotl_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +#else + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_shli_i64(t0, arg1, arg2); + tcg_gen_shri_i64(t1, arg1, 64 - arg2); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +#endif + } +} + +static inline void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ +#ifdef TCG_TARGET_HAS_rot_i32 + tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2); +#else + TCGv_i32 t0, t1; + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shr_i32(t0, arg1, arg2); + tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_shl_i32(t1, arg1, t1); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +#endif +} + +static inline void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ +#ifdef TCG_TARGET_HAS_rot_i64 + tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); +#else + TCGv_i64 t0, t1; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_shr_i64(t0, arg1, arg2); + tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_shl_i64(t1, arg1, t1); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +#endif +} + +static inline void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + tcg_gen_rotli_i32(ret, arg1, 32 - arg2); + } +} + +static inline void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + tcg_gen_rotli_i64(ret, arg1, 64 - arg2); + } +} + +static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, + TCGv_i32 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i32 + tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); +#else + uint32_t mask = (1u << len) - 1; + TCGv_i32 t1 = tcg_temp_new_i32 (); + + tcg_gen_andi_i32(t1, arg2, mask); + tcg_gen_shli_i32(t1, t1, ofs); + tcg_gen_andi_i32(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i32(ret, ret, t1); + + tcg_temp_free_i32(t1); +#endif +} + +static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, + TCGv_i64 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i64 + tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); +#else + uint64_t mask = (1ull << len) - 1; + TCGv_i64 t1 = tcg_temp_new_i64 (); + + tcg_gen_andi_i64(t1, arg2, mask); + tcg_gen_shli_i64(t1, t1, ofs); + tcg_gen_andi_i64(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i64(ret, ret, t1); + + tcg_temp_free_i64(t1); +#endif +} + +/***************************************/ +/* QEMU specific operations. Their type depend on the QEMU CPU + type. */ +#ifndef TARGET_LONG_BITS +#error must include QEMU headers +#endif + +#if TARGET_LONG_BITS == 32 +#define TCGv TCGv_i32 +#define tcg_temp_new() tcg_temp_new_i32() +#define tcg_global_reg_new tcg_global_reg_new_i32 +#define tcg_global_mem_new tcg_global_mem_new_i32 +#define tcg_temp_local_new() tcg_temp_local_new_i32() +#define tcg_temp_free tcg_temp_free_i32 +#define tcg_gen_qemu_ldst_op tcg_gen_op3i_i32 +#define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i32 +#define TCGV_UNUSED(x) TCGV_UNUSED_I32(x) +#define TCGV_EQUAL(a, b) TCGV_EQUAL_I32(a, b) +#else +#define TCGv TCGv_i64 +#define tcg_temp_new() tcg_temp_new_i64() +#define tcg_global_reg_new tcg_global_reg_new_i64 +#define tcg_global_mem_new tcg_global_mem_new_i64 +#define tcg_temp_local_new() tcg_temp_local_new_i64() +#define tcg_temp_free tcg_temp_free_i64 +#define tcg_gen_qemu_ldst_op tcg_gen_op3i_i64 +#define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i64 +#define TCGV_UNUSED(x) TCGV_UNUSED_I64(x) +#define TCGV_EQUAL(a, b) TCGV_EQUAL_I64(a, b) +#endif + +/* debug info: write the PC of the corresponding QEMU CPU instruction */ +static inline void tcg_gen_debug_insn_start(uint64_t pc) +{ + /* XXX: must really use a 32 bit size for TCGArg in all cases */ +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS + tcg_gen_op2ii(INDEX_op_debug_insn_start, + (uint32_t)(pc), (uint32_t)(pc >> 32)); +#else + tcg_gen_op1i(INDEX_op_debug_insn_start, pc); +#endif +} + +static inline void tcg_gen_exit_tb(tcg_target_long val) +{ + tcg_gen_op1i(INDEX_op_exit_tb, val); +} + +static inline void tcg_gen_goto_tb(int idx) +{ + tcg_gen_op1i(INDEX_op_goto_tb, idx); +} + +#if TCG_TARGET_REG_BITS == 32 +static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_ld8u, ret, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_ld8u, TCGV_LOW(ret), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +#endif +} + +static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_ld8s, ret, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_ld8s, TCGV_LOW(ret), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +#endif +} + +static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_ld16u, ret, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_ld16u, TCGV_LOW(ret), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +#endif +} + +static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_ld16s, ret, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_ld16s, TCGV_LOW(ret), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +#endif +} + +static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +#endif +} + +static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +#endif +} + +static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op4i_i32(INDEX_op_qemu_ld64, TCGV_LOW(ret), TCGV_HIGH(ret), addr, mem_index); +#else + tcg_gen_op5i_i32(INDEX_op_qemu_ld64, TCGV_LOW(ret), TCGV_HIGH(ret), + TCGV_LOW(addr), TCGV_HIGH(addr), mem_index); +#endif +} + +static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_st8, arg, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_st8, TCGV_LOW(arg), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); +#endif +} + +static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_st16, arg, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_st16, TCGV_LOW(arg), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); +#endif +} + +static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op3i_i32(INDEX_op_qemu_st32, arg, addr, mem_index); +#else + tcg_gen_op4i_i32(INDEX_op_qemu_st32, TCGV_LOW(arg), TCGV_LOW(addr), + TCGV_HIGH(addr), mem_index); +#endif +} + +static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op4i_i32(INDEX_op_qemu_st64, TCGV_LOW(arg), TCGV_HIGH(arg), addr, + mem_index); +#else + tcg_gen_op5i_i32(INDEX_op_qemu_st64, TCGV_LOW(arg), TCGV_HIGH(arg), + TCGV_LOW(addr), TCGV_HIGH(addr), mem_index); +#endif +} + +#define tcg_gen_ld_ptr tcg_gen_ld_i32 +#define tcg_gen_discard_ptr tcg_gen_discard_i32 + +#else /* TCG_TARGET_REG_BITS == 32 */ + +static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld8u, ret, addr, mem_index); +} + +static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld8s, ret, addr, mem_index); +} + +static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld16u, ret, addr, mem_index); +} + +static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld16s, ret, addr, mem_index); +} + +static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32u, ret, addr, mem_index); +#endif +} + +static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32s, ret, addr, mem_index); +#endif +} + +static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_ld64, ret, addr, mem_index); +} + +static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_st8, arg, addr, mem_index); +} + +static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_st16, arg, addr, mem_index); +} + +static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op(INDEX_op_qemu_st32, arg, addr, mem_index); +} + +static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) +{ + tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_st64, arg, addr, mem_index); +} + +#define tcg_gen_ld_ptr tcg_gen_ld_i64 +#define tcg_gen_discard_ptr tcg_gen_discard_i64 + +#endif /* TCG_TARGET_REG_BITS != 32 */ + +#if TARGET_LONG_BITS == 64 +#define tcg_gen_movi_tl tcg_gen_movi_i64 +#define tcg_gen_mov_tl tcg_gen_mov_i64 +#define tcg_gen_ld8u_tl tcg_gen_ld8u_i64 +#define tcg_gen_ld8s_tl tcg_gen_ld8s_i64 +#define tcg_gen_ld16u_tl tcg_gen_ld16u_i64 +#define tcg_gen_ld16s_tl tcg_gen_ld16s_i64 +#define tcg_gen_ld32u_tl tcg_gen_ld32u_i64 +#define tcg_gen_ld32s_tl tcg_gen_ld32s_i64 +#define tcg_gen_ld_tl tcg_gen_ld_i64 +#define tcg_gen_st8_tl tcg_gen_st8_i64 +#define tcg_gen_st16_tl tcg_gen_st16_i64 +#define tcg_gen_st32_tl tcg_gen_st32_i64 +#define tcg_gen_st_tl tcg_gen_st_i64 +#define tcg_gen_add_tl tcg_gen_add_i64 +#define tcg_gen_addi_tl tcg_gen_addi_i64 +#define tcg_gen_sub_tl tcg_gen_sub_i64 +#define tcg_gen_neg_tl tcg_gen_neg_i64 +#define tcg_gen_subfi_tl tcg_gen_subfi_i64 +#define tcg_gen_subi_tl tcg_gen_subi_i64 +#define tcg_gen_and_tl tcg_gen_and_i64 +#define tcg_gen_andi_tl tcg_gen_andi_i64 +#define tcg_gen_or_tl tcg_gen_or_i64 +#define tcg_gen_ori_tl tcg_gen_ori_i64 +#define tcg_gen_xor_tl tcg_gen_xor_i64 +#define tcg_gen_xori_tl tcg_gen_xori_i64 +#define tcg_gen_not_tl tcg_gen_not_i64 +#define tcg_gen_shl_tl tcg_gen_shl_i64 +#define tcg_gen_shli_tl tcg_gen_shli_i64 +#define tcg_gen_shr_tl tcg_gen_shr_i64 +#define tcg_gen_shri_tl tcg_gen_shri_i64 +#define tcg_gen_sar_tl tcg_gen_sar_i64 +#define tcg_gen_sari_tl tcg_gen_sari_i64 +#define tcg_gen_brcond_tl tcg_gen_brcond_i64 +#define tcg_gen_brcondi_tl tcg_gen_brcondi_i64 +#define tcg_gen_setcond_tl tcg_gen_setcond_i64 +#define tcg_gen_setcondi_tl tcg_gen_setcondi_i64 +#define tcg_gen_mul_tl tcg_gen_mul_i64 +#define tcg_gen_muli_tl tcg_gen_muli_i64 +#define tcg_gen_div_tl tcg_gen_div_i64 +#define tcg_gen_rem_tl tcg_gen_rem_i64 +#define tcg_gen_divu_tl tcg_gen_divu_i64 +#define tcg_gen_remu_tl tcg_gen_remu_i64 +#define tcg_gen_discard_tl tcg_gen_discard_i64 +#define tcg_gen_trunc_tl_i32 tcg_gen_trunc_i64_i32 +#define tcg_gen_trunc_i64_tl tcg_gen_mov_i64 +#define tcg_gen_extu_i32_tl tcg_gen_extu_i32_i64 +#define tcg_gen_ext_i32_tl tcg_gen_ext_i32_i64 +#define tcg_gen_extu_tl_i64 tcg_gen_mov_i64 +#define tcg_gen_ext_tl_i64 tcg_gen_mov_i64 +#define tcg_gen_ext8u_tl tcg_gen_ext8u_i64 +#define tcg_gen_ext8s_tl tcg_gen_ext8s_i64 +#define tcg_gen_ext16u_tl tcg_gen_ext16u_i64 +#define tcg_gen_ext16s_tl tcg_gen_ext16s_i64 +#define tcg_gen_ext32u_tl tcg_gen_ext32u_i64 +#define tcg_gen_ext32s_tl tcg_gen_ext32s_i64 +#define tcg_gen_bswap16_tl tcg_gen_bswap16_i64 +#define tcg_gen_bswap32_tl tcg_gen_bswap32_i64 +#define tcg_gen_bswap64_tl tcg_gen_bswap64_i64 +#define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64 +#define tcg_gen_andc_tl tcg_gen_andc_i64 +#define tcg_gen_eqv_tl tcg_gen_eqv_i64 +#define tcg_gen_nand_tl tcg_gen_nand_i64 +#define tcg_gen_nor_tl tcg_gen_nor_i64 +#define tcg_gen_orc_tl tcg_gen_orc_i64 +#define tcg_gen_rotl_tl tcg_gen_rotl_i64 +#define tcg_gen_rotli_tl tcg_gen_rotli_i64 +#define tcg_gen_rotr_tl tcg_gen_rotr_i64 +#define tcg_gen_rotri_tl tcg_gen_rotri_i64 +#define tcg_gen_deposit_tl tcg_gen_deposit_i64 +#define tcg_const_tl tcg_const_i64 +#define tcg_const_local_tl tcg_const_local_i64 +#else +#define tcg_gen_movi_tl tcg_gen_movi_i32 +#define tcg_gen_mov_tl tcg_gen_mov_i32 +#define tcg_gen_ld8u_tl tcg_gen_ld8u_i32 +#define tcg_gen_ld8s_tl tcg_gen_ld8s_i32 +#define tcg_gen_ld16u_tl tcg_gen_ld16u_i32 +#define tcg_gen_ld16s_tl tcg_gen_ld16s_i32 +#define tcg_gen_ld32u_tl tcg_gen_ld_i32 +#define tcg_gen_ld32s_tl tcg_gen_ld_i32 +#define tcg_gen_ld_tl tcg_gen_ld_i32 +#define tcg_gen_st8_tl tcg_gen_st8_i32 +#define tcg_gen_st16_tl tcg_gen_st16_i32 +#define tcg_gen_st32_tl tcg_gen_st_i32 +#define tcg_gen_st_tl tcg_gen_st_i32 +#define tcg_gen_add_tl tcg_gen_add_i32 +#define tcg_gen_addi_tl tcg_gen_addi_i32 +#define tcg_gen_sub_tl tcg_gen_sub_i32 +#define tcg_gen_neg_tl tcg_gen_neg_i32 +#define tcg_gen_subfi_tl tcg_gen_subfi_i32 +#define tcg_gen_subi_tl tcg_gen_subi_i32 +#define tcg_gen_and_tl tcg_gen_and_i32 +#define tcg_gen_andi_tl tcg_gen_andi_i32 +#define tcg_gen_or_tl tcg_gen_or_i32 +#define tcg_gen_ori_tl tcg_gen_ori_i32 +#define tcg_gen_xor_tl tcg_gen_xor_i32 +#define tcg_gen_xori_tl tcg_gen_xori_i32 +#define tcg_gen_not_tl tcg_gen_not_i32 +#define tcg_gen_shl_tl tcg_gen_shl_i32 +#define tcg_gen_shli_tl tcg_gen_shli_i32 +#define tcg_gen_shr_tl tcg_gen_shr_i32 +#define tcg_gen_shri_tl tcg_gen_shri_i32 +#define tcg_gen_sar_tl tcg_gen_sar_i32 +#define tcg_gen_sari_tl tcg_gen_sari_i32 +#define tcg_gen_brcond_tl tcg_gen_brcond_i32 +#define tcg_gen_brcondi_tl tcg_gen_brcondi_i32 +#define tcg_gen_setcond_tl tcg_gen_setcond_i32 +#define tcg_gen_setcondi_tl tcg_gen_setcondi_i32 +#define tcg_gen_mul_tl tcg_gen_mul_i32 +#define tcg_gen_muli_tl tcg_gen_muli_i32 +#define tcg_gen_div_tl tcg_gen_div_i32 +#define tcg_gen_rem_tl tcg_gen_rem_i32 +#define tcg_gen_divu_tl tcg_gen_divu_i32 +#define tcg_gen_remu_tl tcg_gen_remu_i32 +#define tcg_gen_discard_tl tcg_gen_discard_i32 +#define tcg_gen_trunc_tl_i32 tcg_gen_mov_i32 +#define tcg_gen_trunc_i64_tl tcg_gen_trunc_i64_i32 +#define tcg_gen_extu_i32_tl tcg_gen_mov_i32 +#define tcg_gen_ext_i32_tl tcg_gen_mov_i32 +#define tcg_gen_extu_tl_i64 tcg_gen_extu_i32_i64 +#define tcg_gen_ext_tl_i64 tcg_gen_ext_i32_i64 +#define tcg_gen_ext8u_tl tcg_gen_ext8u_i32 +#define tcg_gen_ext8s_tl tcg_gen_ext8s_i32 +#define tcg_gen_ext16u_tl tcg_gen_ext16u_i32 +#define tcg_gen_ext16s_tl tcg_gen_ext16s_i32 +#define tcg_gen_ext32u_tl tcg_gen_mov_i32 +#define tcg_gen_ext32s_tl tcg_gen_mov_i32 +#define tcg_gen_bswap16_tl tcg_gen_bswap16_i32 +#define tcg_gen_bswap32_tl tcg_gen_bswap32_i32 +#define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64 +#define tcg_gen_andc_tl tcg_gen_andc_i32 +#define tcg_gen_eqv_tl tcg_gen_eqv_i32 +#define tcg_gen_nand_tl tcg_gen_nand_i32 +#define tcg_gen_nor_tl tcg_gen_nor_i32 +#define tcg_gen_orc_tl tcg_gen_orc_i32 +#define tcg_gen_rotl_tl tcg_gen_rotl_i32 +#define tcg_gen_rotli_tl tcg_gen_rotli_i32 +#define tcg_gen_rotr_tl tcg_gen_rotr_i32 +#define tcg_gen_rotri_tl tcg_gen_rotri_i32 +#define tcg_gen_deposit_tl tcg_gen_deposit_i32 +#define tcg_const_tl tcg_const_i32 +#define tcg_const_local_tl tcg_const_local_i32 +#endif + +#if TCG_TARGET_REG_BITS == 32 +#define tcg_gen_add_ptr tcg_gen_add_i32 +#define tcg_gen_addi_ptr tcg_gen_addi_i32 +#define tcg_gen_ext_i32_ptr tcg_gen_mov_i32 +#else /* TCG_TARGET_REG_BITS == 32 */ +#define tcg_gen_add_ptr tcg_gen_add_i64 +#define tcg_gen_addi_ptr tcg_gen_addi_i64 +#define tcg_gen_ext_i32_ptr tcg_gen_ext_i32_i64 +#endif /* TCG_TARGET_REG_BITS != 32 */ diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h new file mode 100644 index 0000000..2c7ca1a --- /dev/null +++ b/tcg/tcg-opc.h @@ -0,0 +1,310 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * DEF(name, oargs, iargs, cargs, flags) + */ + +/* predefined ops */ +DEF(end, 0, 0, 0, 0) /* must be kept first */ +DEF(nop, 0, 0, 0, 0) +DEF(nop1, 0, 0, 1, 0) +DEF(nop2, 0, 0, 2, 0) +DEF(nop3, 0, 0, 3, 0) +DEF(nopn, 0, 0, 1, 0) /* variable number of parameters */ + +DEF(discard, 1, 0, 0, 0) + +DEF(set_label, 0, 0, 1, 0) +DEF(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */ +DEF(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) + +DEF(mov_i32, 1, 1, 0, 0) +DEF(movi_i32, 1, 0, 1, 0) +DEF(setcond_i32, 1, 2, 1, 0) +/* load/store */ +DEF(ld8u_i32, 1, 1, 1, 0) +DEF(ld8s_i32, 1, 1, 1, 0) +DEF(ld16u_i32, 1, 1, 1, 0) +DEF(ld16s_i32, 1, 1, 1, 0) +DEF(ld_i32, 1, 1, 1, 0) +DEF(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +/* arith */ +DEF(add_i32, 1, 2, 0, 0) +DEF(sub_i32, 1, 2, 0, 0) +DEF(mul_i32, 1, 2, 0, 0) +#ifdef TCG_TARGET_HAS_div_i32 +DEF(div_i32, 1, 2, 0, 0) +DEF(divu_i32, 1, 2, 0, 0) +DEF(rem_i32, 1, 2, 0, 0) +DEF(remu_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_div2_i32 +DEF(div2_i32, 2, 3, 0, 0) +DEF(divu2_i32, 2, 3, 0, 0) +#endif +DEF(and_i32, 1, 2, 0, 0) +DEF(or_i32, 1, 2, 0, 0) +DEF(xor_i32, 1, 2, 0, 0) +/* shifts/rotates */ +DEF(shl_i32, 1, 2, 0, 0) +DEF(shr_i32, 1, 2, 0, 0) +DEF(sar_i32, 1, 2, 0, 0) +#ifdef TCG_TARGET_HAS_rot_i32 +DEF(rotl_i32, 1, 2, 0, 0) +DEF(rotr_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_deposit_i32 +DEF(deposit_i32, 1, 2, 2, 0) +#endif + +DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +#if TCG_TARGET_REG_BITS == 32 +DEF(add2_i32, 2, 4, 0, 0) +DEF(sub2_i32, 2, 4, 0, 0) +DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(mulu2_i32, 2, 2, 0, 0) +DEF(setcond2_i32, 1, 4, 1, 0) +#endif +#ifdef TCG_TARGET_HAS_ext8s_i32 +DEF(ext8s_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext16s_i32 +DEF(ext16s_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext8u_i32 +DEF(ext8u_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext16u_i32 +DEF(ext16u_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_bswap16_i32 +DEF(bswap16_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_bswap32_i32 +DEF(bswap32_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_not_i32 +DEF(not_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_neg_i32 +DEF(neg_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_andc_i32 +DEF(andc_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_orc_i32 +DEF(orc_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_eqv_i32 +DEF(eqv_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nand_i32 +DEF(nand_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nor_i32 +DEF(nor_i32, 1, 2, 0, 0) +#endif + +#if TCG_TARGET_REG_BITS == 64 +DEF(mov_i64, 1, 1, 0, 0) +DEF(movi_i64, 1, 0, 1, 0) +DEF(setcond_i64, 1, 2, 1, 0) +/* load/store */ +DEF(ld8u_i64, 1, 1, 1, 0) +DEF(ld8s_i64, 1, 1, 1, 0) +DEF(ld16u_i64, 1, 1, 1, 0) +DEF(ld16s_i64, 1, 1, 1, 0) +DEF(ld32u_i64, 1, 1, 1, 0) +DEF(ld32s_i64, 1, 1, 1, 0) +DEF(ld_i64, 1, 1, 1, 0) +DEF(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +/* arith */ +DEF(add_i64, 1, 2, 0, 0) +DEF(sub_i64, 1, 2, 0, 0) +DEF(mul_i64, 1, 2, 0, 0) +#ifdef TCG_TARGET_HAS_div_i64 +DEF(div_i64, 1, 2, 0, 0) +DEF(divu_i64, 1, 2, 0, 0) +DEF(rem_i64, 1, 2, 0, 0) +DEF(remu_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_div2_i64 +DEF(div2_i64, 2, 3, 0, 0) +DEF(divu2_i64, 2, 3, 0, 0) +#endif +DEF(and_i64, 1, 2, 0, 0) +DEF(or_i64, 1, 2, 0, 0) +DEF(xor_i64, 1, 2, 0, 0) +/* shifts/rotates */ +DEF(shl_i64, 1, 2, 0, 0) +DEF(shr_i64, 1, 2, 0, 0) +DEF(sar_i64, 1, 2, 0, 0) +#ifdef TCG_TARGET_HAS_rot_i64 +DEF(rotl_i64, 1, 2, 0, 0) +DEF(rotr_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_deposit_i64 +DEF(deposit_i64, 1, 2, 2, 0) +#endif + +DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +#ifdef TCG_TARGET_HAS_ext8s_i64 +DEF(ext8s_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext16s_i64 +DEF(ext16s_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext32s_i64 +DEF(ext32s_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext8u_i64 +DEF(ext8u_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext16u_i64 +DEF(ext16u_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_ext32u_i64 +DEF(ext32u_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_bswap16_i64 +DEF(bswap16_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_bswap32_i64 +DEF(bswap32_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_bswap64_i64 +DEF(bswap64_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_not_i64 +DEF(not_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_neg_i64 +DEF(neg_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_andc_i64 +DEF(andc_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_orc_i64 +DEF(orc_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_eqv_i64 +DEF(eqv_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nand_i64 +DEF(nand_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nor_i64 +DEF(nor_i64, 1, 2, 0, 0) +#endif +#endif + +/* QEMU specific */ +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS +DEF(debug_insn_start, 0, 0, 2, 0) +#else +DEF(debug_insn_start, 0, 0, 1, 0) +#endif +DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +/* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op + constants must be defined */ +#if TCG_TARGET_REG_BITS == 32 +#if TARGET_LONG_BITS == 32 +DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_ld32, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif + +#if TARGET_LONG_BITS == 32 +DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif +#if TARGET_LONG_BITS == 32 +DEF(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#else +DEF(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +#endif + +#else /* TCG_TARGET_REG_BITS == 32 */ + +DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) + +DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) + +#endif /* TCG_TARGET_REG_BITS != 32 */ + +#undef DEF diff --git a/tcg/tcg-runtime.h b/tcg/tcg-runtime.h new file mode 100644 index 0000000..5615b13 --- /dev/null +++ b/tcg/tcg-runtime.h @@ -0,0 +1,18 @@ +#ifndef TCG_RUNTIME_H +#define TCG_RUNTIME_H + +/* tcg-runtime.c */ +int32_t tcg_helper_div_i32(int32_t arg1, int32_t arg2); +int32_t tcg_helper_rem_i32(int32_t arg1, int32_t arg2); +uint32_t tcg_helper_divu_i32(uint32_t arg1, uint32_t arg2); +uint32_t tcg_helper_remu_i32(uint32_t arg1, uint32_t arg2); + +int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2); +int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2); +int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2); +int64_t tcg_helper_div_i64(int64_t arg1, int64_t arg2); +int64_t tcg_helper_rem_i64(int64_t arg1, int64_t arg2); +uint64_t tcg_helper_divu_i64(uint64_t arg1, uint64_t arg2); +uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2); + +#endif diff --git a/tcg/tcg.c b/tcg/tcg.c new file mode 100644 index 0000000..5dd6a2c --- /dev/null +++ b/tcg/tcg.c @@ -0,0 +1,2175 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* define it to use liveness analysis (better code) */ +#define USE_LIVENESS_ANALYSIS + +#include "config.h" + +#if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) +/* define it to suppress various consistency checks (faster) */ +#define NDEBUG +#endif + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#ifdef _WIN32 +#include <malloc.h> +#endif +#ifdef _AIX +#include <alloca.h> +#endif + +#include "qemu-common.h" +#include "cache-utils.h" +#include "host-utils.h" +#include "qemu-timer.h" + +/* Note: the long term plan is to reduce the dependancies on the QEMU + CPU definitions. Currently they are used for qemu_ld/st + instructions */ +#define NO_CPU_IO_DEFS +#include "cpu.h" +#include "exec-all.h" + +#include "tcg-op.h" +#include "elf.h" + +#if defined(CONFIG_USE_GUEST_BASE) && !defined(TCG_TARGET_HAS_GUEST_BASE) +#error GUEST_BASE not supported on this host. +#endif + +static void tcg_target_init(TCGContext *s); +static void tcg_target_qemu_prologue(TCGContext *s); +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend); + +static TCGOpDef tcg_op_defs[] = { +#define DEF(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, +#include "tcg-opc.h" +#undef DEF +}; + +static TCGRegSet tcg_target_available_regs[2]; +static TCGRegSet tcg_target_call_clobber_regs; + +/* XXX: move that inside the context */ +uint16_t *gen_opc_ptr; +TCGArg *gen_opparam_ptr; + +static inline void tcg_out8(TCGContext *s, uint8_t v) +{ + *s->code_ptr++ = v; +} + +static inline void tcg_out16(TCGContext *s, uint16_t v) +{ + *(uint16_t *)s->code_ptr = v; + s->code_ptr += 2; +} + +static inline void tcg_out32(TCGContext *s, uint32_t v) +{ + *(uint32_t *)s->code_ptr = v; + s->code_ptr += 4; +} + +/* label relocation processing */ + +static void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, + int label_index, long addend) +{ + TCGLabel *l; + TCGRelocation *r; + + l = &s->labels[label_index]; + if (l->has_value) { + /* FIXME: This may break relocations on RISC targets that + modify instruction fields in place. The caller may not have + written the initial value. */ + patch_reloc(code_ptr, type, l->u.value, addend); + } else { + /* add a new relocation entry */ + r = tcg_malloc(sizeof(TCGRelocation)); + r->type = type; + r->ptr = code_ptr; + r->addend = addend; + r->next = l->u.first_reloc; + l->u.first_reloc = r; + } +} + +static void tcg_out_label(TCGContext *s, int label_index, + tcg_target_long value) +{ + TCGLabel *l; + TCGRelocation *r; + + l = &s->labels[label_index]; + if (l->has_value) + tcg_abort(); + r = l->u.first_reloc; + while (r != NULL) { + patch_reloc(r->ptr, r->type, value, r->addend); + r = r->next; + } + l->has_value = 1; + l->u.value = value; +} + +int gen_new_label(void) +{ + TCGContext *s = &tcg_ctx; + int idx; + TCGLabel *l; + + if (s->nb_labels >= TCG_MAX_LABELS) + tcg_abort(); + idx = s->nb_labels++; + l = &s->labels[idx]; + l->has_value = 0; + l->u.first_reloc = NULL; + return idx; +} + +#include "tcg-target.c" + +/* pool based memory allocation */ +void *tcg_malloc_internal(TCGContext *s, int size) +{ + TCGPool *p; + int pool_size; + + if (size > TCG_POOL_CHUNK_SIZE) { + /* big malloc: insert a new pool (XXX: could optimize) */ + p = qemu_malloc(sizeof(TCGPool) + size); + p->size = size; + if (s->pool_current) + s->pool_current->next = p; + else + s->pool_first = p; + p->next = s->pool_current; + } else { + p = s->pool_current; + if (!p) { + p = s->pool_first; + if (!p) + goto new_pool; + } else { + if (!p->next) { + new_pool: + pool_size = TCG_POOL_CHUNK_SIZE; + p = qemu_malloc(sizeof(TCGPool) + pool_size); + p->size = pool_size; + p->next = NULL; + if (s->pool_current) + s->pool_current->next = p; + else + s->pool_first = p; + } else { + p = p->next; + } + } + } + s->pool_current = p; + s->pool_cur = p->data + size; + s->pool_end = p->data + p->size; + return p->data; +} + +void tcg_pool_reset(TCGContext *s) +{ + s->pool_cur = s->pool_end = NULL; + s->pool_current = NULL; +} + +void tcg_context_init(TCGContext *s) +{ + int op, total_args, n; + TCGOpDef *def; + TCGArgConstraint *args_ct; + int *sorted_args; + + memset(s, 0, sizeof(*s)); + s->temps = s->static_temps; + s->nb_globals = 0; + + /* Count total number of arguments and allocate the corresponding + space */ + total_args = 0; + for(op = 0; op < NB_OPS; op++) { + def = &tcg_op_defs[op]; + n = def->nb_iargs + def->nb_oargs; + total_args += n; + } + + args_ct = qemu_malloc(sizeof(TCGArgConstraint) * total_args); + sorted_args = qemu_malloc(sizeof(int) * total_args); + + for(op = 0; op < NB_OPS; op++) { + def = &tcg_op_defs[op]; + def->args_ct = args_ct; + def->sorted_args = sorted_args; + n = def->nb_iargs + def->nb_oargs; + sorted_args += n; + args_ct += n; + } + + tcg_target_init(s); +} + +void tcg_prologue_init(TCGContext *s) +{ + /* init global prologue and epilogue */ + s->code_buf = code_gen_prologue; + s->code_ptr = s->code_buf; + tcg_target_qemu_prologue(s); + flush_icache_range((unsigned long)s->code_buf, + (unsigned long)s->code_ptr); +} + +void tcg_set_frame(TCGContext *s, int reg, + tcg_target_long start, tcg_target_long size) +{ + s->frame_start = start; + s->frame_end = start + size; + s->frame_reg = reg; +} + +void tcg_func_start(TCGContext *s) +{ + int i; + tcg_pool_reset(s); + s->nb_temps = s->nb_globals; + for(i = 0; i < (TCG_TYPE_COUNT * 2); i++) + s->first_free_temp[i] = -1; + s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS); + s->nb_labels = 0; + s->current_frame_offset = s->frame_start; + + gen_opc_ptr = gen_opc_buf; + gen_opparam_ptr = gen_opparam_buf; +} + +static inline void tcg_temp_alloc(TCGContext *s, int n) +{ + if (n > TCG_MAX_TEMPS) + tcg_abort(); +} + +static inline int tcg_global_reg_new_internal(TCGType type, int reg, + const char *name) +{ + TCGContext *s = &tcg_ctx; + TCGTemp *ts; + int idx; + +#if TCG_TARGET_REG_BITS == 32 + if (type != TCG_TYPE_I32) + tcg_abort(); +#endif + if (tcg_regset_test_reg(s->reserved_regs, reg)) + tcg_abort(); + idx = s->nb_globals; + tcg_temp_alloc(s, s->nb_globals + 1); + ts = &s->temps[s->nb_globals]; + ts->base_type = type; + ts->type = type; + ts->fixed_reg = 1; + ts->reg = reg; + ts->name = name; + s->nb_globals++; + tcg_regset_set_reg(s->reserved_regs, reg); + return idx; +} + +TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name) +{ + int idx; + + idx = tcg_global_reg_new_internal(TCG_TYPE_I32, reg, name); + return MAKE_TCGV_I32(idx); +} + +TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name) +{ + int idx; + + idx = tcg_global_reg_new_internal(TCG_TYPE_I64, reg, name); + return MAKE_TCGV_I64(idx); +} + +static inline int tcg_global_mem_new_internal(TCGType type, int reg, + tcg_target_long offset, + const char *name) +{ + TCGContext *s = &tcg_ctx; + TCGTemp *ts; + int idx; + + idx = s->nb_globals; +#if TCG_TARGET_REG_BITS == 32 + if (type == TCG_TYPE_I64) { + char buf[64]; + tcg_temp_alloc(s, s->nb_globals + 2); + ts = &s->temps[s->nb_globals]; + ts->base_type = type; + ts->type = TCG_TYPE_I32; + ts->fixed_reg = 0; + ts->mem_allocated = 1; + ts->mem_reg = reg; +#ifdef TCG_TARGET_WORDS_BIGENDIAN + ts->mem_offset = offset + 4; +#else + ts->mem_offset = offset; +#endif + pstrcpy(buf, sizeof(buf), name); + pstrcat(buf, sizeof(buf), "_0"); + ts->name = strdup(buf); + ts++; + + ts->base_type = type; + ts->type = TCG_TYPE_I32; + ts->fixed_reg = 0; + ts->mem_allocated = 1; + ts->mem_reg = reg; +#ifdef TCG_TARGET_WORDS_BIGENDIAN + ts->mem_offset = offset; +#else + ts->mem_offset = offset + 4; +#endif + pstrcpy(buf, sizeof(buf), name); + pstrcat(buf, sizeof(buf), "_1"); + ts->name = strdup(buf); + + s->nb_globals += 2; + } else +#endif + { + tcg_temp_alloc(s, s->nb_globals + 1); + ts = &s->temps[s->nb_globals]; + ts->base_type = type; + ts->type = type; + ts->fixed_reg = 0; + ts->mem_allocated = 1; + ts->mem_reg = reg; + ts->mem_offset = offset; + ts->name = name; + s->nb_globals++; + } + return idx; +} + +TCGv_i32 tcg_global_mem_new_i32(int reg, tcg_target_long offset, + const char *name) +{ + int idx; + + idx = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name); + return MAKE_TCGV_I32(idx); +} + +TCGv_i64 tcg_global_mem_new_i64(int reg, tcg_target_long offset, + const char *name) +{ + int idx; + + idx = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name); + return MAKE_TCGV_I64(idx); +} + +static inline int tcg_temp_new_internal(TCGType type, int temp_local) +{ + TCGContext *s = &tcg_ctx; + TCGTemp *ts; + int idx, k; + + k = type; + if (temp_local) + k += TCG_TYPE_COUNT; + idx = s->first_free_temp[k]; + if (idx != -1) { + /* There is already an available temp with the + right type */ + ts = &s->temps[idx]; + s->first_free_temp[k] = ts->next_free_temp; + ts->temp_allocated = 1; + assert(ts->temp_local == temp_local); + } else { + idx = s->nb_temps; +#if TCG_TARGET_REG_BITS == 32 + if (type == TCG_TYPE_I64) { + tcg_temp_alloc(s, s->nb_temps + 2); + ts = &s->temps[s->nb_temps]; + ts->base_type = type; + ts->type = TCG_TYPE_I32; + ts->temp_allocated = 1; + ts->temp_local = temp_local; + ts->name = NULL; + ts++; + ts->base_type = TCG_TYPE_I32; + ts->type = TCG_TYPE_I32; + ts->temp_allocated = 1; + ts->temp_local = temp_local; + ts->name = NULL; + s->nb_temps += 2; + } else +#endif + { + tcg_temp_alloc(s, s->nb_temps + 1); + ts = &s->temps[s->nb_temps]; + ts->base_type = type; + ts->type = type; + ts->temp_allocated = 1; + ts->temp_local = temp_local; + ts->name = NULL; + s->nb_temps++; + } + } + return idx; +} + +TCGv_i32 tcg_temp_new_internal_i32(int temp_local) +{ + int idx; + + idx = tcg_temp_new_internal(TCG_TYPE_I32, temp_local); + return MAKE_TCGV_I32(idx); +} + +TCGv_i64 tcg_temp_new_internal_i64(int temp_local) +{ + int idx; + + idx = tcg_temp_new_internal(TCG_TYPE_I64, temp_local); + return MAKE_TCGV_I64(idx); +} + +static inline void tcg_temp_free_internal(int idx) +{ + TCGContext *s = &tcg_ctx; + TCGTemp *ts; + int k; + + assert(idx >= s->nb_globals && idx < s->nb_temps); + ts = &s->temps[idx]; + assert(ts->temp_allocated != 0); + ts->temp_allocated = 0; + k = ts->base_type; + if (ts->temp_local) + k += TCG_TYPE_COUNT; + ts->next_free_temp = s->first_free_temp[k]; + s->first_free_temp[k] = idx; +} + +void tcg_temp_free_i32(TCGv_i32 arg) +{ + tcg_temp_free_internal(GET_TCGV_I32(arg)); +} + +void tcg_temp_free_i64(TCGv_i64 arg) +{ + tcg_temp_free_internal(GET_TCGV_I64(arg)); +} + +TCGv_i32 tcg_const_i32(int32_t val) +{ + TCGv_i32 t0; + t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, val); + return t0; +} + +TCGv_i64 tcg_const_i64(int64_t val) +{ + TCGv_i64 t0; + t0 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t0, val); + return t0; +} + +TCGv_i32 tcg_const_local_i32(int32_t val) +{ + TCGv_i32 t0; + t0 = tcg_temp_local_new_i32(); + tcg_gen_movi_i32(t0, val); + return t0; +} + +TCGv_i64 tcg_const_local_i64(int64_t val) +{ + TCGv_i64 t0; + t0 = tcg_temp_local_new_i64(); + tcg_gen_movi_i64(t0, val); + return t0; +} + +void tcg_register_helper(void *func, const char *name) +{ + TCGContext *s = &tcg_ctx; + int n; + if ((s->nb_helpers + 1) > s->allocated_helpers) { + n = s->allocated_helpers; + if (n == 0) { + n = 4; + } else { + n *= 2; + } + s->helpers = realloc(s->helpers, n * sizeof(TCGHelperInfo)); + s->allocated_helpers = n; + } + s->helpers[s->nb_helpers].func = (tcg_target_ulong)func; + s->helpers[s->nb_helpers].name = name; + s->nb_helpers++; +} + +/* Note: we convert the 64 bit args to 32 bit and do some alignment + and endian swap. Maybe it would be better to do the alignment + and endian swap in tcg_reg_alloc_call(). */ +void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, + int sizemask, TCGArg ret, int nargs, TCGArg *args) +{ +#ifdef TCG_TARGET_I386 + int call_type; +#endif + int i; + int real_args; + int nb_rets; + TCGArg *nparam; + +#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 + for (i = 0; i < nargs; ++i) { + int is_64bit = sizemask & (1 << (i+1)*2); + int is_signed = sizemask & (2 << (i+1)*2); + if (!is_64bit) { + TCGv_i64 temp = tcg_temp_new_i64(); + TCGv_i64 orig = MAKE_TCGV_I64(args[i]); + if (is_signed) { + tcg_gen_ext32s_i64(temp, orig); + } else { + tcg_gen_ext32u_i64(temp, orig); + } + args[i] = GET_TCGV_I64(temp); + } + } +#endif /* TCG_TARGET_EXTEND_ARGS */ + + *gen_opc_ptr++ = INDEX_op_call; + nparam = gen_opparam_ptr++; +#ifdef TCG_TARGET_I386 + call_type = (flags & TCG_CALL_TYPE_MASK); +#endif + if (ret != TCG_CALL_DUMMY_ARG) { +#if TCG_TARGET_REG_BITS < 64 + if (sizemask & 1) { +#ifdef TCG_TARGET_WORDS_BIGENDIAN + *gen_opparam_ptr++ = ret + 1; + *gen_opparam_ptr++ = ret; +#else + *gen_opparam_ptr++ = ret; + *gen_opparam_ptr++ = ret + 1; +#endif + nb_rets = 2; + } else +#endif + { + *gen_opparam_ptr++ = ret; + nb_rets = 1; + } + } else { + nb_rets = 0; + } + real_args = 0; + for (i = 0; i < nargs; i++) { +#if TCG_TARGET_REG_BITS < 64 + int is_64bit = sizemask & (1 << (i+1)*2); + if (is_64bit) { +#ifdef TCG_TARGET_I386 + /* REGPARM case: if the third parameter is 64 bit, it is + allocated on the stack */ + if (i == 2 && call_type == TCG_CALL_TYPE_REGPARM) { + call_type = TCG_CALL_TYPE_REGPARM_2; + flags = (flags & ~TCG_CALL_TYPE_MASK) | call_type; + } +#endif +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + /* some targets want aligned 64 bit args */ + if (real_args & 1) { + *gen_opparam_ptr++ = TCG_CALL_DUMMY_ARG; + real_args++; + } +#endif + /* If stack grows up, then we will be placing successive + arguments at lower addresses, which means we need to + reverse the order compared to how we would normally + treat either big or little-endian. For those arguments + that will wind up in registers, this still works for + HPPA (the only current STACK_GROWSUP target) since the + argument registers are *also* allocated in decreasing + order. If another such target is added, this logic may + have to get more complicated to differentiate between + stack arguments and register arguments. */ +#if defined(TCG_TARGET_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP) + *gen_opparam_ptr++ = args[i] + 1; + *gen_opparam_ptr++ = args[i]; +#else + *gen_opparam_ptr++ = args[i]; + *gen_opparam_ptr++ = args[i] + 1; +#endif + real_args += 2; + continue; + } +#endif /* TCG_TARGET_REG_BITS < 64 */ + + *gen_opparam_ptr++ = args[i]; + real_args++; + } + *gen_opparam_ptr++ = GET_TCGV_PTR(func); + + *gen_opparam_ptr++ = flags; + + *nparam = (nb_rets << 16) | (real_args + 1); + + /* total parameters, needed to go backward in the instruction stream */ + *gen_opparam_ptr++ = 1 + nb_rets + real_args + 3; + +#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 + for (i = 0; i < nargs; ++i) { + int is_64bit = sizemask & (1 << (i+1)*2); + if (!is_64bit) { + TCGv_i64 temp = MAKE_TCGV_I64(args[i]); + tcg_temp_free_i64(temp); + } + } +#endif /* TCG_TARGET_EXTEND_ARGS */ +} + +#if TCG_TARGET_REG_BITS == 32 +void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, + int c, int right, int arith) +{ + if (c == 0) { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); + } else if (c >= 32) { + c -= 32; + if (right) { + if (arith) { + tcg_gen_sari_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31); + } else { + tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } + } else { + tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), c); + tcg_gen_movi_i32(TCGV_LOW(ret), 0); + } + } else { + TCGv_i32 t0, t1; + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + if (right) { + tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c); + if (arith) + tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c); + else + tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c); + tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_LOW(arg1), c); + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t0); + tcg_gen_mov_i32(TCGV_HIGH(ret), t1); + } else { + tcg_gen_shri_i32(t0, TCGV_LOW(arg1), 32 - c); + /* Note: ret can be the same as arg1, so we use t1 */ + tcg_gen_shli_i32(t1, TCGV_LOW(arg1), c); + tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0); + tcg_gen_mov_i32(TCGV_LOW(ret), t1); + } + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} +#endif + + +static void tcg_reg_alloc_start(TCGContext *s) +{ + int i; + TCGTemp *ts; + for(i = 0; i < s->nb_globals; i++) { + ts = &s->temps[i]; + if (ts->fixed_reg) { + ts->val_type = TEMP_VAL_REG; + } else { + ts->val_type = TEMP_VAL_MEM; + } + } + for(i = s->nb_globals; i < s->nb_temps; i++) { + ts = &s->temps[i]; + ts->val_type = TEMP_VAL_DEAD; + ts->mem_allocated = 0; + ts->fixed_reg = 0; + } + for(i = 0; i < TCG_TARGET_NB_REGS; i++) { + s->reg_to_temp[i] = -1; + } +} + +static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size, + int idx) +{ + TCGTemp *ts; + + ts = &s->temps[idx]; + if (idx < s->nb_globals) { + pstrcpy(buf, buf_size, ts->name); + } else { + if (ts->temp_local) + snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); + else + snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); + } + return buf; +} + +char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg) +{ + return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I32(arg)); +} + +char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg) +{ + return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I64(arg)); +} + +static int helper_cmp(const void *p1, const void *p2) +{ + const TCGHelperInfo *th1 = p1; + const TCGHelperInfo *th2 = p2; + if (th1->func < th2->func) + return -1; + else if (th1->func == th2->func) + return 0; + else + return 1; +} + +/* find helper definition (Note: A hash table would be better) */ +static TCGHelperInfo *tcg_find_helper(TCGContext *s, tcg_target_ulong val) +{ + int m, m_min, m_max; + TCGHelperInfo *th; + tcg_target_ulong v; + + if (unlikely(!s->helpers_sorted)) { + qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo), + helper_cmp); + s->helpers_sorted = 1; + } + + /* binary search */ + m_min = 0; + m_max = s->nb_helpers - 1; + while (m_min <= m_max) { + m = (m_min + m_max) >> 1; + th = &s->helpers[m]; + v = th->func; + if (v == val) + return th; + else if (val < v) { + m_max = m - 1; + } else { + m_min = m + 1; + } + } + return NULL; +} + +static const char * const cond_name[] = +{ + [TCG_COND_EQ] = "eq", + [TCG_COND_NE] = "ne", + [TCG_COND_LT] = "lt", + [TCG_COND_GE] = "ge", + [TCG_COND_LE] = "le", + [TCG_COND_GT] = "gt", + [TCG_COND_LTU] = "ltu", + [TCG_COND_GEU] = "geu", + [TCG_COND_LEU] = "leu", + [TCG_COND_GTU] = "gtu" +}; + +void tcg_dump_ops(TCGContext *s, FILE *outfile) +{ + const uint16_t *opc_ptr; + const TCGArg *args; + TCGArg arg; + TCGOpcode c; + int i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; + const TCGOpDef *def; + char buf[128]; + + first_insn = 1; + opc_ptr = gen_opc_buf; + args = gen_opparam_buf; + while (opc_ptr < gen_opc_ptr) { + c = *opc_ptr++; + def = &tcg_op_defs[c]; + if (c == INDEX_op_debug_insn_start) { + uint64_t pc; +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS + pc = ((uint64_t)args[1] << 32) | args[0]; +#else + pc = args[0]; +#endif + if (!first_insn) + fprintf(outfile, "\n"); + fprintf(outfile, " ---- 0x%" PRIx64, pc); + first_insn = 0; + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; + nb_cargs = def->nb_cargs; + } else if (c == INDEX_op_call) { + TCGArg arg; + + /* variable number of arguments */ + arg = *args++; + nb_oargs = arg >> 16; + nb_iargs = arg & 0xffff; + nb_cargs = def->nb_cargs; + + fprintf(outfile, " %s ", def->name); + + /* function name */ + fprintf(outfile, "%s", + tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + nb_iargs - 1])); + /* flags */ + fprintf(outfile, ",$0x%" TCG_PRIlx, + args[nb_oargs + nb_iargs]); + /* nb out args */ + fprintf(outfile, ",$%d", nb_oargs); + for(i = 0; i < nb_oargs; i++) { + fprintf(outfile, ","); + fprintf(outfile, "%s", + tcg_get_arg_str_idx(s, buf, sizeof(buf), args[i])); + } + for(i = 0; i < (nb_iargs - 1); i++) { + fprintf(outfile, ","); + if (args[nb_oargs + i] == TCG_CALL_DUMMY_ARG) { + fprintf(outfile, "<dummy>"); + } else { + fprintf(outfile, "%s", + tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + i])); + } + } + } else if (c == INDEX_op_movi_i32 +#if TCG_TARGET_REG_BITS == 64 + || c == INDEX_op_movi_i64 +#endif + ) { + tcg_target_ulong val; + TCGHelperInfo *th; + + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; + nb_cargs = def->nb_cargs; + fprintf(outfile, " %s %s,$", def->name, + tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0])); + val = args[1]; + th = tcg_find_helper(s, val); + if (th) { + fprintf(outfile, "%s", th->name); + } else { + if (c == INDEX_op_movi_i32) + fprintf(outfile, "0x%x", (uint32_t)val); + else + fprintf(outfile, "0x%" PRIx64 , (uint64_t)val); + } + } else { + fprintf(outfile, " %s ", def->name); + if (c == INDEX_op_nopn) { + /* variable number of arguments */ + nb_cargs = *args; + nb_oargs = 0; + nb_iargs = 0; + } else { + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; + nb_cargs = def->nb_cargs; + } + + k = 0; + for(i = 0; i < nb_oargs; i++) { + if (k != 0) + fprintf(outfile, ","); + fprintf(outfile, "%s", + tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++])); + } + for(i = 0; i < nb_iargs; i++) { + if (k != 0) + fprintf(outfile, ","); + fprintf(outfile, "%s", + tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++])); + } + switch (c) { + case INDEX_op_brcond_i32: +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: +#elif TCG_TARGET_REG_BITS == 64 + case INDEX_op_brcond_i64: +#endif + case INDEX_op_setcond_i32: +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_setcond2_i32: +#elif TCG_TARGET_REG_BITS == 64 + case INDEX_op_setcond_i64: +#endif + if (args[k] < ARRAY_SIZE(cond_name) && cond_name[args[k]]) + fprintf(outfile, ",%s", cond_name[args[k++]]); + else + fprintf(outfile, ",$0x%" TCG_PRIlx, args[k++]); + i = 1; + break; + default: + i = 0; + break; + } + for(; i < nb_cargs; i++) { + if (k != 0) + fprintf(outfile, ","); + arg = args[k++]; + fprintf(outfile, "$0x%" TCG_PRIlx, arg); + } + } + fprintf(outfile, "\n"); + args += nb_iargs + nb_oargs + nb_cargs; + } +} + +/* we give more priority to constraints with less registers */ +static int get_constraint_priority(const TCGOpDef *def, int k) +{ + const TCGArgConstraint *arg_ct; + + int i, n; + arg_ct = &def->args_ct[k]; + if (arg_ct->ct & TCG_CT_ALIAS) { + /* an alias is equivalent to a single register */ + n = 1; + } else { + if (!(arg_ct->ct & TCG_CT_REG)) + return 0; + n = 0; + for(i = 0; i < TCG_TARGET_NB_REGS; i++) { + if (tcg_regset_test_reg(arg_ct->u.regs, i)) + n++; + } + } + return TCG_TARGET_NB_REGS - n + 1; +} + +/* sort from highest priority to lowest */ +static void sort_constraints(TCGOpDef *def, int start, int n) +{ + int i, j, p1, p2, tmp; + + for(i = 0; i < n; i++) + def->sorted_args[start + i] = start + i; + if (n <= 1) + return; + for(i = 0; i < n - 1; i++) { + for(j = i + 1; j < n; j++) { + p1 = get_constraint_priority(def, def->sorted_args[start + i]); + p2 = get_constraint_priority(def, def->sorted_args[start + j]); + if (p1 < p2) { + tmp = def->sorted_args[start + i]; + def->sorted_args[start + i] = def->sorted_args[start + j]; + def->sorted_args[start + j] = tmp; + } + } + } +} + +void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) +{ + TCGOpcode op; + TCGOpDef *def; + const char *ct_str; + int i, nb_args; + + for(;;) { + if (tdefs->op == (TCGOpcode)-1) + break; + op = tdefs->op; + assert((unsigned)op < NB_OPS); + def = &tcg_op_defs[op]; +#if defined(CONFIG_DEBUG_TCG) + /* Duplicate entry in op definitions? */ + assert(!def->used); + def->used = 1; +#endif + nb_args = def->nb_iargs + def->nb_oargs; + for(i = 0; i < nb_args; i++) { + ct_str = tdefs->args_ct_str[i]; + /* Incomplete TCGTargetOpDef entry? */ + assert(ct_str != NULL); + tcg_regset_clear(def->args_ct[i].u.regs); + def->args_ct[i].ct = 0; + if (ct_str[0] >= '0' && ct_str[0] <= '9') { + int oarg; + oarg = ct_str[0] - '0'; + assert(oarg < def->nb_oargs); + assert(def->args_ct[oarg].ct & TCG_CT_REG); + /* TCG_CT_ALIAS is for the output arguments. The input + argument is tagged with TCG_CT_IALIAS. */ + def->args_ct[i] = def->args_ct[oarg]; + def->args_ct[oarg].ct = TCG_CT_ALIAS; + def->args_ct[oarg].alias_index = i; + def->args_ct[i].ct |= TCG_CT_IALIAS; + def->args_ct[i].alias_index = oarg; + } else { + for(;;) { + if (*ct_str == '\0') + break; + switch(*ct_str) { + case 'i': + def->args_ct[i].ct |= TCG_CT_CONST; + ct_str++; + break; + default: + if (target_parse_constraint(&def->args_ct[i], &ct_str) < 0) { + fprintf(stderr, "Invalid constraint '%s' for arg %d of operation '%s'\n", + ct_str, i, def->name); + exit(1); + } + } + } + } + } + + /* TCGTargetOpDef entry with too much information? */ + assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL); + + /* sort the constraints (XXX: this is just an heuristic) */ + sort_constraints(def, 0, def->nb_oargs); + sort_constraints(def, def->nb_oargs, def->nb_iargs); + +#if 0 + { + int i; + + printf("%s: sorted=", def->name); + for(i = 0; i < def->nb_oargs + def->nb_iargs; i++) + printf(" %d", def->sorted_args[i]); + printf("\n"); + } +#endif + tdefs++; + } + +#if defined(CONFIG_DEBUG_TCG) + i = 0; + for (op = 0; op < ARRAY_SIZE(tcg_op_defs); op++) { + if (op < INDEX_op_call || op == INDEX_op_debug_insn_start) { + /* Wrong entry in op definitions? */ + if (tcg_op_defs[op].used) { + fprintf(stderr, "Invalid op definition for %s\n", + tcg_op_defs[op].name); + i = 1; + } + } else { + /* Missing entry in op definitions? */ + if (!tcg_op_defs[op].used) { + fprintf(stderr, "Missing op definition for %s\n", + tcg_op_defs[op].name); + i = 1; + } + } + } + if (i == 1) { + tcg_abort(); + } +#endif +} + +#ifdef USE_LIVENESS_ANALYSIS + +/* set a nop for an operation using 'nb_args' */ +static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr, + TCGArg *args, int nb_args) +{ + if (nb_args == 0) { + *opc_ptr = INDEX_op_nop; + } else { + *opc_ptr = INDEX_op_nopn; + args[0] = nb_args; + args[nb_args - 1] = nb_args; + } +} + +/* liveness analysis: end of function: globals are live, temps are + dead. */ +/* XXX: at this stage, not used as there would be little gains because + most TBs end with a conditional jump. */ +static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps) +{ + memset(dead_temps, 0, s->nb_globals); + memset(dead_temps + s->nb_globals, 1, s->nb_temps - s->nb_globals); +} + +/* liveness analysis: end of basic block: globals are live, temps are + dead, local temps are live. */ +static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps) +{ + int i; + TCGTemp *ts; + + memset(dead_temps, 0, s->nb_globals); + ts = &s->temps[s->nb_globals]; + for(i = s->nb_globals; i < s->nb_temps; i++) { + if (ts->temp_local) + dead_temps[i] = 0; + else + dead_temps[i] = 1; + ts++; + } +} + +/* Liveness analysis : update the opc_dead_iargs array to tell if a + given input arguments is dead. Instructions updating dead + temporaries are removed. */ +static void tcg_liveness_analysis(TCGContext *s) +{ + int i, op_index, nb_args, nb_iargs, nb_oargs, arg, nb_ops; + TCGOpcode op; + TCGArg *args; + const TCGOpDef *def; + uint8_t *dead_temps; + unsigned int dead_iargs; + + gen_opc_ptr++; /* skip end */ + + nb_ops = gen_opc_ptr - gen_opc_buf; + + s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t)); + + dead_temps = tcg_malloc(s->nb_temps); + memset(dead_temps, 1, s->nb_temps); + + args = gen_opparam_ptr; + op_index = nb_ops - 1; + while (op_index >= 0) { + op = gen_opc_buf[op_index]; + def = &tcg_op_defs[op]; + switch(op) { + case INDEX_op_call: + { + int call_flags; + + nb_args = args[-1]; + args -= nb_args; + nb_iargs = args[0] & 0xffff; + nb_oargs = args[0] >> 16; + args++; + call_flags = args[nb_oargs + nb_iargs]; + + /* pure functions can be removed if their result is not + used */ + if (call_flags & TCG_CALL_PURE) { + for(i = 0; i < nb_oargs; i++) { + arg = args[i]; + if (!dead_temps[arg]) + goto do_not_remove_call; + } + tcg_set_nop(s, gen_opc_buf + op_index, + args - 1, nb_args); + } else { + do_not_remove_call: + + /* output args are dead */ + for(i = 0; i < nb_oargs; i++) { + arg = args[i]; + dead_temps[arg] = 1; + } + + if (!(call_flags & TCG_CALL_CONST)) { + /* globals are live (they may be used by the call) */ + memset(dead_temps, 0, s->nb_globals); + } + + /* input args are live */ + dead_iargs = 0; + for(i = 0; i < nb_iargs; i++) { + arg = args[i + nb_oargs]; + if (arg != TCG_CALL_DUMMY_ARG) { + if (dead_temps[arg]) { + dead_iargs |= (1 << i); + } + dead_temps[arg] = 0; + } + } + s->op_dead_iargs[op_index] = dead_iargs; + } + args--; + } + break; + case INDEX_op_set_label: + args--; + /* mark end of basic block */ + tcg_la_bb_end(s, dead_temps); + break; + case INDEX_op_debug_insn_start: + args -= def->nb_args; + break; + case INDEX_op_nopn: + nb_args = args[-1]; + args -= nb_args; + break; + case INDEX_op_discard: + args--; + /* mark the temporary as dead */ + dead_temps[args[0]] = 1; + break; + case INDEX_op_end: + break; + /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */ + default: + args -= def->nb_args; + nb_iargs = def->nb_iargs; + nb_oargs = def->nb_oargs; + + /* Test if the operation can be removed because all + its outputs are dead. We assume that nb_oargs == 0 + implies side effects */ + if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) { + for(i = 0; i < nb_oargs; i++) { + arg = args[i]; + if (!dead_temps[arg]) + goto do_not_remove; + } + tcg_set_nop(s, gen_opc_buf + op_index, args, def->nb_args); +#ifdef CONFIG_PROFILER + s->del_op_count++; +#endif + } else { + do_not_remove: + + /* output args are dead */ + for(i = 0; i < nb_oargs; i++) { + arg = args[i]; + dead_temps[arg] = 1; + } + + /* if end of basic block, update */ + if (def->flags & TCG_OPF_BB_END) { + tcg_la_bb_end(s, dead_temps); + } else if (def->flags & TCG_OPF_CALL_CLOBBER) { + /* globals are live */ + memset(dead_temps, 0, s->nb_globals); + } + + /* input args are live */ + dead_iargs = 0; + for(i = 0; i < nb_iargs; i++) { + arg = args[i + nb_oargs]; + if (dead_temps[arg]) { + dead_iargs |= (1 << i); + } + dead_temps[arg] = 0; + } + s->op_dead_iargs[op_index] = dead_iargs; + } + break; + } + op_index--; + } + + if (args != gen_opparam_buf) + tcg_abort(); +} +#else +/* dummy liveness analysis */ +static void tcg_liveness_analysis(TCGContext *s) +{ + int nb_ops; + nb_ops = gen_opc_ptr - gen_opc_buf; + + s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t)); + memset(s->op_dead_iargs, 0, nb_ops * sizeof(uint16_t)); +} +#endif + +#ifndef NDEBUG +static void dump_regs(TCGContext *s) +{ + TCGTemp *ts; + int i; + char buf[64]; + + for(i = 0; i < s->nb_temps; i++) { + ts = &s->temps[i]; + printf(" %10s: ", tcg_get_arg_str_idx(s, buf, sizeof(buf), i)); + switch(ts->val_type) { + case TEMP_VAL_REG: + printf("%s", tcg_target_reg_names[ts->reg]); + break; + case TEMP_VAL_MEM: + printf("%d(%s)", (int)ts->mem_offset, tcg_target_reg_names[ts->mem_reg]); + break; + case TEMP_VAL_CONST: + printf("$0x%" TCG_PRIlx, ts->val); + break; + case TEMP_VAL_DEAD: + printf("D"); + break; + default: + printf("???"); + break; + } + printf("\n"); + } + + for(i = 0; i < TCG_TARGET_NB_REGS; i++) { + if (s->reg_to_temp[i] >= 0) { + printf("%s: %s\n", + tcg_target_reg_names[i], + tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i])); + } + } +} + +static void check_regs(TCGContext *s) +{ + int reg, k; + TCGTemp *ts; + char buf[64]; + + for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { + k = s->reg_to_temp[reg]; + if (k >= 0) { + ts = &s->temps[k]; + if (ts->val_type != TEMP_VAL_REG || + ts->reg != reg) { + printf("Inconsistency for register %s:\n", + tcg_target_reg_names[reg]); + goto fail; + } + } + } + for(k = 0; k < s->nb_temps; k++) { + ts = &s->temps[k]; + if (ts->val_type == TEMP_VAL_REG && + !ts->fixed_reg && + s->reg_to_temp[ts->reg] != k) { + printf("Inconsistency for temp %s:\n", + tcg_get_arg_str_idx(s, buf, sizeof(buf), k)); + fail: + printf("reg state:\n"); + dump_regs(s); + tcg_abort(); + } + } +} +#endif + +static void temp_allocate_frame(TCGContext *s, int temp) +{ + TCGTemp *ts; + ts = &s->temps[temp]; + s->current_frame_offset = (s->current_frame_offset + sizeof(tcg_target_long) - 1) & ~(sizeof(tcg_target_long) - 1); + if (s->current_frame_offset + sizeof(tcg_target_long) > s->frame_end) + tcg_abort(); + ts->mem_offset = s->current_frame_offset; + ts->mem_reg = s->frame_reg; + ts->mem_allocated = 1; + s->current_frame_offset += sizeof(tcg_target_long); +} + +/* free register 'reg' by spilling the corresponding temporary if necessary */ +static void tcg_reg_free(TCGContext *s, int reg) +{ + TCGTemp *ts; + int temp; + + temp = s->reg_to_temp[reg]; + if (temp != -1) { + ts = &s->temps[temp]; + assert(ts->val_type == TEMP_VAL_REG); + if (!ts->mem_coherent) { + if (!ts->mem_allocated) + temp_allocate_frame(s, temp); + tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + } + ts->val_type = TEMP_VAL_MEM; + s->reg_to_temp[reg] = -1; + } +} + +/* Allocate a register belonging to reg1 & ~reg2 */ +static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2) +{ + int i, reg; + TCGRegSet reg_ct; + + tcg_regset_andnot(reg_ct, reg1, reg2); + + /* first try free registers */ + for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) { + reg = tcg_target_reg_alloc_order[i]; + if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == -1) + return reg; + } + + /* XXX: do better spill choice */ + for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) { + reg = tcg_target_reg_alloc_order[i]; + if (tcg_regset_test_reg(reg_ct, reg)) { + tcg_reg_free(s, reg); + return reg; + } + } + + tcg_abort(); +} + +/* save a temporary to memory. 'allocated_regs' is used in case a + temporary registers needs to be allocated to store a constant. */ +static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs) +{ + TCGTemp *ts; + int reg; + + ts = &s->temps[temp]; + if (!ts->fixed_reg) { + switch(ts->val_type) { + case TEMP_VAL_REG: + tcg_reg_free(s, ts->reg); + break; + case TEMP_VAL_DEAD: + ts->val_type = TEMP_VAL_MEM; + break; + case TEMP_VAL_CONST: + reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + allocated_regs); + if (!ts->mem_allocated) + temp_allocate_frame(s, temp); + tcg_out_movi(s, ts->type, reg, ts->val); + tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + ts->val_type = TEMP_VAL_MEM; + break; + case TEMP_VAL_MEM: + break; + default: + tcg_abort(); + } + } +} + +/* save globals to their cannonical location and assume they can be + modified be the following code. 'allocated_regs' is used in case a + temporary registers needs to be allocated to store a constant. */ +static void save_globals(TCGContext *s, TCGRegSet allocated_regs) +{ + int i; + + for(i = 0; i < s->nb_globals; i++) { + temp_save(s, i, allocated_regs); + } +} + +/* at the end of a basic block, we assume all temporaries are dead and + all globals are stored at their canonical location. */ +static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) +{ + TCGTemp *ts; + int i; + + for(i = s->nb_globals; i < s->nb_temps; i++) { + ts = &s->temps[i]; + if (ts->temp_local) { + temp_save(s, i, allocated_regs); + } else { + if (ts->val_type == TEMP_VAL_REG) { + s->reg_to_temp[ts->reg] = -1; + } + ts->val_type = TEMP_VAL_DEAD; + } + } + + save_globals(s, allocated_regs); +} + +#define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1) + +static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args) +{ + TCGTemp *ots; + tcg_target_ulong val; + + ots = &s->temps[args[0]]; + val = args[1]; + + if (ots->fixed_reg) { + /* for fixed registers, we do not do any constant + propagation */ + tcg_out_movi(s, ots->type, ots->reg, val); + } else { + /* The movi is not explicitly generated here */ + if (ots->val_type == TEMP_VAL_REG) + s->reg_to_temp[ots->reg] = -1; + ots->val_type = TEMP_VAL_CONST; + ots->val = val; + } +} + +static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, + const TCGArg *args, + unsigned int dead_iargs) +{ + TCGTemp *ts, *ots; + int reg; + const TCGArgConstraint *arg_ct; + + ots = &s->temps[args[0]]; + ts = &s->temps[args[1]]; + arg_ct = &def->args_ct[0]; + + /* XXX: always mark arg dead if IS_DEAD_IARG(0) */ + if (ts->val_type == TEMP_VAL_REG) { + if (IS_DEAD_IARG(0) && !ts->fixed_reg && !ots->fixed_reg) { + /* the mov can be suppressed */ + if (ots->val_type == TEMP_VAL_REG) + s->reg_to_temp[ots->reg] = -1; + reg = ts->reg; + s->reg_to_temp[reg] = -1; + ts->val_type = TEMP_VAL_DEAD; + } else { + if (ots->val_type == TEMP_VAL_REG) { + reg = ots->reg; + } else { + reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs); + } + if (ts->reg != reg) { + tcg_out_mov(s, ots->type, reg, ts->reg); + } + } + } else if (ts->val_type == TEMP_VAL_MEM) { + if (ots->val_type == TEMP_VAL_REG) { + reg = ots->reg; + } else { + reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs); + } + tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + } else if (ts->val_type == TEMP_VAL_CONST) { + if (ots->fixed_reg) { + reg = ots->reg; + tcg_out_movi(s, ots->type, reg, ts->val); + } else { + /* propagate constant */ + if (ots->val_type == TEMP_VAL_REG) + s->reg_to_temp[ots->reg] = -1; + ots->val_type = TEMP_VAL_CONST; + ots->val = ts->val; + return; + } + } else { + tcg_abort(); + } + s->reg_to_temp[reg] = args[0]; + ots->reg = reg; + ots->val_type = TEMP_VAL_REG; + ots->mem_coherent = 0; +} + +static void tcg_reg_alloc_op(TCGContext *s, + const TCGOpDef *def, TCGOpcode opc, + const TCGArg *args, + unsigned int dead_iargs) +{ + TCGRegSet allocated_regs; + int i, k, nb_iargs, nb_oargs, reg; + TCGArg arg; + const TCGArgConstraint *arg_ct; + TCGTemp *ts; + TCGArg new_args[TCG_MAX_OP_ARGS]; + int const_args[TCG_MAX_OP_ARGS]; + + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; + + /* copy constants */ + memcpy(new_args + nb_oargs + nb_iargs, + args + nb_oargs + nb_iargs, + sizeof(TCGArg) * def->nb_cargs); + + /* satisfy input constraints */ + tcg_regset_set(allocated_regs, s->reserved_regs); + for(k = 0; k < nb_iargs; k++) { + i = def->sorted_args[nb_oargs + k]; + arg = args[i]; + arg_ct = &def->args_ct[i]; + ts = &s->temps[arg]; + if (ts->val_type == TEMP_VAL_MEM) { + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; + ts->mem_coherent = 1; + s->reg_to_temp[reg] = arg; + } else if (ts->val_type == TEMP_VAL_CONST) { + if (tcg_target_const_match(ts->val, arg_ct)) { + /* constant is OK for instruction */ + const_args[i] = 1; + new_args[i] = ts->val; + goto iarg_end; + } else { + /* need to move to a register */ + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + tcg_out_movi(s, ts->type, reg, ts->val); + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; + ts->mem_coherent = 0; + s->reg_to_temp[reg] = arg; + } + } + assert(ts->val_type == TEMP_VAL_REG); + if (arg_ct->ct & TCG_CT_IALIAS) { + if (ts->fixed_reg) { + /* if fixed register, we must allocate a new register + if the alias is not the same register */ + if (arg != args[arg_ct->alias_index]) + goto allocate_in_reg; + } else { + /* if the input is aliased to an output and if it is + not dead after the instruction, we must allocate + a new register and move it */ + if (!IS_DEAD_IARG(i - nb_oargs)) + goto allocate_in_reg; + } + } + reg = ts->reg; + if (tcg_regset_test_reg(arg_ct->u.regs, reg)) { + /* nothing to do : the constraint is satisfied */ + } else { + allocate_in_reg: + /* allocate a new register matching the constraint + and move the temporary register into it */ + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + tcg_out_mov(s, ts->type, reg, ts->reg); + } + new_args[i] = reg; + const_args[i] = 0; + tcg_regset_set_reg(allocated_regs, reg); + iarg_end: ; + } + + if (def->flags & TCG_OPF_BB_END) { + tcg_reg_alloc_bb_end(s, allocated_regs); + } else { + /* mark dead temporaries and free the associated registers */ + for(i = 0; i < nb_iargs; i++) { + arg = args[nb_oargs + i]; + if (IS_DEAD_IARG(i)) { + ts = &s->temps[arg]; + if (!ts->fixed_reg) { + if (ts->val_type == TEMP_VAL_REG) + s->reg_to_temp[ts->reg] = -1; + ts->val_type = TEMP_VAL_DEAD; + } + } + } + + if (def->flags & TCG_OPF_CALL_CLOBBER) { + /* XXX: permit generic clobber register list ? */ + for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { + if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { + tcg_reg_free(s, reg); + } + } + /* XXX: for load/store we could do that only for the slow path + (i.e. when a memory callback is called) */ + + /* store globals and free associated registers (we assume the insn + can modify any global. */ + save_globals(s, allocated_regs); + } + + /* satisfy the output constraints */ + tcg_regset_set(allocated_regs, s->reserved_regs); + for(k = 0; k < nb_oargs; k++) { + i = def->sorted_args[k]; + arg = args[i]; + arg_ct = &def->args_ct[i]; + ts = &s->temps[arg]; + if (arg_ct->ct & TCG_CT_ALIAS) { + reg = new_args[arg_ct->alias_index]; + } else { + /* if fixed register, we try to use it */ + reg = ts->reg; + if (ts->fixed_reg && + tcg_regset_test_reg(arg_ct->u.regs, reg)) { + goto oarg_end; + } + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + } + tcg_regset_set_reg(allocated_regs, reg); + /* if a fixed register is used, then a move will be done afterwards */ + if (!ts->fixed_reg) { + if (ts->val_type == TEMP_VAL_REG) + s->reg_to_temp[ts->reg] = -1; + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; + /* temp value is modified, so the value kept in memory is + potentially not the same */ + ts->mem_coherent = 0; + s->reg_to_temp[reg] = arg; + } + oarg_end: + new_args[i] = reg; + } + } + + /* emit instruction */ + tcg_out_op(s, opc, new_args, const_args); + + /* move the outputs in the correct register if needed */ + for(i = 0; i < nb_oargs; i++) { + ts = &s->temps[args[i]]; + reg = new_args[i]; + if (ts->fixed_reg && ts->reg != reg) { + tcg_out_mov(s, ts->type, ts->reg, reg); + } + } +} + +#ifdef TCG_TARGET_STACK_GROWSUP +#define STACK_DIR(x) (-(x)) +#else +#define STACK_DIR(x) (x) +#endif + +static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, + TCGOpcode opc, const TCGArg *args, + unsigned int dead_iargs) +{ + int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params; + TCGArg arg, func_arg; + TCGTemp *ts; + tcg_target_long stack_offset, call_stack_size, func_addr; + int const_func_arg, allocate_args; + TCGRegSet allocated_regs; + const TCGArgConstraint *arg_ct; + + arg = *args++; + + nb_oargs = arg >> 16; + nb_iargs = arg & 0xffff; + nb_params = nb_iargs - 1; + + flags = args[nb_oargs + nb_iargs]; + + nb_regs = tcg_target_get_call_iarg_regs_count(flags); + if (nb_regs > nb_params) + nb_regs = nb_params; + + /* assign stack slots first */ + /* XXX: preallocate call stack */ + call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long); + call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE); + if (allocate_args) { + tcg_out_addi(s, TCG_REG_CALL_STACK, -STACK_DIR(call_stack_size)); + } + + stack_offset = TCG_TARGET_CALL_STACK_OFFSET; + for(i = nb_regs; i < nb_params; i++) { + arg = args[nb_oargs + i]; +#ifdef TCG_TARGET_STACK_GROWSUP + stack_offset -= sizeof(tcg_target_long); +#endif + if (arg != TCG_CALL_DUMMY_ARG) { + ts = &s->temps[arg]; + if (ts->val_type == TEMP_VAL_REG) { + tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); + } else if (ts->val_type == TEMP_VAL_MEM) { + reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + s->reserved_regs); + /* XXX: not correct if reading values from the stack */ + tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); + } else if (ts->val_type == TEMP_VAL_CONST) { + reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + s->reserved_regs); + /* XXX: sign extend may be needed on some targets */ + tcg_out_movi(s, ts->type, reg, ts->val); + tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); + } else { + tcg_abort(); + } + } +#ifndef TCG_TARGET_STACK_GROWSUP + stack_offset += sizeof(tcg_target_long); +#endif + } + + /* assign input registers */ + tcg_regset_set(allocated_regs, s->reserved_regs); + for(i = 0; i < nb_regs; i++) { + arg = args[nb_oargs + i]; + if (arg != TCG_CALL_DUMMY_ARG) { + ts = &s->temps[arg]; + reg = tcg_target_call_iarg_regs[i]; + tcg_reg_free(s, reg); + if (ts->val_type == TEMP_VAL_REG) { + if (ts->reg != reg) { + tcg_out_mov(s, ts->type, reg, ts->reg); + } + } else if (ts->val_type == TEMP_VAL_MEM) { + tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + } else if (ts->val_type == TEMP_VAL_CONST) { + /* XXX: sign extend ? */ + tcg_out_movi(s, ts->type, reg, ts->val); + } else { + tcg_abort(); + } + tcg_regset_set_reg(allocated_regs, reg); + } + } + + /* assign function address */ + func_arg = args[nb_oargs + nb_iargs - 1]; + arg_ct = &def->args_ct[0]; + ts = &s->temps[func_arg]; + func_addr = ts->val; + const_func_arg = 0; + if (ts->val_type == TEMP_VAL_MEM) { + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); + func_arg = reg; + tcg_regset_set_reg(allocated_regs, reg); + } else if (ts->val_type == TEMP_VAL_REG) { + reg = ts->reg; + if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) { + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + tcg_out_mov(s, ts->type, reg, ts->reg); + } + func_arg = reg; + tcg_regset_set_reg(allocated_regs, reg); + } else if (ts->val_type == TEMP_VAL_CONST) { + if (tcg_target_const_match(func_addr, arg_ct)) { + const_func_arg = 1; + func_arg = func_addr; + } else { + reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); + tcg_out_movi(s, ts->type, reg, func_addr); + func_arg = reg; + tcg_regset_set_reg(allocated_regs, reg); + } + } else { + tcg_abort(); + } + + + /* mark dead temporaries and free the associated registers */ + for(i = 0; i < nb_iargs; i++) { + arg = args[nb_oargs + i]; + if (IS_DEAD_IARG(i)) { + ts = &s->temps[arg]; + if (!ts->fixed_reg) { + if (ts->val_type == TEMP_VAL_REG) + s->reg_to_temp[ts->reg] = -1; + ts->val_type = TEMP_VAL_DEAD; + } + } + } + + /* clobber call registers */ + for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { + if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { + tcg_reg_free(s, reg); + } + } + + /* store globals and free associated registers (we assume the call + can modify any global. */ + if (!(flags & TCG_CALL_CONST)) { + save_globals(s, allocated_regs); + } + + tcg_out_op(s, opc, &func_arg, &const_func_arg); + + if (allocate_args) { + tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size)); + } + + /* assign output registers and emit moves if needed */ + for(i = 0; i < nb_oargs; i++) { + arg = args[i]; + ts = &s->temps[arg]; + reg = tcg_target_call_oarg_regs[i]; + assert(s->reg_to_temp[reg] == -1); + if (ts->fixed_reg) { + if (ts->reg != reg) { + tcg_out_mov(s, ts->type, ts->reg, reg); + } + } else { + if (ts->val_type == TEMP_VAL_REG) + s->reg_to_temp[ts->reg] = -1; + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; + ts->mem_coherent = 0; + s->reg_to_temp[reg] = arg; + } + } + + return nb_iargs + nb_oargs + def->nb_cargs + 1; +} + +#ifdef CONFIG_PROFILER + +static int64_t tcg_table_op_count[NB_OPS]; + +static void dump_op_count(void) +{ + int i; + FILE *f; + f = fopen("/tmp/op.log", "w"); + for(i = INDEX_op_end; i < NB_OPS; i++) { + fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, tcg_table_op_count[i]); + } + fclose(f); +} +#endif + + +static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, + long search_pc) +{ + TCGOpcode opc; + int op_index; + const TCGOpDef *def; + unsigned int dead_iargs; + const TCGArg *args; + +#ifdef DEBUG_DISAS + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + qemu_log("OP:\n"); + tcg_dump_ops(s, logfile); + qemu_log("\n"); + } +#endif + +#ifdef CONFIG_PROFILER + s->la_time -= profile_getclock(); +#endif + tcg_liveness_analysis(s); +#ifdef CONFIG_PROFILER + s->la_time += profile_getclock(); +#endif + +#ifdef DEBUG_DISAS + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT))) { + qemu_log("OP after liveness analysis:\n"); + tcg_dump_ops(s, logfile); + qemu_log("\n"); + } +#endif + + tcg_reg_alloc_start(s); + + s->code_buf = gen_code_buf; + s->code_ptr = gen_code_buf; + + args = gen_opparam_buf; + op_index = 0; + + for(;;) { + opc = gen_opc_buf[op_index]; +#ifdef CONFIG_PROFILER + tcg_table_op_count[opc]++; +#endif + def = &tcg_op_defs[opc]; +#if 0 + printf("%s: %d %d %d\n", def->name, + def->nb_oargs, def->nb_iargs, def->nb_cargs); + // dump_regs(s); +#endif + switch(opc) { + case INDEX_op_mov_i32: +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_mov_i64: +#endif + dead_iargs = s->op_dead_iargs[op_index]; + tcg_reg_alloc_mov(s, def, args, dead_iargs); + break; + case INDEX_op_movi_i32: +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_movi_i64: +#endif + tcg_reg_alloc_movi(s, args); + break; + case INDEX_op_debug_insn_start: + /* debug instruction */ + break; + case INDEX_op_nop: + case INDEX_op_nop1: + case INDEX_op_nop2: + case INDEX_op_nop3: + break; + case INDEX_op_nopn: + args += args[0]; + goto next; + case INDEX_op_discard: + { + TCGTemp *ts; + ts = &s->temps[args[0]]; + /* mark the temporary as dead */ + if (!ts->fixed_reg) { + if (ts->val_type == TEMP_VAL_REG) + s->reg_to_temp[ts->reg] = -1; + ts->val_type = TEMP_VAL_DEAD; + } + } + break; + case INDEX_op_set_label: + tcg_reg_alloc_bb_end(s, s->reserved_regs); + tcg_out_label(s, args[0], (long)s->code_ptr); + break; + case INDEX_op_call: + dead_iargs = s->op_dead_iargs[op_index]; + args += tcg_reg_alloc_call(s, def, opc, args, dead_iargs); + goto next; + case INDEX_op_end: + goto the_end; + default: + /* Note: in order to speed up the code, it would be much + faster to have specialized register allocator functions for + some common argument patterns */ + dead_iargs = s->op_dead_iargs[op_index]; + tcg_reg_alloc_op(s, def, opc, args, dead_iargs); + break; + } + args += def->nb_args; + next: + if (search_pc >= 0 && search_pc < s->code_ptr - gen_code_buf) { + return op_index; + } + op_index++; +#ifndef NDEBUG + check_regs(s); +#endif + } + the_end: + return -1; +} + +int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf) +{ +#ifdef CONFIG_PROFILER + { + int n; + n = (gen_opc_ptr - gen_opc_buf); + s->op_count += n; + if (n > s->op_count_max) + s->op_count_max = n; + + s->temp_count += s->nb_temps; + if (s->nb_temps > s->temp_count_max) + s->temp_count_max = s->nb_temps; + } +#endif + + tcg_gen_code_common(s, gen_code_buf, -1); + + /* flush instruction cache */ + flush_icache_range((unsigned long)gen_code_buf, + (unsigned long)s->code_ptr); + return s->code_ptr - gen_code_buf; +} + +/* Return the index of the micro operation such as the pc after is < + offset bytes from the start of the TB. The contents of gen_code_buf must + not be changed, though writing the same values is ok. + Return -1 if not found. */ +int tcg_gen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset) +{ + return tcg_gen_code_common(s, gen_code_buf, offset); +} + +#ifdef CONFIG_PROFILER +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) +{ + TCGContext *s = &tcg_ctx; + int64_t tot; + + tot = s->interm_time + s->code_time; + cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n", + tot, tot / 2.4e9); + cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", + s->tb_count, + s->tb_count1 - s->tb_count, + s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0); + cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n", + s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max); + cpu_fprintf(f, "deleted ops/TB %0.2f\n", + s->tb_count ? + (double)s->del_op_count / s->tb_count : 0); + cpu_fprintf(f, "avg temps/TB %0.2f max=%d\n", + s->tb_count ? + (double)s->temp_count / s->tb_count : 0, + s->temp_count_max); + + cpu_fprintf(f, "cycles/op %0.1f\n", + s->op_count ? (double)tot / s->op_count : 0); + cpu_fprintf(f, "cycles/in byte %0.1f\n", + s->code_in_len ? (double)tot / s->code_in_len : 0); + cpu_fprintf(f, "cycles/out byte %0.1f\n", + s->code_out_len ? (double)tot / s->code_out_len : 0); + if (tot == 0) + tot = 1; + cpu_fprintf(f, " gen_interm time %0.1f%%\n", + (double)s->interm_time / tot * 100.0); + cpu_fprintf(f, " gen_code time %0.1f%%\n", + (double)s->code_time / tot * 100.0); + cpu_fprintf(f, "liveness/code time %0.1f%%\n", + (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0); + cpu_fprintf(f, "cpu_restore count %" PRId64 "\n", + s->restore_count); + cpu_fprintf(f, " avg cycles %0.1f\n", + s->restore_count ? (double)s->restore_time / s->restore_count : 0); + + dump_op_count(); +} +#else +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) +{ + cpu_fprintf(f, "[TCG profiler not compiled]\n"); +} +#endif diff --git a/tcg/tcg.h b/tcg/tcg.h new file mode 100644 index 0000000..e1afde2 --- /dev/null +++ b/tcg/tcg.h @@ -0,0 +1,489 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "tcg-target.h" +#include "tcg-runtime.h" + +#if TCG_TARGET_REG_BITS == 32 +typedef int32_t tcg_target_long; +typedef uint32_t tcg_target_ulong; +#define TCG_PRIlx PRIx32 +#define TCG_PRIld PRId32 +#elif TCG_TARGET_REG_BITS == 64 +typedef int64_t tcg_target_long; +typedef uint64_t tcg_target_ulong; +#define TCG_PRIlx PRIx64 +#define TCG_PRIld PRId64 +#else +#error unsupported +#endif + +#if TCG_TARGET_NB_REGS <= 32 +typedef uint32_t TCGRegSet; +#elif TCG_TARGET_NB_REGS <= 64 +typedef uint64_t TCGRegSet; +#else +#error unsupported +#endif + +typedef enum TCGOpcode { +#define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, +#include "tcg-opc.h" +#undef DEF + NB_OPS, +} TCGOpcode; + +#define tcg_regset_clear(d) (d) = 0 +#define tcg_regset_set(d, s) (d) = (s) +#define tcg_regset_set32(d, reg, val32) (d) |= (val32) << (reg) +#define tcg_regset_set_reg(d, r) (d) |= 1L << (r) +#define tcg_regset_reset_reg(d, r) (d) &= ~(1L << (r)) +#define tcg_regset_test_reg(d, r) (((d) >> (r)) & 1) +#define tcg_regset_or(d, a, b) (d) = (a) | (b) +#define tcg_regset_and(d, a, b) (d) = (a) & (b) +#define tcg_regset_andnot(d, a, b) (d) = (a) & ~(b) +#define tcg_regset_not(d, a) (d) = ~(a) + +typedef struct TCGRelocation { + struct TCGRelocation *next; + int type; + uint8_t *ptr; + tcg_target_long addend; +} TCGRelocation; + +typedef struct TCGLabel { + int has_value; + union { + tcg_target_ulong value; + TCGRelocation *first_reloc; + } u; +} TCGLabel; + +typedef struct TCGPool { + struct TCGPool *next; + int size; + uint8_t data[0] __attribute__ ((aligned)); +} TCGPool; + +#define TCG_POOL_CHUNK_SIZE 32768 + +#define TCG_MAX_LABELS 512 + +#define TCG_MAX_TEMPS 512 + +/* when the size of the arguments of a called function is smaller than + this value, they are statically allocated in the TB stack frame */ +#define TCG_STATIC_CALL_ARGS_SIZE 128 + +typedef enum TCGType { + TCG_TYPE_I32, + TCG_TYPE_I64, + TCG_TYPE_COUNT, /* number of different types */ + + /* An alias for the size of the host register. */ +#if TCG_TARGET_REG_BITS == 32 + TCG_TYPE_REG = TCG_TYPE_I32, +#else + TCG_TYPE_REG = TCG_TYPE_I64, +#endif + + /* An alias for the size of the native pointer. We don't currently + support any hosts with 64-bit registers and 32-bit pointers. */ + TCG_TYPE_PTR = TCG_TYPE_REG, + + /* An alias for the size of the target "long", aka register. */ +#if TARGET_LONG_BITS == 64 + TCG_TYPE_TL = TCG_TYPE_I64, +#else + TCG_TYPE_TL = TCG_TYPE_I32, +#endif +} TCGType; + +typedef tcg_target_ulong TCGArg; + +/* Define a type and accessor macros for varables. Using a struct is + nice because it gives some level of type safely. Ideally the compiler + be able to see through all this. However in practice this is not true, + expecially on targets with braindamaged ABIs (e.g. i386). + We use plain int by default to avoid this runtime overhead. + Users of tcg_gen_* don't need to know about any of this, and should + treat TCGv as an opaque type. + In additon we do typechecking for different types of variables. TCGv_i32 + and TCGv_i64 are 32/64-bit variables respectively. TCGv and TCGv_ptr + are aliases for target_ulong and host pointer sized values respectively. + */ + +#ifdef CONFIG_DEBUG_TCG +#define DEBUG_TCGV 1 +#endif + +#ifdef DEBUG_TCGV + +typedef struct +{ + int i32; +} TCGv_i32; + +typedef struct +{ + int i64; +} TCGv_i64; + +#define MAKE_TCGV_I32(i) __extension__ \ + ({ TCGv_i32 make_tcgv_tmp = {i}; make_tcgv_tmp;}) +#define MAKE_TCGV_I64(i) __extension__ \ + ({ TCGv_i64 make_tcgv_tmp = {i}; make_tcgv_tmp;}) +#define GET_TCGV_I32(t) ((t).i32) +#define GET_TCGV_I64(t) ((t).i64) +#if TCG_TARGET_REG_BITS == 32 +#define TCGV_LOW(t) MAKE_TCGV_I32(GET_TCGV_I64(t)) +#define TCGV_HIGH(t) MAKE_TCGV_I32(GET_TCGV_I64(t) + 1) +#endif + +#else /* !DEBUG_TCGV */ + +typedef int TCGv_i32; +typedef int TCGv_i64; +#define MAKE_TCGV_I32(x) (x) +#define MAKE_TCGV_I64(x) (x) +#define GET_TCGV_I32(t) (t) +#define GET_TCGV_I64(t) (t) + +#if TCG_TARGET_REG_BITS == 32 +#define TCGV_LOW(t) (t) +#define TCGV_HIGH(t) ((t) + 1) +#endif + +#endif /* DEBUG_TCGV */ + +#define TCGV_EQUAL_I32(a, b) (GET_TCGV_I32(a) == GET_TCGV_I32(b)) +#define TCGV_EQUAL_I64(a, b) (GET_TCGV_I64(a) == GET_TCGV_I64(b)) + +/* Dummy definition to avoid compiler warnings. */ +#define TCGV_UNUSED_I32(x) x = MAKE_TCGV_I32(-1) +#define TCGV_UNUSED_I64(x) x = MAKE_TCGV_I64(-1) + +/* call flags */ +#define TCG_CALL_TYPE_MASK 0x000f +#define TCG_CALL_TYPE_STD 0x0000 /* standard C call */ +#define TCG_CALL_TYPE_REGPARM_1 0x0001 /* i386 style regparm call (1 reg) */ +#define TCG_CALL_TYPE_REGPARM_2 0x0002 /* i386 style regparm call (2 regs) */ +#define TCG_CALL_TYPE_REGPARM 0x0003 /* i386 style regparm call (3 regs) */ +/* A pure function only reads its arguments and TCG global variables + and cannot raise exceptions. Hence a call to a pure function can be + safely suppressed if the return value is not used. */ +#define TCG_CALL_PURE 0x0010 +/* A const function only reads its arguments and does not use TCG + global variables. Hence a call to such a function does not + save TCG global variables back to their canonical location. */ +#define TCG_CALL_CONST 0x0020 + +/* used to align parameters */ +#define TCG_CALL_DUMMY_TCGV MAKE_TCGV_I32(-1) +#define TCG_CALL_DUMMY_ARG ((TCGArg)(-1)) + +typedef enum { + TCG_COND_EQ, + TCG_COND_NE, + TCG_COND_LT, + TCG_COND_GE, + TCG_COND_LE, + TCG_COND_GT, + /* unsigned */ + TCG_COND_LTU, + TCG_COND_GEU, + TCG_COND_LEU, + TCG_COND_GTU, +} TCGCond; + +/* Invert the sense of the comparison. */ +static inline TCGCond tcg_invert_cond(TCGCond c) +{ + return (TCGCond)(c ^ 1); +} + +/* Swap the operands in a comparison. */ +static inline TCGCond tcg_swap_cond(TCGCond c) +{ + int mask = (c < TCG_COND_LT ? 0 : c < TCG_COND_LTU ? 7 : 15); + return (TCGCond)(c ^ mask); +} + +static inline TCGCond tcg_unsigned_cond(TCGCond c) +{ + return (c >= TCG_COND_LT && c <= TCG_COND_GT ? c + 4 : c); +} + +#define TEMP_VAL_DEAD 0 +#define TEMP_VAL_REG 1 +#define TEMP_VAL_MEM 2 +#define TEMP_VAL_CONST 3 + +/* XXX: optimize memory layout */ +typedef struct TCGTemp { + TCGType base_type; + TCGType type; + int val_type; + int reg; + tcg_target_long val; + int mem_reg; + tcg_target_long mem_offset; + unsigned int fixed_reg:1; + unsigned int mem_coherent:1; + unsigned int mem_allocated:1; + unsigned int temp_local:1; /* If true, the temp is saved accross + basic blocks. Otherwise, it is not + preserved accross basic blocks. */ + unsigned int temp_allocated:1; /* never used for code gen */ + /* index of next free temp of same base type, -1 if end */ + int next_free_temp; + const char *name; +} TCGTemp; + +typedef struct TCGHelperInfo { + tcg_target_ulong func; + const char *name; +} TCGHelperInfo; + +typedef struct TCGContext TCGContext; + +struct TCGContext { + uint8_t *pool_cur, *pool_end; + TCGPool *pool_first, *pool_current; + TCGLabel *labels; + int nb_labels; + TCGTemp *temps; /* globals first, temps after */ + int nb_globals; + int nb_temps; + /* index of free temps, -1 if none */ + int first_free_temp[TCG_TYPE_COUNT * 2]; + + /* goto_tb support */ + uint8_t *code_buf; + unsigned long *tb_next; + uint16_t *tb_next_offset; + uint16_t *tb_jmp_offset; /* != NULL if USE_DIRECT_JUMP */ + + /* liveness analysis */ + uint16_t *op_dead_iargs; /* for each operation, each bit tells if the + corresponding input argument is dead */ + + /* tells in which temporary a given register is. It does not take + into account fixed registers */ + int reg_to_temp[TCG_TARGET_NB_REGS]; + TCGRegSet reserved_regs; + tcg_target_long current_frame_offset; + tcg_target_long frame_start; + tcg_target_long frame_end; + int frame_reg; + + uint8_t *code_ptr; + TCGTemp static_temps[TCG_MAX_TEMPS]; + + TCGHelperInfo *helpers; + int nb_helpers; + int allocated_helpers; + int helpers_sorted; + +#ifdef CONFIG_PROFILER + /* profiling info */ + int64_t tb_count1; + int64_t tb_count; + int64_t op_count; /* total insn count */ + int op_count_max; /* max insn per TB */ + int64_t temp_count; + int temp_count_max; + int64_t del_op_count; + int64_t code_in_len; + int64_t code_out_len; + int64_t interm_time; + int64_t code_time; + int64_t la_time; + int64_t restore_count; + int64_t restore_time; +#endif +}; + +extern TCGContext tcg_ctx; +extern uint16_t *gen_opc_ptr; +extern TCGArg *gen_opparam_ptr; +extern uint16_t gen_opc_buf[]; +extern TCGArg gen_opparam_buf[]; + +/* pool based memory allocation */ + +void *tcg_malloc_internal(TCGContext *s, int size); +void tcg_pool_reset(TCGContext *s); +void tcg_pool_delete(TCGContext *s); + +static inline void *tcg_malloc(int size) +{ + TCGContext *s = &tcg_ctx; + uint8_t *ptr, *ptr_end; + size = (size + sizeof(long) - 1) & ~(sizeof(long) - 1); + ptr = s->pool_cur; + ptr_end = ptr + size; + if (unlikely(ptr_end > s->pool_end)) { + return tcg_malloc_internal(&tcg_ctx, size); + } else { + s->pool_cur = ptr_end; + return ptr; + } +} + +void tcg_context_init(TCGContext *s); +void tcg_prologue_init(TCGContext *s); +void tcg_func_start(TCGContext *s); + +int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf); +int tcg_gen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset); + +void tcg_set_frame(TCGContext *s, int reg, + tcg_target_long start, tcg_target_long size); + +TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name); +TCGv_i32 tcg_global_mem_new_i32(int reg, tcg_target_long offset, + const char *name); +TCGv_i32 tcg_temp_new_internal_i32(int temp_local); +static inline TCGv_i32 tcg_temp_new_i32(void) +{ + return tcg_temp_new_internal_i32(0); +} +static inline TCGv_i32 tcg_temp_local_new_i32(void) +{ + return tcg_temp_new_internal_i32(1); +} +void tcg_temp_free_i32(TCGv_i32 arg); +char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg); + +TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name); +TCGv_i64 tcg_global_mem_new_i64(int reg, tcg_target_long offset, + const char *name); +TCGv_i64 tcg_temp_new_internal_i64(int temp_local); +static inline TCGv_i64 tcg_temp_new_i64(void) +{ + return tcg_temp_new_internal_i64(0); +} +static inline TCGv_i64 tcg_temp_local_new_i64(void) +{ + return tcg_temp_new_internal_i64(1); +} +void tcg_temp_free_i64(TCGv_i64 arg); +char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg); + +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf); + +#define TCG_CT_ALIAS 0x80 +#define TCG_CT_IALIAS 0x40 +#define TCG_CT_REG 0x01 +#define TCG_CT_CONST 0x02 /* any constant of register size */ + +typedef struct TCGArgConstraint { + uint16_t ct; + uint8_t alias_index; + union { + TCGRegSet regs; + } u; +} TCGArgConstraint; + +#define TCG_MAX_OP_ARGS 16 + +#define TCG_OPF_BB_END 0x01 /* instruction defines the end of a basic + block */ +#define TCG_OPF_CALL_CLOBBER 0x02 /* instruction clobbers call registers + and potentially update globals. */ +#define TCG_OPF_SIDE_EFFECTS 0x04 /* instruction has side effects : it + cannot be removed if its output + are not used */ + +typedef struct TCGOpDef { + const char *name; + uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; + uint8_t flags; + TCGArgConstraint *args_ct; + int *sorted_args; +#if defined(CONFIG_DEBUG_TCG) + int used; +#endif +} TCGOpDef; + +typedef struct TCGTargetOpDef { + TCGOpcode op; + const char *args_ct_str[TCG_MAX_OP_ARGS]; +} TCGTargetOpDef; + +#define tcg_abort() \ +do {\ + fprintf(stderr, "%s:%d: tcg fatal error\n", __FILE__, __LINE__);\ + abort();\ +} while (0) + +void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs); + +#if TCG_TARGET_REG_BITS == 32 +#define tcg_const_ptr tcg_const_i32 +#define tcg_add_ptr tcg_add_i32 +#define tcg_sub_ptr tcg_sub_i32 +#define TCGv_ptr TCGv_i32 +#define GET_TCGV_PTR GET_TCGV_I32 +#define tcg_global_reg_new_ptr tcg_global_reg_new_i32 +#define tcg_global_mem_new_ptr tcg_global_mem_new_i32 +#define tcg_temp_new_ptr tcg_temp_new_i32 +#define tcg_temp_free_ptr tcg_temp_free_i32 +#else +#define tcg_const_ptr tcg_const_i64 +#define tcg_add_ptr tcg_add_i64 +#define tcg_sub_ptr tcg_sub_i64 +#define TCGv_ptr TCGv_i64 +#define GET_TCGV_PTR GET_TCGV_I64 +#define tcg_global_reg_new_ptr tcg_global_reg_new_i64 +#define tcg_global_mem_new_ptr tcg_global_mem_new_i64 +#define tcg_temp_new_ptr tcg_temp_new_i64 +#define tcg_temp_free_ptr tcg_temp_free_i64 +#endif + +void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, + int sizemask, TCGArg ret, int nargs, TCGArg *args); + +void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, + int c, int right, int arith); + +/* only used for debugging purposes */ +void tcg_register_helper(void *func, const char *name); +const char *tcg_helper_get_name(TCGContext *s, void *func); +void tcg_dump_ops(TCGContext *s, FILE *outfile); + +void dump_ops(const uint16_t *opc_buf, const TCGArg *opparam_buf); +TCGv_i32 tcg_const_i32(int32_t val); +TCGv_i64 tcg_const_i64(int64_t val); +TCGv_i32 tcg_const_local_i32(int32_t val); +TCGv_i64 tcg_const_local_i64(int64_t val); + +extern uint8_t code_gen_prologue[]; +#if defined(_ARCH_PPC) && !defined(_ARCH_PPC64) +#define tcg_qemu_tb_exec(tb_ptr) \ + ((long REGPARM __attribute__ ((longcall)) (*)(void *))code_gen_prologue)(tb_ptr) +#else +#define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM (*)(void *))code_gen_prologue)(tb_ptr) +#endif |