Add support for new DWARF overlay operations

Another complex DWARF expression operations, that are usefull for
SIMD/SIMT like architectures are: DW_OP_LLVM_overlay and
DW_OP_LLVM_bit_overlay. These operations pop four stack entries,
where the first must be an integral that represents an overlay size,
the second must be an integral that represents a starting point of the
overlay from the base location, the third must be a location
description that represents the overlay location description and the
forth must be a location description that represents the base location
description.

Resulting composite location description contains parts from base
location description, overlayed by the overlay location description,
starting from the overlay offset, ending at a sum of the overlay offset
and overlay size.

A new test in gdb.dwarf2 called dw2-llvm-overlay has been also added to
test the support for both operations.
diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c
index 8386327..c3de7e7 100644
--- a/gdb/compile/compile-loc2c.c
+++ b/gdb/compile/compile-loc2c.c
@@ -370,6 +370,11 @@
 	  stack_depth -= 2;
 	  break;
 
+	case DW_OP_LLVM_overlay:
+	case DW_OP_LLVM_bit_overlay:
+	  stack_depth -= 3;
+	  break;
+
 	case DW_OP_LLVM_extend:
 	case DW_OP_LLVM_piece_end:
 	case DW_OP_LLVM_offset_constu:
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 66bb8dd..c9c8f3a 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -2837,6 +2837,21 @@
   void create_select_composite (const loc_offset &piece_size,
 				ULONGEST pieces_count);
 
+  /* It pops two stack entries.  First must be a location description
+     that represents the overlay location description.  The Second
+     must be a location description that represents the base location
+     description.  The OVERLAY_SIZE represents the size of the overlay
+     piece of the composite and the OVERLAY_OFFSET represent a starting
+     point of the overlay from the base location.
+
+     A complete composite location description created with parts from
+     base location description, overlayed by the overlay location
+     description, starting from the overlay offset, ending at
+     a sum of the overlay offset and overlay size, is pushed
+     on top of the DWARF stack.  */
+  void create_overlay_composite (loc_offset overlay_size,
+				 loc_offset overlay_offset);
+
   /* The engine for the expression evaluator.  Using the context in this
      object, evaluate the expression between OP_PTR and OP_END.  */
   void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
@@ -3279,6 +3294,38 @@
 }
 
 void
+dwarf_expr_context::create_overlay_composite (loc_offset overlay_size,
+					      loc_offset overlay_offset)
+{
+  gdbarch *arch = this->m_per_objfile->objfile->arch ();
+
+  if (stack_empty_p ())
+    ill_formed_expression ();
+
+  dwarf_location_up overlay = to_location (pop (), arch);
+
+  if (stack_empty_p ())
+    ill_formed_expression ();
+
+  dwarf_location_up base = to_location (pop (), arch);
+
+  std::unique_ptr<dwarf_composite> composite
+    = make_unique<dwarf_composite> (arch, this->m_per_cu);
+
+  composite->add_piece (std::move (base->slice (0, overlay_offset)),
+			overlay_offset);
+  composite->add_piece (std::move (overlay), overlay_size);
+
+  loc_offset end_offset = overlay_offset + overlay_size;
+  loc_offset end_size = base->size () - end_offset;
+
+  composite->add_piece
+    (std::move (base->slice (end_offset, end_size)), end_size);
+  composite->set_completed (true);
+  push (std::move (composite));
+}
+
+void
 dwarf_expr_context::eval (const gdb_byte *addr, size_t len)
 {
   int old_recursion_depth = this->m_recursion_depth;
@@ -4540,6 +4587,37 @@
 	    break;
 	  }
 
