aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorNils-Christian Kempke <nils-christian.kempke@intel.com>2022-03-21 15:43:38 +0100
committerIjaz, Abdul B <abdul.b.ijaz@intel.com>2023-12-29 11:31:10 +0100
commit3396471b4cd4032adcc8eabb46d2a3ab0c876368 (patch)
tree38407aa4e5e42bf1f9865125211cd1f705c249db /gdb
parent06740cf11f601c6a5a0f3807f37aaac31d13a542 (diff)
downloadgdb-3396471b4cd4032adcc8eabb46d2a3ab0c876368.zip
gdb-3396471b4cd4032adcc8eabb46d2a3ab0c876368.tar.gz
gdb-3396471b4cd4032adcc8eabb46d2a3ab0c876368.tar.bz2
dwarf, fortran: add support for DW_TAG_entry_point
Fortran provides additional entry points for subroutines and functions. These entry points may use only a subset (or a different set) of the parameters of the original subroutine. The entry points may be described via the DWARF tag DW_TAG_entry_point. This commit adds support for parsing the DW_TAG_entry_point DWARF tag. Currently, between ifx/ifort/gfortran, only ifort is actually emitting this tag. Both, ifx and gfortran use the DW_TAG_subprogram tag as workaround/alternative. Thus, this patch really only adds more ifort support. Even so, some of the attached tests still fail for ifort, due to some wrong line info generated for the entry points in ifort. After this patch it is possible to set a breakpoint in gdb with the ifort compiled example at the entry points 'foo' and 'foobar', which was not possible before. As gcc and ifx do not emit the tag I also added a test to gdb.dwarf2 which uses some underlying c compiled code and adds some Fortran style DWARF to it emitting the DW_TAG_entry_point. Before this patch it was not possible to actually define breakpoint at the entry point tags. For gfortran there actually exists a bug on bugzilla, asking for the use of DW_TAG_entry_point over DW_TAG_subprogram: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37134 This patch was originally posted here https://sourceware.org/legacy-ml/gdb-patches/2017-07/msg00317.html but its review/pinging got lost after a while. I reworked it to fit the current GDB. Co-authored-by: Bernhard Heckel <bernhard.heckel@intel.com> Co-authored-by: Tim Wiederhake <tim.wiederhake@intel.com> Approved-by: Tom Tromey <tom@tromey.com>
Diffstat (limited to 'gdb')
-rw-r--r--gdb/dwarf2/abbrev.c1
-rw-r--r--gdb/dwarf2/cooked-index.h3
-rw-r--r--gdb/dwarf2/index-write.c3
-rw-r--r--gdb/dwarf2/read.c70
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-entry-points.c43
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp213
-rw-r--r--gdb/testsuite/gdb.fortran/entry-point.exp84
-rw-r--r--gdb/testsuite/gdb.fortran/entry-point.f9067
8 files changed, 479 insertions, 5 deletions
diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c
index 3a429fd..3d6e16a 100644
--- a/gdb/dwarf2/abbrev.c
+++ b/gdb/dwarf2/abbrev.c
@@ -88,6 +88,7 @@ tag_interesting_for_index (dwarf_tag tag)
case DW_TAG_base_type:
case DW_TAG_class_type:
case DW_TAG_constant:
+ case DW_TAG_entry_point:
case DW_TAG_enumeration_type:
case DW_TAG_enumerator:
case DW_TAG_imported_declaration:
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 5675ea6..3c6b9c3 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -141,7 +141,8 @@ struct cooked_index_entry : public allocate_on_obstack
|| tag == DW_TAG_constant
|| tag == DW_TAG_enumerator);
case FUNCTIONS_DOMAIN:
- return tag == DW_TAG_subprogram;
+ return (tag == DW_TAG_subprogram
+ || tag == DW_TAG_entry_point);
case TYPES_DOMAIN:
return tag_is_type (tag);
case MODULES_DOMAIN:
diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c
index 842708a..a03320b 100644
--- a/gdb/dwarf2/index-write.c
+++ b/gdb/dwarf2/index-write.c
@@ -1307,7 +1307,8 @@ write_cooked_index (cooked_index *table,
}
gdb_index_symbol_kind kind;
- if (entry->tag == DW_TAG_subprogram)
+ if (entry->tag == DW_TAG_subprogram
+ || entry->tag == DW_TAG_entry_point)
kind = GDB_INDEX_SYMBOL_KIND_FUNCTION;
else if (entry->tag == DW_TAG_variable
|| entry->tag == DW_TAG_constant
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 6d95874..ec75276 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -6481,6 +6481,8 @@ process_die (struct die_info *die, struct dwarf2_cu *cu)
&& die->parent->tag == DW_TAG_subprogram)
cu->processing_has_namespace_info = true;
[[fallthrough]];
+ /* Fall through. */
+ case DW_TAG_entry_point:
case DW_TAG_inlined_subroutine:
read_func_scope (die, cu);
break;
@@ -6597,6 +6599,7 @@ die_needs_namespace (struct die_info *die, struct dwarf2_cu *cu)
case DW_TAG_enumerator:
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine:
+ case DW_TAG_entry_point:
case DW_TAG_member:
case DW_TAG_imported_declaration:
return 1;
@@ -10987,6 +10990,41 @@ dwarf2_ranges_read_low_addrs (unsigned offset, struct dwarf2_cu *cu,
});
}
+/* Determine the low and high pc of a DW_TAG_entry_point. */
+
+static pc_bounds_kind
+dwarf2_get_pc_bounds_entry_point (die_info *die, unrelocated_addr *low,
+ unrelocated_addr *high, dwarf2_cu *cu)
+{
+ gdb_assert (low != nullptr);
+ gdb_assert (high != nullptr);
+
+ if (die->parent->tag != DW_TAG_subprogram)
+ {
+ complaint (_("DW_TAG_entry_point not embedded in DW_TAG_subprogram"));
+ return PC_BOUNDS_INVALID;
+ }
+
+ /* A DW_TAG_entry_point is embedded in an subprogram. Therefore, we can use
+ the highpc from its enveloping subprogram and get the lowpc from
+ DWARF. */
+ const enum pc_bounds_kind bounds_kind = dwarf2_get_pc_bounds (die->parent,
+ low, high,
+ cu, nullptr,
+ nullptr);
+ if (bounds_kind == PC_BOUNDS_INVALID || bounds_kind == PC_BOUNDS_NOT_PRESENT)
+ return bounds_kind;
+
+ attribute *attr_low = dwarf2_attr (die, DW_AT_low_pc, cu);
+ if (!attr_low)
+ {
+ complaint (_("DW_TAG_entry_point is missing DW_AT_low_pc"));
+ return PC_BOUNDS_INVALID;
+ }
+ *low = attr_low->as_address ();
+ return bounds_kind;
+}
+
/* Determine the low and high pc using the DW_AT_low_pc and DW_AT_high_pc or
DW_AT_ranges attributes of a DIE. */
@@ -11070,8 +11108,11 @@ dwarf2_get_pc_bounds (struct die_info *die, unrelocated_addr *lowpc,
unrelocated_addr high = {};
enum pc_bounds_kind ret;
- ret = dwarf_get_pc_bounds_ranges_or_highlow_pc (die, &low, &high, cu, map,
- datum);
+ if (die->tag == DW_TAG_entry_point)
+ ret = dwarf2_get_pc_bounds_entry_point (die, &low, &high, cu);
+ else
+ ret = dwarf_get_pc_bounds_ranges_or_highlow_pc (die, &low, &high, cu, map,
+ datum);
if (ret == PC_BOUNDS_NOT_PRESENT || ret == PC_BOUNDS_INVALID)
return ret;
@@ -16376,6 +16417,11 @@ cooked_indexer::index_dies (cutu_reader *reader,
cooked_index_flag flags = IS_STATIC;
sect_offset sibling {};
const cooked_index_entry *this_parent_entry = parent_entry;
+
+ /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
+ its surrounding subroutine. */
+ if (abbrev->tag == DW_TAG_entry_point)
+ this_parent_entry = parent_entry->parent_entry;
info_ptr = scan_attributes (reader->cu->per_cu, reader, info_ptr,
info_ptr, abbrev, &name, &linkage_name,
&flags, &sibling, &this_parent_entry,
@@ -18886,6 +18932,20 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
sym->set_domain (LABEL_DOMAIN);
add_symbol_to_list (sym, cu->list_in_scope);
break;
+ case DW_TAG_entry_point:
+ /* SYMBOL_BLOCK_VALUE (sym) will be filled in later by
+ finish_block. */
+ sym->set_aclass_index (LOC_BLOCK);
+ /* DW_TAG_entry_point provides an additional entry_point to an
+ existing sub_program. Therefore, we inherit the "external"
+ attribute from the sub_program to which the entry_point
+ belongs to. */
+ attr2 = dwarf2_attr (die->parent, DW_AT_external, cu);
+ if (attr2 != nullptr && attr2->as_boolean ())
+ list_to_add = cu->get_builder ()->get_global_symbols ();
+ else
+ list_to_add = cu->list_in_scope;
+ break;
case DW_TAG_subprogram:
/* SYMBOL_BLOCK_VALUE (sym) will be filled in later by
finish_block. */
@@ -19631,6 +19691,7 @@ read_type_die_1 (struct die_info *die, struct dwarf2_cu *cu)
case DW_TAG_enumeration_type:
this_type = read_enumeration_type (die, cu);
break;
+ case DW_TAG_entry_point:
case DW_TAG_subprogram:
case DW_TAG_subroutine_type:
case DW_TAG_inlined_subroutine:
@@ -19950,12 +20011,15 @@ determine_prefix (struct die_info *die, struct dwarf2_cu *cu)
return "";
case DW_TAG_subprogram:
/* Nested subroutines in Fortran get a prefix with the name
- of the parent's subroutine. */
+ of the parent's subroutine. Entry points are prefixed by the
+ parent's namespace. */
if (cu->lang () == language_fortran)
{
if ((die->tag == DW_TAG_subprogram)
&& (dwarf2_name (parent, cu) != NULL))
return dwarf2_name (parent, cu);
+ else if (die->tag == DW_TAG_entry_point)
+ return determine_prefix (parent, cu);
}
return "";
case DW_TAG_enumeration_type:
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c
new file mode 100644
index 0000000..87b4691
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c
@@ -0,0 +1,43 @@
+/* Copyright 2023 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* In the generated DWARF, we'll use the locations of foo_entry_label and
+ foobar_entry_label as the low_pc's of our entry point TAGs. */
+
+int I = 0;
+int J = 0;
+int K = 0;
+
+__attribute__ ((noinline))
+void
+bar_helper (void)
+{
+ asm ("bar_helper_label: .globl bar_helper_label");
+ I++;
+ J++;
+ asm ("foo_entry_label: .globl foo_entry_label");
+ J++;
+ K++;
+ asm ("foobar_entry_label: .globl foobar_entry_label");
+}
+
+int
+main (void)
+{
+ asm ("main_label: .globl main_label");
+ bar_helper ();
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp
new file mode 100644
index 0000000..f361820
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp
@@ -0,0 +1,213 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that the DW_TAG_entry_point is handled properly by GDB and that we can
+# set breakpoints on function entry points.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets that support DWARF-2 and use
+# gas.
+require dwarf2_support
+
+standard_testfile .c -dw.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return -1
+}
+
+# Make some DWARF for the test.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global srcfile
+ declare_labels int_label int2_label
+
+ get_func_info main
+ get_func_info bar_helper
+
+ set int_size [get_sizeof "int" 4]
+
+ set prog_line 1
+ set bar_line 2
+ set foo_line 3
+ set foobar_line 4
+
+ set global_I [gdb_target_symbol I]
+ set global_J [gdb_target_symbol J]
+ set global_K [gdb_target_symbol K]
+
+ cu {} {
+ compile_unit {
+ {language @DW_LANG_Fortran90}
+ {name dw2-entry-points.f90}
+ {comp_dir /tmp}
+ } {
+ int_label: base_type {
+ {name "int"}
+ {byte_size $int_size sdata}
+ {encoding @DW_ATE_signed}
+ }
+ subprogram {
+ {name prog}
+ {decl_file 1 data1}
+ {decl_line $prog_line data1}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ {external 1 flag}
+ {main_subprogram 1 flag}
+ }
+ subprogram {
+ {name bar}
+ {decl_file 1 data1}
+ {decl_line $bar_line data1}
+ {external 1 flag}
+ {low_pc $bar_helper_start addr}
+ {high_pc "$bar_helper_start + $bar_helper_len" addr}
+ } {
+ formal_parameter {
+ {name I}
+ {type :$int_label}
+ {location {addr $global_I} SPECIAL_expr}
+ }
+ formal_parameter {
+ {name J}
+ {type :$int_label}
+ {location {addr $global_J} SPECIAL_expr}
+ }
+ entry_point {
+ {name foo}
+ {decl_file 1 data1}
+ {decl_line $foo_line data1}
+ {low_pc foo_entry_label addr}
+ } {
+ formal_parameter {
+ {name J}
+ {type :$int_label}
+ {location {addr $global_J} SPECIAL_expr}
+ }
+ formal_parameter {
+ {name K}
+ {type :$int_label}
+ {location {addr $global_K} SPECIAL_expr}
+ }
+ }
+ entry_point {
+ {name foobar}
+ {decl_file 1 data1}
+ {decl_line $foobar_line data1}
+ {low_pc foobar_entry_label addr}
+ } {
+ formal_parameter {
+ {name J}
+ {type :$int_label}
+ {location {addr $global_J} SPECIAL_expr}
+ }
+ }
+ }
+ }
+ }
+
+ cu {} {
+ compile_unit {
+ {language @DW_LANG_Fortran90}
+ {name dw2-entry-points-2.f90}
+ {comp_dir /tmp}
+ } {
+ int2_label: base_type {
+ {name "int"}
+ {byte_size $int_size sdata}
+ {encoding @DW_ATE_signed}
+ }
+ subprogram {
+ {name barso}
+ {decl_file 1 data1}
+ {decl_line $bar_line data1}
+ {external 1 flag}
+ {low_pc $bar_helper_start addr}
+ {high_pc "$bar_helper_start + $bar_helper_len" addr}
+ } {
+ formal_parameter {
+ {name I}
+ {type :$int2_label}
+ {location {addr $global_I} SPECIAL_expr}
+ }
+ formal_parameter {
+ {name J}
+ {type :$int2_label}
+ {location {addr $global_J} SPECIAL_expr}
+ }
+ entry_point {
+ {name fooso}
+ {decl_file 1 data1}
+ {decl_line $foo_line data1}
+ {low_pc foo_entry_label addr}
+ } {
+ formal_parameter {
+ {name J}
+ {type :$int2_label}
+ {location {addr $global_J} SPECIAL_expr}
+ }
+ formal_parameter {
+ {name K}
+ {type :$int2_label}
+ {location {addr $global_K} SPECIAL_expr}
+ }
+ }
+ }
+ }
+ }
+}
+
+if {[prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] {nodebug}]} {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# Try whether we can set and hit breakpoints at the entry_points.
+gdb_breakpoint "foo"
+gdb_breakpoint "foobar"
+
+# Now hit the entry_point break point and check their call-stack.
+gdb_continue_to_breakpoint "foo"
+gdb_test "bt" [multi_line \
+ "#0.*${hex} in foo \\(J=1, K=0\\).*" \
+ "#1.*${hex} in prog \\(\\).*" \
+ ] "bt foo"
+
+gdb_continue_to_breakpoint "foobar"
+gdb_test "bt" [multi_line \
+ "#0.*${hex} in foobar \\(J=2\\).*" \
+ "#1.*${hex} in prog \\(\\).*" \
+ ] "bt foobar"
+
+# Now try whether we can also set breakpoints on entry_points from other CUs.
+
+clean_restart ${testfile}
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_breakpoint "fooso"
+gdb_continue_to_breakpoint "foo_so"
+
+gdb_test "bt" [multi_line \
+ "#0.*${hex} in foo \\(J=1, K=0\\).*" \
+ "#1.*${hex} in prog \\(\\).*" \
+] "bt fooso"
diff --git a/gdb/testsuite/gdb.fortran/entry-point.exp b/gdb/testsuite/gdb.fortran/entry-point.exp
new file mode 100644
index 0000000..02d7b18
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/entry-point.exp
@@ -0,0 +1,84 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test Fortran entry points for subroutines.
+
+require allow_fortran_tests
+
+standard_testfile .f90
+load_lib "fortran.exp"
+
+if { [prepare_for_testing $testfile.exp $testfile $srcfile {debug f90}] } {
+ return -1
+}
+
+if { ![fortran_runto_main] } {
+ untested "could not run to main"
+ return -1
+}
+
+# Test if we can set a breakpoint via the entry-point name.
+set entry_point_name "foo"
+gdb_breakpoint $entry_point_name
+gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \
+ ".*entry foo\\(J,K,L,I1\\).*"
+
+gdb_test "print j" "= 11" "print j, entered via $entry_point_name"
+gdb_test "print k" "= 22" "print k, entered via $entry_point_name"
+gdb_test "print l" "= 33" "print l, entered via $entry_point_name"
+gdb_test "print i1" "= 44" "print i1, entered via $entry_point_name"
+gdb_test "info args" \
+ [multi_line "j = 11" \
+ "k = 22" \
+ "l = 33" \
+ "i1 = 44"] \
+ "info args, entered via $entry_point_name"
+
+# Test if we can set a breakpoint via the function name.
+set entry_point_name "bar"
+gdb_breakpoint $entry_point_name
+gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \
+ ".*subroutine bar\\(I,J,K,I1\\).*"
+
+gdb_test "print i" "= 444" "print i, entered via $entry_point_name"
+gdb_test "print j" "= 555" "print j, entered via $entry_point_name"
+gdb_test "print k" "= 666" "print k, entered via $entry_point_name"
+gdb_test "print i1" "= 777" "print i1, entered via $entry_point_name"
+
+# Test a second entry point.
+set entry_point_name "foobar"
+gdb_breakpoint $entry_point_name
+gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \
+ ".* entry foobar\\(J\\).*"
+
+gdb_test "print j" "= 1" "print j, entered via $entry_point_name"
+gdb_test "info args" "j = 1" "info args, entered via $entry_point_name"
+
+# Test breaking at the entrypoint defined inside the module mod via its
+# scoped name.
+set entry_point_name "mod::mod_foo"
+
+# GCC moves subroutines with entry points out of the module scope into the
+# compile unit scope.
+if {[test_compiler_info {gcc-*}]} {
+ setup_xfail "gcc/105272" "*-*-*"
+}
+gdb_breakpoint $entry_point_name
+
+if {[test_compiler_info {gcc-*}]} {
+ setup_xfail "gcc/105272" "*-*-*"
+}
+gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \
+ ".* entry mod_foo\\(\\).*"
diff --git a/gdb/testsuite/gdb.fortran/entry-point.f90 b/gdb/testsuite/gdb.fortran/entry-point.f90
new file mode 100644
index 0000000..7b1c7f9
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/entry-point.f90
@@ -0,0 +1,67 @@
+! Copyright 2023 Free Software Foundation, Inc.
+!
+! This program is free software; you can redistribute it and/or modify
+! it under the terms of the GNU General Public License as published by
+! the Free Software Foundation; either version 3 of the License, or
+! (at your option) any later version.
+!
+! This program is distributed in the hope that it will be useful,
+! but WITHOUT ANY WARRANTY; without even the implied warranty of
+! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+! GNU General Public License for more details.
+!
+! You should have received a copy of the GNU General Public License
+! along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+module mod
+implicit none
+
+contains
+ subroutine mod_bar
+ integer :: I = 3
+
+ goto 100
+
+ entry mod_foo
+ I = 33
+
+100 print *, I
+ end subroutine mod_bar
+end module mod
+
+
+subroutine bar(I,J,K,I1)
+ integer :: I,J,K,L,I1
+ integer :: A
+ real :: C
+
+ A = 0
+ C = 0.0
+
+ A = I + K + I1
+ goto 300
+
+ entry foo(J,K,L,I1)
+ A = J + K + L + I1
+
+200 C = J
+ goto 300
+
+ entry foobar(J)
+ goto 200
+
+300 A = C + 1
+ C = J * 1.5
+
+ return
+end subroutine
+
+program TestEntryPoint
+ use mod
+
+ call foo(11,22,33,44)
+ call bar(444,555,666,777)
+ call foobar(1)
+
+ call mod_foo()
+end program TestEntryPoint