// SPDX-License-Identifier: GPL-2.0 /* * Perf annotate functions. * * Copyright (C) 2020-2023 Loongson Technology Corporation Limited */ static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) { char *c, *endptr, *tok, *name; struct map *map = ms->map; struct addr_map_symbol target = { .ms = { .map = map, }, }; 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.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; return 0; } static struct ins_ops loongarch_call_ops = { .parse = loongarch_call__parse, .scnprintf = call__scnprintf, }; static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) { struct map *map = ms->map; struct symbol *sym = ms->sym; struct addr_map_symbol target = { .ms = { .map = 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; } return 0; } static struct ins_ops loongarch_jump_ops = { .parse = loongarch_jump__parse, .scnprintf = jump__scnprintf, }; static struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) { 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 = '#'; } return 0; }