+	case DW_OP_LLVM_overlay:
+	case DW_OP_LLVM_bit_overlay:
+	  {
+	    if (stack_empty_p ())
+	      ill_formed_expression ();
+
+	    dwarf_value_up overlay_size_val
+	      = to_value (pop (), address_type);
+	    dwarf_require_integral (overlay_size_val->type ());
+	    LONGEST overlay_size = overlay_size_val->to_long ();
+
+	    if (stack_empty_p () || overlay_size < 0)
+	      ill_formed_expression ();
+
+	    dwarf_value_up overlay_offset_val
+	       = to_value (pop (), address_type);
+	    dwarf_require_integral (overlay_offset_val->type ());
+	    LONGEST overlay_offset = overlay_offset_val->to_long ();
+
+	    if (overlay_offset < 0)
+	      ill_formed_expression ();
+
+	    if (op == DW_OP_LLVM_overlay)
+	      create_overlay_composite ({(ULONGEST) overlay_size, 0},
+					{(ULONGEST) overlay_offset, 0});
+	    else
+	      create_overlay_composite ((ULONGEST) overlay_size,
+					(ULONGEST) overlay_offset);
+	    break;
+	  }
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 071baad..c7aa8de 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1928,6 +1928,8 @@
 	case DW_OP_LLVM_bit_offset:
 	case DW_OP_LLVM_undefined:
 	case DW_OP_LLVM_piece_end:
