diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2a05a66ea9b87..f9a9fe8726b18 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1285,6 +1285,7 @@ OBJS = \
 	dwarf2cfi.o \
 	dwarf2out.o \
 	early-remat.o \
+	einput.o \
 	emit-rtl.o \
 	et-forest.o \
 	except.o \
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 31e098a0c707a..3ad3f83fd60f5 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -45,6 +45,7 @@
 #include "tree-iterator.h"
 #include "gimplify.h"
 #include "opts.h"
+#include "einput.h"
 #include "common/common-target.h"
 #include "stringpool.h"
 #include "attribs.h"
@@ -300,6 +301,12 @@ gigi (Node_Id gnat_root,
 
   type_annotate_only = (gigi_operating_mode == 1);
 
+  if (Generate_SCO_Instance_Table != 0)
+    {
+      loc_discriminator_kind = LOC_DISCRIMINATOR_INSTANCE_ID;
+      instance_table = (int *) xcalloc (number_file, sizeof (int));
+    }
+
   for (i = 0; i < number_file; i++)
     {
       /* Use the identifier table to make a permanent copy of the filename as
@@ -322,6 +329,9 @@ gigi (Node_Id gnat_root,
       linemap_line_start (line_table, file_info_ptr[i].Num_Source_Lines, 252);
       linemap_position_for_column (line_table, 252 - 1);
       linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
+
+      if (instance_table)
+	instance_table[i] = file_info_ptr[i].Instance;
     }
 
   gcc_assert (Nkind (gnat_root) == N_Compilation_Unit);
diff --git a/gcc/common.opt b/gcc/common.opt
index 4d031e81b09a2..b72ff1fd673e6 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -176,6 +176,18 @@ HOST_WIDE_INT function_entry_patch_area_start
 Variable
 enum dwarf_gnat_encodings gnat_encodings = DWARF_GNAT_ENCODINGS_DEFAULT
 
+; Kind of discriminator information we associate with object code
+; locations in debug information. By default this is set to the basic
+; block discriminator, an arbitrary values that is guaranteed to be
+; distinct for each basic block associated with a given source
+; location. Language front-ends that support multiple instantiations
+; of a given source template (such as Ada with generics) may instead
+; provide instance identifiers, allowing external tools to
+; identify which instance a given object instruction comes from.
+
+Variable
+enum loc_discriminator_type loc_discriminator_kind = LOC_DISCRIMINATOR_BB
+
 ; -dP causes the rtl to be emitted as a comment in assembly.
 Variable
 int flag_dump_rtl_in_asm
diff --git a/gcc/einput.c b/gcc/einput.c
new file mode 100644
index 0000000000000..32768282a34ac
--- /dev/null
+++ b/gcc/einput.c
@@ -0,0 +1,55 @@
+/* Data and functions implementing the input expansion extension services.
+   Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "intl.h"
+#include "input.h"
+#include "einput.h"
+
+/* Map from ordinary map index to instance id.
+   See gcc/ada/trans.c for an example of how to fill it in.  */
+
+int *instance_table;
+
+/* Same as expand_location, filling line context information in addition.  */
+
+expanded_location
+context_expand_location (source_location loc, location_lc *lcp)
+{
+  const struct line_map *map;
+  expanded_location xloc = map_expand_location (loc, /*pmap=*/&map);
+
+  /* If an invalid LOC reaches here somehow or we don't have an instance
+     table to query, assign a constant instance id.  Otherwise, fetch the
+     instance id associated with the map corresponding to the input LOC.  */
+
+  if (map == NULL || instance_table == NULL)
+    lcp->instance = 0;
+  else
+    {
+      int map_index = ORDINARY_MAP_INDEX (linemap_check_ordinary (map),
+					  line_table);
+
+      lcp->instance = instance_table [map_index];
+    }
+
+  return xloc;
+}
diff --git a/gcc/einput.h b/gcc/einput.h
new file mode 100644
index 0000000000000..099aead637492
--- /dev/null
+++ b/gcc/einput.h
@@ -0,0 +1,50 @@
+/* Extensions to the basic input location expansion services, using
+   extra datastructures optionally filled by language front-ends.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_EINPUT_H
+#define GCC_EINPUT_H
+
+#include "input.h"
+
+/* For languages that have the notion of instantiating a given
+   template multiple times, different linemaps can be allocated for
+   each instance, which are distinguished by an instance id.  This
+   table, which has the same indices as the ordinary linemaps in
+   line_table, stores these instance ids.  */
+
+extern int *instance_table;
+
+/* Line context information: extra data associated with source *lines* (not
+   individual source locations), filled by context_expand_location from extra
+   tables that front-ends may provide by the time we reach the "final"
+   pass.  */
+
+/* The line context information per se, filled by context_expand_location
+   below from data found in the tables declared above.  */
+
+typedef struct location_lc {
+  /* The instance id associated with the source line, if any.  */
+  int instance;
+} location_lc;
+
+extern expanded_location context_expand_location (source_location loc,
+						  location_lc *lcp);
+
+#endif
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index e4b070486e87e..64af8082c5ddc 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -6567,11 +6567,16 @@ insn_file (const rtx_insn *insn)
   return LOCATION_FILE (INSN_LOCATION (insn));
 }
 
-/* Return expanded location of the statement that produced this insn.  */
+/* Return expanded location of the statement that produced INSN, and
+   fill the associated line context information we have into LC if not
+   NULL.  */
 expanded_location
-insn_location (const rtx_insn *insn)
+insn_location (const rtx_insn *insn, location_lc *lcp)
 {
-  return expand_location (INSN_LOCATION (insn));
+  if (lcp == NULL)
+    return expand_location (INSN_LOCATION (insn));
+  else
+    return context_expand_location (INSN_LOCATION (insn), lcp);
 }
 
 /* Return true if memory model MODEL requires a pre-operation (release-style)
diff --git a/gcc/final.c b/gcc/final.c
index 445a3fe938a67..b1266affbc108 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -122,12 +122,20 @@ static int last_linenum;
 /* Column number of last NOTE.  */
 static int last_columnnum;
 
-/* Last discriminator written to assembly.  */
+/* Discriminator written to assembly.  */
 static int last_discriminator;
 
-/* Discriminator of current block.  */
+/* Discriminator to be written to assembly for current instruction.
+   Note: actual usage depends on loc_discriminator_kind setting.  */
 static int discriminator;
 
+/* Discriminator identifying current basic block among others sharing
+   the same locus.  */
+static int bb_discriminator;
+
+/* Basic block discriminator for previous instruction.  */
+static int last_bb_discriminator;
+
 /* Highest line number in current block.  */
 static int high_block_linenum;
 
@@ -1701,6 +1709,7 @@ final_start_function_1 (rtx_insn **firstp, FILE *file, int *seen,
   last_linenum = LOCATION_LINE (prologue_location);
   last_columnnum = LOCATION_COLUMN (prologue_location);
   last_discriminator = discriminator = 0;
+  last_bb_discriminator = bb_discriminator = 0;
 
   high_block_linenum = high_function_linenum = last_linenum;
 
@@ -2236,8 +2245,7 @@ final_scan_insn_1 (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
 	  if (targetm.asm_out.unwind_emit)
 	    targetm.asm_out.unwind_emit (asm_out_file, insn);
 
-          discriminator = NOTE_BASIC_BLOCK (insn)->discriminator;
-
+	  bb_discriminator = NOTE_BASIC_BLOCK (insn)->discriminator;
 	  break;
 
 	case NOTE_INSN_EH_REGION_BEG:
@@ -3188,10 +3196,21 @@ notice_source_line (rtx_insn *insn, bool *is_stmt)
     }
   else if (INSN_HAS_LOCATION (insn))
     {
-      expanded_location xloc = insn_location (insn);
+      location_lc llc;
+      expanded_location xloc = insn_location (insn, &llc);
       filename = xloc.file;
       linenum = xloc.line;
       columnnum = xloc.column;
+
+      switch (loc_discriminator_kind)
+	{
+	case LOC_DISCRIMINATOR_BB:
+	  discriminator = bb_discriminator;
+	  break;
+	case LOC_DISCRIMINATOR_INSTANCE_ID:
+	  discriminator = llc.instance;
+	  break;
+	}
     }
   else
     {
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 500f6638f36ac..15e80188ef040 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -90,6 +90,20 @@ enum debug_struct_file
   DINFO_STRUCT_FILE_ANY     /* Debug structs defined in all files. */
 };
 
+/* Information stored in source location discriminators.  */
+
+enum loc_discriminator_type
+{
+  LOC_DISCRIMINATOR_BB,		/* Basic block discriminator.
+				   Arbitrary, distinct values are set
+				   for each of the basic blocks that
+				   share a given source location.  */
+  LOC_DISCRIMINATOR_INSTANCE_ID	/* Instance identifier provided by a
+				   language front-end to differentiate
+				   instances of a given generic
+				   template.  */
+};
+
 /* Balance between GNAT encodings and standard DWARF to emit.  */
 
 enum dwarf_gnat_encodings
diff --git a/gcc/input.c b/gcc/input.c
index d65a82dc26eac..f5a17ebbdf239 100644
--- a/gcc/input.c
+++ b/gcc/input.c
@@ -149,15 +149,19 @@ static const size_t fcache_line_record_size = 100;
    location towards its expansion point) that is in real source
    code.
 
+   If PMAP is non-null, *PMAP is set to designate the line map
+   from which the location was resolved.
+
    ASPECT controls which part of the location to use.  */
 
 static expanded_location
 expand_location_1 (source_location loc,
 		   bool expansion_point_p,
+		   const struct line_map **pmap,
 		   enum location_aspect aspect)
 {
   expanded_location xloc;
-  const line_map_ordinary *map;
+  const line_map_ordinary *map = NULL;
   enum location_resolution_kind lrk = LRK_MACRO_EXPANSION_POINT;
   tree block = NULL;
 
@@ -203,14 +207,16 @@ expand_location_1 (source_location loc,
 	  {
 	    source_location start = get_start (loc);
 	    if (start != loc)
-	      return expand_location_1 (start, expansion_point_p, aspect);
+	      return expand_location_1 (start, expansion_point_p, NULL,
+					aspect);
 	  }
 	  break;
 	case LOCATION_ASPECT_FINISH:
 	  {
 	    source_location finish = get_finish (loc);
 	    if (finish != loc)
-	      return expand_location_1 (finish, expansion_point_p, aspect);
+	      return expand_location_1 (finish, expansion_point_p, NULL,
+					aspect);
 	  }
 	  break;
 	}
@@ -221,6 +227,9 @@ expand_location_1 (source_location loc,
   if (loc <= BUILTINS_LOCATION)
     xloc.file = loc == UNKNOWN_LOCATION ? NULL : _("<built-in>");
 
+  if (pmap)
+    *pmap = map;
+
   return xloc;
 }
 
@@ -802,7 +811,17 @@ is_location_from_builtin_token (source_location loc)
 expanded_location
 expand_location (source_location loc)
 {
-  return expand_location_1 (loc, /*expansion_point_p=*/true,
+  return expand_location_1 (loc, /*expansion_point_p=*/true, /*pmap=*/NULL,
+			    LOCATION_ASPECT_CARET);
+}
+
+/* Same as expand_location, filling PMAP with the map that
+   was used to resolve, if PMAP is not null.  */
+
+expanded_location
+map_expand_location (source_location loc, const struct line_map **pmap)
+{
+  return expand_location_1 (loc, /*expansion_point_p=*/true, /*pmap=*/pmap,
 			    LOCATION_ASPECT_CARET);
 }
 
@@ -815,7 +834,7 @@ expand_location (source_location loc)
 expanded_location
 expand_location_to_spelling_point (source_location loc)
 {
-  return expand_location_1 (loc, /*expansion_point_p=*/false,
+  return expand_location_1 (loc, /*expansion_point_p=*/false, /*pmap=*/NULL,
 			    LOCATION_ASPECT_CARET);
 }
 
@@ -832,7 +851,8 @@ expanded_location
 linemap_client_expand_location_to_spelling_point (source_location loc,
 						  enum location_aspect aspect)
 {
-  return expand_location_1 (loc, /*expansion_point_p=*/false, aspect);
+  return expand_location_1 (loc, /*expansion_point_p=*/false, /*pmap=*/NULL,
+			    aspect);
 }
 
 
diff --git a/gcc/input.h b/gcc/input.h
index 4619663519a46..25cb8a94147cb 100644
--- a/gcc/input.h
+++ b/gcc/input.h
@@ -37,6 +37,8 @@ extern GTY(()) struct line_maps *saved_line_table;
 STATIC_ASSERT (BUILTINS_LOCATION < RESERVED_LOCATION_COUNT);
 
 extern bool is_location_from_builtin_token (source_location);
+extern expanded_location map_expand_location (source_location loc,
+					      const struct line_map **pmap);
 extern expanded_location expand_location (source_location);
 
 /* A class capturing the bounds of a buffer, to allow for run-time
diff --git a/gcc/modulo-sched.c b/gcc/modulo-sched.c
index 9a27365bfbc4e..1eb9362d31065 100644
--- a/gcc/modulo-sched.c
+++ b/gcc/modulo-sched.c
@@ -1237,7 +1237,7 @@ dump_insn_location (rtx_insn *insn)
 {
   if (dump_file && INSN_HAS_LOCATION (insn))
     {
-      expanded_location xloc = insn_location (insn);
+      expanded_location xloc = insn_location (insn, NULL);
       fprintf (dump_file, " %s:%i", xloc.file, xloc.line);
     }
 }
diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c
index 37c0d53fae276..0632bf0ec1a99 100644
--- a/gcc/print-rtl.c
+++ b/gcc/print-rtl.c
@@ -397,7 +397,7 @@ rtx_writer::print_rtx_operand_code_i (const_rtx in_rtx, int idx)
 	  when there is no location information available.  */
       if (INSN_HAS_LOCATION (in_insn))
 	{
-	  expanded_location xloc = insn_location (in_insn);
+	  expanded_location xloc = insn_location (in_insn, NULL);
 	  fprintf (m_outfile, " \"%s\":%i", xloc.file, xloc.line);
 	}
 #endif
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 565ce3abbe4e1..05c42fe515c39 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #endif  /* GENERATOR_FILE */
 
 #include "hard-reg-set.h"
+#include "einput.h"
 
 /* Value used by some passes to "recognize" noop moves as valid
  instructions.  */
@@ -3285,7 +3286,7 @@ extern rtx_insn *prev_cc0_setter (rtx_insn *);
 extern int insn_line (const rtx_insn *);
 extern const char * insn_file (const rtx_insn *);
 extern tree insn_scope (const rtx_insn *);
-extern expanded_location insn_location (const rtx_insn *);
+extern expanded_location insn_location (const rtx_insn *, location_lc *);
 extern location_t prologue_location, epilogue_location;
 
 /* In jump.c */
diff --git a/gcc/testsuite/gnat.dg/dinst.adb b/gcc/testsuite/gnat.dg/dinst.adb
new file mode 100644
index 0000000000000..460e6c5f914f4
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/dinst.adb
@@ -0,0 +1,20 @@
+-- { dg-do compile { target *-*-gnu* } }
+-- { dg-options "-gnateS -gdwarf -g -O -gno-column-info" }
+-- { dg-final { scan-assembler "loc \[0-9] 5 \[0-9]( is_stmt \[0-9])? discriminator 1\n" } } */
+-- { dg-final { scan-assembler-not "loc \[0-9] 5 \[0-9]( is_stmt \[0-9])? discriminator 2\n" } } */
+-- { dg-final { scan-assembler "loc \[0-9] 5 \[0-9]( is_stmt \[0-9])? discriminator 3\n" } } */
+-- { dg-final { scan-assembler "loc \[0-9] 5 \[0-9]( is_stmt \[0-9])? discriminator 4\n" } } */
+
+
+with DInst_Pkg;
+procedure DInst is
+   package I1 is new DInst_Pkg; -- instance 1 
+   package I2 is new DInst_Pkg; -- instance 2
+   package I3 is new DInst_Pkg; -- instance 3
+   package I4 is new DInst_Pkg; -- instance 4
+begin
+   I1.Foo;
+   -- I2.Foo;
+   I3.Foo;
+   I4.Foo;
+end;
diff --git a/gcc/testsuite/gnat.dg/dinst_pkg.adb b/gcc/testsuite/gnat.dg/dinst_pkg.adb
new file mode 100644
index 0000000000000..09a9baea1e4e2
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/dinst_pkg.adb
@@ -0,0 +1,7 @@
+with Ada.Text_IO; use Ada.Text_IO;
+package body DInst_Pkg is
+   procedure Foo is
+   begin
+      Put_Line ("hello there");
+   end;
+end;
diff --git a/gcc/testsuite/gnat.dg/dinst_pkg.ads b/gcc/testsuite/gnat.dg/dinst_pkg.ads
new file mode 100644
index 0000000000000..d22afdbcd6af5
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/dinst_pkg.ads
@@ -0,0 +1,4 @@
+generic
+package DInst_Pkg is
+   procedure Foo;
+end;
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 6a16ce546cb04..1c75d805566cd 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -1813,6 +1813,8 @@ copy_bb (copy_body_data *id, basic_block bb,
   copy_basic_block = create_basic_block (NULL, (basic_block) prev->aux);
   copy_basic_block->count = bb->count.apply_scale (num, den);
 
+  copy_basic_block->discriminator = bb->discriminator;
+
   copy_gsi = gsi_start_bb (copy_basic_block);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index ba1750d3cf1bb..0c6fcdbe5ff8c 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -926,6 +926,14 @@ LINEMAPS_ORDINARY_MAP_AT (const line_maps *set, int index)
   return &set->info_ordinary.maps[index];
 }
 
+/* Return the index of ORD_MAP, which is an ordinary map in SET.  */
+
+inline int
+ORDINARY_MAP_INDEX (const line_map_ordinary *ord_map, const line_maps *set)
+{
+  return ord_map - LINEMAPS_ORDINARY_MAP_AT (set, 0);
+}
+
 /* Return the number of ordinary maps allocated in the line table
    SET.  */
 inline unsigned int
