Path: blob/master/tools/perf/arch/powerpc/annotate/instructions.c
29274 views
// SPDX-License-Identifier: GPL-2.01#include <linux/compiler.h>23static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, const char *name)4{5int i;6struct ins_ops *ops;78/*9* - Interested only if instruction starts with 'b'.10* - Few start with 'b', but aren't branch instructions.11*/12if (name[0] != 'b' ||13!strncmp(name, "bcd", 3) ||14!strncmp(name, "brinc", 5) ||15!strncmp(name, "bper", 4))16return NULL;1718ops = &jump_ops;1920i = strlen(name) - 1;21if (i < 0)22return NULL;2324/* ignore optional hints at the end of the instructions */25if (name[i] == '+' || name[i] == '-')26i--;2728if (name[i] == 'l' || (name[i] == 'a' && name[i-1] == 'l')) {29/*30* if the instruction ends up with 'l' or 'la', then31* those are considered 'calls' since they update LR.32* ... except for 'bnl' which is branch if not less than33* and the absolute form of the same.34*/35if (strcmp(name, "bnl") && strcmp(name, "bnl+") &&36strcmp(name, "bnl-") && strcmp(name, "bnla") &&37strcmp(name, "bnla+") && strcmp(name, "bnla-"))38ops = &call_ops;39}40if (name[i] == 'r' && name[i-1] == 'l')41/*42* instructions ending with 'lr' are considered to be43* return instructions44*/45ops = &ret_ops;4647arch__associate_ins_ops(arch, name, ops);48return ops;49}5051#define PPC_OP(op) (((op) >> 26) & 0x3F)52#define PPC_21_30(R) (((R) >> 1) & 0x3ff)53#define PPC_22_30(R) (((R) >> 1) & 0x1ff)5455struct insn_offset {56const char *name;57int value;58};5960/*61* There are memory instructions with opcode 31 which are62* of X Form, Example:63* ldx RT,RA,RB64* ______________________________________65* | 31 | RT | RA | RB | 21 |/|66* --------------------------------------67* 0 6 11 16 21 30 3168*69* But all instructions with opcode 31 are not memory.70* Example: add RT,RA,RB71*72* Use bits 21 to 30 to check memory insns with 31 as opcode.73* In ins_array below, for ldx instruction:74* name => OP_31_XOP_LDX75* value => 2176*/7778static struct insn_offset ins_array[] = {79{ .name = "OP_31_XOP_LXSIWZX", .value = 12, },80{ .name = "OP_31_XOP_LWARX", .value = 20, },81{ .name = "OP_31_XOP_LDX", .value = 21, },82{ .name = "OP_31_XOP_LWZX", .value = 23, },83{ .name = "OP_31_XOP_LDUX", .value = 53, },84{ .name = "OP_31_XOP_LWZUX", .value = 55, },85{ .name = "OP_31_XOP_LXSIWAX", .value = 76, },86{ .name = "OP_31_XOP_LDARX", .value = 84, },87{ .name = "OP_31_XOP_LBZX", .value = 87, },88{ .name = "OP_31_XOP_LVX", .value = 103, },89{ .name = "OP_31_XOP_LBZUX", .value = 119, },90{ .name = "OP_31_XOP_STXSIWX", .value = 140, },91{ .name = "OP_31_XOP_STDX", .value = 149, },92{ .name = "OP_31_XOP_STWX", .value = 151, },93{ .name = "OP_31_XOP_STDUX", .value = 181, },94{ .name = "OP_31_XOP_STWUX", .value = 183, },95{ .name = "OP_31_XOP_STBX", .value = 215, },96{ .name = "OP_31_XOP_STVX", .value = 231, },97{ .name = "OP_31_XOP_STBUX", .value = 247, },98{ .name = "OP_31_XOP_LHZX", .value = 279, },99{ .name = "OP_31_XOP_LHZUX", .value = 311, },100{ .name = "OP_31_XOP_LXVDSX", .value = 332, },101{ .name = "OP_31_XOP_LWAX", .value = 341, },102{ .name = "OP_31_XOP_LHAX", .value = 343, },103{ .name = "OP_31_XOP_LWAUX", .value = 373, },104{ .name = "OP_31_XOP_LHAUX", .value = 375, },105{ .name = "OP_31_XOP_STHX", .value = 407, },106{ .name = "OP_31_XOP_STHUX", .value = 439, },107{ .name = "OP_31_XOP_LXSSPX", .value = 524, },108{ .name = "OP_31_XOP_LDBRX", .value = 532, },109{ .name = "OP_31_XOP_LSWX", .value = 533, },110{ .name = "OP_31_XOP_LWBRX", .value = 534, },111{ .name = "OP_31_XOP_LFSUX", .value = 567, },112{ .name = "OP_31_XOP_LXSDX", .value = 588, },113{ .name = "OP_31_XOP_LSWI", .value = 597, },114{ .name = "OP_31_XOP_LFDX", .value = 599, },115{ .name = "OP_31_XOP_LFDUX", .value = 631, },116{ .name = "OP_31_XOP_STXSSPX", .value = 652, },117{ .name = "OP_31_XOP_STDBRX", .value = 660, },118{ .name = "OP_31_XOP_STXWX", .value = 661, },119{ .name = "OP_31_XOP_STWBRX", .value = 662, },120{ .name = "OP_31_XOP_STFSX", .value = 663, },121{ .name = "OP_31_XOP_STFSUX", .value = 695, },122{ .name = "OP_31_XOP_STXSDX", .value = 716, },123{ .name = "OP_31_XOP_STSWI", .value = 725, },124{ .name = "OP_31_XOP_STFDX", .value = 727, },125{ .name = "OP_31_XOP_STFDUX", .value = 759, },126{ .name = "OP_31_XOP_LXVW4X", .value = 780, },127{ .name = "OP_31_XOP_LHBRX", .value = 790, },128{ .name = "OP_31_XOP_LXVD2X", .value = 844, },129{ .name = "OP_31_XOP_LFIWAX", .value = 855, },130{ .name = "OP_31_XOP_LFIWZX", .value = 887, },131{ .name = "OP_31_XOP_STXVW4X", .value = 908, },132{ .name = "OP_31_XOP_STHBRX", .value = 918, },133{ .name = "OP_31_XOP_STXVD2X", .value = 972, },134{ .name = "OP_31_XOP_STFIWX", .value = 983, },135};136137/*138* Arithmetic instructions which are having opcode as 31.139* These instructions are tracked to save the register state140* changes. Example:141*142* lwz r10,264(r3)143* add r31, r3, r3144* lwz r9, 0(r31)145*146* Here instruction tracking needs to identify the "add"147* instruction and save data type of r3 to r31. If a sample148* is hit at next "lwz r9, 0(r31)", by this instruction tracking,149* data type of r31 can be resolved.150*/151static struct insn_offset arithmetic_ins_op_31[] = {152{ .name = "SUB_CARRY_XO_FORM", .value = 8, },153{ .name = "MUL_HDW_XO_FORM1", .value = 9, },154{ .name = "ADD_CARRY_XO_FORM", .value = 10, },155{ .name = "MUL_HW_XO_FORM1", .value = 11, },156{ .name = "SUB_XO_FORM", .value = 40, },157{ .name = "MUL_HDW_XO_FORM", .value = 73, },158{ .name = "MUL_HW_XO_FORM", .value = 75, },159{ .name = "SUB_EXT_XO_FORM", .value = 136, },160{ .name = "ADD_EXT_XO_FORM", .value = 138, },161{ .name = "SUB_ZERO_EXT_XO_FORM", .value = 200, },162{ .name = "ADD_ZERO_EXT_XO_FORM", .value = 202, },163{ .name = "SUB_EXT_XO_FORM2", .value = 232, },164{ .name = "MUL_DW_XO_FORM", .value = 233, },165{ .name = "ADD_EXT_XO_FORM2", .value = 234, },166{ .name = "MUL_W_XO_FORM", .value = 235, },167{ .name = "ADD_XO_FORM", .value = 266, },168{ .name = "DIV_DW_XO_FORM1", .value = 457, },169{ .name = "DIV_W_XO_FORM1", .value = 459, },170{ .name = "DIV_DW_XO_FORM", .value = 489, },171{ .name = "DIV_W_XO_FORM", .value = 491, },172};173174static struct insn_offset arithmetic_two_ops[] = {175{ .name = "mulli", .value = 7, },176{ .name = "subfic", .value = 8, },177{ .name = "addic", .value = 12, },178{ .name = "addic.", .value = 13, },179{ .name = "addi", .value = 14, },180{ .name = "addis", .value = 15, },181};182183static int cmp_offset(const void *a, const void *b)184{185const struct insn_offset *val1 = a;186const struct insn_offset *val2 = b;187188return (val1->value - val2->value);189}190191static struct ins_ops *check_ppc_insn(struct disasm_line *dl)192{193int raw_insn = dl->raw.raw_insn;194int opcode = PPC_OP(raw_insn);195int mem_insn_31 = PPC_21_30(raw_insn);196struct insn_offset *ret;197struct insn_offset mem_insns_31_opcode = {198"OP_31_INSN",199mem_insn_31200};201char name_insn[32];202203/*204* Instructions with opcode 32 to 63 are memory205* instructions in powerpc206*/207if ((opcode & 0x20)) {208/*209* Set name in case of raw instruction to210* opcode to be used in insn-stat211*/212if (!strlen(dl->ins.name)) {213sprintf(name_insn, "%d", opcode);214dl->ins.name = strdup(name_insn);215}216return &load_store_ops;217} else if (opcode == 31) {218/* Check for memory instructions with opcode 31 */219ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset);220if (ret) {221if (!strlen(dl->ins.name))222dl->ins.name = strdup(ret->name);223return &load_store_ops;224} else {225mem_insns_31_opcode.value = PPC_22_30(raw_insn);226ret = bsearch(&mem_insns_31_opcode, arithmetic_ins_op_31, ARRAY_SIZE(arithmetic_ins_op_31),227sizeof(arithmetic_ins_op_31[0]), cmp_offset);228if (ret != NULL)229return &arithmetic_ops;230/* Bits 21 to 30 has value 444 for "mr" insn ie, OR X form */231if (PPC_21_30(raw_insn) == 444)232return &arithmetic_ops;233}234} else {235mem_insns_31_opcode.value = opcode;236ret = bsearch(&mem_insns_31_opcode, arithmetic_two_ops, ARRAY_SIZE(arithmetic_two_ops),237sizeof(arithmetic_two_ops[0]), cmp_offset);238if (ret != NULL)239return &arithmetic_ops;240}241242return NULL;243}244245/*246* Instruction tracking function to track register state moves.247* Example sequence:248* ld r10,264(r3)249* mr r31,r3250* <<after some sequence>251* ld r9,312(r31)252*253* Previous instruction sequence shows that register state of r3254* is moved to r31. update_insn_state_powerpc tracks these state255* changes256*/257#ifdef HAVE_LIBDW_SUPPORT258static void update_insn_state_powerpc(struct type_state *state,259struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused,260struct disasm_line *dl)261{262struct annotated_insn_loc loc;263struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];264struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];265struct type_state_reg *tsr;266u32 insn_offset = dl->al.offset;267268if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)269return;270271/*272* Value 444 for bits 21:30 is for "mr"273* instruction. "mr" is extended OR. So set the274* source and destination reg correctly275*/276if (PPC_21_30(dl->raw.raw_insn) == 444) {277int src_reg = src->reg1;278279src->reg1 = dst->reg1;280dst->reg1 = src_reg;281}282283if (!has_reg_type(state, dst->reg1))284return;285286tsr = &state->regs[dst->reg1];287288if (!has_reg_type(state, src->reg1) ||289!state->regs[src->reg1].ok) {290tsr->ok = false;291return;292}293294tsr->type = state->regs[src->reg1].type;295tsr->kind = state->regs[src->reg1].kind;296tsr->ok = true;297298pr_debug_dtp("mov [%x] reg%d -> reg%d",299insn_offset, src->reg1, dst->reg1);300pr_debug_type_name(&tsr->type, tsr->kind);301}302#endif /* HAVE_LIBDW_SUPPORT */303304static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)305{306if (!arch->initialized) {307arch->initialized = true;308arch->associate_instruction_ops = powerpc__associate_instruction_ops;309arch->objdump.comment_char = '#';310annotate_opts.show_asm_raw = true;311arch->e_machine = EM_PPC;312arch->e_flags = 0;313}314315return 0;316}317318319