+	case DW_OP_LLVM_overlay:
+	case DW_OP_LLVM_bit_overlay:
 	  break;
 
 	case DW_OP_form_tls_address:
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-overlay.c b/gdb/testsuite/gdb.dwarf2/dw2-llvm-overlay.c
new file mode 100644
index 0000000..be496bd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-overlay.c
@@ -0,0 +1,33 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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/>.  */
+
+unsigned buff[] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+void foo (unsigned dst[], unsigned src[], int len)
+{
+  asm volatile ("foo_label: .globl foo_label");
+  for (int i = 0; i < len; ++i)
+    dst[i] += src[i];
+}
+
+int
+main (void)
+{
+  asm volatile ("main_label: .globl main_label");
+  foo (buff, buff, 1);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-overlay.exp b/gdb/testsuite/gdb.dwarf2/dw2-llvm-overlay.exp
new file mode 100644
index 0000000..c840745
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-overlay.exp
@@ -0,0 +1,213 @@
+# Copyright (C) 2022 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 the new DW_OP_LLVM_overlay operation.
+#
+# The test uses a composite location description, where variable buff
+# address is used as a base location and a reg1 is used as an overlay
+# location.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum {0 1}
+
+if { [is_aarch64_target] } {
+    set regname {x0 x1}
+} elseif { [is_aarch32_target]
+	   || [istarget "s390*-*-*" ]
+	   || [istarget "powerpc*-*-*"]
+	   || [istarget "rs6000*-*-aix*"] } {
+    set regname {r0 r1}
+} elseif { [is_x86_like_target] } {
+    set regname {eax ecx}
+} elseif { [is_amd64_regs_target] } {
+    set regname {rax rdx}
+} else {
+    verbose "Skipping $gdb_test_file_name."
+    return
+}
+
+standard_testfile .c -dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global dwarf_regnum regname srcdir subdir srcfile
+    set buff_src [gdb_target_symbol buff]
+
+    set foo_result [function_range foo ${srcdir}/${subdir}/${srcfile}]
+    set foo_start [lindex $foo_result 0]
+    set foo_length [lindex $foo_result 1]
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_name $srcfile}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels int_type_label uint_type_label array_type_label
+
+	    uint_type_label: DW_TAG_base_type {
+		{DW_AT_name "uint32_t"}
+		{DW_AT_encoding @DW_ATE_unsigned}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    int_type_label: DW_TAG_base_type {
+		{DW_AT_name "int"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    array_type_label: DW_TAG_array_type {
+		{DW_AT_type :$uint_type_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_upper_bound 7 DW_FORM_udata}
+		}
+	    }
+
+	    DW_TAG_subprogram {
+		{DW_AT_name foo}
+		{DW_AT_low_pc $foo_start addr}
+		{DW_AT_high_pc $foo_length data8}
+	    } {
+
+		DW_TAG_variable {
+		    {DW_AT_name dst_v1}
+		    {DW_AT_type :$array_type_label}
+		    {DW_AT_location {
+			# 1. Memory location description of dst elements located in memory:
+			DW_OP_addr $buff_src
+
+			# 2. Register location description of element dst\[i\] is located in a register:
+			DW_OP_regx [lindex $dwarf_regnum 1]
+
+			# 3. Offset of the register within the memory of dst:
+			DW_OP_bregx [lindex $dwarf_regnum 0] 0
+			DW_OP_lit4
+			DW_OP_mul
+
+			# 4. The size of the register element:
+			DW_OP_lit4
+
+			# 5. Make a composite location description for dst that is the memory #1 with
+			#    the register #2 positioned as an overlay at offset #3 of size #4:
+			DW_OP_LLVM_overlay
+		    } SPECIAL_expr}
+		}
+
+		DW_TAG_variable {
+		    {DW_AT_name dst_v2}
+		    {DW_AT_type :$array_type_label}
+		    {DW_AT_location {
+			# 1. Memory location description of dst elements located in memory:
+			DW_OP_addr $buff_src
+
+			# 2. Register location description of element dst\[i\] is located in a register:
+			DW_OP_regx [lindex $dwarf_regnum 1]
+
+			# 3. Offset of the register within the memory of dst:
+			DW_OP_bregx [lindex $dwarf_regnum 0] 0
+			DW_OP_lit4
+			DW_OP_mul
+
+			# 4. The size of the register element:
+			DW_OP_lit4
+
+			# 5. Make a composite location description for dst that is the memory #1 with
+			#    the register #2 positioned as an overlay at offset #3 of size #4:
+			DW_OP_LLVM_bit_overlay
+		    } SPECIAL_expr}
+		}
+
+		DW_TAG_variable {
+		    {DW_AT_name src}
+		    {DW_AT_type :$array_type_label}
+		    {DW_AT_location {
+			DW_OP_addr $buff_src
+		    } SPECIAL_expr}
+		}
+
+		DW_TAG_variable {
+		    {DW_AT_name i}
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_location {
+			DW_OP_regx [lindex $dwarf_regnum 0]
+		    } SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+     [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test "break foo" "Breakpoint.*at.*" "break at function foo"
+gdb_test "continue"  "Continuing\\..*Breakpoint \[0-9\]+,.*foo \\(\\).*" \
+	 "continue to foo"
+
+gdb_test_no_output "set var \$[lindex $regname 0] = 0x0" "init reg 0"
+gdb_test_no_output "set var \$[lindex $regname 1] = 0xdeadbeef" "init reg 1"
+
+# gdb_interact
+
+# Determine byte order.
+set endian [get_endianness]
+
+switch $endian {
+	little {set val_v1 "0xdeadbeef, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7"}
+	big {set val_v1 "0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xdeadbeef"}
+}
+
+gdb_test "print/x dst_v1" " = \\{${val_v1}\\}" "dst_v1 print i = 0"
+
+switch $endian {
+	little {set val_v2 "0xf, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7"}
+	big {set val_v2 "0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xf"}
+}
+
+gdb_test "print/x dst_v2" " = \\{${val_v2}\\}" "dst_v2 print i = 0"
+
+gdb_test_no_output "set var i = 0x2" "init reg 0 to 2"
+
+switch $endian {
+	little {set val_v1 "0x0, 0x1, 0xdeadbeef, 0x3, 0x4, 0x5, 0x6, 0x7"}
+	big {set val_v1 "0x7, 0x6, 0x5, 0x4, 0x3, 0xdeadbeef, 0x1, 0x0"}
+}
+
+gdb_test "print/x dst_v1" " = \\{${val_v1}\\}" "dst_v1 print i = 2"
+
+switch $endian {
+	little {set val_v2 "0xf00, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7"}
+	big {set val_v2 "0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xf00"}
+}
+
+gdb_test "print/x dst_v2" " = \\{${val_v2}\\}" "dst_v2 print i = 2"
diff --git a/include/dwarf2.def b/include/dwarf2.def
index abc1125..df91a0e 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -712,6 +712,8 @@
 DW_OP_DUP (DW_OP_LLVM_piece_end, 0xea)
 DW_OP (DW_OP_LLVM_extend, 0xeb)
 DW_OP (DW_OP_LLVM_select_bit_piece, 0xec)
+DW_OP (DW_OP_LLVM_bit_overlay, 0xed)
+DW_OP (DW_OP_LLVM_overlay, 0xee)
 DW_END_OP
 
 DW_FIRST_ATE (DW_ATE_void, 0x0)