multi-entry function (debug info)

Hi Muhui,

In general the function’s start address would be the low_pc. From time to time I hear about people wanting to put cold code blocks before the entry point; I think so far that has not been implemented, but if it is, then low_pc would be the lowest code address in the function and DW_AT_entry_pc would record the entrypoint address.

Regarding multi-entry functions, I’m aware of two cases where this occurs in a source language. One is when you have optional parameters in C++, which effectively creates one or more overloads for the function; the other is PL/I which allows defining an entry label within the body of the function. For the C++ case, I’d expect the front-end to create stubs that fill in the defaulted parameters and then tail-call the main function; in this case, each stub would have its own debug-info entry and be treated as its own independent function for debug-info purposes. For PL/I, I would probably do the same, although I admit it has been a long time since I did any PL/I programming and I never worked on a PL/I compiler.

If I am not covering the cases you are interested in, please provide more details.

Thanks,

–paulr

Regarding multi-entry functions, I’m aware of two cases where this occurs in a source language. One is when you have optional parameters in C++, which effectively creates one or more overloads for the function; the other is PL/I which allows defining an entry label within the body of the function. For the C++ case, I’d expect the front-end to create stubs that fill in the defaulted parameters and then tail-call the main function; in this case, each stub would have its own debug-info entry and be treated as its own independent function for debug-info purposes. For PL/I, I would probably do the same, although I admit it has been a long time since I did any PL/I programming and I never worked on a PL/I compiler.

Another example of source language with functions that can have more than entry point is Fortran via the ENTRY statement. I think most compilers use an extra integer parameter to discriminate between the different entry points at the call sites.

Interesting. So the FORTRAN subprogram would have an implicit parameter and effectively have a computed GOTO at the beginning to dispatch to the correct ENTRY? Would you expect each ENTRY to be described as its own subprogram in the debug info? (My FORTRAN experience is even more ancient than my PL/I.)

–paulr

At least in Fortran 77, each ENTRY has its own name, so there is no "dynamic dispatch". They are really more like separate functions with a common tail.

-Krzysztof

Yep, I think wasn’t clear enough when I said “an integer parameter to discriminate”. Perhaps an example may help

module moo
contains
function bad_cos(x) result(y)
real, value :: x ! degrees
real :: y
real, parameter :: pi = 3.14159265359

x = x + pi / 2

entry bad_sin(x)

y = …

end function bad_cos
end module moo

program main
use moo
implicit none

real :: c, s

c = bad_cos(0.6)
s = bad_sin(0.6)

print *, c, s, c2 + s2
end program main

the code above might be lowered into something like

float moo_bad_cos__(float x, int discriminator)
{
switch (discriminator) {

case 0: goto moo_bad_cos;

case 1: goto moo_bad_sin;
}

moo_bad_cos:

x = x - pi;

moo_bad_sin:
y = …;

return y;
}

int main() {

float c = moo_bad_cos__(0.6, 0);

float s = moo_bad_cos__(0.6, 1);

// print etc.

}

I’m afraid I don’t know enough about the consequences of describing the ENTRY as a subprogram of its own in the debug info, so apologies if what follows is unrelated to that.

In my (definitely biased) view of the Fortran things, I’d probably be OK if the debugger knows I’m inside (the original) “bad_cos” given that an ENTRY statement is always going to be found inside a function/procedure (where the enclosing function/procedure acts as the “principal” entry), so it would surprise me a bit if the debugger thinks I’m in a function called “bad_sin” as I don’t have any procedure (at least at the “program function” or “module procedure”, in Fortran parlance, level) with that name in the code. But that’s my view.

Perhaps flang/f18 folks want to weigh in here.

Kind regards,
Roger

Missatge de Krzysztof Parzyszek via llvm-dev <llvm-dev@lists.llvm.org> del dia dt., 23 d’oct. 2018 a les 18:31:

Regarding multi-entry functions, I’m aware of two cases where this occurs in a source language. One is when you have optional parameters in C++, which effectively creates one or more overloads for the function; the other is PL/I which allows defining an entry label within the body of the function. For the C++ case, I’d expect the front-end to create stubs that fill in the defaulted parameters and then tail-call the main function; in this case, each stub would have its own debug-info entry and be treated as its own independent function for debug-info purposes.

C++ doesn’t work that way, parameters with default values are defined to evaluate the default values in the calling function then call with the results rather than creating a set of trampolines that evaluate the default values inside them.

From https://en.cppreference.com/w/cpp/language/default_arguments :
“The names used in the default arguments are looked up, checked for accessibility, and bound at the point of declaration, but are executed at the point of the function call”

Note that a function with default arguments can’t be cast to a function pointer that doesn’t have those defaulted arguments. So, if there is a function:
void f(int = 0);
then the following cast won’t compile:
static_cast<void(*)()>(f);

Note that the defined semantics for source_location::current() wouldn’t work if default arguments worked as you described:
source_location::current() is defined to return the location of the call-site when it is used in a default argument.
In the following example, the assertion in f doesn’t fail:
void f(int line, source_location loc = source_location::current())
{
assert(line == loc.line());
}

void g()
{
f(LINE);
}

void h()
{
f(LINE);
}

int main()
{
g();
h();
}
See https://en.cppreference.com/w/cpp/experimental/source_location

Jacob Lifshay

Interesting, that’s not what I remember from 20 years ago, but then C++ is pretty malleable. That tactic makes it a bit harder to maintain old APIs, you have to do it with explicit overloads rather than defaulting parameters. In which case of course the explicit overload is its own function with its own debug info.

–paulr

Getting back to what the DWARF should look like, I solicited examples on the dwarf-discuss list. gfortran emits sibling DW_TAG_subprogram entries for the SUBROUTINE and each ENTRY, which seems like a hack. But both IBM XL Fortran and OpenVMS Fortran will emit a DW_TAG_subprogram for the SUBROUTINE, and it will have a child DW_TAG_entry_point for each ENTRY within the SUBROUTINE. Each of these entries will have its own set of DW_TAG_formal_parameter entries, according to the spec.

This aligns with my expectation, that the ENTRY is “contained” within a SUBROUTINE, and the natural way for DWARF to represent this is by having entry_point children of the subprogram DIE.

–paulr

Hi paulr

Thanks for your answer and sorry for the late reply. Actually, I am not very interested in the cases that occur in the source language. What I want to know is that when there are more than one entry point, how does dwarf represent this. Now I have the idea according to your answer. Thanks

I would like to ask more about the dwarf debugging information. As there are many components/ tags in the debugging information. When I use the dwarfdump -debug-info to dump the debugging information. I found it would be rather difficult to parse the dumped information. Does llvm provide any tools or do you have any suggestions on parsing the dwarf debugging information? Many Thanks

Regards
Muhui

<paul.robinson@sony.com> 于2018年10月24日周三 下午11:54写道:

Hi Muhui,

If you are looking for a tool to provide a more human-friendly dump of the DWARF information, at Sony we have a tool called DIVA which might meet your needs. dwarfdump is what you might call a “disassembler” for DWARF, while DIVA is more like a de-compiler.

https://github.com/SNSystems/DIVA

Hope this helps!

–paulr