diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 38c34009a5d..5bb83659b81 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -18,6 +18,7 @@ static void set_can_do_io(DisasContextBase *db, bool val) { +#ifndef CONFIG_LIBTCG if (db->saved_can_do_io != val) { db->saved_can_do_io = val; @@ -26,6 +27,7 @@ static void set_can_do_io(DisasContextBase *db, bool val) offsetof(ArchCPU, parent_obj.neg.can_do_io) - offsetof(ArchCPU, env)); } +#endif } bool translator_io_start(DisasContextBase *db) @@ -129,7 +131,9 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, { uint32_t cflags = tb_cflags(tb); TCGOp *icount_start_insn; +#ifndef CONFIG_LIBTCG bool plugin_enabled; +#endif /* Initialize DisasContext */ db->tb = tb; @@ -148,20 +152,25 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, /* Start translating. */ icount_start_insn = gen_tb_start(db, cflags); +#ifndef CONFIG_LIBTCG ops->tb_start(db, cpu); +#ifndef CONFIG_LIBTCG tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); db->plugin_enabled = plugin_enabled; +#endif while (true) { *max_insns = ++db->num_insns; ops->insn_start(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ +#ifndef CONFIG_LIBTCG if (plugin_enabled) { plugin_gen_insn_start(cpu, db); } +#endif /* * Disassemble one instruction. The translate_insn hook should @@ -184,9 +193,11 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, * needs to see a matching plugin_gen_insn_{start,end}() pair in order * to accurately track instrumented helpers that might access memory. */ +#ifndef CONFIG_LIBTCG if (plugin_enabled) { plugin_gen_insn_end(); } +#endif /* Stop translation if translate_insn so indicated. */ if (db->is_jmp != DISAS_NEXT) { @@ -195,7 +206,8 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, /* Stop translation if the output buffer is full, or we have executed all of the allowed instructions. */ - if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { + if (tcg_op_buf_full() || db->num_insns >= db->max_insns || + db->pc_next >= tb->max_pc) { db->is_jmp = DISAS_TOO_MANY; break; } @@ -205,9 +217,11 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, ops->tb_stop(db, cpu); gen_tb_end(tb, cflags, icount_start_insn, db->num_insns); +#ifndef CONFIG_LIBTCG if (plugin_enabled) { plugin_gen_tb_end(cpu, db->num_insns); } +#endif /* The disas_log hook may use these values rather than recompute. */ tb->size = db->pc_next - db->pc_first; @@ -225,6 +239,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, } } +#ifndef CONFIG_LIBTSCG static void *translator_access(CPUArchState *env, DisasContextBase *db, vaddr pc, size_t len) { @@ -289,6 +304,7 @@ static void *translator_access(CPUArchState *env, DisasContextBase *db, tcg_debug_assert(pc >= base); return host + (pc - base); } +#endif static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) { @@ -314,59 +330,28 @@ static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) { uint8_t ret; - void *p = translator_access(env, db, pc, sizeof(ret)); - - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldub_p(p); - } ret = cpu_ldub_code(env, pc); - plugin_insn_append(pc, &ret, sizeof(ret)); return ret; } uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) { - uint16_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); - - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return lduw_p(p); - } + uint16_t ret; ret = cpu_lduw_code(env, pc); - plug = tswap16(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); return ret; } uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) { - uint32_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); - - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldl_p(p); - } + uint32_t ret; ret = cpu_ldl_code(env, pc); - plug = tswap32(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); return ret; } uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) { - uint64_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); - - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldq_p(p); - } + uint64_t ret; ret = cpu_ldq_code(env, pc); - plug = tswap64(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); return ret; } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 68b252cb8e8..6d6b6222c8d 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -1121,6 +1121,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, clear_helper_retaddr(); } +#ifndef CONFIG_LIBTCG uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) { uint32_t ret; @@ -1160,6 +1161,7 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) clear_helper_retaddr(); return ret; } +#endif uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) diff --git a/configure b/configure index d7e0926ff19..ca6b070d66e 100755 --- a/configure +++ b/configure @@ -733,6 +733,10 @@ for opt do ;; --enable-bsd-user) bsd_user="yes" ;; + --disable-libtcg) libtcg="no" + ;; + --enable-libtcg) libtcg="yes"; linux_user="yes"; + ;; --enable-pie) pie="yes" ;; --disable-pie) pie="no" @@ -1828,6 +1832,8 @@ if test "$skip_meson" = no; then test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE" test "$plugins" = yes && meson_option_add "-Dplugins=true" test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg" + test "$libtcg" = yes && meson_option_add -Dlibtcg=true + test "$libtcg" = yes && meson_option_add -Db_staticpic=true run_meson() { NINJA=$ninja $meson setup "$@" "$PWD" "$source_path" } diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index e2b26e16da1..58f784e3298 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -63,6 +63,8 @@ struct TranslationBlock { */ uint64_t cs_base; + vaddr max_pc; /* maximum PC for this block */ + uint32_t flags; /* flags defining in which context the code was generated */ uint32_t cflags; /* compile flags */ diff --git a/include/libtcg/libtcg.h b/include/libtcg/libtcg.h new file mode 100644 index 00000000000..f18d2451e8f --- /dev/null +++ b/include/libtcg/libtcg.h @@ -0,0 +1,459 @@ +#ifndef LIBTCG_H +#define LIBTCG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBTCG_INSN_MAX_ARGS 16 +#define LIBTCG_MAX_NAME_LEN 32 +#define LIBTCG_MAX_TEMPS 512 +#define LIBTCG_MAX_LABELS 512 +#define LIBTCG_MAX_INSTRUCTIONS 1024 + +/* + * We start out with a bunch of constants and enums taken from + * various `tcg/...` files. + */ + +/* + * Taken from `tcg/tcg.h` + * Needed by `tcg/tcg-opc.h` + */ +#ifndef TCG_TARGET_REG_BITS +# if UINTPTR_MAX == UINT32_MAX +# define TCG_TARGET_REG_BITS 32 +# elif UINTPTR_MAX == UINT64_MAX +# define TCG_TARGET_REG_BITS 64 +# else +# error Unknown pointer size for tcg target +# endif +#endif + +/* + * TODO(anjo): I explicitly exclude vector instructions here + * as they are very target dependent. Is this what we wanna do? + * Needed by `tcg/tcg-opc.h` + */ +#ifndef TCG_TARGET_MAYBE_vec +#define TCG_TARGET_MAYBE_vec 0 +#endif + +/* Taken from `tcg/tcg.h` */ +typedef enum LibTcgOpcode { +#define DEF(name, oargs, iargs, cargs, flags) LIBTCG_op_ ## name, +#include "tcg/tcg-opc.h" +#undef DEF + LIBTCG_NB_OPS, +} LibTcgOpcode; + +/* Taken from exec/memop.h */ +typedef enum LibTcgMemOp { + LIBTCG_MO_8 = 0, + LIBTCG_MO_16 = 1, + LIBTCG_MO_32 = 2, + LIBTCG_MO_64 = 3, + LIBTCG_MO_128 = 4, + LIBTCG_MO_256 = 5, + LIBTCG_MO_512 = 6, + LIBTCG_MO_1024 = 7, + LIBTCG_MO_SIZE = 0x07, /* Mask for the above. */ + + LIBTCG_MO_SIGN = 0x08, /* Sign-extended, otherwise zero-extended. */ + + LIBTCG_MO_BSWAP = 0x10, /* Host reverse endian. */ +//#if HOST_BIG_ENDIAN +// LIBTCG_MO_LE = LIBTCG_MO_BSWAP, +// LIBTCG_MO_BE = 0, +//#else +// LIBTCG_MO_LE = 0, +// LIBTCG_MO_BE = LIBTCG_MO_BSWAP, +//#endif + + /* + * MO_UNALN accesses are never checked for alignment. + * MO_ALIGN accesses will result in a call to the CPU's + * do_unaligned_access hook if the guest address is not aligned. + * + * Some architectures (e.g. ARMv8) need the address which is aligned + * to a size more than the size of the memory access. + * Some architectures (e.g. SPARCv9) need an address which is aligned, + * but less strictly than the natural alignment. + * + * MO_ALIGN supposes the alignment size is the size of a memory access. + * + * There are three options: + * - unaligned access permitted (MO_UNALN). + * - an alignment to the size of an access (MO_ALIGN); + * - an alignment to a specified size, which may be more or less than + * the access size (MO_ALIGN_x where 'x' is a size in bytes); + */ + LIBTCG_MO_ASHIFT = 5, + LIBTCG_MO_AMASK = 0x7 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_UNALN = 0, + LIBTCG_MO_ALIGN_2 = 1 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_ALIGN_4 = 2 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_ALIGN_8 = 3 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_ALIGN_16 = 4 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_ALIGN_32 = 5 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_ALIGN_64 = 6 << LIBTCG_MO_ASHIFT, + LIBTCG_MO_ALIGN = LIBTCG_MO_AMASK, + + /* + * MO_ATOM_* describes the atomicity requirements of the operation: + * MO_ATOM_IFALIGN: the operation must be single-copy atomic if it + * is aligned; if unaligned there is no atomicity. + * MO_ATOM_IFALIGN_PAIR: the entire operation may be considered to + * be a pair of half-sized operations which are packed together + * for convenience, with single-copy atomicity on each half if + * the half is aligned. + * This is the atomicity e.g. of Arm pre-FEAT_LSE2 LDP. + * MO_ATOM_WITHIN16: the operation is single-copy atomic, even if it + * is unaligned, so long as it does not cross a 16-byte boundary; + * if it crosses a 16-byte boundary there is no atomicity. + * This is the atomicity e.g. of Arm FEAT_LSE2 LDR. + * MO_ATOM_WITHIN16_PAIR: the entire operation is single-copy atomic, + * if it happens to be within a 16-byte boundary, otherwise it + * devolves to a pair of half-sized MO_ATOM_WITHIN16 operations. + * Depending on alignment, one or both will be single-copy atomic. + * This is the atomicity e.g. of Arm FEAT_LSE2 LDP. + * MO_ATOM_SUBALIGN: the operation is single-copy atomic by parts + * by the alignment. E.g. if the address is 0 mod 4, then each + * 4-byte subobject is single-copy atomic. + * This is the atomicity e.g. of IBM Power. + * MO_ATOM_NONE: the operation has no atomicity requirements. + * + * Note the default (i.e. 0) value is single-copy atomic to the + * size of the operation, if aligned. This retains the behaviour + * from before this field was introduced. + */ + LIBTCG_MO_ATOM_SHIFT = 8, + LIBTCG_MO_ATOM_IFALIGN = 0 << LIBTCG_MO_ATOM_SHIFT, + LIBTCG_MO_ATOM_IFALIGN_PAIR = 1 << LIBTCG_MO_ATOM_SHIFT, + LIBTCG_MO_ATOM_WITHIN16 = 2 << LIBTCG_MO_ATOM_SHIFT, + LIBTCG_MO_ATOM_WITHIN16_PAIR = 3 << LIBTCG_MO_ATOM_SHIFT, + LIBTCG_MO_ATOM_SUBALIGN = 4 << LIBTCG_MO_ATOM_SHIFT, + LIBTCG_MO_ATOM_NONE = 5 << LIBTCG_MO_ATOM_SHIFT, + LIBTCG_MO_ATOM_MASK = 7 << LIBTCG_MO_ATOM_SHIFT, + + /* Combinations of the above, for ease of use. */ + LIBTCG_MO_UB = LIBTCG_MO_8, + LIBTCG_MO_UW = LIBTCG_MO_16, + LIBTCG_MO_UL = LIBTCG_MO_32, + LIBTCG_MO_UQ = LIBTCG_MO_64, + LIBTCG_MO_UO = LIBTCG_MO_128, + LIBTCG_MO_SB = LIBTCG_MO_SIGN | LIBTCG_MO_8, + LIBTCG_MO_SW = LIBTCG_MO_SIGN | LIBTCG_MO_16, + LIBTCG_MO_SL = LIBTCG_MO_SIGN | LIBTCG_MO_32, + LIBTCG_MO_SQ = LIBTCG_MO_SIGN | LIBTCG_MO_64, + LIBTCG_MO_SO = LIBTCG_MO_SIGN | LIBTCG_MO_128, + +// LIBTCG_MO_LEUW = LIBTCG_MO_LE | LIBTCG_MO_UW, +// LIBTCG_MO_LEUL = LIBTCG_MO_LE | LIBTCG_MO_UL, +// LIBTCG_MO_LEUQ = LIBTCG_MO_LE | LIBTCG_MO_UQ, +// LIBTCG_MO_LESW = LIBTCG_MO_LE | LIBTCG_MO_SW, +// LIBTCG_MO_LESL = LIBTCG_MO_LE | LIBTCG_MO_SL, +// LIBTCG_MO_LESQ = LIBTCG_MO_LE | LIBTCG_MO_SQ, +// +// LIBTCG_MO_BEUW = LIBTCG_MO_BE | LIBTCG_MO_UW, +// LIBTCG_MO_BEUL = LIBTCG_MO_BE | LIBTCG_MO_UL, +// LIBTCG_MO_BEUQ = LIBTCG_MO_BE | LIBTCG_MO_UQ, +// LIBTCG_MO_BESW = LIBTCG_MO_BE | LIBTCG_MO_SW, +// LIBTCG_MO_BESL = LIBTCG_MO_BE | LIBTCG_MO_SL, +// LIBTCG_MO_BESQ = LIBTCG_MO_BE | LIBTCG_MO_SQ, + + LIBTCG_MO_SSIZE = LIBTCG_MO_SIZE | LIBTCG_MO_SIGN, +} LibTcgMemOp; + +/* More MemOp stuff taken from `tcg/tcg.h` */ + +typedef uint32_t LibTcgMemOpIdx; + +inline LibTcgMemOp libtcg_get_memop(LibTcgMemOpIdx oi) +{ + return (LibTcgMemOp) (oi >> 4); +} + +inline unsigned libtcg_get_mmuidx(LibTcgMemOpIdx oi) +{ + return oi & 15; +} + +/* Taken from tcg/tcg.h */ +typedef enum LibTcgBSwap{ + LIBTCG_BSWAP_IZ = 1, + LIBTCG_BSWAP_OZ = 2, + LIBTCG_BSWAP_OS = 4, +} LibTcgBSwap; + +/* Taken from tcg/tcg-cond.h */ +typedef enum LibTcgCond { + /* non-signed */ + LIBTCG_COND_NEVER = 0 | 0 | 0 | 0, + LIBTCG_COND_ALWAYS = 0 | 0 | 0 | 1, + LIBTCG_COND_EQ = 8 | 0 | 0 | 0, + LIBTCG_COND_NE = 8 | 0 | 0 | 1, + /* signed */ + LIBTCG_COND_LT = 0 | 0 | 2 | 0, + LIBTCG_COND_GE = 0 | 0 | 2 | 1, + LIBTCG_COND_LE = 8 | 0 | 2 | 0, + LIBTCG_COND_GT = 8 | 0 | 2 | 1, + /* unsigned */ + LIBTCG_COND_LTU = 0 | 4 | 0 | 0, + LIBTCG_COND_GEU = 0 | 4 | 0 | 1, + LIBTCG_COND_LEU = 8 | 4 | 0 | 0, + LIBTCG_COND_GTU = 8 | 4 | 0 | 1, +} LibTcgCond; + +/* From `TCGTempKind` in `tcg/tcg.c` */ +typedef enum LibTcgTempKind { + /* + * Temp is dead at the end of the extended basic block (EBB), + * the single-entry multiple-exit region that falls through + * conditional branches. + */ + LIBTCG_TEMP_EBB, + /* Temp is live across the entire translation block, but dead at end. */ + LIBTCG_TEMP_TB, + /* Temp is live across the entire translation block, and between them. */ + LIBTCG_TEMP_GLOBAL, + /* Temp is in a fixed register. */ + LIBTCG_TEMP_FIXED, + /* Temp is a fixed constant. */ + LIBTCG_TEMP_CONST, +} LibTcgTempKind; + +/* From `TCGType` in `tcg/tcg.c` */ +typedef enum LibTcgTempType { + LIBTCG_TYPE_I32, + LIBTCG_TYPE_I64, + LIBTCG_TYPE_I128, + + /* TODO(anjo): Remove vector types? */ + LIBTCG_TYPE_V64, + LIBTCG_TYPE_V128, + LIBTCG_TYPE_V256, + + /* number of different types */ + LIBTCG_TYPE_COUNT, +} LibTcgTempType; + +/* From tcg/tcg.h */ +/* call flags */ +/* Helper does not read globals (either directly or through an exception). It + implies LIBTCG_TCG_CALL_NO_WRITE_GLOBALS. */ +#define LIBTCG_CALL_NO_READ_GLOBALS 0x0001 +/* Helper does not write globals */ +#define LIBTCG_CALL_NO_WRITE_GLOBALS 0x0002 +/* Helper can be safely suppressed if the return value is not used. */ +#define LIBTCG_CALL_NO_SIDE_EFFECTS 0x0004 +/* Helper is G_NORETURN. */ +#define LIBTCG_CALL_NO_RETURN 0x0008 +/* Helper is part of Plugins. */ +#define LIBTCG_CALL_PLUGIN 0x0010 + +/* + * Now we finally get into our adapted versions of the various + * TCG structs needed to represent our TCG op data. + */ + +typedef struct LibTcgTemp { + LibTcgTempKind kind; + LibTcgTempType type; + int64_t val; + uint32_t index; + intptr_t mem_offset; /* Only used by globals */ + char name[LIBTCG_MAX_NAME_LEN]; +} LibTcgTemp; + +typedef struct LibTcgLabel { + /* + * Currently `id` is the only field of the label used in + * dumping the tinycode instruction. There are more goodies + * in `tcg/tcg.h` tho. + */ + uint32_t id; +} LibTcgLabel; + +typedef struct LibTcgMemOpIndex { + LibTcgMemOp op; + unsigned mmu_index; +} LibTcgMemOpIndex; + +typedef enum LibTcgArgumentKind { + LIBTCG_ARG_CONSTANT, + LIBTCG_ARG_MEM_OP_INDEX, + LIBTCG_ARG_COND, + LIBTCG_ARG_BSWAP, + LIBTCG_ARG_TEMP, + LIBTCG_ARG_LABEL, +} LibTcgArgumentKind; + +/* + * Note that LIBTCG_ARG_CONSTANT, as in QEMU, can + * be a bit of whatever depending on context: + * - If it's an arg to a ld/st op then it usually contains MemOp flags; + * - If it's an arg to a bswap op is usually holds bswap flags; + * + * TODO(anjo): separate out arguments that are flags aswell, such as + * MemOp, Bswap. This will aid a lot in simpliyfing the dump + * function for instructions. + */ +typedef struct LibTcgArgument { + LibTcgArgumentKind kind; + union { + uint64_t constant; + LibTcgMemOpIndex mem_op_index; + LibTcgCond cond; + uint32_t bswap_flag; + LibTcgTemp *temp; + LibTcgLabel *label; + }; +} LibTcgArgument; + +typedef struct LibTcgHelperInfo { + const char *func_name; + /* + * TODO(anjo): Does the func_flags replace def.flags? + * In that case move func_flags -> insn.flags + */ + uint32_t func_flags; +} LibTcgHelperInfo; + +typedef struct LibTcgArchInfo { + uint16_t num_globals; + const char *arch_cpu_name; + intptr_t env_offset; + intptr_t exception_index; + intptr_t is_thumb; + intptr_t pc; + intptr_t sp; + intptr_t bp; +} LibTcgArchInfo; + +typedef struct LibTcgInstruction { + LibTcgOpcode opcode; + uint32_t flags; + /* + * Arguments are handled in the same way as in QEMU, + * so output args first, followed by input, followed + * by constants. Output and input arguments are temps. + */ + uint8_t nb_oargs; + uint8_t nb_iargs; + uint8_t nb_cargs; + uint8_t nb_args; + LibTcgArgument output_args[LIBTCG_INSN_MAX_ARGS]; + LibTcgArgument input_args[LIBTCG_INSN_MAX_ARGS]; + LibTcgArgument constant_args[LIBTCG_INSN_MAX_ARGS]; +} LibTcgInstruction; + +typedef struct LibTcgTranslationBlock { + LibTcgInstruction *list; + size_t instruction_count; + + /* Keeps track of all temporaries */ + LibTcgTemp *temps; + size_t temp_count; + + /* Keeps track of all labels */ + LibTcgLabel *labels; + size_t label_count; + + size_t size_in_bytes; +} LibTcgTranslationBlock; + +typedef enum LibTcgTranslateFlags { + LIBTCG_TRANSLATE_ARM_THUMB = 1, + LIBTCG_TRANSLATE_OPTIMIZE_TCG = 2, + LIBTCG_TRANSLATE_HELPER_TO_TCG = 4, +} LibTcgTranslateFlags; + +/* + * Lastly we have the functions we expose. + */ + +/* + * Description struct used in the creation of + * LibTcgContext. Allows specifying + * functions used for allocation/freeing + * memory. + * + * Zero-initialize to use default values + * (malloc/free). + */ +typedef struct LibTcgDesc { + void *(*mem_alloc)(size_t); + void (*mem_free)(void *); +} LibTcgDesc; + +struct LibTcgContext; +typedef struct LibTcgContext LibTcgContext; + +/* + * Following are a bunch of macros that help in defining a function prototype + * along with a typedef of the function type. + * + * NOTE(anjo): Not really a fan of this, but it does reduce the amount of + * function prototypes you need to keep in sync. :/ + */ + +/* Returns the name of the function's typedef */ +#define LIBTCG_FUNC_TYPE(name) \ + name ## _func + +/* Declares and typedefs a function */ +#define LIBTCG_EXPORT(ret, name, params) \ + ret name params; /* Function declaration */ \ + typedef ret LIBTCG_FUNC_TYPE(name) params /* Funciton typedef */ + +LIBTCG_EXPORT(const char *, libtcg_get_instruction_name, (LibTcgOpcode opcode)); +LIBTCG_EXPORT(LibTcgHelperInfo, libtcg_get_helper_info, (LibTcgInstruction *insn)); +LIBTCG_EXPORT(LibTcgArchInfo, libtcg_get_arch_info, (void)); +LIBTCG_EXPORT(LibTcgContext *, libtcg_context_create, (LibTcgDesc *desc)); +LIBTCG_EXPORT(void, libtcg_context_destroy, (LibTcgContext *context)); +LIBTCG_EXPORT(LibTcgTranslationBlock, libtcg_translate_block, (LibTcgContext *context, const unsigned char *buffer, size_t size, uint64_t virtual_address, uint32_t translate_flags)); +LIBTCG_EXPORT(void, libtcg_translation_block_destroy, (LibTcgContext *context, LibTcgTranslationBlock)); +LIBTCG_EXPORT(uint8_t *, libtcg_env_ptr, (LibTcgContext *context)); +LIBTCG_EXPORT(void, libtcg_dump_instruction_to_buffer, (LibTcgInstruction *insn, char *buf, size_t size)); +LIBTCG_EXPORT(void, libtcg_dump_instruction_name_to_buffer, (LibTcgInstruction *insn, char *buf, size_t size)); +LIBTCG_EXPORT(void, libtcg_dump_constant_arg_to_buffer, (LibTcgArgument *arg, char *buf, size_t size)); + +/* + * struct to help load functions we expose, + * useful when `dlopen`ing. + */ +typedef struct LibTcgInterface { + LIBTCG_FUNC_TYPE(libtcg_get_instruction_name) *get_instruction_name; + LIBTCG_FUNC_TYPE(libtcg_get_helper_info) *get_helper_info; + LIBTCG_FUNC_TYPE(libtcg_get_arch_info) *get_arch_info; + LIBTCG_FUNC_TYPE(libtcg_context_create) *context_create; + LIBTCG_FUNC_TYPE(libtcg_context_destroy) *context_destroy; + LIBTCG_FUNC_TYPE(libtcg_translate_block) *translate_block; + LIBTCG_FUNC_TYPE(libtcg_translation_block_destroy) *translation_block_destroy; + LIBTCG_FUNC_TYPE(libtcg_env_ptr) *env_ptr; + LIBTCG_FUNC_TYPE(libtcg_dump_instruction_to_buffer) *dump_instruction_to_buffer; + LIBTCG_FUNC_TYPE(libtcg_dump_instruction_name_to_buffer) *dump_instruction_name_to_buffer; + LIBTCG_FUNC_TYPE(libtcg_dump_constant_arg_to_buffer) *dump_constant_arg_to_buffer; +} LibTcgInterface; + +/* + * Last function we export takes care of creating/populating a LibTcgInterface. + * This is the only funciton needing to be manually loaded using `dlsym`. + */ +LIBTCG_EXPORT(LibTcgInterface, libtcg_load, (void)); + +#undef LIBTCG_EXPORT +/* + * NOTE(anjo): LIBTCG_FUNC_TYPE remains defined, so it can be used + * to get the typedef'd function types. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LIBTCG_H */ diff --git a/include/libtcg/libtcg_loader.h b/include/libtcg/libtcg_loader.h new file mode 100644 index 00000000000..f70328853d9 --- /dev/null +++ b/include/libtcg/libtcg_loader.h @@ -0,0 +1,66 @@ +#ifndef LIBTCG_LOADER_H +#define LIBTCG_LOADER_H + +typedef enum LibTcgArch { + LIBTCG_ARCH_NONE = 0, + LIBTCG_ARCH_AARCH64_BE, + LIBTCG_ARCH_AARCH64, + LIBTCG_ARCH_ALPHA, + LIBTCG_ARCH_ARMEB, + LIBTCG_ARCH_ARM, + LIBTCG_ARCH_CRIS, + LIBTCG_ARCH_HEXAGON, + LIBTCG_ARCH_HPPA, + LIBTCG_ARCH_I386, + LIBTCG_ARCH_LOONGARCH64, + LIBTCG_ARCH_M68K, + LIBTCG_ARCH_MICROBLAZEEL, + LIBTCG_ARCH_MICROBLAZE, + LIBTCG_ARCH_MIPS64EL, + LIBTCG_ARCH_MIPS64, + LIBTCG_ARCH_MIPSEL, + LIBTCG_ARCH_MIPS, + LIBTCG_ARCH_MIPSN32EL, + LIBTCG_ARCH_MIPSN32, + LIBTCG_ARCH_NIOS2, + LIBTCG_ARCH_OR1K, + LIBTCG_ARCH_PPC64LE, + LIBTCG_ARCH_PPC64, + LIBTCG_ARCH_PPC, + LIBTCG_ARCH_RISCV32, + LIBTCG_ARCH_RISCV64, + LIBTCG_ARCH_S390X, + LIBTCG_ARCH_SH4EB, + LIBTCG_ARCH_SH4, + LIBTCG_ARCH_SPARC32PLUS, + LIBTCG_ARCH_SPARC64, + LIBTCG_ARCH_SPARC, + LIBTCG_ARCH_X86_64, + LIBTCG_ARCH_XTENSAEB, + LIBTCG_ARCH_XTENSA, + LIBTCG_ARCH_COUNT, +} LibTcgArch; + +const char *libtcg_arch_name(LibTcgArch arch); +const char *libtcg_arch_file(LibTcgArch arch); +LibTcgArch libtcg_arch_from_str(const char *str); + +typedef struct LibTcgContext LibTcgContext; +typedef struct LibTcgInterface LibTcgInterface; +typedef struct LibTcgDesc LibTcgDesc; + +/* + * For a given LibTcgArch , return the LibTcgInterface into litcg , and + * create and return a LibTcgContext if needed . + */ +void libtcg_open(LibTcgArch arch, + LibTcgDesc *desc, + LibTcgInterface *libtcg, + LibTcgContext **context); + +/* Close a given libtcg library */ +void libtcg_close(LibTcgArch arch); +/* Close all open libtcg libraries */ +void libtcg_close_all(void); + +#endif /* LIBTCG_LOADER_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index daf2a5bf9e4..107a7f2d45b 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -793,6 +793,8 @@ void tb_target_set_jmp_target(const TranslationBlock *, int, void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); +char *tcg_get_arg_str(TCGContext *s, char *buf, int buf_size, TCGArg arg); + #define TCG_CT_CONST 1 /* any constant of register size */ typedef struct TCGArgConstraint { @@ -880,6 +882,10 @@ TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, void tcg_remove_ops_after(TCGOp *op); void tcg_optimize(TCGContext *s); +void reachable_code_pass(TCGContext *s); +void liveness_pass_0(TCGContext *s); +void liveness_pass_1(TCGContext *s); +bool liveness_pass_2(TCGContext *s); TCGLabel *gen_new_label(void); diff --git a/libtcg/dump_tinycode_instruction.c b/libtcg/dump_tinycode_instruction.c new file mode 100644 index 00000000000..d32e5756c28 --- /dev/null +++ b/libtcg/dump_tinycode_instruction.c @@ -0,0 +1,337 @@ +#include "libtcg/libtcg.h" +#include +#include /* for vsnprintf */ +#include + +#define ARRAY_LEN(arr) \ + sizeof(arr)/sizeof(arr[0]) + +/* Taken from `tcg/tcg.c` */ +static const char * const cond_name[] = { + [LIBTCG_COND_NEVER] = "never", + [LIBTCG_COND_ALWAYS] = "always", + [LIBTCG_COND_EQ] = "eq", + [LIBTCG_COND_NE] = "ne", + [LIBTCG_COND_LT] = "lt", + [LIBTCG_COND_GE] = "ge", + [LIBTCG_COND_LE] = "le", + [LIBTCG_COND_GT] = "gt", + [LIBTCG_COND_LTU] = "ltu", + [LIBTCG_COND_GEU] = "geu", + [LIBTCG_COND_LEU] = "leu", + [LIBTCG_COND_GTU] = "gtu" +}; + +/* Taken from `tcg/tcg.c` */ +static const char * const ldst_name[] = { + [LIBTCG_MO_UB] = "ub", + [LIBTCG_MO_SB] = "sb", + //[LIBTCG_MO_LEUW] = "leuw", + //[LIBTCG_MO_LESW] = "lesw", + //[LIBTCG_MO_LEUL] = "leul", + //[LIBTCG_MO_LESL] = "lesl", + //[LIBTCG_MO_LEUQ] = "leq", + //[LIBTCG_MO_BEUW] = "beuw", + //[LIBTCG_MO_BESW] = "besw", + //[LIBTCG_MO_BEUL] = "beul", + //[LIBTCG_MO_BESL] = "besl", + //[LIBTCG_MO_BEUQ] = "beq", + //[LIBTCG_MO_128 + LIBTCG_MO_BE] = "beo", + //[LIBTCG_MO_128 + LIBTCG_MO_LE] = "leo", +}; + +/* Taken from `tcg/tcg.c` */ +static +const char * const alignment_name[(LIBTCG_MO_AMASK >> LIBTCG_MO_ASHIFT) + 1] = { +#ifdef TARGET_ALIGNED_ONLY + [LIBTCG_MO_UNALN >> LIBTCG_MO_ASHIFT] = "un+", + [LIBTCG_MO_ALIGN >> LIBTCG_MO_ASHIFT] = "", +#else + [LIBTCG_MO_UNALN >> LIBTCG_MO_ASHIFT] = "", + [LIBTCG_MO_ALIGN >> LIBTCG_MO_ASHIFT] = "al+", +#endif + [LIBTCG_MO_ALIGN_2 >> LIBTCG_MO_ASHIFT] = "al2+", + [LIBTCG_MO_ALIGN_4 >> LIBTCG_MO_ASHIFT] = "al4+", + [LIBTCG_MO_ALIGN_8 >> LIBTCG_MO_ASHIFT] = "al8+", + [LIBTCG_MO_ALIGN_16 >> LIBTCG_MO_ASHIFT] = "al16+", + [LIBTCG_MO_ALIGN_32 >> LIBTCG_MO_ASHIFT] = "al32+", + [LIBTCG_MO_ALIGN_64 >> LIBTCG_MO_ASHIFT] = "al64+", +}; + +/* Taken from `tcg/tcg.c` */ +static const char bswap_flag_name[][6] = { + [LIBTCG_BSWAP_IZ] = "iz", + [LIBTCG_BSWAP_OZ] = "oz", + [LIBTCG_BSWAP_OS] = "os", + [LIBTCG_BSWAP_IZ | LIBTCG_BSWAP_OZ] = "iz,oz", + [LIBTCG_BSWAP_IZ | LIBTCG_BSWAP_OS] = "iz,os", +}; + +typedef struct StringBuffer { + char *data; + size_t at; + size_t size; +} StringBuffer; + +static inline void fmt_append_to_stringbuffer(StringBuffer *buffer, + const char *fmt, ...) +{ + if (buffer->at >= buffer->size) { + return; + } + + va_list args; + va_start(args, fmt); + size_t size_left = buffer->size - buffer->at; + size_t bytes_written = vsnprintf(buffer->data+buffer->at, size_left, fmt, + args); + va_end(args); + + if (bytes_written > size_left) { + /* Truncation happened */ + buffer->at = buffer->size; + } else { + buffer->at += bytes_written; + } +} + +void libtcg_dump_constant_arg_to_buffer(LibTcgArgument *arg, + char *buf, + size_t size) +{ + StringBuffer buffer = { + .data = buf, + .at = 0, + .size = size, + }; + + (void) bswap_flag_name; + (void) alignment_name; + (void) ldst_name; + (void) cond_name; + + switch(arg->kind) { + case LIBTCG_ARG_CONSTANT: + fmt_append_to_stringbuffer(&buffer, "$0x%lx", arg->constant); + break; + case LIBTCG_ARG_MEM_OP_INDEX: + { + LibTcgMemOp op = arg->mem_op_index.op; + unsigned ix = arg->mem_op_index.mmu_index; + if (op & ~(LIBTCG_MO_AMASK | LIBTCG_MO_BSWAP | LIBTCG_MO_SSIZE)) { + fmt_append_to_stringbuffer(&buffer, "$0x%x,%u", op, ix); + } else { + const char *s_al, *s_op; + s_al = alignment_name[(op & LIBTCG_MO_AMASK) >> LIBTCG_MO_ASHIFT]; + s_op = ldst_name[op & (LIBTCG_MO_BSWAP | LIBTCG_MO_SSIZE)]; + fmt_append_to_stringbuffer(&buffer, "%s%s,%u", s_al, s_op, ix); + } + } + break; + case LIBTCG_ARG_COND: + { + uint64_t constant = arg->cond; + if (constant < ARRAY_LEN(cond_name) + && cond_name[constant]) { + fmt_append_to_stringbuffer(&buffer, "%s", cond_name[constant]); + } else { + fmt_append_to_stringbuffer(&buffer, "$0x%lx", constant); + } + } + break; + case LIBTCG_ARG_BSWAP: + { + uint64_t flags = arg->bswap_flag; + const char *name = NULL; + + if (flags < ARRAY_LEN(bswap_flag_name)) { + name = bswap_flag_name[flags]; + } + if (name) { + fmt_append_to_stringbuffer(&buffer, "%s", name); + } else { + fmt_append_to_stringbuffer(&buffer, "$0x%lx", flags); + } + } + break; + case LIBTCG_ARG_TEMP: + assert(0); + break; + case LIBTCG_ARG_LABEL: + fmt_append_to_stringbuffer(&buffer, "$L%d", arg->label->id); + break; + default: + assert(0); + break; + }; +} + +void libtcg_dump_instruction_name_to_buffer(LibTcgInstruction *insn, char *buf, + size_t size) +{ + LibTcgOpcode c = insn->opcode; + + StringBuffer buffer = { + .data = buf, + .at = 0, + .size = size, + }; + + const char *insn_name = libtcg_get_instruction_name(insn->opcode); + if (c == LIBTCG_op_insn_start) { + fmt_append_to_stringbuffer(&buffer, " ----"); + + for (uint32_t i = 0; i < insn->nb_cargs; ++i) { + fmt_append_to_stringbuffer(&buffer, " %016x", + insn->constant_args[i].constant); + } + } else { + fmt_append_to_stringbuffer(&buffer, "%s", insn_name); + } + + fmt_append_to_stringbuffer(&buffer, "\0"); +} + +/* + * TODO(anjo): Adapted from `tcg_dump_ops` in `tcg/tcg.c`. + * This print function doesnt handle: + * - plugins + * - lifetime + * - output preferences + * This functions is quite shit. It has inherited a distinc C-89 vibe + * from `tcg_dump_ops`. Refactor. + */ +void libtcg_dump_instruction_to_buffer(LibTcgInstruction *insn, char *buf, + size_t size) +{ + LibTcgOpcode c = insn->opcode; + + StringBuffer buffer = { + .data = buf, + .at = 0, + .size = size, + }; + + const char *insn_name = libtcg_get_instruction_name(insn->opcode); + if (c == LIBTCG_op_insn_start) { + fmt_append_to_stringbuffer(&buffer, "\n ----"); + + for (uint32_t i = 0; i < insn->nb_cargs; ++i) { + fmt_append_to_stringbuffer(&buffer, " %016x", + insn->constant_args[i].constant); + } + } else if (c == LIBTCG_op_call) { + LibTcgHelperInfo info = libtcg_get_helper_info(insn); + fmt_append_to_stringbuffer(&buffer, " %s %s", insn_name, + info.func_name); + fmt_append_to_stringbuffer(&buffer, ",$0x%x,$%d", info.func_flags, + insn->nb_oargs); + for (uint32_t i = 0; i < insn->nb_oargs; i++) { + fmt_append_to_stringbuffer(&buffer, ",%s", + insn->output_args[i].temp->name); + } + for (uint32_t i = 0; i < insn->nb_iargs; i++) { + fmt_append_to_stringbuffer(&buffer, ",%s", + insn->input_args[i].temp->name); + } + } else { + fmt_append_to_stringbuffer(&buffer, " %s ", insn_name); + /* TODO(anjo): We do not print vector arguments, this is how qemu prints them + * + * if (insn->flags & TCG_OPF_VECTOR) { + * // col += qemu_log("v%d,e%d,", 64 << TCGOP_VECL(op), + * } + */ + + for (uint32_t i = 0; i < insn->nb_oargs; ++i) { + if (i > 0) { + fmt_append_to_stringbuffer(&buffer, ","); + } + fmt_append_to_stringbuffer(&buffer, "%s", + insn->output_args[i].temp->name); + } + for (uint32_t i = 0; i < insn->nb_iargs; ++i) { + if (i > 0 || insn->nb_oargs > 0) { + fmt_append_to_stringbuffer(&buffer, ","); + } + fmt_append_to_stringbuffer(&buffer, "%s", + insn->input_args[i].temp->name); + } + + (void) bswap_flag_name; + (void) alignment_name; + (void) ldst_name; + (void) cond_name; + + /* + * The first constant argument might need some special treatment + * depending on the instruction. + */ + + for (uint32_t i = 0; i < insn->nb_cargs; ++i) { + if (i > 0 || insn->nb_oargs > 0 || insn->nb_iargs > 0) { + fmt_append_to_stringbuffer(&buffer, ","); + } + + LibTcgArgument arg = insn->constant_args[i]; + switch(arg.kind) { + case LIBTCG_ARG_CONSTANT: + fmt_append_to_stringbuffer(&buffer, "$0x%lx", arg.constant); + break; + case LIBTCG_ARG_MEM_OP_INDEX: + { + //LibTcgMemOp op = tinycode_get_memop(oi); + //unsigned ix = tinycode_get_mmuidx(oi); + LibTcgMemOp op = arg.mem_op_index.op; + unsigned ix = arg.mem_op_index.mmu_index; + if (op & ~(LIBTCG_MO_AMASK | LIBTCG_MO_BSWAP | LIBTCG_MO_SSIZE)) { + fmt_append_to_stringbuffer(&buffer, ",$0x%x,%u", op, ix); + } else { + const char *s_al, *s_op; + s_al = alignment_name[(op & LIBTCG_MO_AMASK) >> LIBTCG_MO_ASHIFT]; + s_op = ldst_name[op & (LIBTCG_MO_BSWAP | LIBTCG_MO_SSIZE)]; + fmt_append_to_stringbuffer(&buffer, ",%s%s,%u", s_al, s_op, ix); + } + } + break; + case LIBTCG_ARG_COND: + { + uint64_t constant = arg.cond; + if (constant < ARRAY_LEN(cond_name) + && cond_name[constant]) { + fmt_append_to_stringbuffer(&buffer, ",%s", cond_name[constant]); + } else { + fmt_append_to_stringbuffer(&buffer, ",$0x%lx", constant); + } + } + break; + case LIBTCG_ARG_BSWAP: + { + uint64_t flags = arg.bswap_flag; + const char *name = NULL; + + if (flags < ARRAY_LEN(bswap_flag_name)) { + name = bswap_flag_name[flags]; + } + if (name) { + fmt_append_to_stringbuffer(&buffer, ",%s", name); + } else { + fmt_append_to_stringbuffer(&buffer, ",$0x%lx", flags); + } + } + break; + case LIBTCG_ARG_TEMP: + assert(0); + break; + case LIBTCG_ARG_LABEL: + fmt_append_to_stringbuffer(&buffer, "$L%d", arg.label->id); + break; + default: + assert(0); + break; + }; + } + } + + fmt_append_to_stringbuffer(&buffer, "\0"); +} diff --git a/libtcg/libtcg.c b/libtcg/libtcg.c new file mode 100644 index 00000000000..69f0b5ba3bd --- /dev/null +++ b/libtcg/libtcg.c @@ -0,0 +1,691 @@ +/* TODO(anjo): Can we cut down on these includes? */ +#include +#include + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/help-texts.h" +#include "qemu/units.h" +#include "qemu/accel.h" +#include "qemu-version.h" +#include +#include +#include +#include + +#include "qapi/error.h" + +#include "qemu.h" +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu-param.h" +#include "tcg/insn-start-words.h" +#include "disas/disas.h" +#include "exec/exec-all.h" +#include "exec/translator.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-internal.h" +#include "tcg/tcg.h" +#include "tcg/startup.h" +#include "qemu/accel.h" +#include "elf.h" +#include "target_elf.h" +#include "target_syscall.h" /* for struct target_pt_regs */ +#include "cpu_loop-common.h" /* for target_cpu_copy_regs */ + +/* + * Including our header last, otherwise we run into trouble with + * TCG_TARGET_MAYBE_vec being doubly deinfed. We define it to 0 to + * avoid exposing target dependent vector instructions in `libtcg.h`. + */ +#include "libtcg/libtcg.h" + +typedef struct LibTcgContext { + LibTcgDesc desc; + CPUState *cpu; +} LibTcgContext; + +/* + * Here we hold some information about the bytecode we're going + * to translate. This struct will be passed via CPUState->opaque + * to the memory access functions below. + */ +typedef struct BytecodeRegion { + const unsigned char *buffer; + size_t size; + uint64_t virtual_address; +} BytecodeRegion; + +/* + * Here we have the functions to replace QEMUs memory access functions in + * accel/tcg/user-exec.c. We override them to read bytecode from the + * BytecodeRegion struct passed by via CPUState->opaque instead. + */ +static inline void *vaddr_to_buf_ptr(CPUArchState *env, abi_ptr ptr, size_t type_size) +{ + CPUState *cpu = env_cpu(env); + BytecodeRegion *region = cpu->opaque; + uint64_t offset = (uintptr_t)ptr - region->virtual_address; + + assert(offset + type_size <= region->size); + return (void *) ((uintptr_t) region->buffer + offset); +} + +uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) { return ldub_p(vaddr_to_buf_ptr(env, ptr, 1)); } +uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr) { return lduw_p(vaddr_to_buf_ptr(env, ptr, 2)); } +uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr) { return ldl_p(vaddr_to_buf_ptr(env, ptr, 4)); } +uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) { return ldq_p(vaddr_to_buf_ptr(env, ptr, 8)); } + +//static inline bool instruction_has_label_argument(TCGOpcode opc) +//{ +// return (opc == INDEX_op_set_label || +// opc == INDEX_op_br || +// opc == INDEX_op_brcond_i32 || +// opc == INDEX_op_brcond_i64 || +// opc == INDEX_op_brcond2_i32); +//} + +const char *libtcg_get_instruction_name(LibTcgOpcode opcode) +{ + TCGOpDef def = tcg_op_defs[(TCGOpcode) opcode]; + return def.name; +} + +LibTcgHelperInfo libtcg_get_helper_info(LibTcgInstruction *insn) +{ + /* + * For a call instruction, the first constant argument holds + * a pointer to a TCGHelperInfo struct allocated in a static + * hash table g_helper_table. + * + * NOTE(anjo): This function needs to be kept up to date with + * tcg_call_info(op), since we effectively + * reimplement it here. + * + */ + assert(insn->opcode == LIBTCG_op_call); + uintptr_t ptr_to_helper_info = insn->constant_args[1].constant; + TCGHelperInfo *info = (void *) ptr_to_helper_info; + return (LibTcgHelperInfo) { + .func_name = info->name, + .func_flags = info->flags, + }; +} + +LibTcgArchInfo libtcg_get_arch_info(void) +{ + return (LibTcgArchInfo) { + .num_globals = tcg_ctx->nb_globals, + .exception_index = offsetof(ArchCPU, parent_obj) + + offsetof(CPUState, exception_index), + + .env_offset = offsetof(ArchCPU, env), + + /* Target specific CPU state */ +#if defined(TARGET_ALPHA) + .pc = offsetof(CPUArchState, pc), + .sp = offsetof(CPUArchState, ir[31]), + .bp = offsetof(CPUArchState, ir[30]), /* gp? */ + .arch_cpu_name = "AlphaCPU", +#elif defined(TARGET_ARM) + #if defined(TARGET_AARCH64) + .pc = offsetof(CPUArchState, pc), + .sp = offsetof(CPUArchState, xregs[31]), + .bp = 0, /* TODO(anjo) */ + #else + .pc = offsetof(CPUArchState, regs[15]), + .sp = offsetof(CPUArchState, regs[13]), + .bp = 0, /* TODO(anjo) */ + .is_thumb = offsetof(CPUArchState, thumb), + #endif + .arch_cpu_name = "ARMCPU", +#elif defined(TARGET_AVR) + .pc = offsetof(CPUArchState, pc_w), + .sp = offsetof(CPUArchState, sp), + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "AVRCPU", +#elif defined(TARGET_CRIS) + .pc = 0, /* NOTE(anjo): UNCHECKED */ + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "CRISCPU", +#elif defined(TARGET_HEXAGON) + .pc = offsetof(CPUArchState, gpr[41]), + .sp = offsetof(CPUArchState, gpr[29]), + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "HexagonCPU", +#elif defined(TARGET_HPPA) + .pc = 0, /* NOTE(anjo): UNCHECKED */ + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "HPPACPU", +#elif defined(TARGET_I386) + #if defined(TARGET_X86_64) + .pc = offsetof(CPUArchState, eip), /* NOTE(anjo): UNCHECKED */ + .sp = offsetof(CPUArchState, regs[R_ESP]), /* NOTE(anjo): UNCHECKED */ + .bp = offsetof(CPUArchState, regs[R_EBP]), /* NOTE(anjo): UNCHECKED */ + #else + .pc = offsetof(CPUArchState, eip), /* NOTE(anjo): UNCHECKED */ + .sp = offsetof(CPUArchState, regs[R_ESP]), /* NOTE(anjo): UNCHECKED */ + .bp = offsetof(CPUArchState, regs[R_EBP]), /* NOTE(anjo): UNCHECKED */ + #endif + .arch_cpu_name = "X86CPU", +#elif defined(TARGET_M68K) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "M68kCPU", +#elif defined(TARGET_MICROBLAZE) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "MicroBlazeCPU", +#elif defined(TARGET_MIPS) + #if defined(TARGET_MIPS64) + .pc = offsetof(CPUArchState, active_tc.PC), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + #else + .pc = offsetof(CPUArchState, active_tc.PC), + .sp = offsetof(CPUArchState, active_tc.gpr[29]), + .bp = 0, /* TODO(anjo) */ + #endif + .arch_cpu_name = "MIPSCPU", +#elif defined(TARGET_NIOS2) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "Nios2CPU", +#elif defined(TARGET_OPENRISC) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "OpenRISCCPU", +#elif defined(TARGET_PPC) + #if defined(TARGET_PPC64) + .pc = offsetof(CPUArchState, nip), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + #else + .pc = offsetof(CPUArchState, nip), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + #endif + .arch_cpu_name = "PowerPCCPU", +#elif defined(TARGET_RISCV32) + .pc = offsetof(CPUArchState, pc), + .sp = offsetof(CPUArchState, gpr[2]), + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "RISCVCPU", +#elif defined(TARGET_RISCV64) + /* + * NOTE(anjo): TARGET_RISCV64 is the only 64-bit arch not defined + * alongside the 32-bit variant (TARGET_RISCV32). + */ + .pc = offsetof(CPUArchState, pc), + .sp = offsetof(CPUArchState, gpr[2]), + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "RISCVCPU", +#elif defined(TARGET_RX) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "RXCPU", +#elif defined(TARGET_S390X) + .pc = offsetof(CPUArchState, psw.addr), + .sp = offsetof(CPUArchState, regs[15]), + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "S390CPU", +#elif defined(TARGET_SH4) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "SuperHCPU", +#elif defined(TARGET_SPARC) + #if defined(TARGET_SPARC64) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + #else + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + #endif + .arch_cpu_name = "SPARCCPU", +#elif defined(TARGET_TRICORE) + .pc = offsetof(CPUArchState, PC), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "TriCoreCPU", +#elif defined(TARGET_XTENSA) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "XtensaCPU", +#elif defined(TARGET_LOONGARCH64) + .pc = offsetof(CPUArchState, pc), + .sp = 0, /* NOTE(anjo): UNCHECKED */ + .bp = 0, /* TODO(anjo) */ + .arch_cpu_name = "", +#else + #error Unhandled target +#endif + }; +} + +LibTcgContext *libtcg_context_create(LibTcgDesc *desc) +{ + assert(desc); + + /* + * Default to using malloc/free for memory allocation + * if both mem_alloc and mem_free are NULL. Users might + * want to specify only allocation when using stack + * allocators or similar. + */ + if (desc->mem_alloc == NULL && desc->mem_free == NULL) { + desc->mem_alloc = malloc; + desc->mem_free = free; + } + + /* Initialize context */ + LibTcgContext *context = desc->mem_alloc(sizeof(LibTcgContext)); + if (context == NULL) { + return NULL; + } + context->desc = *desc; + + qemu_init_cpu_list(); + module_call_init(MODULE_INIT_QOM); +#if defined(TARGET_HEXAGON) + uint32_t elf_flags = 0x73; +#else + uint32_t elf_flags = 0; +#endif + const char *cpu_model = cpu_get_model(elf_flags); + const char *cpu_type = parse_cpu_option(cpu_model); + /* Initializes accel/tcg */ + { + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + + accel_init_interfaces(ac); + ac->init_machine(NULL); + } + + context->cpu = cpu_create(cpu_type); + cpu_reset(context->cpu); + tcg_prologue_init(); + struct target_pt_regs regs = {0}; + + struct image_info info = {0}; + TaskState ts = {0}; + ts.info = &info; + context->cpu->opaque = &ts; + target_cpu_copy_regs(cpu_env(context->cpu), ®s); + + return context; +} + +void libtcg_context_destroy(LibTcgContext *context) +{ + if (context->desc.mem_free != NULL) { + context->desc.mem_free(context); + } +} + +LibTcgTranslationBlock libtcg_translate_block(LibTcgContext *context, + const unsigned char *buffer, + size_t size, + uint64_t virtual_address, + uint32_t translate_flags) +{ + BytecodeRegion region = { + .buffer = buffer, + .size = size, + .virtual_address = virtual_address, + }; + context->cpu->opaque = ®ion; + + vaddr pc; + uint64_t cs_base; + uint32_t flags; + /* + * We're using this call to setup `flags` and `cs_base` correctly. + * We then override `pc`. + */ + cpu_get_tb_cpu_state(cpu_env(context->cpu), &pc, &cs_base, &flags); + pc = virtual_address; + + /* Set flags */ +#ifdef TARGET_ARM + if ((translate_flags & LIBTCG_TRANSLATE_ARM_THUMB) != 0) { + CPUARMTBFlags arm_flags = {flags, cs_base}; + /* flags |= THUMB; */ + DP_TBFLAG_AM32(arm_flags, THUMB, 1); + flags = arm_flags.flags; + cs_base = arm_flags.flags2; + } +#endif + + /* Set cflags */ + uint32_t cflags = context->cpu->cflags_next_tb; + if (cflags == -1) { + cflags = curr_cflags(context->cpu); + } else { + context->cpu->cflags_next_tb = -1; + } + cflags |= CF_NO_GOTO_TB; + cflags |= CF_NO_GOTO_PTR; + cflags &= ~CF_USE_ICOUNT; + cflags |= CF_NOIRQ; + + /* + * Initialize backend fields to avoid + * triggering asserts in tcg_func_start + * */ + tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; + tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; + /* Needed to initialize fields in `tcg_ctx` */ + tcg_func_start(tcg_ctx); + +#ifdef CONFIG_LLVM_TO_TCG +# ifdef TARGET_DISPATCHER + if ((translate_flags & LIBTCG_TRANSLATE_HELPER_TO_TCG) != 0) { + tcg_ctx->dispatcher = TARGET_DISPATCHER; + } +# endif +#endif + + /* + * Set `max_insns` to the number of bytes in the buffer + * so we don't have to worry about it being too small. + */ + int max_insns = TCG_MAX_INSNS; + + TranslationBlock *tb = tcg_tb_alloc(tcg_ctx); + tb->pc = pc; + tb->cs_base = cs_base; + tb->max_pc = virtual_address + size; + tb->flags = flags; + tb->cflags = cflags; + + tcg_ctx->gen_tb = tb; + + int ret; +restart_translation: + ret = sigsetjmp(tcg_ctx->jmp_trans, 0); + if (ret != 0) { + switch (ret) { + case -2: + assert(max_insns > 1); + max_insns /= 2; + goto restart_translation; + break; + case -3: + return (LibTcgTranslationBlock) { + .size_in_bytes = sizeof(target_ulong), + }; + } + } + + /* Needed to initialize fields in `tcg_ctx` */ + tcg_func_start(tcg_ctx); + + void *host_pc = NULL; + gen_intermediate_code(context->cpu, tb, &max_insns, pc, host_pc); + + if ((translate_flags & LIBTCG_TRANSLATE_OPTIMIZE_TCG) != 0) { + tcg_optimize(tcg_ctx); + reachable_code_pass(tcg_ctx); + liveness_pass_0(tcg_ctx); + liveness_pass_1(tcg_ctx); + if (tcg_ctx->nb_indirects > 0) { + /* Replace indirect temps with direct temps. */ + if (liveness_pass_2(tcg_ctx)) { + /* If changes were made, re-run liveness. */ + liveness_pass_1(tcg_ctx); + } + } + } + + LibTcgTranslationBlock instruction_list = { + .list = context->desc.mem_alloc(sizeof(LibTcgInstruction) * tcg_ctx->nb_ops), + .instruction_count = 0, + + /* Note: tcg_ctx->nb_temps includes tcg_ctx->nb_globals */ + .temps = context->desc.mem_alloc(sizeof(LibTcgTemp) * tcg_ctx->nb_temps), + .temp_count = 0, + + .labels = context->desc.mem_alloc(sizeof(LibTcgLabel) * (tcg_ctx->nb_labels)), + .label_count = 0, + + .size_in_bytes = tb->size, + }; + + assert(instruction_list.list != NULL && + instruction_list.temps != NULL && + instruction_list.labels != NULL); + + /* + * Loop over each TCG op and translate it to our format that we expose. + */ + TCGOp *op = NULL; + QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { + TCGOpcode opc = op->opc; + TCGOpDef def = tcg_op_defs[opc]; + + assert(def.nb_oargs <= LIBTCG_INSN_MAX_ARGS); + assert(def.nb_iargs <= LIBTCG_INSN_MAX_ARGS); + assert(def.nb_cargs <= LIBTCG_INSN_MAX_ARGS); + LibTcgInstruction insn = { + .opcode = (LibTcgOpcode) opc, + .flags = def.flags, + .nb_oargs = def.nb_oargs, + .nb_iargs = def.nb_iargs, + .nb_cargs = def.nb_cargs, + .nb_args = def.nb_args, + }; + + if (opc == INDEX_op_call) { + const TCGHelperInfo *info = tcg_call_info(op); + + insn.nb_oargs = TCGOP_CALLO(op); + insn.nb_iargs = TCGOP_CALLI(op); + insn.nb_args = insn.nb_oargs + insn.nb_iargs + insn.nb_cargs; + + void *func = tcg_call_func(op); + assert(func == info->func); + } + + /* + * Here we handle `temp` arguments so output and input args. + * Note: `insn.args[i]` and `op->args[i]` may have different + * integer sizes. + */ + for (uint32_t i = 0; i < insn.nb_oargs; ++i) { + TCGTemp *ts = arg_temp(op->args[i]); + int idx = temp_idx(ts); + /* + * TODO(anjo): Here we are casting between TCG's enums and ours. + * This can of course cause problems. I am here assuming that the + * TCG enums are stable. + */ + assert(instruction_list.temp_count < LIBTCG_MAX_TEMPS); + assert(idx < LIBTCG_MAX_TEMPS); + LibTcgTemp *temp = &instruction_list.temps[idx]; + temp->kind = (LibTcgTempKind) ts->kind; + temp->type = (LibTcgTempType) ts->type; + temp->val = ts->val; + temp->index = idx; + temp->mem_offset = ts->mem_offset; + tcg_get_arg_str(tcg_ctx, temp->name, LIBTCG_MAX_NAME_LEN, op->args[i]); + + insn.output_args[i] = (LibTcgArgument) { + .kind = LIBTCG_ARG_TEMP, + .temp = temp, + }; + } + + for (uint32_t i = 0; i < insn.nb_iargs; ++i) { + TCGTemp *ts = arg_temp(op->args[insn.nb_oargs + i]); + int idx = temp_idx(ts); + /* + * TODO(anjo): Here we are casting between TCG's enums and ours. + * This can of course cause problems. I am here assuming that the + * TCG enums are stable. + */ + assert(instruction_list.temp_count < LIBTCG_MAX_TEMPS); + assert(idx < LIBTCG_MAX_TEMPS); + LibTcgTemp *temp = &instruction_list.temps[idx]; + temp->kind = (LibTcgTempKind) ts->kind; + temp->type = (LibTcgTempType) ts->type; + temp->val = ts->val; + temp->index = idx; + temp->mem_offset = ts->mem_offset; + tcg_get_arg_str(tcg_ctx, temp->name, LIBTCG_MAX_NAME_LEN, op->args[insn.nb_oargs + i]); + + insn.input_args[i] = (LibTcgArgument) { + .kind = LIBTCG_ARG_TEMP, + .temp = temp, + }; + } + + /* + * Here we handle constant args. + */ + /* + * Constant arguments are weird. + * - 1st arg: {constant, mmu id, cond, bswap flag, label}, + * - 2nd arg: {constant, label} + * - nth arg: constant + */ + + uint32_t start_index = 0; + + switch (opc) { + case INDEX_op_brcond_i32: + case INDEX_op_setcond_i32: + case INDEX_op_negsetcond_i32: + case INDEX_op_movcond_i32: + case INDEX_op_brcond2_i32: + case INDEX_op_setcond2_i32: + case INDEX_op_brcond_i64: + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i64: + case INDEX_op_movcond_i64: + case INDEX_op_cmp_vec: + case INDEX_op_cmpsel_vec: + insn.constant_args[start_index] = (LibTcgArgument) { + .kind = LIBTCG_ARG_COND, + .cond = op->args[insn.nb_oargs + insn.nb_iargs + start_index], + }; + start_index++; + break; + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st8_a32_i32: + case INDEX_op_qemu_st8_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + { + MemOpIdx oi = op->args[insn.nb_oargs + insn.nb_iargs + start_index]; + insn.constant_args[start_index] = (LibTcgArgument) { + .kind = LIBTCG_ARG_MEM_OP_INDEX, + .mem_op_index = { + .op = libtcg_get_memop(oi), + .mmu_index = libtcg_get_mmuidx(oi), + }, + }; + start_index++; + } + break; + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + case INDEX_op_bswap64_i64: + { + insn.constant_args[start_index] = (LibTcgArgument) { + .kind = LIBTCG_ARG_BSWAP, + .bswap_flag = op->args[insn.nb_oargs + insn.nb_iargs + start_index], + }; + start_index++; + } + break; + default: + break; + } + + switch (opc) { + case INDEX_op_set_label: + case INDEX_op_br: + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + case INDEX_op_brcond2_i32: + { + TCGLabel *label = + arg_label(op->args[insn.nb_oargs + insn.nb_iargs + start_index]); + LibTcgLabel *our_label = &instruction_list.labels[label->id]; + our_label->id = label->id; + insn.constant_args[start_index] = (LibTcgArgument) { + .kind = LIBTCG_ARG_LABEL, + .label = our_label + }; + } + start_index++; + break; + default: + break; + } + + for (uint32_t i = start_index; i < insn.nb_cargs; ++i) { + insn.constant_args[i] = (LibTcgArgument) { + .kind = LIBTCG_ARG_CONSTANT, + .constant = op->args[insn.nb_oargs + insn.nb_iargs + i], + }; + } + + instruction_list.list[instruction_list.instruction_count++] = insn; + } + + return instruction_list; +} + +void libtcg_translation_block_destroy(LibTcgContext *context, + LibTcgTranslationBlock tb) +{ + if (context->desc.mem_free != NULL) { + context->desc.mem_free(tb.list); + context->desc.mem_free(tb.temps); + context->desc.mem_free(tb.labels); + } +} + +uint8_t *libtcg_env_ptr(LibTcgContext *context) +{ + return (uint8_t *) cpu_env(context->cpu); +} + +LibTcgInterface libtcg_load(void) { + return (LibTcgInterface) { + .get_instruction_name = libtcg_get_instruction_name, + .get_helper_info = libtcg_get_helper_info, + .get_arch_info = libtcg_get_arch_info, + .context_create = libtcg_context_create, + .context_destroy = libtcg_context_destroy, + .translate_block = libtcg_translate_block, + .translation_block_destroy = libtcg_translation_block_destroy, + .env_ptr = libtcg_env_ptr, + .dump_instruction_to_buffer = libtcg_dump_instruction_to_buffer, + .dump_instruction_name_to_buffer = libtcg_dump_instruction_name_to_buffer, + .dump_constant_arg_to_buffer = libtcg_dump_constant_arg_to_buffer, + }; +} diff --git a/libtcg/libtcg_loader.c b/libtcg/libtcg_loader.c new file mode 100644 index 00000000000..3a5bf728bd8 --- /dev/null +++ b/libtcg/libtcg_loader.c @@ -0,0 +1,130 @@ +#include "libtcg/libtcg_loader.h" +#include "libtcg/libtcg.h" +#include +#include +#include + +#define ARRLEN(arr) (sizeof(arr) / sizeof(arr[0])) + +typedef struct LibTcgLibraryInfo { + void *handle; + LibTcgContext *context; + LibTcgInterface libtcg; +} LibTcgLibraryInfo; + +static LibTcgLibraryInfo library_info[LIBTCG_ARCH_COUNT] = {0}; + +static const char *arch_names[] = { + [LIBTCG_ARCH_AARCH64_BE] = "aarch64_be", + [LIBTCG_ARCH_AARCH64] = "aarch64", + [LIBTCG_ARCH_ALPHA] = "alpha", + [LIBTCG_ARCH_ARMEB] = "armeb", + [LIBTCG_ARCH_ARM] = "arm", + [LIBTCG_ARCH_CRIS] = "cris", + [LIBTCG_ARCH_HEXAGON] = "hexagon", + [LIBTCG_ARCH_HPPA] = "hppa", + [LIBTCG_ARCH_I386] = "i386", + [LIBTCG_ARCH_LOONGARCH64] = "loongarch64", + [LIBTCG_ARCH_M68K] = "m68k", + [LIBTCG_ARCH_MICROBLAZEEL] = "microblazeel", + [LIBTCG_ARCH_MICROBLAZE] = "microblaze", + [LIBTCG_ARCH_MIPS64EL] = "mips64el", + [LIBTCG_ARCH_MIPS64] = "mips64", + [LIBTCG_ARCH_MIPSEL] = "mipsel", + [LIBTCG_ARCH_MIPS] = "mipsn32el", + [LIBTCG_ARCH_MIPSN32EL] = "mipsn32", + [LIBTCG_ARCH_MIPSN32] = "mips", + [LIBTCG_ARCH_NIOS2] = "nios2", + [LIBTCG_ARCH_OR1K] = "or1k", + [LIBTCG_ARCH_PPC64LE] = "ppc64le", + [LIBTCG_ARCH_PPC64] = "ppc64", + [LIBTCG_ARCH_PPC] = "ppc", + [LIBTCG_ARCH_RISCV32] = "riscv32", + [LIBTCG_ARCH_RISCV64] = "riscv64", + [LIBTCG_ARCH_S390X] = "s390x", + [LIBTCG_ARCH_SH4EB] = "sh4eb", + [LIBTCG_ARCH_SH4] = "sh4", + [LIBTCG_ARCH_SPARC32PLUS] = "sparc32plus", + [LIBTCG_ARCH_SPARC64] = "sparc64", + [LIBTCG_ARCH_SPARC] = "sparc", + [LIBTCG_ARCH_X86_64] = "x86_64", + [LIBTCG_ARCH_XTENSAEB] = "xtensaeb", + [LIBTCG_ARCH_XTENSA] = "xtensa", +}; + +const char *libtcg_arch_name(LibTcgArch arch) { + return arch_names[arch]; +} + +const char *libtcg_arch_file(LibTcgArch arch) { + static char buf[64] = {0}; + const char *name = arch_names[arch]; + snprintf(buf, ARRLEN(buf)-1, "libtcg-%s.so", name); + return buf; +} + +LibTcgArch libtcg_arch_from_str(const char *str) { + for (int i = 0; i < ARRLEN(arch_names); ++i) { + if (i == LIBTCG_ARCH_NONE) { + continue; + } + if (strcmp(arch_names[i], str) == 0) { + return i; + } + } + return LIBTCG_ARCH_NONE; +} + +void libtcg_open(LibTcgArch arch, + LibTcgDesc *desc, + LibTcgInterface *libtcg, + LibTcgContext **context) { + if (arch == LIBTCG_ARCH_NONE) { + fprintf(stderr, "[error]: libtcg invalid architecture \"%d\"", arch); + return; + } + + LibTcgLibraryInfo *info = &library_info[arch]; + if (info->handle != NULL) { + *libtcg = info->libtcg; + *context = info->context; + return; + } + + const char *arch_file = libtcg_arch_file(arch); + void *handle = dlopen(arch_file, RTLD_LAZY); + if (handle == NULL) { + fprintf(stderr, "[error]: libtcg failed to dlopen \"%s\"", arch_file); + return; + } + + LIBTCG_FUNC_TYPE(libtcg_load) *libtcg_load = dlsym(handle, "libtcg_load"); + *libtcg = libtcg_load(); + *context = libtcg->context_create(desc); + + info->handle = handle; + info->libtcg = *libtcg; + info->context = *context; +} + +void libtcg_close(LibTcgArch arch) { + if (arch == LIBTCG_ARCH_NONE) { + fprintf(stderr, "[error]: libtcg invalid architecture \"%d\"", arch); + return; + } + + LibTcgLibraryInfo *info = &library_info[arch]; + if (info->handle != NULL) { + info->libtcg.context_destroy(info->context); + dlclose(info->handle); + } +} + +void libtcg_close_all(void) { + for (int i = 0; i < LIBTCG_ARCH_COUNT; ++i) { + if (i == LIBTCG_ARCH_NONE) { + continue; + } + libtcg_close(i); + } +} diff --git a/libtcg/meson.build b/libtcg/meson.build new file mode 100644 index 00000000000..8a025f1fe4d --- /dev/null +++ b/libtcg/meson.build @@ -0,0 +1,16 @@ +libtcg_ss = ss.source_set() + +libtcg_ss.add(files( + 'libtcg.c', + 'dump_tinycode_instruction.c' +)) + +dl = cc.find_library('dl') +install_headers('../include/libtcg/libtcg_loader.h', subdir: 'qemu/libtcg') +libtcg_loader = shared_library('tcg-loader', + sources: 'libtcg_loader.c', + dependencies: dl, + install: true) + + +specific_ss.add_all(when: 'CONFIG_LIBTCG', if_true: libtcg_ss) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 8c20dc8a39a..14065e22786 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -212,9 +212,11 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) arm_rebuild_hflags(env); #endif +#ifndef CONFIG_LIBTCG if (cpu_isar_feature(aa64_pauth, cpu)) { qemu_guest_getrandom_nofail(&env->keys, sizeof(env->keys)); } +#endif ts->stack_base = info->start_stack; ts->heap_base = info->brk; diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index c04a97c73a3..a1caf63c68c 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -850,12 +850,12 @@ static abi_long host_to_target_data_vlan_list_nlattr(struct nlattr *nlattr, switch (nlattr->nla_type) { /* struct ifla_vf_vlan_info */ - case IFLA_VF_VLAN_INFO: - vlan_info = NLA_DATA(nlattr); - vlan_info->vf = tswap32(vlan_info->vf); - vlan_info->vlan = tswap32(vlan_info->vlan); - vlan_info->qos = tswap32(vlan_info->qos); - break; + //case IFLA_VF_VLAN_INFO: + // vlan_info = NLA_DATA(nlattr); + // vlan_info->vf = tswap32(vlan_info->vf); + // vlan_info->vlan = tswap32(vlan_info->vlan); + // vlan_info->qos = tswap32(vlan_info->qos); + // break; default: qemu_log_mask(LOG_UNIMP, "Unknown host VLAN LIST type: %d\n", nlattr->nla_type); @@ -921,12 +921,12 @@ static abi_long host_to_target_data_vfinfo_nlattr(struct nlattr *nlattr, vlan->qos = tswap32(vlan->qos); break; /* struct ifla_vf_vlan_info */ - case QEMU_IFLA_VF_TX_RATE: - vlan_info = NLA_DATA(nlattr); - vlan_info->vf = tswap32(vlan_info->vf); - vlan_info->vlan = tswap32(vlan_info->vlan); - vlan_info->qos = tswap32(vlan_info->qos); - break; + //case QEMU_IFLA_VF_TX_RATE: + // vlan_info = NLA_DATA(nlattr); + // vlan_info->vf = tswap32(vlan_info->vf); + // vlan_info->vlan = tswap32(vlan_info->vlan); + // vlan_info->qos = tswap32(vlan_info->qos); + // break; /* struct ifla_vf_spoofchk */ case QEMU_IFLA_VF_SPOOFCHK: spoofchk = NLA_DATA(nlattr); @@ -960,11 +960,11 @@ static abi_long host_to_target_data_vfinfo_nlattr(struct nlattr *nlattr, break; /* struct ifla_vf_guid */ case QEMU_IFLA_VF_IB_NODE_GUID: - case QEMU_IFLA_VF_IB_PORT_GUID: - guid = NLA_DATA(nlattr); - guid->vf = tswap32(guid->vf); - guid->guid = tswap32(guid->guid); - break; + //case QEMU_IFLA_VF_IB_PORT_GUID: + // guid = NLA_DATA(nlattr); + // guid->vf = tswap32(guid->vf); + // guid->guid = tswap32(guid->guid); + // break; /* nested */ case QEMU_IFLA_VF_VLAN_LIST: return host_to_target_for_each_nlattr(RTA_DATA(nlattr), nlattr->nla_len, diff --git a/linux-user/main.c b/linux-user/main.c index 0cdaf30d346..111dcf1a21d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -217,7 +217,7 @@ void init_task_state(TaskState *ts) ticks_per_sec = sysconf(_SC_CLK_TCK); - if ((ticks_per_sec > 0) && !clock_gettime(CLOCK_BOOTTIME, &bt)) { + if ((ticks_per_sec > 0) && !clock_gettime(CLOCK_MONOTONIC, &bt)) { /* start_boottime is expressed in clock ticks */ ts->start_boottime = bt.tv_sec * (uint64_t) ticks_per_sec; ts->start_boottime += bt.tv_nsec * (uint64_t) ticks_per_sec / diff --git a/linux-user/strace.c b/linux-user/strace.c index cf26e552643..5852d025797 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -1264,26 +1264,6 @@ UNUSED static const struct flags statx_mask[] = { FLAG_END, }; -UNUSED static const struct flags falloc_flags[] = { - FLAG_GENERIC(FALLOC_FL_KEEP_SIZE), - FLAG_GENERIC(FALLOC_FL_PUNCH_HOLE), -#ifdef FALLOC_FL_NO_HIDE_STALE - FLAG_GENERIC(FALLOC_FL_NO_HIDE_STALE), -#endif -#ifdef FALLOC_FL_COLLAPSE_RANGE - FLAG_GENERIC(FALLOC_FL_COLLAPSE_RANGE), -#endif -#ifdef FALLOC_FL_ZERO_RANGE - FLAG_GENERIC(FALLOC_FL_ZERO_RANGE), -#endif -#ifdef FALLOC_FL_INSERT_RANGE - FLAG_GENERIC(FALLOC_FL_INSERT_RANGE), -#endif -#ifdef FALLOC_FL_UNSHARE_RANGE - FLAG_GENERIC(FALLOC_FL_UNSHARE_RANGE), -#endif -}; - UNUSED static const struct flags termios_iflags[] = { FLAG_TARGET(IGNBRK), FLAG_TARGET(BRKINT), @@ -2091,7 +2071,7 @@ print_fallocate(CPUArchState *cpu_env, const struct syscallname *name, { print_syscall_prologue(name); print_raw_param("%d", arg0, 0); - print_flags(falloc_flags, arg1, 0); + assert(false); #if TARGET_ABI_BITS == 32 print_raw_param("%" PRIu64, target_offset64(arg2, arg3), 0); print_raw_param("%" PRIu64, target_offset64(arg4, arg5), 1); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e384e142489..61cbb678cf6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -987,7 +987,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) { abi_ulong target_rlim_swap; rlim_t result; - + target_rlim_swap = tswapal(target_rlim); if (target_rlim_swap == TARGET_RLIM_INFINITY) return RLIM_INFINITY; @@ -995,7 +995,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) result = target_rlim_swap; if (target_rlim_swap != (rlim_t)result) return RLIM_INFINITY; - + return result; } #endif @@ -1005,13 +1005,13 @@ static inline abi_ulong host_to_target_rlim(rlim_t rlim) { abi_ulong target_rlim_swap; abi_ulong result; - + if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim) target_rlim_swap = TARGET_RLIM_INFINITY; else target_rlim_swap = rlim; result = tswapal(target_rlim_swap); - + return result; } #endif @@ -1743,9 +1743,9 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, abi_ulong target_cmsg_addr; struct target_cmsghdr *target_cmsg, *target_cmsg_start; socklen_t space = 0; - + msg_controllen = tswapal(target_msgh->msg_controllen); - if (msg_controllen < sizeof (struct target_cmsghdr)) + if (msg_controllen < sizeof (struct target_cmsghdr)) goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); @@ -1805,15 +1805,8 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, __get_user(cred->pid, &target_cred->pid); __get_user(cred->uid, &target_cred->uid); __get_user(cred->gid, &target_cred->gid); - } else if (cmsg->cmsg_level == SOL_ALG) { - uint32_t *dst = (uint32_t *)data; - - memcpy(dst, target_data, len); - /* fix endianness of first 32-bit word */ - if (len >= sizeof(uint32_t)) { - *dst = tswap32(*dst); - } } else { + abort(); qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(data, target_data, len); @@ -1839,7 +1832,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, socklen_t space = 0; msg_controllen = tswapal(target_msgh->msg_controllen); - if (msg_controllen < sizeof (struct target_cmsghdr)) + if (msg_controllen < sizeof (struct target_cmsghdr)) goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); @@ -6111,7 +6104,7 @@ abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) } unlock_user_struct(target_ldt_info, ptr, 1); - if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || + if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX) return -TARGET_EINVAL; seg_32bit = ldt_info.flags & 1; @@ -6189,7 +6182,7 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) lp = (uint32_t *)(gdt_table + idx); entry_1 = tswap32(lp[0]); entry_2 = tswap32(lp[1]); - + read_exec_only = ((entry_2 >> 9) & 1) ^ 1; contents = (entry_2 >> 10) & 3; seg_not_present = ((entry_2 >> 15) & 1) ^ 1; @@ -6205,8 +6198,8 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) (read_exec_only << 3) | (limit_in_pages << 4) | (seg_not_present << 5) | (useable << 6) | (lm << 7); limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000); - base_addr = (entry_1 >> 16) | - (entry_2 & 0xff000000) | + base_addr = (entry_1 >> 16) | + (entry_2 & 0xff000000) | ((entry_2 & 0xff) << 16); target_ldt_info->base_addr = tswapal(base_addr); target_ldt_info->limit = tswap32(limit); @@ -6455,8 +6448,8 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_GET_CHILD_SUBREAPER: case PR_SET_CHILD_SUBREAPER: - case PR_GET_SPECULATION_CTRL: - case PR_SET_SPECULATION_CTRL: + //case PR_GET_SPECULATION_CTRL: + //case PR_SET_SPECULATION_CTRL: case PR_GET_TID_ADDRESS: /* TODO */ return -TARGET_EINVAL; @@ -6557,6 +6550,9 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, flags &= ~(CLONE_VFORK | CLONE_VM); if (flags & CLONE_VM) { +#ifdef CONFIG_LIBTCG + abort(); +#else TaskState *parent_ts = (TaskState *)cpu->opaque; new_thread_info info; pthread_attr_t attr; @@ -6638,6 +6634,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, pthread_cond_destroy(&info.cond); pthread_mutex_destroy(&info.mutex); pthread_mutex_unlock(&clone_lock); +#endif } else { /* if no CLONE_VM, we consider it is a fork */ if (flags & CLONE_INVALID_FORK_FLAGS) { @@ -6680,11 +6677,13 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, put_user_u32(sys_gettid(), child_tidptr); if (flags & CLONE_PARENT_SETTID) put_user_u32(sys_gettid(), parent_tidptr); - ts = (TaskState *)cpu->opaque; if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); +#ifndef CONFIG_LIBTCB + ts = (TaskState *)cpu->opaque; if (flags & CLONE_CHILD_CLEARTID) ts->child_tidptr = child_tidptr; +#endif } else { cpu_clone_regs_parent(env, flags); if (flags & CLONE_PIDFD) { @@ -11775,7 +11774,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); #if defined(TARGET_NR_fchownat) case TARGET_NR_fchownat: - if (!(p = lock_user_string(arg2))) + if (!(p = lock_user_string(arg2))) return -TARGET_EFAULT; ret = get_errno(fchownat(arg1, p, low2highuid(arg3), low2highgid(arg4), arg5)); diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index c63ef45fc78..734c315bbbb 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -22,6 +22,7 @@ #include "exec/exec-all.h" #include "exec/tb-flush.h" #include "qemu/log.h" +#include extern char *exec_path; void init_task_state(TaskState *ts); diff --git a/llvm-helpers/meson.build b/llvm-helpers/meson.build new file mode 100644 index 00000000000..58e4984a7f4 --- /dev/null +++ b/llvm-helpers/meson.build @@ -0,0 +1,6 @@ +llvm_dep = dependency('llvm', modules : ['support']) +opt = find_program(llvm_dep.get_variable(configtool: 'bindir') / 'opt') +clang = find_program(llvm_dep.get_variable(configtool: 'bindir') / 'clang') +llvm_link = find_program(llvm_dep.get_variable(configtool: 'bindir') / 'llvm-link') + +to_ll = files('to_ll.py') diff --git a/llvm-helpers/to_ll.py b/llvm-helpers/to_ll.py new file mode 100755 index 00000000000..3d585b77704 --- /dev/null +++ b/llvm-helpers/to_ll.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import shlex +import sys +import subprocess + +helpers = { + "arm" : [ + "target/arm/helper.c", + "target/arm/tcg/crypto_helper.c", + "target/arm/tcg/hflags.c", + "target/arm/tcg/iwmmxt_helper.c", + "target/arm/tcg/m_helper.c", + "target/arm/tcg/mve_helper.c", + "target/arm/tcg/neon_helper.c", + "target/arm/tcg/op_helper.c", + "target/arm/tcg/tlb_helper.c", + "target/arm/tcg/translate-m-nocp.c", + "target/arm/tcg/translate-mve.c", + "target/arm/tcg/translate-neon.c", + "target/arm/tcg/translate-vfp.c", + "target/arm/tcg/translate.c", + "target/arm/tcg/vec_helper.c", + "target/arm/vfp_helper.c", + "target/arm/debug_helper.c", + "linux-user/arm/nwfpe/fpa11.c", + "linux-user/arm/nwfpe/fpa11_cpdo.c", + "linux-user/arm/nwfpe/fpa11_cpdt.c", + "linux-user/arm/nwfpe/fpa11_cprt.c", + "linux-user/arm/nwfpe/fpopcode.c", + "linux-user/arm/nwfpe/single_cpdo.c", + "linux-user/arm/nwfpe/double_cpdo.c", + "linux-user/arm/nwfpe/extended_cpdo.c", + "linux-user/arm/cpu_loop.c", + "linux-user/arm/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + "qom/object.c", + ], + "aarch64" : [ + "target/arm/helper.c", + "target/arm/debug_helper.c", + "target/arm/tcg/crypto_helper.c", + "target/arm/tcg/helper-a64.c", + "target/arm/tcg/hflags.c", + "target/arm/tcg/iwmmxt_helper.c", + "target/arm/tcg/m_helper.c", + "target/arm/tcg/mte_helper.c", + "target/arm/tcg/mve_helper.c", + "target/arm/tcg/neon_helper.c", + "target/arm/tcg/op_helper.c", + "target/arm/tcg/pauth_helper.c", + "target/arm/tcg/sme_helper.c", + "target/arm/tcg/sve_helper.c", + "target/arm/tcg/tlb_helper.c", + "target/arm/tcg/translate-a64.c", + "target/arm/tcg/translate-m-nocp.c", + "target/arm/tcg/translate-mve.c", + "target/arm/tcg/translate-neon.c", + "target/arm/tcg/translate-sme.c", + "target/arm/tcg/translate-sve.c", + "target/arm/tcg/translate-vfp.c", + "target/arm/tcg/translate.c", + "target/arm/tcg/vec_helper.c", + "target/arm/vfp_helper.c", + "linux-user/aarch64/cpu_loop.c", + "linux-user/aarch64/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + "qom/object.c", + ], + "s390x" : [ + "target/s390x/tcg/cc_helper.c", + "target/s390x/tcg/crypto_helper.c", + "target/s390x/tcg/excp_helper.c", + "target/s390x/tcg/fpu_helper.c", + "target/s390x/tcg/int_helper.c", + "target/s390x/tcg/mem_helper.c", + "target/s390x/tcg/misc_helper.c", + "target/s390x/tcg/translate.c", + "target/s390x/tcg/vec_fpu_helper.c", + "target/s390x/tcg/vec_helper.c", + "target/s390x/tcg/vec_int_helper.c", + "target/s390x/tcg/vec_string_helper.c", + "linux-user/s390x/cpu_loop.c", + "linux-user/s390x/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "i386" : [ + "target/i386/tcg/misc_helper.c", + "target/i386/tcg/translate.c", + "target/i386/tcg/bpt_helper.c", + "target/i386/tcg/cc_helper.c", + "target/i386/tcg/excp_helper.c", + "target/i386/tcg/fpu_helper.c", + "target/i386/tcg/int_helper.c", + "target/i386/tcg/mem_helper.c", + "target/i386/tcg/mpx_helper.c", + "target/i386/tcg/seg_helper.c", + "linux-user/i386/cpu_loop.c", + "linux-user/i386/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "x86_64" : [ + "target/i386/tcg/misc_helper.c", + "target/i386/tcg/translate.c", + "target/i386/tcg/bpt_helper.c", + "target/i386/tcg/cc_helper.c", + "target/i386/tcg/excp_helper.c", + "target/i386/tcg/fpu_helper.c", + "target/i386/tcg/int_helper.c", + "target/i386/tcg/mem_helper.c", + "target/i386/tcg/mpx_helper.c", + "target/i386/tcg/seg_helper.c", + "linux-user/x86_64/cpu_loop.c", + "linux-user/x86_64/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "mips" : [ + "target/mips/tcg/dsp_helper.c", + "target/mips/tcg/exception.c", + "target/mips/tcg/fpu_helper.c", + "target/mips/tcg/ldst_helper.c", + "target/mips/tcg/lmmi_helper.c", + "target/mips/tcg/msa_helper.c", + "target/mips/tcg/msa_translate.c", + "target/mips/tcg/op_helper.c", + "target/mips/tcg/translate.c", + "target/mips/tcg/vr54xx_helper.c", + "target/mips/tcg/vr54xx_translate.c", + "target/mips/tcg/mxu_translate.c", + "linux-user/mips/cpu_loop.c", + "linux-user/mips/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "mipsel" : [ + "target/mips/tcg/dsp_helper.c", + "target/mips/tcg/exception.c", + "target/mips/tcg/fpu_helper.c", + "target/mips/tcg/ldst_helper.c", + "target/mips/tcg/lmmi_helper.c", + "target/mips/tcg/msa_helper.c", + "target/mips/tcg/msa_translate.c", + "target/mips/tcg/op_helper.c", + "target/mips/tcg/translate.c", + "target/mips/tcg/vr54xx_helper.c", + "target/mips/tcg/vr54xx_translate.c", + "target/mips/tcg/mxu_translate.c", + "linux-user/mips/cpu_loop.c", + "linux-user/mips/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "hexagon" : [ + "target/hexagon/decode.c", + "target/hexagon/genptr.c", + "target/hexagon/op_helper.c", + "target/hexagon/translate.c", + "linux-user/hexagon/cpu_loop.c", + "linux-user/hexagon/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "loongarch64" : [ + "target/loongarch/fpu_helper.c", + "target/loongarch/op_helper.c", + "target/loongarch/translate.c", + "target/loongarch/vec_helper.c", + "linux-user/loongarch64/cpu_loop.c", + "linux-user/loongarch64/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "aarch64_be" : [], + "alpha" : [], + "armeb" : [], + "cris" : [], + "hppa" : [], + "m68k" : [], + "microblazeel" : [], + "microblaze" : [], + "mips64el" : [], + "mips64" : [], + "mipsn32el" : [], + "mipsn32" : [], + "nios2" : [], + "or1k" : [], + "ppc64le" : [], + "ppc64" : [], + "ppc" : [], + "riscv32" : [], + "riscv64" : [], + "s390x" : [ + "target/s390x/tcg/cc_helper.c", + "target/s390x/tcg/crypto_helper.c", + "target/s390x/tcg/excp_helper.c", + "target/s390x/tcg/fpu_helper.c", + "target/s390x/tcg/int_helper.c", + "target/s390x/tcg/mem_helper.c", + "target/s390x/tcg/misc_helper.c", + "target/s390x/tcg/translate.c", + "target/s390x/tcg/vec_fpu_helper.c", + "target/s390x/tcg/vec_helper.c", + "target/s390x/tcg/vec_int_helper.c", + "target/s390x/tcg/vec_string_helper.c", + "linux-user/s390x/cpu_loop.c", + "linux-user/s390x/signal.c", + "linux-user/main.c", + "linux-user/syscall.c", + "linux-user/thunk.c", + "linux-user/mmap.c", + "linux-user/signal.c", + "linux-user/uaccess.c", + "linux-user/uname.c", + ], + "sh4eb" : [], + "sh4" : [], + "sparc32plus" : [], + "sparc64" : [], + "sparc" : [], + "xtensaeb" : [], + "xtensa" : [], +} + +def run_command(argv): + proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out,err = proc.communicate() + if proc.wait() != 0: + print("Command exited with %d:\n%s\n" % (proc.returncode," ".join(argv))) + print(f"out:\n{out}\n") + print(f"err:\n{err}\n") + +def find_compile_commands(target_name, clang_path, input_path): + if os.path.exists("compile_commands.json"): + with open("compile_commands.json", "r") as compile_commands_file: + compile_commands = json.load(compile_commands_file) + for compile_command in compile_commands: + path = compile_command["file"] + if os.path.abspath(path) != os.path.abspath(input_path): + continue + + + os.chdir(compile_command["directory"]) + command = compile_command["command"] + print(f"TARGET_NAME: {target_name}"); + print(f"OUTPUT_NAME: {compile_command['output']}"); + if not compile_command["output"].startswith(f"libqemu-{target_name}-") \ + and not compile_command["output"].startswith("libqom"): + continue + + argv = shlex.split(command) + argv[0] = clang_path + return argv + + # If we coulnd't find "compile_commands" just default + # to the following. + print(f"Unable to find {input_path}") + assert(False) + return [clang_path, input_path] + +def generate_llvm_ir(target_name, clang_path, input_path, output_path): + argv = find_compile_commands(target_name, clang_path, input_path) + + for i, arg in reversed(list(enumerate(argv))): + if arg in {'-MQ', '-o', '-MF'}: + del argv[i:i+2] + + argv += ["-Wno-unknown-warning-option", "-emit-llvm", "-O0", "-Xclang", "-disable-O0-optnone", "-o", output_path] + + run_command(argv) + +def link(llvm_link_path, ll_paths, output_path): + argv = [llvm_link_path] + ll_paths + ['-o', output_path] + run_command(argv) + +def temp_file(output_folder, file): + _, containing_folder = os.path.split(os.path.abspath(os.path.dirname(file))) + dir = os.path.join(output_folder, containing_folder) + os.makedirs(dir, exist_ok=True) + filename, _ = os.path.splitext(os.path.basename(file)) + new_file = os.path.join(dir, filename) + ".bc" + return new_file + +def main(): + parser = argparse.ArgumentParser(description='Produce the LLVM IR for a given source file.') + parser.add_argument('source_dir', metavar='SOURCE_DIR', help='qemu source directory') + parser.add_argument('target_name', metavar='TARGET_NAME', help='Name of target') + parser.add_argument('target', metavar='TARGET', help='Target') + parser.add_argument("clang", metavar="CLANG_PATH", help="Path to clang.") + parser.add_argument("llvm_link", metavar="LLVM_LINK_PATH", help="Path to llvm-link") + parser.add_argument("opt", metavar="OPT_PATH", help="Path to opt") + parser.add_argument('output', metavar='OUTPUT_PATH', help='Path to output file') + args = parser.parse_args() + + output_folder = os.path.join("llvm-helpers", args.target_name) + os.makedirs(output_folder, exist_ok=True) + + # Check that all paths in `helper` actually exist + has_nonexistent_files = False + for target in helpers: + for file in helpers[target]: + if not os.path.exists(os.path.join(args.source_dir, file)): + has_nonexistent_files = True + print(f"Error for {target}: File does not exist {file}") + assert(not has_nonexistent_files) + + # Generate LLVM IR + ll_paths = [] + print(args.source_dir) + for file in helpers[args.target_name]: + input_path = os.path.join(args.source_dir, file) + output_path = temp_file(output_folder, input_path) + print(f" {input_path}") + print(f" -> {output_path}") + ll_paths.append(output_path) + generate_llvm_ir(args.target_name, args.clang, input_path, output_path) + + link(args.llvm_link, ll_paths, args.output) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/meson.build b/meson.build index 6c77d9687de..b4681641322 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('qemu', ['c'], meson_version: '>=0.63.0', +project('qemu', ['c', 'cpp'], meson_version: '>=0.63.0', default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], version: files('VERSION')) @@ -64,6 +64,8 @@ target_dirs = config_host['TARGET_DIRS'].split() have_linux_user = false have_bsd_user = false have_system = false +have_libtcg = get_option('libtcg') +have_llvm_helpers = get_option('llvm-helpers') foreach target : target_dirs have_linux_user = have_linux_user or target.endswith('linux-user') have_bsd_user = have_bsd_user or target.endswith('bsd-user') @@ -3139,6 +3141,10 @@ config_host_data.set('CONFIG_CAPSTONE', capstone.found()) config_host_data.set('CONFIG_FDT', fdt.found()) config_host_data.set('CONFIG_SLIRP', slirp.found()) +if have_llvm_helpers + subdir('llvm-helpers') +endif + ##################### # Generated sources # ##################### @@ -3506,6 +3512,7 @@ subdir('fpu') subdir('accel') subdir('plugins') subdir('ebpf') +subdir('libtcg') common_user_inc = [] @@ -3846,6 +3853,26 @@ foreach target : target_dirs build_by_default: false, name_suffix: 'fa') + # TODO(anjo): We should only feed the bare minimum of + # files through this! + if have_llvm_helpers + # TODO(anjo): We're not feeding any input files which means meson isn't able to + # track dependencies correctly. Problem is, for targets which uses the decodetree + # this will cause decode-insns.c.inc to be generated twice, causing meson to bail. + # + # We also don't use the @INPUT@ field in the command, but it's required to ensure + # we depend on generated files and can compiler to .bc without troubles. + custom_target('to-ll-' + target_name, + output: 'libtcg-helpers-' + target_name + '.bc', + depend_files: to_ll, + input: genh, + command: [to_ll, meson.current_source_dir(), target_name, 'target'/target_base_arch, clang, llvm_link, opt, '@OUTPUT@'], + install: true, + install_dir: 'lib', + build_by_default: true, + ) + endif + if target.endswith('-softmmu') execs = [{ 'name': 'qemu-system-' + target_name, @@ -3870,6 +3897,20 @@ foreach target : target_dirs 'dependencies': specific_fuzz.dependencies(), }] endif + elif have_libtcg + install_headers('include/libtcg/libtcg.h', subdir: 'qemu/libtcg') + install_headers('include/tcg/tcg-opc.h', subdir: 'qemu/libtcg/tcg') + # Note "lib" gets prepended automatically! + emulator = shared_library('tcg-' + target_name, + include_directories: target_inc, + install: true, + objects: lib.extract_all_objects(recursive: true), + dependencies: arch_deps + deps, + c_args: c_args, + link_depends: [block_syms, qemu_syms], + link_args: link_args) + + execs = [] else execs = [{ 'name': 'qemu-' + target_name, @@ -3960,7 +4001,7 @@ subdir('qga') # Don't build qemu-keymap if xkbcommon is not explicitly enabled # when we don't build tools or system -if xkbcommon.found() +if not have_libtcg and xkbcommon.found() # used for the update-keymaps target, so include rules even if !have_tools qemu_keymap = executable('qemu-keymap', files('qemu-keymap.c', 'ui/input-keymap.c') + genh, dependencies: [qemuutil, xkbcommon], install: have_tools) @@ -4008,14 +4049,16 @@ if have_tools endif endif -subdir('scripts') -subdir('tools') -subdir('pc-bios') -subdir('docs') -subdir('tests') -if gtk.found() - subdir('po') +if not have_libtcg + subdir('scripts') + subdir('tools') + subdir('pc-bios') + subdir('tests') + if gtk.found() + subdir('po') + endif endif +subdir('docs') if host_machine.system() == 'windows' nsis_cmd = [ diff --git a/meson_options.txt b/meson_options.txt index c9baeda6395..1f1c2eeea21 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -87,6 +87,10 @@ option('debug_tcg', type: 'boolean', value: false, description: 'TCG debugging') option('tcg_interpreter', type: 'boolean', value: false, description: 'TCG with bytecode interpreter (slow)') +option('libtcg', type: 'boolean', value: false, + description: 'Build *-user target as a shared library, exposing tcg frontend') +option('llvm-helpers', type: 'boolean', value: false, + description: 'Build LLVM bitcode target containing relevant helper functions') option('safe_stack', type: 'boolean', value: false, description: 'SafeStack Stack Smash Protection (requires clang/llvm and coroutine backend ucontext)') option('sanitizers', type: 'boolean', value: false, diff --git a/qom/object.c b/qom/object.c index 95c0dc8285f..b07c2402587 100644 --- a/qom/object.c +++ b/qom/object.c @@ -884,8 +884,8 @@ Object *object_dynamic_cast(Object *obj, const char *typename) Object *object_dynamic_cast_assert(Object *obj, const char *typename, const char *file, int line, const char *func) { - trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", - typename, file, line, func); + //trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", + // typename, file, line, func); #ifdef CONFIG_QOM_CAST_DEBUG int i; diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 680fa3f581d..7af9638edbe 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -36,6 +36,10 @@ meson_options_help() { printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' printf "%s\n" ' --enable-fuzzing build fuzzing targets' printf "%s\n" ' --enable-gcov Enable coverage tracking.' + printf "%s\n" ' --enable-libtcg Build *-user target as a shared library, exposing' + printf "%s\n" ' tcg frontend' + printf "%s\n" ' --enable-llvm-helpers Build LLVM bitcode target containing relevant' + printf "%s\n" ' helper functions' printf "%s\n" ' --enable-lto Use link time optimization' printf "%s\n" ' --enable-malloc=CHOICE choose memory allocator to use [system] (choices:' printf "%s\n" ' jemalloc/system/tcmalloc)' @@ -370,6 +374,8 @@ _meson_option_parse() { --disable-libpmem) printf "%s" -Dlibpmem=disabled ;; --enable-libssh) printf "%s" -Dlibssh=enabled ;; --disable-libssh) printf "%s" -Dlibssh=disabled ;; + --enable-libtcg) printf "%s" -Dlibtcg=true ;; + --disable-libtcg) printf "%s" -Dlibtcg=false ;; --enable-libudev) printf "%s" -Dlibudev=enabled ;; --disable-libudev) printf "%s" -Dlibudev=disabled ;; --enable-libusb) printf "%s" -Dlibusb=enabled ;; @@ -382,6 +388,8 @@ _meson_option_parse() { --disable-linux-io-uring) printf "%s" -Dlinux_io_uring=disabled ;; --enable-live-block-migration) printf "%s" -Dlive_block_migration=enabled ;; --disable-live-block-migration) printf "%s" -Dlive_block_migration=disabled ;; + --enable-llvm-helpers) printf "%s" -Dllvm-helpers=true ;; + --disable-llvm-helpers) printf "%s" -Dllvm-helpers=false ;; --localedir=*) quote_sh "-Dlocaledir=$2" ;; --localstatedir=*) quote_sh "-Dlocalstatedir=$2" ;; --enable-lzfse) printf "%s" -Dlzfse=enabled ;; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a0282e0d281..2e8ada1cf7a 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3435,8 +3435,9 @@ extern const uint64_t pred_esz_masks[5]; */ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) { - ARMCPU *cpu = ARM_CPU(cs); - if (cpu->env.tagged_addr_enable) { + //ARMCPU *cpu = ARM_CPU(cs); + CPUArchState *env = cpu_env(cs); + if (env->tagged_addr_enable) { /* * TBI is enabled for userspace but not kernelspace addresses. * Only clear the tag if bit 55 is clear. diff --git a/target/cris/translate.c b/target/cris/translate.c index b3974ba0bbb..dd9e96c6cf9 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -50,7 +50,7 @@ #endif #define D(x) -#define BUG() (gen_BUG(dc, __FILE__, __LINE__)) +#define BUG() (siglongjmp(tcg_ctx->jmp_trans, -3)) #define BUG_ON(x) ({if (x) BUG();}) /* diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 946c55cc71d..cc8a161d151 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -933,7 +933,9 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, encoding32 = words[words_read]; end_of_packet = is_packet_end(encoding32); new_insns = decode_insns(&pkt->insn[num_insns], encoding32); - g_assert(new_insns > 0); + if (new_insns == 0) { + siglongjmp(tcg_ctx->jmp_trans, -3); + } /* * If we saw an extender, mark next word extended so immediate * decode works diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index 4af020933aa..55c41e5492c 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -2122,6 +2122,9 @@ void free_instruction(Context *c) for (unsigned i = 0; i < c->inst.strings->len; i++) { g_string_free(g_array_index(c->inst.strings, GString*, i), TRUE); } + if (c->inst.init_list != NULL) { + g_array_free(c->inst.init_list, TRUE); + } g_array_free(c->inst.strings, TRUE); /* Free INAME token value */ g_string_free(c->inst.name, TRUE); diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index da10ac58476..134fc532c79 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -44,7 +44,8 @@ void do_raise_exception_err(CPUHexagonState *env, CPUState *cs = env_cpu(env); qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); cs->exception_index = exception; - cpu_loop_exit_restore(cs, pc); + // TODO(anjo): fix + cpu_loop_exit(cs); } G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 666c0611802..943d4350110 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -154,7 +154,7 @@ static void gen_end_tb(DisasContext *ctx) { Packet *pkt = ctx->pkt; - gen_exec_counters(ctx); + //gen_exec_counters(ctx); if (ctx->branch_cond != TCG_COND_NEVER) { if (ctx->branch_cond != TCG_COND_ALWAYS) { diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 8df00b7df9f..1e42c1175f5 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -6488,6 +6488,7 @@ static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) dc->base.is_jmp = translate_one(env, dc); if (dc->base.is_jmp == DISAS_NEXT) { if (dc->ex_value || + dcbase->pc_next == dcbase->tb->max_pc || !is_same_page(dcbase, dc->base.pc_next) || !is_same_page(dcbase, get_next_pc(env, dc, dc->base.pc_next))) { dc->base.is_jmp = DISAS_TOO_MANY; diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 33f15a564ab..98623baabe0 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -138,8 +138,8 @@ typedef enum { #define TCG_TARGET_HAS_qemu_ldst_i128 1 #endif -#define TCG_TARGET_HAS_v64 1 -#define TCG_TARGET_HAS_v128 1 +#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v128 0 #define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_andc_vec 1 diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index a712cc80adf..fd44282948b 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -125,8 +125,8 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_qemu_ldst_i128 0 -#define TCG_TARGET_HAS_v64 use_neon_instructions -#define TCG_TARGET_HAS_v128 use_neon_instructions +#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v128 0 #define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_andc_vec 1 diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index fa34deec47b..2cd7e7a7635 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -196,12 +196,12 @@ typedef enum { #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ - (TCG_TARGET_REG_BITS == 64 && (cpuinfo & CPUINFO_ATOMIC_VMOVDQA)) + 0 /* We do not support older SSE systems, only beginning with AVX1. */ -#define TCG_TARGET_HAS_v64 have_avx1 -#define TCG_TARGET_HAS_v128 have_avx1 -#define TCG_TARGET_HAS_v256 have_avx2 +#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v128 0 +#define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_andc_vec 1 #define TCG_TARGET_HAS_orc_vec have_avx512vl diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 9c70ebfefc8..5d308e6ad26 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -170,7 +170,7 @@ typedef enum { #define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) #define TCG_TARGET_HAS_v64 0 -#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) +#define TCG_TARGET_HAS_v128 0 #define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_not_vec 1 diff --git a/tcg/optimize.c b/tcg/optimize.c index f2d01654c59..cc0a2e0ff2e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2388,6 +2388,20 @@ void tcg_optimize(TCGContext *s) ctx.z_mask = -1; ctx.s_mask = 0; + if (def->nb_oargs > 0) { + bool skip = false; + for (int i = 0; i < def->nb_oargs; ++i) { + TCGTempKind kind = arg_temp(op->args[i])->kind; + if (kind == TEMP_GLOBAL || kind == TEMP_FIXED) { + skip = true; + break; + } + } + if (skip) { + continue; + } + } + /* * Process each opcode. * Sorted alphabetically by opcode as much as possible. diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 5295e4f9abd..fd507d09ae6 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -148,8 +148,8 @@ typedef enum { * instruction and substituting two 32-bit stores makes the generated * code quite large. */ -#define TCG_TARGET_HAS_v64 have_vsx -#define TCG_TARGET_HAS_v128 have_altivec +#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v128 0 #define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_andc_vec 1 diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index e69b0d2dddb..e692b470d70 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -138,8 +138,8 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_qemu_ldst_i128 1 -#define TCG_TARGET_HAS_v64 HAVE_FACILITY(VECTOR) -#define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) +#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v128 0 #define TCG_TARGET_HAS_v256 0 #define TCG_TARGET_HAS_andc_vec 1 diff --git a/tcg/tcg.c b/tcg/tcg.c index e2c38f6d11c..b051f5eb831 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2463,8 +2463,8 @@ static char *tcg_get_arg_str_ptr(TCGContext *s, char *buf, int buf_size, return buf; } -static char *tcg_get_arg_str(TCGContext *s, char *buf, - int buf_size, TCGArg arg) +char *tcg_get_arg_str(TCGContext *s, char *buf, + int buf_size, TCGArg arg) { return tcg_get_arg_str_ptr(s, buf, buf_size, arg_temp(arg)); } @@ -3258,7 +3258,7 @@ static void move_label_uses(TCGLabel *to, TCGLabel *from) } /* Reachable analysis : remove unreachable code. */ -static void __attribute__((noinline)) +void __attribute__((noinline)) reachable_code_pass(TCGContext *s) { TCGOp *op, *op_next, *op_prev; @@ -3497,7 +3497,7 @@ static void la_cross_call(TCGContext *s, int nt) * Liveness analysis: Verify the lifetime of TEMP_TB, and reduce * to TEMP_EBB, if possible. */ -static void __attribute__((noinline)) +void __attribute__((noinline)) liveness_pass_0(TCGContext *s) { void * const multiple_ebb = (void *)(uintptr_t)-1; @@ -3565,7 +3565,7 @@ liveness_pass_0(TCGContext *s) /* Liveness analysis : update the opc_arg_life array to tell if a given input arguments is dead. Instructions updating dead temporaries are removed. */ -static void __attribute__((noinline)) +void __attribute__((noinline)) liveness_pass_1(TCGContext *s) { int nb_globals = s->nb_globals; @@ -3906,7 +3906,7 @@ liveness_pass_1(TCGContext *s) } /* Liveness analysis: Convert indirect regs to direct temporaries. */ -static bool __attribute__((noinline)) +bool __attribute__((noinline)) liveness_pass_2(TCGContext *s) { int nb_globals = s->nb_globals;