diff options
| author | Ian Rogers <irogers@google.com> | 2026-01-22 13:35:12 -0800 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2026-01-23 16:58:38 -0300 |
| commit | 07b972ff09f45cfb7acd20cd9b3769c6975bc434 (patch) | |
| tree | f8cecf6a6a63113dd96b4305a42ef5af5eecef2b /tools/perf/arch | |
| parent | 9273085273103e5994952dc2725f1f0109af97d1 (diff) | |
| download | lwn-07b972ff09f45cfb7acd20cd9b3769c6975bc434.tar.gz lwn-07b972ff09f45cfb7acd20cd9b3769c6975bc434.zip | |
perf disasm: Don't include C files from the arch directory
Move the arch instructions.c files into appropriately named files in
annotate-arch in the util directory.
Don't #include to compile the code, switch to building the files and fix
up the #includes accordingly.
Move powerpc specific disasm code out of disasm.c and into
annotate-powerpc.c.
Declarations and static removed as appropriate for the code to compile
as separate compilation units.
The e_machine and e_flags set up is moved to the disasm.c architectures
array so that later patches can sort by them.
Reviewed-by: James Clark <james.clark@linaro.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Aditya Bodkhe <aditya.b1@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Bill Wendling <morbo@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Julia Lawall <Julia.Lawall@inria.fr>
Cc: Justin Stitt <justinstitt@google.com>
Cc: Krzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergei Trofimovich <slyich@gmail.com>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Suchit Karunakaran <suchitkarunakaran@gmail.com>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Tianyou Li <tianyou.li@intel.com>
Cc: Will Deacon <will@kernel.org>
Cc: Zecheng Li <zecheng@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/arch')
| -rw-r--r-- | tools/perf/arch/arc/annotate/instructions.c | 11 | ||||
| -rw-r--r-- | tools/perf/arch/arm/annotate/instructions.c | 66 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/annotate/instructions.c | 126 | ||||
| -rw-r--r-- | tools/perf/arch/csky/annotate/instructions.c | 53 | ||||
| -rw-r--r-- | tools/perf/arch/loongarch/annotate/instructions.c | 145 | ||||
| -rw-r--r-- | tools/perf/arch/mips/annotate/instructions.c | 48 | ||||
| -rw-r--r-- | tools/perf/arch/powerpc/annotate/instructions.c | 317 | ||||
| -rw-r--r-- | tools/perf/arch/riscv64/annotate/instructions.c | 36 | ||||
| -rw-r--r-- | tools/perf/arch/s390/annotate/instructions.c | 178 | ||||
| -rw-r--r-- | tools/perf/arch/sparc/annotate/instructions.c | 171 | ||||
| -rw-r--r-- | tools/perf/arch/x86/annotate/instructions.c | 797 |
11 files changed, 0 insertions, 1948 deletions
diff --git a/tools/perf/arch/arc/annotate/instructions.c b/tools/perf/arch/arc/annotate/instructions.c deleted file mode 100644 index e5619770a1af..000000000000 --- a/tools/perf/arch/arc/annotate/instructions.c +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/compiler.h> - -static int arc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - arch->initialized = true; - arch->objdump.comment_char = ';'; - arch->e_machine = EM_ARC; - arch->e_flags = 0; - return 0; -} diff --git a/tools/perf/arch/arm/annotate/instructions.c b/tools/perf/arch/arm/annotate/instructions.c deleted file mode 100644 index b997d127fedd..000000000000 --- a/tools/perf/arch/arm/annotate/instructions.c +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/compiler.h> -#include <linux/zalloc.h> -#include <errno.h> -#include <sys/types.h> -#include <regex.h> -#include <stdlib.h> - -struct arm_annotate { - regex_t call_insn, - jump_insn; -}; - -static const struct ins_ops *arm__associate_instruction_ops(struct arch *arch, const char *name) -{ - struct arm_annotate *arm = arch->priv; - const struct ins_ops *ops; - regmatch_t match[2]; - - if (!regexec(&arm->call_insn, name, 2, match, 0)) - ops = &call_ops; - else if (!regexec(&arm->jump_insn, name, 2, match, 0)) - ops = &jump_ops; - else - return NULL; - - arch__associate_ins_ops(arch, name, ops); - return ops; -} - -static int arm__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - struct arm_annotate *arm; - int err; - - if (arch->initialized) - return 0; - - arm = zalloc(sizeof(*arm)); - if (!arm) - return ENOMEM; - -#define ARM_CONDS "(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl|vc|vs)" - err = regcomp(&arm->call_insn, "^blx?" ARM_CONDS "?$", REG_EXTENDED); - if (err) - goto out_free_arm; - err = regcomp(&arm->jump_insn, "^bx?" ARM_CONDS "?$", REG_EXTENDED); - if (err) - goto out_free_call; -#undef ARM_CONDS - - arch->initialized = true; - arch->priv = arm; - arch->associate_instruction_ops = arm__associate_instruction_ops; - arch->objdump.comment_char = ';'; - arch->objdump.skip_functions_char = '+'; - arch->e_machine = EM_ARM; - arch->e_flags = 0; - return 0; - -out_free_call: - regfree(&arm->call_insn); -out_free_arm: - free(arm); - return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP; -} diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c deleted file mode 100644 index 44db33854dba..000000000000 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/compiler.h> -#include <errno.h> -#include <sys/types.h> -#include <regex.h> -#include <stdlib.h> - -struct arm64_annotate { - regex_t call_insn, - jump_insn; -}; - -static int arm64_mov__parse(const struct arch *arch __maybe_unused, - struct ins_operands *ops, - struct map_symbol *ms __maybe_unused, - struct disasm_line *dl __maybe_unused) -{ - char *s = strchr(ops->raw, ','), *target, *endptr; - - if (s == NULL) - return -1; - - *s = '\0'; - ops->source.raw = strdup(ops->raw); - *s = ','; - - if (ops->source.raw == NULL) - return -1; - - target = ++s; - ops->target.raw = strdup(target); - if (ops->target.raw == NULL) - goto out_free_source; - - ops->target.addr = strtoull(target, &endptr, 16); - if (endptr == target) - goto out_free_target; - - s = strchr(endptr, '<'); - if (s == NULL) - goto out_free_target; - endptr = strchr(s + 1, '>'); - if (endptr == NULL) - goto out_free_target; - - *endptr = '\0'; - *s = ' '; - ops->target.name = strdup(s); - *s = '<'; - *endptr = '>'; - if (ops->target.name == NULL) - goto out_free_target; - - return 0; - -out_free_target: - zfree(&ops->target.raw); -out_free_source: - zfree(&ops->source.raw); - return -1; -} - -static int mov__scnprintf(const struct ins *ins, char *bf, size_t size, - struct ins_operands *ops, int max_ins_name); - -static const struct ins_ops arm64_mov_ops = { - .parse = arm64_mov__parse, - .scnprintf = mov__scnprintf, -}; - -static const struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name) -{ - struct arm64_annotate *arm = arch->priv; - const struct ins_ops *ops; - regmatch_t match[2]; - - if (!regexec(&arm->jump_insn, name, 2, match, 0)) - ops = &jump_ops; - else if (!regexec(&arm->call_insn, name, 2, match, 0)) - ops = &call_ops; - else if (!strcmp(name, "ret")) - ops = &ret_ops; - else - ops = &arm64_mov_ops; - - arch__associate_ins_ops(arch, name, ops); - return ops; -} - -static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - struct arm64_annotate *arm; - int err; - - if (arch->initialized) - return 0; - - arm = zalloc(sizeof(*arm)); - if (!arm) - return ENOMEM; - - /* bl, blr */ - err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED); - if (err) - goto out_free_arm; - /* b, b.cond, br, cbz/cbnz, tbz/tbnz */ - err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|hs|le|lo|ls|lt|mi|ne|pl|vc|vs)?n?z?$", - REG_EXTENDED); - if (err) - goto out_free_call; - - arch->initialized = true; - arch->priv = arm; - arch->associate_instruction_ops = arm64__associate_instruction_ops; - arch->objdump.comment_char = '/'; - arch->objdump.skip_functions_char = '+'; - arch->e_machine = EM_AARCH64; - arch->e_flags = 0; - return 0; - -out_free_call: - regfree(&arm->call_insn); -out_free_arm: - free(arm); - return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP; -} diff --git a/tools/perf/arch/csky/annotate/instructions.c b/tools/perf/arch/csky/annotate/instructions.c deleted file mode 100644 index 4a55c84a320a..000000000000 --- a/tools/perf/arch/csky/annotate/instructions.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. - -#include <linux/compiler.h> - -static const struct ins_ops *csky__associate_ins_ops(struct arch *arch, - const char *name) -{ - const struct ins_ops *ops = NULL; - - /* catch all kind of jumps */ - if (!strcmp(name, "bt") || - !strcmp(name, "bf") || - !strcmp(name, "bez") || - !strcmp(name, "bnez") || - !strcmp(name, "bnezad") || - !strcmp(name, "bhsz") || - !strcmp(name, "bhz") || - !strcmp(name, "blsz") || - !strcmp(name, "blz") || - !strcmp(name, "br") || - !strcmp(name, "jmpi") || - !strcmp(name, "jmp")) - ops = &jump_ops; - - /* catch function call */ - if (!strcmp(name, "bsr") || - !strcmp(name, "jsri") || - !strcmp(name, "jsr")) - ops = &call_ops; - - /* catch function return */ - if (!strcmp(name, "rts")) - ops = &ret_ops; - - if (ops) - arch__associate_ins_ops(arch, name, ops); - return ops; -} - -static int csky__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - arch->initialized = true; - arch->objdump.comment_char = '/'; - arch->associate_instruction_ops = csky__associate_ins_ops; - arch->e_machine = EM_CSKY; -#if defined(__CSKYABIV2__) - arch->e_flags = EF_CSKY_ABIV2; -#else - arch->e_flags = EF_CSKY_ABIV1; -#endif - return 0; -} diff --git a/tools/perf/arch/loongarch/annotate/instructions.c b/tools/perf/arch/loongarch/annotate/instructions.c deleted file mode 100644 index 5010d5d58375..000000000000 --- a/tools/perf/arch/loongarch/annotate/instructions.c +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Perf annotate functions. - * - * Copyright (C) 2020-2023 Loongson Technology Corporation Limited - */ - -static int loongarch_call__parse(const struct arch *arch, struct ins_operands *ops, - struct map_symbol *ms, - struct disasm_line *dl __maybe_unused) - -{ - char *c, *endptr, *tok, *name; - struct map *map = ms->map; - struct addr_map_symbol target; - - c = strchr(ops->raw, '#'); - if (c++ == NULL) - return -1; - - ops->target.addr = strtoull(c, &endptr, 16); - - name = strchr(endptr, '<'); - name++; - - if (arch->objdump.skip_functions_char && - strchr(name, arch->objdump.skip_functions_char)) - return -1; - - tok = strchr(name, '>'); - if (tok == NULL) - return -1; - - *tok = '\0'; - ops->target.name = strdup(name); - *tok = '>'; - - if (ops->target.name == NULL) - return -1; - - target = (struct addr_map_symbol) { - .ms = { .map = map__get(map), }, - .addr = map__objdump_2mem(map, ops->target.addr), - }; - - if (maps__find_ams(ms->maps, &target) == 0 && - map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) - ops->target.sym = target.ms.sym; - - addr_map_symbol__exit(&target); - return 0; -} - -static const struct ins_ops loongarch_call_ops = { - .parse = loongarch_call__parse, - .scnprintf = call__scnprintf, -}; - -static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *ops, - struct map_symbol *ms, - struct disasm_line *dl __maybe_unused) - -{ - struct map *map = ms->map; - struct symbol *sym = ms->sym; - struct addr_map_symbol target = { - .ms = { .map = map__get(map), }, - }; - const char *c = strchr(ops->raw, '#'); - u64 start, end; - - ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char); - ops->jump.raw_func_start = strchr(ops->raw, '<'); - - if (ops->jump.raw_func_start && c > ops->jump.raw_func_start) - c = NULL; - - if (c++ != NULL) - ops->target.addr = strtoull(c, NULL, 16); - else - ops->target.addr = strtoull(ops->raw, NULL, 16); - - target.addr = map__objdump_2mem(map, ops->target.addr); - start = map__unmap_ip(map, sym->start); - end = map__unmap_ip(map, sym->end); - - ops->target.outside = target.addr < start || target.addr > end; - - if (maps__find_ams(ms->maps, &target) == 0 && - map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) - ops->target.sym = target.ms.sym; - - if (!ops->target.outside) { - ops->target.offset = target.addr - start; - ops->target.offset_avail = true; - } else { - ops->target.offset_avail = false; - } - addr_map_symbol__exit(&target); - return 0; -} - -static const struct ins_ops loongarch_jump_ops = { - .parse = loongarch_jump__parse, - .scnprintf = jump__scnprintf, -}; - -static -const struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) -{ - const struct ins_ops *ops = NULL; - - if (!strcmp(name, "bl")) - ops = &loongarch_call_ops; - else if (!strcmp(name, "jirl")) - ops = &ret_ops; - else if (!strcmp(name, "b") || - !strncmp(name, "beq", 3) || - !strncmp(name, "bne", 3) || - !strncmp(name, "blt", 3) || - !strncmp(name, "bge", 3) || - !strncmp(name, "bltu", 4) || - !strncmp(name, "bgeu", 4)) - ops = &loongarch_jump_ops; - else - return NULL; - - arch__associate_ins_ops(arch, name, ops); - - return ops; -} - -static -int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - if (!arch->initialized) { - arch->associate_instruction_ops = loongarch__associate_ins_ops; - arch->initialized = true; - arch->objdump.comment_char = '#'; - arch->e_machine = EM_LOONGARCH; - arch->e_flags = 0; - } - - return 0; -} diff --git a/tools/perf/arch/mips/annotate/instructions.c b/tools/perf/arch/mips/annotate/instructions.c deleted file mode 100644 index 0fbe0a7df95a..000000000000 --- a/tools/perf/arch/mips/annotate/instructions.c +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -static -const struct ins_ops *mips__associate_ins_ops(struct arch *arch, const char *name) -{ - const struct ins_ops *ops = NULL; - - if (!strncmp(name, "bal", 3) || - !strncmp(name, "bgezal", 6) || - !strncmp(name, "bltzal", 6) || - !strncmp(name, "bgtzal", 6) || - !strncmp(name, "blezal", 6) || - !strncmp(name, "beqzal", 6) || - !strncmp(name, "bnezal", 6) || - !strncmp(name, "bgtzl", 5) || - !strncmp(name, "bltzl", 5) || - !strncmp(name, "bgezl", 5) || - !strncmp(name, "blezl", 5) || - !strncmp(name, "jialc", 5) || - !strncmp(name, "beql", 4) || - !strncmp(name, "bnel", 4) || - !strncmp(name, "jal", 3)) - ops = &call_ops; - else if (!strncmp(name, "jr", 2)) - ops = &ret_ops; - else if (name[0] == 'j' || name[0] == 'b') - ops = &jump_ops; - else - return NULL; - - arch__associate_ins_ops(arch, name, ops); - - return ops; -} - -static -int mips__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - if (!arch->initialized) { - arch->associate_instruction_ops = mips__associate_ins_ops; - arch->initialized = true; - arch->objdump.comment_char = '#'; - arch->e_machine = EM_MIPS; - arch->e_flags = 0; - } - - return 0; -} diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c deleted file mode 100644 index d1be55425e35..000000000000 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ /dev/null @@ -1,317 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/compiler.h> - -static const struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, const char *name) -{ - int i; - const struct ins_ops *ops; - - /* - * - Interested only if instruction starts with 'b'. - * - Few start with 'b', but aren't branch instructions. - */ - if (name[0] != 'b' || - !strncmp(name, "bcd", 3) || - !strncmp(name, "brinc", 5) || - !strncmp(name, "bper", 4)) - return NULL; - - ops = &jump_ops; - - i = strlen(name) - 1; - if (i < 0) - return NULL; - - /* ignore optional hints at the end of the instructions */ - if (name[i] == '+' || name[i] == '-') - i--; - - if (name[i] == 'l' || (name[i] == 'a' && name[i-1] == 'l')) { - /* - * if the instruction ends up with 'l' or 'la', then - * those are considered 'calls' since they update LR. - * ... except for 'bnl' which is branch if not less than - * and the absolute form of the same. - */ - if (strcmp(name, "bnl") && strcmp(name, "bnl+") && - strcmp(name, "bnl-") && strcmp(name, "bnla") && - strcmp(name, "bnla+") && strcmp(name, "bnla-")) - ops = &call_ops; - } - if (name[i] == 'r' && name[i-1] == 'l') - /* - * instructions ending with 'lr' are considered to be - * return instructions - */ - ops = &ret_ops; - - arch__associate_ins_ops(arch, name, ops); - return ops; -} - -#define PPC_OP(op) (((op) >> 26) & 0x3F) -#define PPC_21_30(R) (((R) >> 1) & 0x3ff) -#define PPC_22_30(R) (((R) >> 1) & 0x1ff) - -struct insn_offset { - const char *name; - int value; -}; - -/* - * There are memory instructions with opcode 31 which are - * of X Form, Example: - * ldx RT,RA,RB - * ______________________________________ - * | 31 | RT | RA | RB | 21 |/| - * -------------------------------------- - * 0 6 11 16 21 30 31 - * - * But all instructions with opcode 31 are not memory. - * Example: add RT,RA,RB - * - * Use bits 21 to 30 to check memory insns with 31 as opcode. - * In ins_array below, for ldx instruction: - * name => OP_31_XOP_LDX - * value => 21 - */ - -static struct insn_offset ins_array[] = { - { .name = "OP_31_XOP_LXSIWZX", .value = 12, }, - { .name = "OP_31_XOP_LWARX", .value = 20, }, - { .name = "OP_31_XOP_LDX", .value = 21, }, - { .name = "OP_31_XOP_LWZX", .value = 23, }, - { .name = "OP_31_XOP_LDUX", .value = 53, }, - { .name = "OP_31_XOP_LWZUX", .value = 55, }, - { .name = "OP_31_XOP_LXSIWAX", .value = 76, }, - { .name = "OP_31_XOP_LDARX", .value = 84, }, - { .name = "OP_31_XOP_LBZX", .value = 87, }, - { .name = "OP_31_XOP_LVX", .value = 103, }, - { .name = "OP_31_XOP_LBZUX", .value = 119, }, - { .name = "OP_31_XOP_STXSIWX", .value = 140, }, - { .name = "OP_31_XOP_STDX", .value = 149, }, - { .name = "OP_31_XOP_STWX", .value = 151, }, - { .name = "OP_31_XOP_STDUX", .value = 181, }, - { .name = "OP_31_XOP_STWUX", .value = 183, }, - { .name = "OP_31_XOP_STBX", .value = 215, }, - { .name = "OP_31_XOP_STVX", .value = 231, }, - { .name = "OP_31_XOP_STBUX", .value = 247, }, - { .name = "OP_31_XOP_LHZX", .value = 279, }, - { .name = "OP_31_XOP_LHZUX", .value = 311, }, - { .name = "OP_31_XOP_LXVDSX", .value = 332, }, - { .name = "OP_31_XOP_LWAX", .value = 341, }, - { .name = "OP_31_XOP_LHAX", .value = 343, }, - { .name = "OP_31_XOP_LWAUX", .value = 373, }, - { .name = "OP_31_XOP_LHAUX", .value = 375, }, - { .name = "OP_31_XOP_STHX", .value = 407, }, - { .name = "OP_31_XOP_STHUX", .value = 439, }, - { .name = "OP_31_XOP_LXSSPX", .value = 524, }, - { .name = "OP_31_XOP_LDBRX", .value = 532, }, - { .name = "OP_31_XOP_LSWX", .value = 533, }, - { .name = "OP_31_XOP_LWBRX", .value = 534, }, - { .name = "OP_31_XOP_LFSUX", .value = 567, }, - { .name = "OP_31_XOP_LXSDX", .value = 588, }, - { .name = "OP_31_XOP_LSWI", .value = 597, }, - { .name = "OP_31_XOP_LFDX", .value = 599, }, - { .name = "OP_31_XOP_LFDUX", .value = 631, }, - { .name = "OP_31_XOP_STXSSPX", .value = 652, }, - { .name = "OP_31_XOP_STDBRX", .value = 660, }, - { .name = "OP_31_XOP_STXWX", .value = 661, }, - { .name = "OP_31_XOP_STWBRX", .value = 662, }, - { .name = "OP_31_XOP_STFSX", .value = 663, }, - { .name = "OP_31_XOP_STFSUX", .value = 695, }, - { .name = "OP_31_XOP_STXSDX", .value = 716, }, - { .name = "OP_31_XOP_STSWI", .value = 725, }, - { .name = "OP_31_XOP_STFDX", .value = 727, }, - { .name = "OP_31_XOP_STFDUX", .value = 759, }, - { .name = "OP_31_XOP_LXVW4X", .value = 780, }, - { .name = "OP_31_XOP_LHBRX", .value = 790, }, - { .name = "OP_31_XOP_LXVD2X", .value = 844, }, - { .name = "OP_31_XOP_LFIWAX", .value = 855, }, - { .name = "OP_31_XOP_LFIWZX", .value = 887, }, - { .name = "OP_31_XOP_STXVW4X", .value = 908, }, - { .name = "OP_31_XOP_STHBRX", .value = 918, }, - { .name = "OP_31_XOP_STXVD2X", .value = 972, }, - { .name = "OP_31_XOP_STFIWX", .value = 983, }, -}; - -/* - * Arithmetic instructions which are having opcode as 31. - * These instructions are tracked to save the register state - * changes. Example: - * - * lwz r10,264(r3) - * add r31, r3, r3 - * lwz r9, 0(r31) - * - * Here instruction tracking needs to identify the "add" - * instruction and save data type of r3 to r31. If a sample - * is hit at next "lwz r9, 0(r31)", by this instruction tracking, - * data type of r31 can be resolved. - */ -static struct insn_offset arithmetic_ins_op_31[] = { - { .name = "SUB_CARRY_XO_FORM", .value = 8, }, - { .name = "MUL_HDW_XO_FORM1", .value = 9, }, - { .name = "ADD_CARRY_XO_FORM", .value = 10, }, - { .name = "MUL_HW_XO_FORM1", .value = 11, }, - { .name = "SUB_XO_FORM", .value = 40, }, - { .name = "MUL_HDW_XO_FORM", .value = 73, }, - { .name = "MUL_HW_XO_FORM", .value = 75, }, - { .name = "SUB_EXT_XO_FORM", .value = 136, }, - { .name = "ADD_EXT_XO_FORM", .value = 138, }, - { .name = "SUB_ZERO_EXT_XO_FORM", .value = 200, }, - { .name = "ADD_ZERO_EXT_XO_FORM", .value = 202, }, - { .name = "SUB_EXT_XO_FORM2", .value = 232, }, - { .name = "MUL_DW_XO_FORM", .value = 233, }, - { .name = "ADD_EXT_XO_FORM2", .value = 234, }, - { .name = "MUL_W_XO_FORM", .value = 235, }, - { .name = "ADD_XO_FORM", .value = 266, }, - { .name = "DIV_DW_XO_FORM1", .value = 457, }, - { .name = "DIV_W_XO_FORM1", .value = 459, }, - { .name = "DIV_DW_XO_FORM", .value = 489, }, - { .name = "DIV_W_XO_FORM", .value = 491, }, -}; - -static struct insn_offset arithmetic_two_ops[] = { - { .name = "mulli", .value = 7, }, - { .name = "subfic", .value = 8, }, - { .name = "addic", .value = 12, }, - { .name = "addic.", .value = 13, }, - { .name = "addi", .value = 14, }, - { .name = "addis", .value = 15, }, -}; - -static int cmp_offset(const void *a, const void *b) -{ - const struct insn_offset *val1 = a; - const struct insn_offset *val2 = b; - - return (val1->value - val2->value); -} - -static const struct ins_ops *check_ppc_insn(struct disasm_line *dl) -{ - int raw_insn = dl->raw.raw_insn; - int opcode = PPC_OP(raw_insn); - int mem_insn_31 = PPC_21_30(raw_insn); - struct insn_offset *ret; - struct insn_offset mem_insns_31_opcode = { - "OP_31_INSN", - mem_insn_31 - }; - char name_insn[32]; - - /* - * Instructions with opcode 32 to 63 are memory - * instructions in powerpc - */ - if ((opcode & 0x20)) { - /* - * Set name in case of raw instruction to - * opcode to be used in insn-stat - */ - if (!strlen(dl->ins.name)) { - sprintf(name_insn, "%d", opcode); - dl->ins.name = strdup(name_insn); - } - return &load_store_ops; - } else if (opcode == 31) { - /* Check for memory instructions with opcode 31 */ - ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset); - if (ret) { - if (!strlen(dl->ins.name)) - dl->ins.name = strdup(ret->name); - return &load_store_ops; - } else { - mem_insns_31_opcode.value = PPC_22_30(raw_insn); - ret = bsearch(&mem_insns_31_opcode, arithmetic_ins_op_31, ARRAY_SIZE(arithmetic_ins_op_31), - sizeof(arithmetic_ins_op_31[0]), cmp_offset); - if (ret != NULL) - return &arithmetic_ops; - /* Bits 21 to 30 has value 444 for "mr" insn ie, OR X form */ - if (PPC_21_30(raw_insn) == 444) - return &arithmetic_ops; - } - } else { - mem_insns_31_opcode.value = opcode; - ret = bsearch(&mem_insns_31_opcode, arithmetic_two_ops, ARRAY_SIZE(arithmetic_two_ops), - sizeof(arithmetic_two_ops[0]), cmp_offset); - if (ret != NULL) - return &arithmetic_ops; - } - - return NULL; -} - -/* - * Instruction tracking function to track register state moves. - * Example sequence: - * ld r10,264(r3) - * mr r31,r3 - * <<after some sequence> - * ld r9,312(r31) - * - * Previous instruction sequence shows that register state of r3 - * is moved to r31. update_insn_state_powerpc tracks these state - * changes - */ -#ifdef HAVE_LIBDW_SUPPORT -static void update_insn_state_powerpc(struct type_state *state, - struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused, - struct disasm_line *dl) -{ - struct annotated_insn_loc loc; - struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE]; - struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET]; - struct type_state_reg *tsr; - u32 insn_offset = dl->al.offset; - - if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0) - return; - - /* - * Value 444 for bits 21:30 is for "mr" - * instruction. "mr" is extended OR. So set the - * source and destination reg correctly - */ - if (PPC_21_30(dl->raw.raw_insn) == 444) { - int src_reg = src->reg1; - - src->reg1 = dst->reg1; - dst->reg1 = src_reg; - } - - if (!has_reg_type(state, dst->reg1)) - return; - - tsr = &state->regs[dst->reg1]; - - if (!has_reg_type(state, src->reg1) || - !state->regs[src->reg1].ok) { - tsr->ok = false; - return; - } - - tsr->type = state->regs[src->reg1].type; - tsr->kind = state->regs[src->reg1].kind; - tsr->ok = true; - - pr_debug_dtp("mov [%x] reg%d -> reg%d", - insn_offset, src->reg1, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); -} -#endif /* HAVE_LIBDW_SUPPORT */ - -static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - if (!arch->initialized) { - arch->initialized = true; - arch->associate_instruction_ops = powerpc__associate_instruction_ops; - arch->objdump.comment_char = '#'; - annotate_opts.show_asm_raw = true; - arch->e_machine = EM_PPC; - arch->e_flags = 0; - } - - return 0; -} diff --git a/tools/perf/arch/riscv64/annotate/instructions.c b/tools/perf/arch/riscv64/annotate/instructions.c deleted file mode 100644 index a34798864fab..000000000000 --- a/tools/perf/arch/riscv64/annotate/instructions.c +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -static -const struct ins_ops *riscv64__associate_ins_ops(struct arch *arch, const char *name) -{ - const struct ins_ops *ops = NULL; - - if (!strncmp(name, "jal", 3) || - !strncmp(name, "jr", 2) || - !strncmp(name, "call", 4)) - ops = &call_ops; - else if (!strncmp(name, "ret", 3)) - ops = &ret_ops; - else if (name[0] == 'j' || name[0] == 'b') - ops = &jump_ops; - else - return NULL; - - arch__associate_ins_ops(arch, name, ops); - - return ops; -} - -static -int riscv64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - if (!arch->initialized) { - arch->associate_instruction_ops = riscv64__associate_ins_ops; - arch->initialized = true; - arch->objdump.comment_char = '#'; - arch->e_machine = EM_RISCV; - arch->e_flags = 0; - } - - return 0; -} diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c deleted file mode 100644 index 1b22e6276e7d..000000000000 --- a/tools/perf/arch/s390/annotate/instructions.c +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/compiler.h> - -static int s390_call__parse(const struct arch *arch, struct ins_operands *ops, - struct map_symbol *ms, - struct disasm_line *dl __maybe_unused) -{ - char *endptr, *tok, *name; - struct map *map = ms->map; - struct addr_map_symbol target; - - tok = strchr(ops->raw, ','); - if (!tok) - return -1; - - ops->target.addr = strtoull(tok + 1, &endptr, 16); - - name = strchr(endptr, '<'); - if (name == NULL) - return -1; - - name++; - - if (arch->objdump.skip_functions_char && - strchr(name, arch->objdump.skip_functions_char)) - return -1; - - tok = strchr(name, '>'); - if (tok == NULL) - return -1; - - *tok = '\0'; - ops->target.name = strdup(name); - *tok = '>'; - - if (ops->target.name == NULL) - return -1; - - target = (struct addr_map_symbol) { - .ms = { .map = map__get(map), }, - .addr = map__objdump_2mem(map, ops->target.addr), - }; - - if (maps__find_ams(ms->maps, &target) == 0 && - map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) - ops->target.sym = target.ms.sym; - - addr_map_symbol__exit(&target); - return 0; -} - -static const struct ins_ops s390_call_ops = { - .parse = s390_call__parse, - .scnprintf = call__scnprintf, -}; - -static int s390_mov__parse(const struct arch *arch __maybe_unused, - struct ins_operands *ops, - struct map_symbol *ms __maybe_unused, - struct disasm_line *dl __maybe_unused) -{ - char *s = strchr(ops->raw, ','), *target, *endptr; - - if (s == NULL) - return -1; - - *s = '\0'; - ops->source.raw = strdup(ops->raw); - *s = ','; - - if (ops->source.raw == NULL) - return -1; - - target = ++s; - ops->target.raw = strdup(target); - if (ops->target.raw == NULL) - goto out_free_source; - - ops->target.addr = strtoull(target, &endptr, 16); - if (endptr == target) - goto out_free_target; - - s = strchr(endptr, '<'); - if (s == NULL) - goto out_free_target; - endptr = strchr(s + 1, '>'); - if (endptr == NULL) - goto out_free_target; - - *endptr = '\0'; - ops->target.name = strdup(s + 1); - *endptr = '>'; - if (ops->target.name == NULL) - goto out_free_target; - - return 0; - -out_free_target: - zfree(&ops->target.raw); -out_free_source: - zfree(&ops->source.raw); - return -1; -} - - -static const struct ins_ops s390_mov_ops = { - .parse = s390_mov__parse, - .scnprintf = mov__scnprintf, -}; - -static const struct ins_ops *s390__associate_ins_ops(struct arch *arch, const char *name) -{ - const struct ins_ops *ops = NULL; - - /* catch all kind of jumps */ - if (strchr(name, 'j') || - !strncmp(name, "bct", 3) || - !strncmp(name, "br", 2)) - ops = &jump_ops; - /* override call/returns */ - if (!strcmp(name, "bras") || - !strcmp(name, "brasl") || - !strcmp(name, "basr")) - ops = &s390_call_ops; - if (!strcmp(name, "br")) - ops = &ret_ops; - /* override load/store relative to PC */ - if (!strcmp(name, "lrl") || - !strcmp(name, "lgrl") || - !strcmp(name, "lgfrl") || - !strcmp(name, "llgfrl") || - !strcmp(name, "strl") || - !strcmp(name, "stgrl")) - ops = &s390_mov_ops; - - if (ops) - arch__associate_ins_ops(arch, name, ops); - return ops; -} - -static int s390__cpuid_parse(struct arch *arch, char *cpuid) -{ - unsigned int family; - char model[16], model_c[16], cpumf_v[16], cpumf_a[16]; - int ret; - - /* - * cpuid string format: - * "IBM,family,model-capacity,model[,cpum_cf-version,cpum_cf-authorization]" - */ - ret = sscanf(cpuid, "%*[^,],%u,%[^,],%[^,],%[^,],%s", &family, model_c, - model, cpumf_v, cpumf_a); - if (ret >= 2) { - arch->family = family; - arch->model = 0; - return 0; - } - - return -1; -} - -static int s390__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - int err = 0; - - if (!arch->initialized) { - arch->initialized = true; - arch->associate_instruction_ops = s390__associate_ins_ops; - if (cpuid) { - if (s390__cpuid_parse(arch, cpuid)) - err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING; - } - arch->e_machine = EM_S390; - arch->e_flags = 0; - } - - return err; -} diff --git a/tools/perf/arch/sparc/annotate/instructions.c b/tools/perf/arch/sparc/annotate/instructions.c deleted file mode 100644 index a08d8734c883..000000000000 --- a/tools/perf/arch/sparc/annotate/instructions.c +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -static int is_branch_cond(const char *cond) -{ - if (cond[0] == '\0') - return 1; - - if (cond[0] == 'a' && cond[1] == '\0') - return 1; - - if (cond[0] == 'c' && - (cond[1] == 'c' || cond[1] == 's') && - cond[2] == '\0') - return 1; - - if (cond[0] == 'e' && - (cond[1] == '\0' || - (cond[1] == 'q' && cond[2] == '\0'))) - return 1; - - if (cond[0] == 'g' && - (cond[1] == '\0' || - (cond[1] == 't' && cond[2] == '\0') || - (cond[1] == 'e' && cond[2] == '\0') || - (cond[1] == 'e' && cond[2] == 'u' && cond[3] == '\0'))) - return 1; - - if (cond[0] == 'l' && - (cond[1] == '\0' || - (cond[1] == 't' && cond[2] == '\0') || - (cond[1] == 'u' && cond[2] == '\0') || - (cond[1] == 'e' && cond[2] == '\0') || - (cond[1] == 'e' && cond[2] == 'u' && cond[3] == '\0'))) - return 1; - - if (cond[0] == 'n' && - (cond[1] == '\0' || - (cond[1] == 'e' && cond[2] == '\0') || - (cond[1] == 'z' && cond[2] == '\0') || - (cond[1] == 'e' && cond[2] == 'g' && cond[3] == '\0'))) - return 1; - - if (cond[0] == 'b' && - cond[1] == 'p' && - cond[2] == 'o' && - cond[3] == 's' && - cond[4] == '\0') - return 1; - - if (cond[0] == 'v' && - (cond[1] == 'c' || cond[1] == 's') && - cond[2] == '\0') - return 1; - - if (cond[0] == 'b' && - cond[1] == 'z' && - cond[2] == '\0') - return 1; - - return 0; -} - -static int is_branch_reg_cond(const char *cond) -{ - if ((cond[0] == 'n' || cond[0] == 'l') && - cond[1] == 'z' && - cond[2] == '\0') - return 1; - - if (cond[0] == 'z' && - cond[1] == '\0') - return 1; - - if ((cond[0] == 'g' || cond[0] == 'l') && - cond[1] == 'e' && - cond[2] == 'z' && - cond[3] == '\0') - return 1; - - if (cond[0] == 'g' && - cond[1] == 'z' && - cond[2] == '\0') - return 1; - - return 0; -} - -static int is_branch_float_cond(const char *cond) -{ - if (cond[0] == '\0') - return 1; - - if ((cond[0] == 'a' || cond[0] == 'e' || - cond[0] == 'z' || cond[0] == 'g' || - cond[0] == 'l' || cond[0] == 'n' || - cond[0] == 'o' || cond[0] == 'u') && - cond[1] == '\0') - return 1; - - if (((cond[0] == 'g' && cond[1] == 'e') || - (cond[0] == 'l' && (cond[1] == 'e' || - cond[1] == 'g')) || - (cond[0] == 'n' && (cond[1] == 'e' || - cond[1] == 'z')) || - (cond[0] == 'u' && (cond[1] == 'e' || - cond[1] == 'g' || - cond[1] == 'l'))) && - cond[2] == '\0') - return 1; - - if (cond[0] == 'u' && - (cond[1] == 'g' || cond[1] == 'l') && - cond[2] == 'e' && - cond[3] == '\0') - return 1; - - return 0; -} - -static const struct ins_ops *sparc__associate_instruction_ops(struct arch *arch, const char *name) -{ - const struct ins_ops *ops = NULL; - - if (!strcmp(name, "call") || - !strcmp(name, "jmp") || - !strcmp(name, "jmpl")) { - ops = &call_ops; - } else if (!strcmp(name, "ret") || - !strcmp(name, "retl") || - !strcmp(name, "return")) { - ops = &ret_ops; - } else if (!strcmp(name, "mov")) { - ops = &mov_ops; - } else { - if (name[0] == 'c' && - (name[1] == 'w' || name[1] == 'x')) - name += 2; - - if (name[0] == 'b') { - const char *cond = name + 1; - - if (cond[0] == 'r') { - if (is_branch_reg_cond(cond + 1)) - ops = &jump_ops; - } else if (is_branch_cond(cond)) { - ops = &jump_ops; - } - } else if (name[0] == 'f' && name[1] == 'b') { - if (is_branch_float_cond(name + 2)) - ops = &jump_ops; - } - } - - if (ops) - arch__associate_ins_ops(arch, name, ops); - - return ops; -} - -static int sparc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) -{ - if (!arch->initialized) { - arch->initialized = true; - arch->associate_instruction_ops = sparc__associate_instruction_ops; - arch->objdump.comment_char = '#'; - arch->e_machine = EM_SPARC; - arch->e_flags = 0; - } - - return 0; -} diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c deleted file mode 100644 index ffca3029388b..000000000000 --- a/tools/perf/arch/x86/annotate/instructions.c +++ /dev/null @@ -1,797 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * x86 instruction nmemonic table to parse disasm lines for annotate. - * This table is searched twice - one for exact match and another for - * match without a size suffix (b, w, l, q) in case of AT&T syntax. - * - * So this table should not have entries with the suffix unless it's - * a complete different instruction than ones without the suffix. - */ -static const struct ins x86__instructions[] = { - { .name = "adc", .ops = &mov_ops, }, - { .name = "add", .ops = &mov_ops, }, - { .name = "addsd", .ops = &mov_ops, }, - { .name = "and", .ops = &mov_ops, }, - { .name = "andpd", .ops = &mov_ops, }, - { .name = "andps", .ops = &mov_ops, }, - { .name = "bsr", .ops = &mov_ops, }, - { .name = "bt", .ops = &mov_ops, }, - { .name = "btr", .ops = &mov_ops, }, - { .name = "bts", .ops = &mov_ops, }, - { .name = "call", .ops = &call_ops, }, - { .name = "cmovae", .ops = &mov_ops, }, - { .name = "cmovbe", .ops = &mov_ops, }, - { .name = "cmove", .ops = &mov_ops, }, - { .name = "cmp", .ops = &mov_ops, }, - { .name = "cmpxch", .ops = &mov_ops, }, - { .name = "cmpxchg", .ops = &mov_ops, }, - { .name = "cs", .ops = &mov_ops, }, - { .name = "dec", .ops = &dec_ops, }, - { .name = "divsd", .ops = &mov_ops, }, - { .name = "divss", .ops = &mov_ops, }, - { .name = "gs", .ops = &mov_ops, }, - { .name = "imul", .ops = &mov_ops, }, - { .name = "inc", .ops = &dec_ops, }, - { .name = "ja", .ops = &jump_ops, }, - { .name = "jae", .ops = &jump_ops, }, - { .name = "jb", .ops = &jump_ops, }, - { .name = "jbe", .ops = &jump_ops, }, - { .name = "jc", .ops = &jump_ops, }, - { .name = "jcxz", .ops = &jump_ops, }, - { .name = "je", .ops = &jump_ops, }, - { .name = "jecxz", .ops = &jump_ops, }, - { .name = "jg", .ops = &jump_ops, }, - { .name = "jge", .ops = &jump_ops, }, - { .name = "jl", .ops = &jump_ops, }, - { .name = "jle", .ops = &jump_ops, }, - { .name = "jmp", .ops = &jump_ops, }, - { .name = "jna", .ops = &jump_ops, }, - { .name = "jnae", .ops = &jump_ops, }, - { .name = "jnb", .ops = &jump_ops, }, - { .name = "jnbe", .ops = &jump_ops, }, - { .name = "jnc", .ops = &jump_ops, }, - { .name = "jne", .ops = &jump_ops, }, - { .name = "jng", .ops = &jump_ops, }, - { .name = "jnge", .ops = &jump_ops, }, - { .name = "jnl", .ops = &jump_ops, }, - { .name = "jnle", .ops = &jump_ops, }, - { .name = "jno", .ops = &jump_ops, }, - { .name = "jnp", .ops = &jump_ops, }, - { .name = "jns", .ops = &jump_ops, }, - { .name = "jnz", .ops = &jump_ops, }, - { .name = "jo", .ops = &jump_ops, }, - { .name = "jp", .ops = &jump_ops, }, - { .name = "jpe", .ops = &jump_ops, }, - { .name = "jpo", .ops = &jump_ops, }, - { .name = "jrcxz", .ops = &jump_ops, }, - { .name = "js", .ops = &jump_ops, }, - { .name = "jz", .ops = &jump_ops, }, - { .name = "lea", .ops = &mov_ops, }, - { .name = "lock", .ops = &lock_ops, }, - { .name = "mov", .ops = &mov_ops, }, - { .name = "movapd", .ops = &mov_ops, }, - { .name = "movaps", .ops = &mov_ops, }, - { .name = "movdqa", .ops = &mov_ops, }, - { .name = "movdqu", .ops = &mov_ops, }, - { .name = "movsb", .ops = &mov_ops, }, - { .name = "movsd", .ops = &mov_ops, }, - { .name = "movsl", .ops = &mov_ops, }, - { .name = "movss", .ops = &mov_ops, }, - { .name = "movsw", .ops = &mov_ops, }, - { .name = "movupd", .ops = &mov_ops, }, - { .name = "movups", .ops = &mov_ops, }, - { .name = "movzb", .ops = &mov_ops, }, - { .name = "movzl", .ops = &mov_ops, }, - { .name = "movzw", .ops = &mov_ops, }, - { .name = "mulsd", .ops = &mov_ops, }, - { .name = "mulss", .ops = &mov_ops, }, - { .name = "nop", .ops = &nop_ops, }, - { .name = "or", .ops = &mov_ops, }, - { .name = "orps", .ops = &mov_ops, }, - { .name = "paddq", .ops = &mov_ops, }, - { .name = "pand", .ops = &mov_ops, }, - { .name = "pcmpeqb", .ops = &mov_ops, }, - { .name = "por", .ops = &mov_ops, }, - { .name = "rcl", .ops = &mov_ops, }, - { .name = "ret", .ops = &ret_ops, }, - { .name = "sbb", .ops = &mov_ops, }, - { .name = "sete", .ops = &mov_ops, }, - { .name = "sub", .ops = &mov_ops, }, - { .name = "subsd", .ops = &mov_ops, }, - { .name = "test", .ops = &mov_ops, }, - { .name = "tzcnt", .ops = &mov_ops, }, - { .name = "ucomisd", .ops = &mov_ops, }, - { .name = "ucomiss", .ops = &mov_ops, }, - { .name = "vaddsd", .ops = &mov_ops, }, - { .name = "vandpd", .ops = &mov_ops, }, - { .name = "vmovdqa", .ops = &mov_ops, }, - { .name = "vmovq", .ops = &mov_ops, }, - { .name = "vmovsd", .ops = &mov_ops, }, - { .name = "vmulsd", .ops = &mov_ops, }, - { .name = "vorpd", .ops = &mov_ops, }, - { .name = "vsubsd", .ops = &mov_ops, }, - { .name = "vucomisd", .ops = &mov_ops, }, - { .name = "xadd", .ops = &mov_ops, }, - { .name = "xbegin", .ops = &jump_ops, }, - { .name = "xchg", .ops = &mov_ops, }, - { .name = "xor", .ops = &mov_ops, }, - { .name = "xorpd", .ops = &mov_ops, }, - { .name = "xorps", .ops = &mov_ops, }, -}; - -static bool amd__ins_is_fused(const struct arch *arch, const char *ins1, - const char *ins2) -{ - if (strstr(ins2, "jmp")) - return false; - - /* Family >= 15h supports cmp/test + branch fusion */ - if (arch->family >= 0x15 && (strstarts(ins1, "test") || - (strstarts(ins1, "cmp") && !strstr(ins1, "xchg")))) { - return true; - } - - /* Family >= 19h supports some ALU + branch fusion */ - if (arch->family >= 0x19 && (strstarts(ins1, "add") || - strstarts(ins1, "sub") || strstarts(ins1, "and") || - strstarts(ins1, "inc") || strstarts(ins1, "dec") || - strstarts(ins1, "or") || strstarts(ins1, "xor"))) { - return true; - } - - return false; -} - -static bool intel__ins_is_fused(const struct arch *arch, const char *ins1, - const char *ins2) -{ - if (arch->family != 6 || arch->model < 0x1e || strstr(ins2, "jmp")) - return false; - - if (arch->model == 0x1e) { - /* Nehalem */ - if ((strstr(ins1, "cmp") && !strstr(ins1, "xchg")) || - strstr(ins1, "test")) { - return true; - } - } else { - /* Newer platform */ - if ((strstr(ins1, "cmp") && !strstr(ins1, "xchg")) || - strstr(ins1, "test") || - strstr(ins1, "add") || - strstr(ins1, "sub") || - strstr(ins1, "and") || - strstr(ins1, "inc") || - strstr(ins1, "dec")) { - return true; - } - } - - return false; -} - -static int x86__cpuid_parse(struct arch *arch, char *cpuid) -{ - unsigned int family, model, stepping; - int ret; - - /* - * cpuid = "GenuineIntel,family,model,stepping" - */ - ret = sscanf(cpuid, "%*[^,],%u,%u,%u", &family, &model, &stepping); - if (ret == 3) { - arch->family = family; - arch->model = model; - arch->ins_is_fused = strstarts(cpuid, "AuthenticAMD") ? - amd__ins_is_fused : - intel__ins_is_fused; - return 0; - } - - return -1; -} - -static int x86__annotate_init(struct arch *arch, char *cpuid) -{ - int err = 0; - - if (arch->initialized) - return 0; - - if (cpuid) { - if (x86__cpuid_parse(arch, cpuid)) - err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING; - } - -#ifndef NDEBUG - { - static bool sorted_check; - - if (!sorted_check) { - for (size_t i = 0; i < arch->nr_instructions - 1; i++) { - assert(strcmp(arch->instructions[i].name, - arch->instructions[i + 1].name) <= 0); - } - sorted_check = true; - } - } -#endif - arch->e_machine = EM_X86_64; - arch->e_flags = 0; - arch->initialized = true; - return err; -} - -#ifdef HAVE_LIBDW_SUPPORT -static void update_insn_state_x86(struct type_state *state, - struct data_loc_info *dloc, Dwarf_Die *cu_die, - struct disasm_line *dl) -{ - struct annotated_insn_loc loc; - struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE]; - struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET]; - struct type_state_reg *tsr; - Dwarf_Die type_die; - u32 insn_offset = dl->al.offset; - int fbreg = dloc->fbreg; - int fboff = 0; - - if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0) - return; - - if (ins__is_call(&dl->ins)) { - struct symbol *func = dl->ops.target.sym; - - if (func == NULL) - return; - - /* __fentry__ will preserve all registers */ - if (!strcmp(func->name, "__fentry__")) - return; - - pr_debug_dtp("call [%x] %s\n", insn_offset, func->name); - - /* Otherwise invalidate caller-saved registers after call */ - for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) { - if (state->regs[i].caller_saved) - state->regs[i].ok = false; - } - - /* Update register with the return type (if any) */ - if (die_find_func_rettype(cu_die, func->name, &type_die)) { - tsr = &state->regs[state->ret_reg]; - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("call [%x] return -> reg%d", - insn_offset, state->ret_reg); - pr_debug_type_name(&type_die, tsr->kind); - } - return; - } - - if (!strncmp(dl->ins.name, "add", 3)) { - u64 imm_value = -1ULL; - int offset; - const char *var_name = NULL; - struct map_symbol *ms = dloc->ms; - u64 ip = ms->sym->start + dl->al.offset; - - if (!has_reg_type(state, dst->reg1)) - return; - - tsr = &state->regs[dst->reg1]; - tsr->copied_from = -1; - - if (src->imm) - imm_value = src->offset; - else if (has_reg_type(state, src->reg1) && - state->regs[src->reg1].kind == TSR_KIND_CONST) - imm_value = state->regs[src->reg1].imm_value; - else if (src->reg1 == DWARF_REG_PC) { - u64 var_addr = annotate_calc_pcrel(dloc->ms, ip, - src->offset, dl); - - if (get_global_var_info(dloc, var_addr, - &var_name, &offset) && - !strcmp(var_name, "this_cpu_off") && - tsr->kind == TSR_KIND_CONST) { - tsr->kind = TSR_KIND_PERCPU_BASE; - tsr->offset = 0; - tsr->ok = true; - imm_value = tsr->imm_value; - } - } - else - return; - - /* Ignore add to non-pointer or non-const types */ - if (tsr->kind == TSR_KIND_POINTER || - (dwarf_tag(&tsr->type) == DW_TAG_pointer_type && - src->reg1 != DWARF_REG_PC && tsr->kind == TSR_KIND_TYPE && !dst->mem_ref)) { - tsr->offset += imm_value; - pr_debug_dtp("add [%x] offset %#"PRIx64" to reg%d", - insn_offset, imm_value, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - - if (tsr->kind == TSR_KIND_CONST) - tsr->imm_value += imm_value; - - if (tsr->kind != TSR_KIND_PERCPU_BASE) - return; - - if (get_global_var_type(cu_die, dloc, ip, imm_value, &offset, - &type_die) && offset == 0) { - /* - * This is not a pointer type, but it should be treated - * as a pointer. - */ - tsr->type = type_die; - tsr->kind = TSR_KIND_PERCPU_POINTER; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d", - insn_offset, imm_value, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - return; - } - - if (!strncmp(dl->ins.name, "sub", 3)) { - u64 imm_value = -1ULL; - - if (!has_reg_type(state, dst->reg1)) - return; - - tsr = &state->regs[dst->reg1]; - tsr->copied_from = -1; - - if (src->imm) - imm_value = src->offset; - else if (has_reg_type(state, src->reg1) && - state->regs[src->reg1].kind == TSR_KIND_CONST) - imm_value = state->regs[src->reg1].imm_value; - - if (tsr->kind == TSR_KIND_POINTER || - (dwarf_tag(&tsr->type) == DW_TAG_pointer_type && - src->reg1 != DWARF_REG_PC && tsr->kind == TSR_KIND_TYPE && !dst->mem_ref)) { - tsr->offset -= imm_value; - pr_debug_dtp("sub [%x] offset %#"PRIx64" to reg%d", - insn_offset, imm_value, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - - if (tsr->kind == TSR_KIND_CONST) - tsr->imm_value -= imm_value; - - return; - } - - if (!strncmp(dl->ins.name, "lea", 3)) { - int sreg = src->reg1; - struct type_state_reg src_tsr; - - if (!has_reg_type(state, sreg) || - !has_reg_type(state, dst->reg1) || - !src->mem_ref) - return; - - src_tsr = state->regs[sreg]; - tsr = &state->regs[dst->reg1]; - - tsr->copied_from = -1; - tsr->ok = false; - - /* Case 1: Based on stack pointer or frame pointer */ - if (sreg == fbreg || sreg == state->stack_reg) { - struct type_state_stack *stack; - int offset = src->offset - fboff; - - stack = find_stack_state(state, offset); - if (!stack) - return; - - tsr->type = stack->type; - tsr->kind = TSR_KIND_POINTER; - tsr->offset = offset - stack->offset; - tsr->ok = true; - - if (sreg == fbreg) { - pr_debug_dtp("lea [%x] address of -%#x(stack) -> reg%d", - insn_offset, -src->offset, dst->reg1); - } else { - pr_debug_dtp("lea [%x] address of %#x(reg%d) -> reg%d", - insn_offset, src->offset, sreg, dst->reg1); - } - - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* Case 2: Based on a register holding a typed pointer */ - else if (src_tsr.ok && (src_tsr.kind == TSR_KIND_POINTER || - (dwarf_tag(&src_tsr.type) == DW_TAG_pointer_type && - src_tsr.kind == TSR_KIND_TYPE))) { - - if (src_tsr.kind == TSR_KIND_TYPE && - __die_get_real_type(&state->regs[sreg].type, &type_die) == NULL) - return; - - if (src_tsr.kind == TSR_KIND_POINTER) - type_die = state->regs[sreg].type; - - /* Check if the target type has a member at the new offset */ - if (die_get_member_type(&type_die, - src->offset + src_tsr.offset, &type_die) == NULL) - return; - - tsr->type = src_tsr.type; - tsr->kind = src_tsr.kind; - tsr->offset = src->offset + src_tsr.offset; - tsr->ok = true; - - pr_debug_dtp("lea [%x] address of %s%#x(reg%d) -> reg%d", - insn_offset, src->offset < 0 ? "-" : "", - abs(src->offset), sreg, dst->reg1); - - pr_debug_type_name(&tsr->type, tsr->kind); - } - return; - } - - /* Invalidate register states for other ops which may change pointers */ - if (has_reg_type(state, dst->reg1) && !dst->mem_ref && - dwarf_tag(&state->regs[dst->reg1].type) == DW_TAG_pointer_type) { - if (!strncmp(dl->ins.name, "imul", 4) || !strncmp(dl->ins.name, "mul", 3) || - !strncmp(dl->ins.name, "idiv", 4) || !strncmp(dl->ins.name, "div", 3) || - !strncmp(dl->ins.name, "shl", 3) || !strncmp(dl->ins.name, "shr", 3) || - !strncmp(dl->ins.name, "sar", 3) || !strncmp(dl->ins.name, "and", 3) || - !strncmp(dl->ins.name, "or", 2) || !strncmp(dl->ins.name, "neg", 3) || - !strncmp(dl->ins.name, "inc", 3) || !strncmp(dl->ins.name, "dec", 3)) { - pr_debug_dtp("%s [%x] invalidate reg%d\n", - dl->ins.name, insn_offset, dst->reg1); - state->regs[dst->reg1].ok = false; - state->regs[dst->reg1].copied_from = -1; - return; - } - - if (!strncmp(dl->ins.name, "xor", 3) && dst->reg1 == src->reg1) { - /* xor reg, reg clears the register */ - pr_debug_dtp("xor [%x] clear reg%d\n", - insn_offset, dst->reg1); - - state->regs[dst->reg1].kind = TSR_KIND_CONST; - state->regs[dst->reg1].imm_value = 0; - state->regs[dst->reg1].ok = true; - state->regs[dst->reg1].copied_from = -1; - return; - } - } - - if (strncmp(dl->ins.name, "mov", 3)) - return; - - if (dloc->fb_cfa) { - u64 ip = dloc->ms->sym->start + dl->al.offset; - u64 pc = map__rip_2objdump(dloc->ms->map, ip); - - if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0) - fbreg = -1; - } - - /* Case 1. register to register or segment:offset to register transfers */ - if (!src->mem_ref && !dst->mem_ref) { - if (!has_reg_type(state, dst->reg1)) - return; - - tsr = &state->regs[dst->reg1]; - tsr->copied_from = -1; - - if (dso__kernel(map__dso(dloc->ms->map)) && - src->segment == INSN_SEG_X86_GS && src->imm) { - u64 ip = dloc->ms->sym->start + dl->al.offset; - u64 var_addr; - int offset; - - /* - * In kernel, %gs points to a per-cpu region for the - * current CPU. Access with a constant offset should - * be treated as a global variable access. - */ - var_addr = src->offset; - - if (var_addr == 40) { - tsr->kind = TSR_KIND_CANARY; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] stack canary -> reg%d\n", - insn_offset, dst->reg1); - return; - } - - if (!get_global_var_type(cu_die, dloc, ip, var_addr, - &offset, &type_die) || - !die_get_member_type(&type_die, offset, &type_die)) { - tsr->ok = false; - return; - } - - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d", - insn_offset, var_addr, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - return; - } - - if (src->imm) { - tsr->kind = TSR_KIND_CONST; - tsr->imm_value = src->offset; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n", - insn_offset, tsr->imm_value, dst->reg1); - return; - } - - if (!has_reg_type(state, src->reg1) || - !state->regs[src->reg1].ok) { - tsr->ok = false; - return; - } - - tsr->type = state->regs[src->reg1].type; - tsr->kind = state->regs[src->reg1].kind; - tsr->imm_value = state->regs[src->reg1].imm_value; - tsr->offset = state->regs[src->reg1].offset; - tsr->ok = true; - - /* To copy back the variable type later (hopefully) */ - if (tsr->kind == TSR_KIND_TYPE || tsr->kind == TSR_KIND_POINTER) - tsr->copied_from = src->reg1; - - pr_debug_dtp("mov [%x] reg%d -> reg%d", - insn_offset, src->reg1, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* Case 2. memory to register transers */ - if (src->mem_ref && !dst->mem_ref) { - int sreg = src->reg1; - - if (!has_reg_type(state, dst->reg1)) - return; - - tsr = &state->regs[dst->reg1]; - tsr->copied_from = -1; - -retry: - /* Check stack variables with offset */ - if (sreg == fbreg || sreg == state->stack_reg) { - struct type_state_stack *stack; - int offset = src->offset - fboff; - - stack = find_stack_state(state, offset); - if (stack == NULL) { - tsr->ok = false; - return; - } else if (!stack->compound) { - tsr->type = stack->type; - tsr->kind = stack->kind; - tsr->offset = stack->ptr_offset; - tsr->ok = true; - } else if (die_get_member_type(&stack->type, - offset - stack->offset, - &type_die)) { - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - } else { - tsr->ok = false; - return; - } - - if (sreg == fbreg) { - pr_debug_dtp("mov [%x] -%#x(stack) -> reg%d", - insn_offset, -offset, dst->reg1); - } else { - pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d", - insn_offset, offset, sreg, dst->reg1); - } - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* And then dereference the pointer if it has one */ - else if (has_reg_type(state, sreg) && state->regs[sreg].ok && - state->regs[sreg].kind == TSR_KIND_TYPE && - die_deref_ptr_type(&state->regs[sreg].type, - src->offset + state->regs[sreg].offset, &type_die)) { - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d", - insn_offset, src->offset, sreg, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* Handle dereference of TSR_KIND_POINTER registers */ - else if (has_reg_type(state, sreg) && state->regs[sreg].ok && - state->regs[sreg].kind == TSR_KIND_POINTER && - die_get_member_type(&state->regs[sreg].type, - src->offset + state->regs[sreg].offset, &type_die)) { - tsr->type = state->regs[sreg].type; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = src->offset + state->regs[sreg].offset; - tsr->ok = true; - - pr_debug_dtp("mov [%x] addr %#x(reg%d) -> reg%d", - insn_offset, src->offset, sreg, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* Or check if it's a global variable */ - else if (sreg == DWARF_REG_PC) { - struct map_symbol *ms = dloc->ms; - u64 ip = ms->sym->start + dl->al.offset; - u64 addr; - int offset; - - addr = annotate_calc_pcrel(ms, ip, src->offset, dl); - - if (!get_global_var_type(cu_die, dloc, ip, addr, &offset, - &type_die) || - !die_get_member_type(&type_die, offset, &type_die)) { - tsr->ok = false; - return; - } - - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d", - insn_offset, addr, dst->reg1); - pr_debug_type_name(&type_die, tsr->kind); - } - /* And check percpu access with base register */ - else if (has_reg_type(state, sreg) && - state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) { - u64 ip = dloc->ms->sym->start + dl->al.offset; - u64 var_addr = src->offset; - int offset; - - if (src->multi_regs) { - int reg2 = (sreg == src->reg1) ? src->reg2 : src->reg1; - - if (has_reg_type(state, reg2) && state->regs[reg2].ok && - state->regs[reg2].kind == TSR_KIND_CONST) - var_addr += state->regs[reg2].imm_value; - } - - /* - * In kernel, %gs points to a per-cpu region for the - * current CPU. Access with a constant offset should - * be treated as a global variable access. - */ - if (get_global_var_type(cu_die, dloc, ip, var_addr, - &offset, &type_die) && - die_get_member_type(&type_die, offset, &type_die)) { - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - - if (src->multi_regs) { - pr_debug_dtp("mov [%x] percpu %#x(reg%d,reg%d) -> reg%d", - insn_offset, src->offset, src->reg1, - src->reg2, dst->reg1); - } else { - pr_debug_dtp("mov [%x] percpu %#x(reg%d) -> reg%d", - insn_offset, src->offset, sreg, dst->reg1); - } - pr_debug_type_name(&tsr->type, tsr->kind); - } else { - tsr->ok = false; - } - } - /* And then dereference the calculated pointer if it has one */ - else if (has_reg_type(state, sreg) && state->regs[sreg].ok && - state->regs[sreg].kind == TSR_KIND_PERCPU_POINTER && - die_get_member_type(&state->regs[sreg].type, - src->offset, &type_die)) { - tsr->type = type_die; - tsr->kind = TSR_KIND_TYPE; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] pointer %#x(reg%d) -> reg%d", - insn_offset, src->offset, sreg, dst->reg1); - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* Or try another register if any */ - else if (src->multi_regs && sreg == src->reg1 && - src->reg1 != src->reg2) { - sreg = src->reg2; - goto retry; - } - else { - int offset; - const char *var_name = NULL; - - /* it might be per-cpu variable (in kernel) access */ - if (src->offset < 0) { - if (get_global_var_info(dloc, (s64)src->offset, - &var_name, &offset) && - !strcmp(var_name, "__per_cpu_offset")) { - tsr->kind = TSR_KIND_PERCPU_BASE; - tsr->offset = 0; - tsr->ok = true; - - pr_debug_dtp("mov [%x] percpu base reg%d\n", - insn_offset, dst->reg1); - return; - } - } - - tsr->ok = false; - } - } - /* Case 3. register to memory transfers */ - if (!src->mem_ref && dst->mem_ref) { - if (!has_reg_type(state, src->reg1) || - !state->regs[src->reg1].ok) - return; - - /* Check stack variables with offset */ - if (dst->reg1 == fbreg || dst->reg1 == state->stack_reg) { - struct type_state_stack *stack; - int offset = dst->offset - fboff; - - tsr = &state->regs[src->reg1]; - - stack = find_stack_state(state, offset); - if (stack) { - /* - * The source register is likely to hold a type - * of member if it's a compound type. Do not - * update the stack variable type since we can - * get the member type later by using the - * die_get_member_type(). - */ - if (!stack->compound) - set_stack_state(stack, offset, tsr->kind, - &tsr->type, tsr->offset); - } else { - findnew_stack_state(state, offset, tsr->kind, - &tsr->type, tsr->offset); - } - - if (dst->reg1 == fbreg) { - pr_debug_dtp("mov [%x] reg%d -> -%#x(stack)", - insn_offset, src->reg1, -offset); - } else { - pr_debug_dtp("mov [%x] reg%d -> %#x(reg%d)", - insn_offset, src->reg1, offset, dst->reg1); - } - if (tsr->offset != 0) { - pr_debug_dtp(" reg%d offset %#x ->", - src->reg1, tsr->offset); - } - - pr_debug_type_name(&tsr->type, tsr->kind); - } - /* - * Ignore other transfers since it'd set a value in a struct - * and won't change the type. - */ - } - /* Case 4. memory to memory transfers (not handled for now) */ -} -#endif |
