655d409703
commit 8b946cc38e upstream.
Andi reported that objtool on vmlinux.o consumes more memory than his
system has, leading to horrific performance.
This is in part because we keep a struct instruction for every
instruction in the file in-memory. Shrink struct instruction by
removing the CFI state (which includes full register state) from it
and demand allocating it.
Given most instructions don't actually change CFI state, there's lots
of repetition there, so add a hash table to find previous CFI
instances.
Reduces memory consumption (and runtime) for processing an
x86_64-allyesconfig:
pre: 4:40.84 real, 143.99 user, 44.18 sys, 30624988 mem
post: 2:14.61 real, 108.58 user, 25.04 sys, 16396184 mem
Suggested-by: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20210624095147.756759107@infradead.org
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
95 lines
2.2 KiB
C
95 lines
2.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
*/
|
|
|
|
#ifndef _CHECK_H
|
|
#define _CHECK_H
|
|
|
|
#include <stdbool.h>
|
|
#include <objtool/cfi.h>
|
|
#include <objtool/arch.h>
|
|
|
|
struct insn_state {
|
|
struct cfi_state cfi;
|
|
unsigned int uaccess_stack;
|
|
bool uaccess;
|
|
bool df;
|
|
bool noinstr;
|
|
s8 instr;
|
|
};
|
|
|
|
struct alt_group {
|
|
/*
|
|
* Pointer from a replacement group to the original group. NULL if it
|
|
* *is* the original group.
|
|
*/
|
|
struct alt_group *orig_group;
|
|
|
|
/* First and last instructions in the group */
|
|
struct instruction *first_insn, *last_insn;
|
|
|
|
/*
|
|
* Byte-offset-addressed len-sized array of pointers to CFI structs.
|
|
* This is shared with the other alt_groups in the same alternative.
|
|
*/
|
|
struct cfi_state **cfi;
|
|
};
|
|
|
|
struct instruction {
|
|
struct list_head list;
|
|
struct hlist_node hash;
|
|
struct list_head call_node;
|
|
struct section *sec;
|
|
unsigned long offset;
|
|
unsigned int len;
|
|
enum insn_type type;
|
|
unsigned long immediate;
|
|
bool dead_end, ignore, ignore_alts;
|
|
bool hint;
|
|
bool retpoline_safe;
|
|
s8 instr;
|
|
u8 visited;
|
|
struct alt_group *alt_group;
|
|
struct symbol *call_dest;
|
|
struct instruction *jump_dest;
|
|
struct instruction *first_jump_src;
|
|
struct reloc *jump_table;
|
|
struct reloc *reloc;
|
|
struct list_head alts;
|
|
struct symbol *func;
|
|
struct list_head stack_ops;
|
|
struct cfi_state *cfi;
|
|
};
|
|
|
|
static inline bool is_static_jump(struct instruction *insn)
|
|
{
|
|
return insn->type == INSN_JUMP_CONDITIONAL ||
|
|
insn->type == INSN_JUMP_UNCONDITIONAL;
|
|
}
|
|
|
|
static inline bool is_dynamic_jump(struct instruction *insn)
|
|
{
|
|
return insn->type == INSN_JUMP_DYNAMIC ||
|
|
insn->type == INSN_JUMP_DYNAMIC_CONDITIONAL;
|
|
}
|
|
|
|
static inline bool is_jump(struct instruction *insn)
|
|
{
|
|
return is_static_jump(insn) || is_dynamic_jump(insn);
|
|
}
|
|
|
|
struct instruction *find_insn(struct objtool_file *file,
|
|
struct section *sec, unsigned long offset);
|
|
|
|
#define for_each_insn(file, insn) \
|
|
list_for_each_entry(insn, &file->insn_list, list)
|
|
|
|
#define sec_for_each_insn(file, sec, insn) \
|
|
for (insn = find_insn(file, sec, 0); \
|
|
insn && &insn->list != &file->insn_list && \
|
|
insn->sec == sec; \
|
|
insn = list_next_entry(insn, list))
|
|
|
|
#endif /* _CHECK_H */
|