0

Introduction

I am trying to make static evaluation of maximum possible stack usage in an embedded program. I've managed to get stack usage of each individual function by using GCC option -fstack-usage and I've built tree of all function calls by parsing RTL produced by option -fdump-rtl-expand.

Now, there is only the easy part left when I set stack usage for every function in my tree and look for the most expensive path by using some sort of Dijkstra algorithm. However, it turned out is that finding out which function from stack usage file correlates with which function from RTL file appears to be actually the hardest part of the whole job.

Problem

So what exactly is the problem?

In short, function identifiers generated by GCC for the two files are different. RTL uses both mangled identifiers and human readable names. The latter ones are similar to the ones you can see in the source code. Stack usage file has only human readable names but they are written in some weird notation which is completely different than the one from RTL file.

Let's look at an example.

Source file test.cpp:

void test() {}

Command (toolchain: GCC v8.2.0):

g++ -c test.cpp -fdump-rtl-expand -fstack-usage

RTL file ./test.cpp.234r.expand:

;; Function test (_Z4testv, funcdef_no=0, decl_uid=2358, cgraph_uid=0, symbol_order=0)


;; Generating RTL for gimple basic block 2


try_optimize_cfg iteration 1

Merging block 3 into block 2...
Merged blocks 2 and 3.
Merged 2 and 3 without moving.
Merging block 4 into block 2...
Merged blocks 2 and 4.
Merged 2 and 4 without moving.


try_optimize_cfg iteration 2



;;
;; Full RTL generated for this function:
;;
(note 1 0 3 NOTE_INSN_DELETED)
(note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
(note 2 3 7 2 NOTE_INSN_FUNCTION_BEG)
(insn 7 2 0 2 (const_int 0 [0]) "test.cpp":1 -1
     (nil))

Stack usage file test.su:

test.cpp:1:6:void test()        16      static

So the function is named test and _Z4testv in the RTL file and void test() in the stack usage file.

It gets even more difficult to tell which functions are the same when we get some real example from my code. The names will be then (in order):

devices::Button<virtualIo::Port_<utils::type::list::List<virtualIo::IndexedPin_<0, virtualIo::PortD, 7, true> >, 1, unsigned char, utils::type::list::List<virtualIo::PortShifter_<virtualIo::PortD, unsigned char, unsigned char, false, 128, 1, utils::type::list::List<virtualIo::PartShifter_<unsigned char, unsigned char, 1, 7, 128> > > >, true>, true>::isUp

_ZNK7devices6ButtonIN9virtualIo5Port_IN5utils4type4list4ListIJNS1_11IndexedPin_ILh0ENS1_5PortDELh7ELb1EEEEEELh1EhNS6_IJNS1_12PortShifter_IS8_hhLb0ELh128ELh1ENS6_IJNS1_12PartShifter_IhhLh1ELa7ELh128EEEEEEEEEEELb1EEELb1ELb0EE4isUpEv

bool devices::Button<PORT, POSITIVE_INPUT, PULL_UP>::isUp() const [with PORT = virtualIo::Port_<utils::type::list::List<virtualIo::IndexedPin_<0, virtualIo::PortD, 7, true> >, 1, unsigned char, utils::type::list::List<virtualIo::PortShifter_<virtualIo::PortD, unsigned char, unsigned char, false, 128, 1, utils::type::list::List<virtualIo::PartShifter_<unsigned char, unsigned char, 1, 7, 128> > > >, true>; bool POSITIVE_INPUT = true; bool PULL_UP = false]

Deciding if this is the same function is barely doable by human and I have no idea how to do it with a script/program.

Solution proposals

The ideal solution would be to have mangled names in stack usage file. They are uniform and unambiguous (aside from some minor annoyances with constructors and destructors).

I'll accept any way that allows to reliably match names from one file to the other one, though.

In the last resort I'll be force to match the functions by their order which appears to be the same in both files. I am very hesitant to make this assumption, though.

GCC version

Make sure to use GCC version 8 for this. It looks like version 7 generates names in different way. I tested versions 8.2.0 and 8.3.0. Both have the described behavior.

I cannot use version 7. It has some bug which prevents my code from compiling.


Edit

More examples

Source file test.cpp:

template<typename T> long some_name(int); template<> long some_name<int>(int) { return 0; }

The first line of RTL file ./test.cpp.234r.expand:

;; Function some_name<int> (_Z9some_nameIiEli, funcdef_no=0, decl_uid=2364, cgraph_uid=0, symbol_order=0)

Stack usage file test.su:

test.cpp:5:6:long int some_name(int) [with T = int] 16  static

Edit2

Experiments have shown that the human readable identifiers from RTL files are not unique. They are the same for overloaded functions.

Piotr Siupa
  • 3,929
  • 2
  • 29
  • 65
  • I am able to join mangled and unmangled names as returned by `c++filt` using `tmp=$(cat *expand | grep '^;; Function' | cut -d'(' -f2- | cut -d, -f1); paste <(cat <<<$tmp) <(<<<$tmp c++filt)`, but the format used in stack usage is complex. Please post an example with template function, where it get's hard, ex. `template long some_name(int); template<> long some_name(int a) { return a; } template<> long some_name(int a) { return a; }` – KamilCuk Apr 15 '19 at 20:05
  • I guess I'll go with the assumption that order of function in both files is the same and pray that nothing will go horribly wrong. Fortunately, my program has small enough number of functions so I can kind of verify this manually. – Piotr Siupa Apr 19 '19 at 18:05

0 Answers0