[binutils-gdb] Make location expressions be code in DWARF assembler

Tom Tromey tromey@sourceware.org
Mon Oct 6 15:31:13 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=c44edec047d180a0b8e66f9f99372136cb62abd0

commit c44edec047d180a0b8e66f9f99372136cb62abd0
Author: Tom Tromey <tromey@adacore.com>
Date:   Tue Sep 16 14:33:15 2025 -0600

    Make location expressions be code in DWARF assembler
    
    Currently the DWARF assembler implements manual parsing for location
    expressions.  With a recent refactoring, this lead to the use of
    [subst] in a number of places.
    
    Following the same logic as the DW_AT_* change, this patch changes
    location expressions to simply be nested Tcl code.  This avoids the
    need for subst and also allows more complex logic, should that ever be
    needed.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33402

Diff:
---
 gdb/testsuite/gdb.cp/incomplete-type-overload.exp  |  20 +-
 gdb/testsuite/gdb.dlang/watch-loc.exp              |   6 +-
 .../DW_OP_piece_with_DW_OP_GNU_uninit.exp          |   8 +-
 gdb/testsuite/gdb.dwarf2/ada-array-bound.exp       |   6 +-
 gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp    |   4 +-
 gdb/testsuite/gdb.dwarf2/arr-opt-out.exp           |   4 +-
 .../assign-variable-value-to-register.exp          |   4 +-
 gdb/testsuite/gdb.dwarf2/count.exp                 |   4 +-
 gdb/testsuite/gdb.dwarf2/cpp-linkage-name.exp      |   4 +-
 gdb/testsuite/gdb.dwarf2/data-loc.exp              |  24 +-
 gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp      |  36 +-
 gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp     |   8 +-
 gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp       |  20 +-
 gdb/testsuite/gdb.dwarf2/dw2-ifort-parameter.exp   |   8 +-
 .../gdb.dwarf2/dw2-inline-with-lexical-scope.exp   |   8 +-
 .../gdb.dwarf2/dw2-lexical-block-bare.exp          |   4 +-
 .../gdb.dwarf2/dw2-namespaceless-anonymous.exp     |   4 +-
 gdb/testsuite/gdb.dwarf2/dw2-noloc.exp             |  32 +-
 gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp       |   4 +-
 .../gdb.dwarf2/dw2-unusual-field-names.exp         |   8 +-
 gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp   |   4 +-
 gdb/testsuite/gdb.dwarf2/dyn-type-unallocated.exp  |   4 +-
 gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp    |   4 +-
 gdb/testsuite/gdb.dwarf2/dynarr-ptr.exp            |  24 +-
 gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp  |   4 +-
 gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp      |   4 +-
 gdb/testsuite/gdb.dwarf2/fission-relative-dwo.exp  |   4 +-
 gdb/testsuite/gdb.dwarf2/implptr-64bit.exp         |  20 +-
 gdb/testsuite/gdb.dwarf2/implptr-optimized-out.exp |   6 +-
 gdb/testsuite/gdb.dwarf2/implptrconst.exp          |  12 +-
 gdb/testsuite/gdb.dwarf2/implptrpiece.exp          |  24 +-
 gdb/testsuite/gdb.dwarf2/implref-array.exp         |   8 +-
 gdb/testsuite/gdb.dwarf2/implref-const.exp         |   4 +-
 gdb/testsuite/gdb.dwarf2/implref-global.exp        |   8 +-
 gdb/testsuite/gdb.dwarf2/implref-struct.exp        |  12 +-
 gdb/testsuite/gdb.dwarf2/intbits.exp               |  36 +-
 .../gdb.dwarf2/locexpr-data-member-location.exp    |  13 +-
 gdb/testsuite/gdb.dwarf2/missing-type-name.exp     |   8 +-
 .../gdb.dwarf2/negative-data-member-location.exp   |   4 +-
 gdb/testsuite/gdb.dwarf2/nonvar-access.exp         |  54 +--
 gdb/testsuite/gdb.dwarf2/opt-out-not-implptr.exp   |   8 +-
 gdb/testsuite/gdb.dwarf2/staticvirtual.exp         |   4 +-
 .../gdb.dwarf2/symbol_needs_eval_fail.exp          |   4 +-
 .../gdb.dwarf2/symbol_needs_eval_timeout.exp       |  12 +-
 gdb/testsuite/gdb.dwarf2/var-access.exp            |  90 ++---
 gdb/testsuite/gdb.dwarf2/varval.exp                |  64 ++--
 gdb/testsuite/gdb.dwarf2/void-type.exp             |   8 +-
 gdb/testsuite/gdb.python/make-visualizer.exp       |   6 +-
 gdb/testsuite/gdb.trace/entry-values.exp           |   8 +-
 gdb/testsuite/lib/dwarf.exp                        | 375 +++++++++++----------
 50 files changed, 549 insertions(+), 503 deletions(-)

diff --git a/gdb/testsuite/gdb.cp/incomplete-type-overload.exp b/gdb/testsuite/gdb.cp/incomplete-type-overload.exp
index efb1fd34f4c..007c238eb69 100644
--- a/gdb/testsuite/gdb.cp/incomplete-type-overload.exp
+++ b/gdb/testsuite/gdb.cp/incomplete-type-overload.exp
@@ -120,32 +120,36 @@ Dwarf::assemble ${asm_file} {
 	DW_TAG_variable {
 	    DW_AT_name "comp"
 	    DW_AT_type :$complete_label
-	    DW_AT_location [subst {DW_OP_addr [gdb_target_symbol "comp"]}] \
-		SPECIAL_expr
+	    DW_AT_location {
+		DW_OP_addr [gdb_target_symbol "comp"]
+	    } SPECIAL_expr
 	    DW_AT_external 1 DW_FORM_flag
 	}
 
 	DW_TAG_variable {
 	    DW_AT_name "cp"
 	    DW_AT_type :$ptr_comp_label
-	    DW_AT_location [subst {DW_OP_addr [gdb_target_symbol "cp"]}] \
-		SPECIAL_expr
+	    DW_AT_location {
+		DW_OP_addr [gdb_target_symbol "cp"]
+	    } SPECIAL_expr
 	    DW_AT_external 1 DW_FORM_flag
 	}
 
 	DW_TAG_variable {
 	    DW_AT_name "inc"
 	    DW_AT_type :$ptr_inc_label
-	    DW_AT_location [subst {DW_OP_addr [gdb_target_symbol "inc"]}] \
-		SPECIAL_expr
+	    DW_AT_location {
+		DW_OP_addr [gdb_target_symbol "inc"]
+	    } SPECIAL_expr
 	    DW_AT_external 1 DW_FORM_flag
 	}
 
 	DW_TAG_variable {
 	    DW_AT_name "ip"
 	    DW_AT_type :$ptr_int_label
-	    DW_AT_location [subst {DW_OP_addr [gdb_target_symbol "ip"]}] \
-		SPECIAL_expr
+	    DW_AT_location {
+		DW_OP_addr [gdb_target_symbol "ip"]
+	    } SPECIAL_expr
 	    DW_AT_external 1 DW_FORM_flag
 	}
 
diff --git a/gdb/testsuite/gdb.dlang/watch-loc.exp b/gdb/testsuite/gdb.dlang/watch-loc.exp
index 7cfc539f6a3..d16b779b57c 100644
--- a/gdb/testsuite/gdb.dlang/watch-loc.exp
+++ b/gdb/testsuite/gdb.dlang/watch-loc.exp
@@ -48,9 +48,9 @@ Dwarf::assemble $asm_file {
 		tag_variable {
 		    DW_AT_name my_data
 		    DW_AT_type :$watch_struct_label
-		    DW_AT_location [subst {
-			addr [gdb_target_symbol my_data]
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr [gdb_target_symbol my_data]
+		    } SPECIAL_expr
 		    DW_AT_external 1 flag
 		}
 
diff --git a/gdb/testsuite/gdb.dwarf2/DW_OP_piece_with_DW_OP_GNU_uninit.exp b/gdb/testsuite/gdb.dwarf2/DW_OP_piece_with_DW_OP_GNU_uninit.exp
index 291bc83e667..d812b0bcbb3 100644
--- a/gdb/testsuite/gdb.dwarf2/DW_OP_piece_with_DW_OP_GNU_uninit.exp
+++ b/gdb/testsuite/gdb.dwarf2/DW_OP_piece_with_DW_OP_GNU_uninit.exp
@@ -49,23 +49,23 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name i64_var
 		DW_AT_type :$i64_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_constu $::c64
 		    DW_OP_stack_value
 		    DW_OP_GNU_uninit
 		    DW_OP_piece 8
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name i32_var
 		DW_AT_type :$i32_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_constu $::c32
 		    DW_OP_stack_value
 		    DW_OP_GNU_uninit
 		    DW_OP_piece 4
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp b/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp
index 779ce009e21..b85777b2fea 100644
--- a/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp
+++ b/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp
@@ -72,9 +72,9 @@ Dwarf::assemble $asm_file {
 		DW_AT_name "value"
 		DW_AT_type :$struct
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location \
-		    [subst {DW_OP_addr [gdb_target_symbol "our_data"]}] \
-		    SPECIAL_expr
+		DW_AT_location {
+		    DW_OP_addr [gdb_target_symbol "our_data"]
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp b/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp
index a7544265da8..a4b6a956298 100644
--- a/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp
+++ b/gdb/testsuite/gdb.dwarf2/ada-valprint-error.exp
@@ -92,9 +92,9 @@ Dwarf::assemble $asm_file {
             DW_TAG_variable {
 		DW_AT_name fd__global
 		DW_AT_type :$ref_type_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol fd__global]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
             }
 
diff --git a/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp b/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp
index d3ccec8af67..38840aa1e4d 100644
--- a/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp
+++ b/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp
@@ -70,9 +70,9 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name the_table
 		DW_AT_type :$array_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol global_array]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/assign-variable-value-to-register.exp b/gdb/testsuite/gdb.dwarf2/assign-variable-value-to-register.exp
index 1ebd622f430..9cbbfe9c601 100644
--- a/gdb/testsuite/gdb.dwarf2/assign-variable-value-to-register.exp
+++ b/gdb/testsuite/gdb.dwarf2/assign-variable-value-to-register.exp
@@ -56,9 +56,9 @@ Dwarf::assemble $dwarf_file {
 		DW_TAG_variable {
 		    DW_AT_name foo
 		    DW_AT_type :$float_label
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_regx $::st0_dwarf_regnum
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/count.exp b/gdb/testsuite/gdb.dwarf2/count.exp
index 92b0ebf194f..23b3c6946f2 100644
--- a/gdb/testsuite/gdb.dwarf2/count.exp
+++ b/gdb/testsuite/gdb.dwarf2/count.exp
@@ -81,8 +81,8 @@ Dwarf::assemble $asm_file {
 	    vla_length_label:
 	    DW_TAG_variable {
 		DW_AT_location {
-		    lit6
-		    stack_value
+		    DW_OP_lit6
+		    DW_OP_stack_value
 		} SPECIAL_expr
 		DW_AT_name "__vla_array_length"
 		DW_AT_type :$long_unsigned_int_label
diff --git a/gdb/testsuite/gdb.dwarf2/cpp-linkage-name.exp b/gdb/testsuite/gdb.dwarf2/cpp-linkage-name.exp
index a21e980fcf0..44d31f6aebd 100644
--- a/gdb/testsuite/gdb.dwarf2/cpp-linkage-name.exp
+++ b/gdb/testsuite/gdb.dwarf2/cpp-linkage-name.exp
@@ -72,9 +72,9 @@ Dwarf::assemble $asm_file {
 		DW_AT_type :$b_l
 		DW_AT_external 1 flag
 		DW_AT_name global_var
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol global_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/data-loc.exp b/gdb/testsuite/gdb.dwarf2/data-loc.exp
index 615d04bfaf5..7630dcc20a1 100644
--- a/gdb/testsuite/gdb.dwarf2/data-loc.exp
+++ b/gdb/testsuite/gdb.dwarf2/data-loc.exp
@@ -60,19 +60,19 @@ Dwarf::assemble $asm_file {
 	    } {
 		DW_TAG_subrange_type {
 		    DW_AT_type	      :$integer_label
-		    DW_AT_lower_bound [subst {
+		    DW_AT_lower_bound {
 			DW_OP_push_object_address
 			DW_OP_plus_uconst $voidp_size
 			DW_OP_deref
 			DW_OP_deref_size $int_size
-		    }] SPECIAL_expr
-		    DW_AT_upper_bound [subst {
+		    } SPECIAL_expr
+		    DW_AT_upper_bound {
 			DW_OP_push_object_address
 			DW_OP_plus_uconst $voidp_size
 			DW_OP_deref
 			DW_OP_plus_uconst $int_size
 			DW_OP_deref_size $int_size
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	    array_ptr_label: DW_TAG_typedef {
@@ -82,33 +82,33 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name foo__three
 		DW_AT_type :$array_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_1]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	    DW_TAG_variable {
 		DW_AT_name foo__three_tdef
 		DW_AT_type :$array_ptr_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_1]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	    DW_TAG_variable {
 		DW_AT_name foo__five
 		DW_AT_type :$array_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_2]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	    DW_TAG_variable {
 		DW_AT_name foo__five_tdef
 		DW_AT_type :$array_ptr_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_2]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp
index 2dda0e5258c..07f5d7401b3 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp
@@ -80,12 +80,16 @@ Dwarf::assemble $asm_file {
 		formal_parameter {
 		    DW_AT_name I
 		    DW_AT_type :$int_label
-		    DW_AT_location [subst {addr $global_I}] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $global_I
+		    } SPECIAL_expr
 		}
 		formal_parameter {
 		    DW_AT_name J
 		    DW_AT_type :$int_label
-		    DW_AT_location [subst {addr $global_J}] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $global_J
+		    } SPECIAL_expr
 		}
 		entry_point {
 		    DW_AT_name foo
@@ -96,12 +100,16 @@ Dwarf::assemble $asm_file {
 		    formal_parameter {
 			DW_AT_name J
 			DW_AT_type :$int_label
-			DW_AT_location [subst {addr $global_J}] SPECIAL_expr
+			DW_AT_location {
+			    DW_OP_addr $global_J
+			} SPECIAL_expr
 		    }
 		    formal_parameter {
 			DW_AT_name K
 			DW_AT_type :$int_label
-			DW_AT_location [subst {addr $global_K}] SPECIAL_expr
+			DW_AT_location {
+			    DW_OP_addr $global_K
+			} SPECIAL_expr
 		    }
 		}
 		entry_point {
@@ -113,7 +121,9 @@ Dwarf::assemble $asm_file {
 		    formal_parameter {
 			DW_AT_name J
 			DW_AT_type :$int_label
-			DW_AT_location [subst {addr $global_J}] SPECIAL_expr
+			DW_AT_location {
+			    DW_OP_addr $global_J
+			} SPECIAL_expr
 		    }
 		}
 	    }
@@ -142,12 +152,16 @@ Dwarf::assemble $asm_file {
 		formal_parameter {
 		    DW_AT_name I
 		    DW_AT_type :$int2_label
-		    DW_AT_location [subst {addr $global_I}] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $global_I
+		    } SPECIAL_expr
 		}
 		formal_parameter {
 		    DW_AT_name J
 		    DW_AT_type :$int2_label
-		    DW_AT_location [subst {addr $global_J}] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $global_J
+		    } SPECIAL_expr
 		}
 		entry_point {
 		    DW_AT_name fooso
@@ -158,12 +172,16 @@ Dwarf::assemble $asm_file {
 		    formal_parameter {
 			DW_AT_name J
 			DW_AT_type :$int2_label
-			DW_AT_location [subst {addr $global_J}] SPECIAL_expr
+			DW_AT_location {
+			    DW_OP_addr $global_J
+			} SPECIAL_expr
 		    }
 		    formal_parameter {
 			DW_AT_name K
 			DW_AT_type :$int2_label
-			DW_AT_location [subst {addr $global_K}] SPECIAL_expr
+			DW_AT_location {
+			    DW_OP_addr $global_K
+			} SPECIAL_expr
 		    }
 		}
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp
index 7dd859ccef6..2945e1c0bc3 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-value-2.exp
@@ -63,11 +63,11 @@ Dwarf::assemble $asm_file {
 		DW_TAG_variable {
 		     DW_AT_name argc
 		     DW_AT_type :$integer
-		     DW_AT_location [subst {
+		     DW_AT_location {
 			DW_OP_entry_value {
 			    DW_OP_regx $::dwarf_regnum
 			}
-		    }] SPECIAL_expr
+		     } SPECIAL_expr
 		}
 	    }
 
@@ -79,13 +79,13 @@ Dwarf::assemble $asm_file {
 		DW_TAG_variable {
 		     DW_AT_name foo
 		     DW_AT_type :$integer
-		     DW_AT_location [subst {
+		     DW_AT_location {
 			DW_OP_entry_value {
 			    DW_OP_bregx $::dwarf_regnum 0
 			    DW_OP_deref_size 4
 			}
 			DW_OP_stack_value
-		    }] SPECIAL_expr
+		     } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp b/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp
index e8a80830931..66adcc8c7ac 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp
@@ -41,18 +41,18 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name pck__fp1_var
 		DW_AT_type :$fp1_base_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol pck__fp1_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name pck__fp1_var2
 		DW_AT_type :$fp1_base_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol pck__fp1_var2]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
@@ -66,9 +66,9 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name pck__fp2_var
 		DW_AT_type :$fp2_base_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol pck__fp2_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
@@ -87,9 +87,9 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name pck__fp3_var
 		DW_AT_type :$fp3_base_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol pck__fp3_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
@@ -103,9 +103,9 @@ Dwarf::assemble $asm_file {
 	     DW_TAG_variable {
 		 DW_AT_name pck__fp1_range_var
 		 DW_AT_type :$fp1_range_type
-		 DW_AT_location [subst {
+		 DW_AT_location {
 		     DW_OP_addr [gdb_target_symbol pck__fp1_range_var]
-		 }] SPECIAL_expr
+		 } SPECIAL_expr
 		 DW_AT_external 1 flag
 	     }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ifort-parameter.exp b/gdb/testsuite/gdb.dwarf2/dw2-ifort-parameter.exp
index 0a1f5d8f710..a77b4307274 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ifort-parameter.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ifort-parameter.exp
@@ -47,10 +47,10 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name param
 		    DW_AT_variable_parameter 1 flag
 		    DW_AT_type :$int_label
-		    DW_AT_location [subst {
-			addr [gdb_target_symbol ptr]
-			deref
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr [gdb_target_symbol ptr]
+			DW_OP_deref
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-with-lexical-scope.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-with-lexical-scope.exp
index 2674c17eb6b..8eaa3a1dcc9 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-inline-with-lexical-scope.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-with-lexical-scope.exp
@@ -87,7 +87,9 @@ Dwarf::assemble $asm_file {
 		} {
 		    DW_TAG_variable {
 			DW_AT_abstract_origin %$num_label
-			DW_AT_location [subst {addr $global_num_addr}] SPECIAL_expr
+			DW_AT_location {
+			    DW_OP_addr $global_num_addr
+			} SPECIAL_expr
 		    }
 		    lexical_block {
 			DW_AT_low_pc scope_label1 addr
@@ -95,7 +97,9 @@ Dwarf::assemble $asm_file {
 		    } {
 			DW_TAG_variable {
 			    DW_AT_abstract_origin %$value_label
-			    DW_AT_location [subst {addr $global_value_addr}] SPECIAL_expr
+			    DW_AT_location {
+				DW_OP_addr $global_value_addr
+			    } SPECIAL_expr
 			}
 		    }
 		}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-lexical-block-bare.exp b/gdb/testsuite/gdb.dwarf2/dw2-lexical-block-bare.exp
index a973a11a66b..88fda0d42db 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-lexical-block-bare.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-lexical-block-bare.exp
@@ -44,9 +44,9 @@ Dwarf::assemble $asm_file {
 			DW_AT_name testvar
 			DW_AT_type :$integer_label
 			DW_AT_external 1 flag
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_addr [gdb_target_symbol main]
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		}
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-namespaceless-anonymous.exp b/gdb/testsuite/gdb.dwarf2/dw2-namespaceless-anonymous.exp
index ebbe6c05f91..b0e07ca3588 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-namespaceless-anonymous.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-namespaceless-anonymous.exp
@@ -43,9 +43,9 @@ Dwarf::assemble $asm_file {
 		DW_TAG_variable {
 		    DW_AT_name v
 		    DW_AT_linkage_name _ZN12_GLOBAL__N_11vE
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol _ZN12_GLOBAL__N_11vE]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		    DW_AT_type :$myint
 		}
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-noloc.exp b/gdb/testsuite/gdb.dwarf2/dw2-noloc.exp
index 00e2863cd16..1ad4b45b3d8 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-noloc.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-noloc.exp
@@ -68,16 +68,16 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name file_locaddr_resolvable
 		DW_AT_type :$integer_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol file_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	    DW_TAG_variable {
 		DW_AT_name file_locaddr_unresolvable
 		DW_AT_type :$integer_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol file_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
@@ -106,17 +106,17 @@ Dwarf::assemble $asm_file {
 		DW_AT_name file_extern_locaddr_resolvable
 		DW_AT_type :$integer_label
 		DW_AT_external 1 flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol file_extern_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	    DW_TAG_variable {
 		DW_AT_name file_extern_locaddr_unresolvable
 		DW_AT_type :$integer_label
 		DW_AT_external 1 flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol file_extern_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
@@ -140,16 +140,16 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name main_local_locaddr_resolvable
 		DW_AT_type :$integer_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol main_local_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	    DW_TAG_variable {
 		DW_AT_name main_local_locaddr_unresolvable
 		DW_AT_type :$integer_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol main_local_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
@@ -178,17 +178,17 @@ Dwarf::assemble $asm_file {
 		DW_AT_name main_extern_locaddr_resolvable
 		DW_AT_type :$integer_label
 		DW_AT_external 1 flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol main_extern_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	    DW_TAG_variable {
 		DW_AT_name main_extern_locaddr_unresolvable
 		DW_AT_type :$integer_label
 		DW_AT_external 1 flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol main_extern_locaddr_resolvable]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
index 6fc5ba52fd0..5e452555a76 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
@@ -88,7 +88,9 @@ proc do_test {suffix} {
 		    DW_AT_name e
 		    DW_AT_external 1 flag
 		    DW_AT_type :$volatile_label
-		    DW_AT_location [subst {addr $e_var}] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $e_var
+		    } SPECIAL_expr
 		}
 		subprogram {
 		    DW_AT_external 1 flag
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-unusual-field-names.exp b/gdb/testsuite/gdb.dwarf2/dw2-unusual-field-names.exp
index f5f22e61abb..3961abdce26 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-unusual-field-names.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-unusual-field-names.exp
@@ -89,18 +89,18 @@ proc run_test { field_name } {
 		DW_TAG_variable {
 		    DW_AT_name obj
 		    DW_AT_type :$stype
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol obj]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		    DW_AT_external 1 flag
 		}
 
 		DW_TAG_variable {
 		    DW_AT_name ptr
 		    DW_AT_type :$ptype
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol ptr]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		    DW_AT_external 1 flag
 		}
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp b/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp
index 122ee614ad0..11c63292ca0 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp
@@ -69,9 +69,9 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name global_var DW_FORM_strp
 		DW_AT_type :$struct_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol global_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
diff --git a/gdb/testsuite/gdb.dwarf2/dyn-type-unallocated.exp b/gdb/testsuite/gdb.dwarf2/dyn-type-unallocated.exp
index 91beb791d2d..1f95a6574ae 100644
--- a/gdb/testsuite/gdb.dwarf2/dyn-type-unallocated.exp
+++ b/gdb/testsuite/gdb.dwarf2/dyn-type-unallocated.exp
@@ -104,9 +104,9 @@ Dwarf::assemble $asm_file {
 	    }
 
 	    DW_TAG_variable {
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol dyn_object]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_name "dyn_object"
 		DW_AT_type :$array_type_label
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp
index a5cf54df80c..072dd8974a5 100644
--- a/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp
+++ b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp
@@ -78,9 +78,9 @@ Dwarf::assemble $asm_file {
 		DW_AT_name "value"
 		DW_AT_type :$struct
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "our_data"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/dynarr-ptr.exp b/gdb/testsuite/gdb.dwarf2/dynarr-ptr.exp
index aeb0aa2ab6b..5312c96c259 100644
--- a/gdb/testsuite/gdb.dwarf2/dynarr-ptr.exp
+++ b/gdb/testsuite/gdb.dwarf2/dynarr-ptr.exp
@@ -57,18 +57,18 @@ Dwarf::assemble $asm_file {
 	    } {
 		DW_TAG_subrange_type {
 		    DW_AT_type	      :$integer_label
-		    DW_AT_lower_bound [subst {
+		    DW_AT_lower_bound {
 			DW_OP_push_object_address
 			DW_OP_const1u [expr {2 * $int_size}]
 			DW_OP_minus
 			DW_OP_deref_size $int_size
-		    }] SPECIAL_expr
-		    DW_AT_upper_bound [subst {
+		    } SPECIAL_expr
+		    DW_AT_upper_bound {
 			DW_OP_push_object_address
 			DW_OP_const1u $int_size
 			DW_OP_minus
 			DW_OP_deref_size $int_size
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	    array_ptr_label: DW_TAG_pointer_type {
@@ -82,33 +82,33 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name foo__three_ptr
 		DW_AT_type :$array_ptr_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_1_ptr]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	    DW_TAG_variable {
 		DW_AT_name foo__three_ptr_tdef
 		DW_AT_type :$array_typedef_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_1_ptr]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	    DW_TAG_variable {
 		DW_AT_name foo__five_ptr
 		DW_AT_type :$array_ptr_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_2_ptr]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	    DW_TAG_variable {
 		DW_AT_name foo__five_ptr_tdef
 		DW_AT_type :$array_typedef_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol table_2_ptr]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
index e5ef01afd1c..51344207de5 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
@@ -77,9 +77,9 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name global_var
 		DW_AT_type :$struct_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_GNU_addr_index [gdb_target_symbol global_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
index 13fb3bf27d0..e252c866f71 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
@@ -71,9 +71,9 @@ Dwarf::assemble $asm_file_1 {
 		DW_TAG_formal_parameter {
 		    DW_AT_name arg
 		    DW_AT_type :$int4_type
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_GNU_addr_index [gdb_target_symbol global_param]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/fission-relative-dwo.exp b/gdb/testsuite/gdb.dwarf2/fission-relative-dwo.exp
index b533f61d65f..e2bae0a1f7e 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-relative-dwo.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-relative-dwo.exp
@@ -74,9 +74,9 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name global_var
 		DW_AT_type :$struct_type
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_GNU_addr_index [gdb_target_symbol global_var]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
diff --git a/gdb/testsuite/gdb.dwarf2/implptr-64bit.exp b/gdb/testsuite/gdb.dwarf2/implptr-64bit.exp
index e7894dd12b2..8ceaa4e3131 100644
--- a/gdb/testsuite/gdb.dwarf2/implptr-64bit.exp
+++ b/gdb/testsuite/gdb.dwarf2/implptr-64bit.exp
@@ -82,11 +82,11 @@ proc test_1 { name dwarf_version offset_size addr_size ref_addr_size two_cu } {
 			 DW_AT_external 1 flag
 		    } {
 			DW_TAG_variable {
-			     DW_AT_name p
-			    DW_AT_location [subst {
-				GNU_implicit_pointer $variable_label 0
-			    }] SPECIAL_expr
-			     DW_AT_type :$pointer_label "DW_FORM_ref$ref_addr_size"
+			    DW_AT_name p
+			    DW_AT_location {
+				DW_OP_GNU_implicit_pointer $variable_label 0
+			    } SPECIAL_expr
+			    DW_AT_type :$pointer_label "DW_FORM_ref$ref_addr_size"
 			}
 		    }
 		}
@@ -110,11 +110,11 @@ proc test_1 { name dwarf_version offset_size addr_size ref_addr_size two_cu } {
 			 DW_AT_external 1 flag
 		    } {
 			DW_TAG_variable {
-			     DW_AT_name p
-			    DW_AT_location [subst {
-				GNU_implicit_pointer $variable_label 0
-			    }] SPECIAL_expr
-			     DW_AT_type %$pointer_label
+			    DW_AT_name p
+			    DW_AT_location {
+				DW_OP_GNU_implicit_pointer $variable_label 0
+			    } SPECIAL_expr
+			    DW_AT_type %$pointer_label
 			}
 		    }
 		}
diff --git a/gdb/testsuite/gdb.dwarf2/implptr-optimized-out.exp b/gdb/testsuite/gdb.dwarf2/implptr-optimized-out.exp
index 701c79db3dc..86a08eb5774 100644
--- a/gdb/testsuite/gdb.dwarf2/implptr-optimized-out.exp
+++ b/gdb/testsuite/gdb.dwarf2/implptr-optimized-out.exp
@@ -66,9 +66,9 @@ Dwarf::assemble $asm_file {
 
 		DW_TAG_variable {
 		    DW_AT_name p
-		    DW_AT_location [subst {
-			GNU_implicit_pointer $variable_label 0
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_GNU_implicit_pointer $variable_label 0
+		    } SPECIAL_expr
 		    DW_AT_type :$pointer_label
 		}
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/implptrconst.exp b/gdb/testsuite/gdb.dwarf2/implptrconst.exp
index 5078296cc20..3b0187682cf 100644
--- a/gdb/testsuite/gdb.dwarf2/implptrconst.exp
+++ b/gdb/testsuite/gdb.dwarf2/implptrconst.exp
@@ -67,17 +67,17 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name c
 		DW_AT_type :$ptr_label
-		DW_AT_location [subst {
-		    GNU_implicit_pointer $var_label 0
-		}] SPECIAL_expr
+		DW_AT_location {
+		    DW_OP_GNU_implicit_pointer $var_label 0
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name d
 		DW_AT_type :$ptr_label
-		DW_AT_location [subst {
-		    GNU_implicit_pointer $var_label 2
-		}] SPECIAL_expr
+		DW_AT_location {
+		    DW_OP_GNU_implicit_pointer $var_label 2
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/implptrpiece.exp b/gdb/testsuite/gdb.dwarf2/implptrpiece.exp
index 1b9dc5eb213..18d3a348600 100644
--- a/gdb/testsuite/gdb.dwarf2/implptrpiece.exp
+++ b/gdb/testsuite/gdb.dwarf2/implptrpiece.exp
@@ -78,24 +78,24 @@ Dwarf::assemble $asm_file {
 		DW_AT_name s
 		DW_AT_type :$struct_label
 		DW_AT_location {
-		    const2u 0x5678
-		    stack_value
-		    piece 2
-		    const1u 2
-		    stack_value
-		    piece 1
-		    const1u 3
-		    stack_value
-		    piece 1
+		    DW_OP_const2u 0x5678
+		    DW_OP_stack_value
+		    DW_OP_piece 2
+		    DW_OP_const1u 2
+		    DW_OP_stack_value
+		    DW_OP_piece 1
+		    DW_OP_const1u 3
+		    DW_OP_stack_value
+		    DW_OP_piece 1
 		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name p
 		DW_AT_type :$ptr_label
-		DW_AT_location [subst {
-		    GNU_implicit_pointer $var_label 2
-		}] SPECIAL_expr
+		DW_AT_location {
+		    DW_OP_GNU_implicit_pointer $var_label 2
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/implref-array.exp b/gdb/testsuite/gdb.dwarf2/implref-array.exp
index 9dec1731908..5473a430fe5 100644
--- a/gdb/testsuite/gdb.dwarf2/implref-array.exp
+++ b/gdb/testsuite/gdb.dwarf2/implref-array.exp
@@ -91,9 +91,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "array"
 		DW_AT_type :${array_label}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "array"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_subprogram {
@@ -104,9 +104,9 @@ Dwarf::assemble ${asm_file} {
 		DW_TAG_variable {
 		    DW_AT_name "ref"
 		    DW_AT_type :${ref_label}
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_GNU_implicit_pointer ${variable_label} 0
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-const.exp b/gdb/testsuite/gdb.dwarf2/implref-const.exp
index bf3892d837c..bebc3b63af7 100644
--- a/gdb/testsuite/gdb.dwarf2/implref-const.exp
+++ b/gdb/testsuite/gdb.dwarf2/implref-const.exp
@@ -83,9 +83,9 @@ Dwarf::assemble ${asm_file} {
 		DW_TAG_variable {
 		    DW_AT_name "ref"
 		    DW_AT_type :${const_label}
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_GNU_implicit_pointer ${variable_label} 0
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-global.exp b/gdb/testsuite/gdb.dwarf2/implref-global.exp
index a9397bd67bd..f4f4d0338e4 100644
--- a/gdb/testsuite/gdb.dwarf2/implref-global.exp
+++ b/gdb/testsuite/gdb.dwarf2/implref-global.exp
@@ -70,9 +70,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "global_var"
 		DW_AT_type :${int_label}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "global_var"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_subprogram {
@@ -83,9 +83,9 @@ Dwarf::assemble ${asm_file} {
 		DW_TAG_variable {
 		    DW_AT_name "ref"
 		    DW_AT_type :${ref_label}
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_GNU_implicit_pointer ${variable_label} 0
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-struct.exp b/gdb/testsuite/gdb.dwarf2/implref-struct.exp
index 450576e1eb1..b0ea0a1d722 100644
--- a/gdb/testsuite/gdb.dwarf2/implref-struct.exp
+++ b/gdb/testsuite/gdb.dwarf2/implref-struct.exp
@@ -98,18 +98,18 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "s1"
 		DW_AT_type :${struct_label}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "s1"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name "s2"
 		DW_AT_type :${struct_label}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "s2"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_subprogram {
@@ -120,9 +120,9 @@ Dwarf::assemble ${asm_file} {
 	        DW_TAG_variable {
 		    DW_AT_name "ref"
 		    DW_AT_type :${ref_label}
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_GNU_implicit_pointer ${variable_label} 0
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/intbits.exp b/gdb/testsuite/gdb.dwarf2/intbits.exp
index fc5a2ae2162..c604123516b 100644
--- a/gdb/testsuite/gdb.dwarf2/intbits.exp
+++ b/gdb/testsuite/gdb.dwarf2/intbits.exp
@@ -50,9 +50,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_i16_m1"
 		DW_AT_type :${i7_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "i16_m1"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    u1_type: DW_TAG_base_type {
@@ -68,9 +68,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_u16_1"
 		DW_AT_type :${u1_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "u16_1"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    u17_type: DW_TAG_base_type {
@@ -85,9 +85,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_u32_m2"
 		DW_AT_type :${u17_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "u32_m2"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    u31_type: DW_TAG_base_type {
@@ -102,9 +102,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_u32_1"
 		DW_AT_type :${u31_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "u32_1"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    u31_1_type: DW_TAG_base_type {
@@ -120,9 +120,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_u32_1_off"
 		DW_AT_type :${u31_1_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "u32_1_off"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    be30_1_type: DW_TAG_base_type {
@@ -138,9 +138,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_be30_1_off"
 		DW_AT_type :${be30_1_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "be30_1_off"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    u32_0_type: DW_TAG_base_type {
@@ -155,9 +155,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_u32_0"
 		DW_AT_type :${u32_0_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "u32_0"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    u0_0_type: DW_TAG_base_type {
@@ -171,9 +171,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_u0_0"
 		DW_AT_type :${u0_0_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "u32_0"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    just_bit_type: DW_TAG_base_type {
@@ -186,9 +186,9 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "v_just_bit"
 		DW_AT_type :${just_bit_type}
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "just_bit_0"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
index ba01137db5e..1664e68a4fb 100644
--- a/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
+++ b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
@@ -202,11 +202,12 @@ Dwarf::assemble ${asm_file} {
 		# chosen.
 		DW_TAG_inheritance {
 		    DW_AT_type :$class_A_label
-		    DW_AT_data_member_location [subst {
+		    DW_AT_data_member_location {
 			DW_OP_constu ${::B_a}
 			DW_OP_plus
 			DW_OP_pick 0
-			DW_OP_drop}] SPECIAL_expr
+			DW_OP_drop
+		    } SPECIAL_expr
 		    DW_AT_accessibility 1 DW_FORM_data1
 		}
 		DW_TAG_member {
@@ -230,18 +231,18 @@ Dwarf::assemble ${asm_file} {
 		DW_AT_name "g_A"
 		DW_AT_type :$class_A_label
 		DW_AT_external 1 flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "g_A"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name "g_B"
 		DW_AT_type :$class_B_label
 		DW_AT_external 1 flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "g_B"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    # We can't use MACRO_AT for the definitions of foo and bar
diff --git a/gdb/testsuite/gdb.dwarf2/missing-type-name.exp b/gdb/testsuite/gdb.dwarf2/missing-type-name.exp
index 6478991bc5a..494ea7b3560 100644
--- a/gdb/testsuite/gdb.dwarf2/missing-type-name.exp
+++ b/gdb/testsuite/gdb.dwarf2/missing-type-name.exp
@@ -78,18 +78,18 @@ Dwarf::assemble $asm_file {
 		DW_AT_name "var_a"
 		DW_AT_type :$main_type
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "var_a"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name "var_ptr"
 		DW_AT_type :$ptr_type
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "var_ptr"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp b/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp
index 48f272e5357..30c4f530af9 100644
--- a/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp
+++ b/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp
@@ -57,9 +57,9 @@ Dwarf::assemble ${asm_file} {
 	    DW_TAG_variable {
 		DW_AT_name "s"
 		DW_AT_type :$struct_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "s"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/nonvar-access.exp b/gdb/testsuite/gdb.dwarf2/nonvar-access.exp
index abf34add464..604d7a0bc7a 100644
--- a/gdb/testsuite/gdb.dwarf2/nonvar-access.exp
+++ b/gdb/testsuite/gdb.dwarf2/nonvar-access.exp
@@ -115,12 +115,12 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name def_s
 		    DW_AT_type :$struct_s_label
 		    DW_AT_location {
-			const1u 0
-			stack_value
-			bit_piece 8 0
-			const1s -1
-			stack_value
-			bit_piece 24 0
+			DW_OP_const1u 0
+			DW_OP_stack_value
+			DW_OP_bit_piece 8 0
+			DW_OP_const1s -1
+			DW_OP_stack_value
+			DW_OP_bit_piece 24 0
 		    } SPECIAL_expr
 		}
 		# Composite location: non-byte-aligned pieces.
@@ -128,12 +128,12 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name def_t
 		    DW_AT_type :$struct_t_label
 		    DW_AT_location {
-			const2s -184
-			stack_value
-			bit_piece 9 0
-			const4u 1752286
-			stack_value
-			bit_piece 23 0
+			DW_OP_const2s -184
+			DW_OP_stack_value
+			DW_OP_bit_piece 9 0
+			DW_OP_const4u 1752286
+			DW_OP_stack_value
+			DW_OP_bit_piece 23 0
 		    } SPECIAL_expr
 		}
 		# Composite location with some empty pieces.
@@ -141,11 +141,11 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name part_def_a
 		    DW_AT_type :$array_a9_label
 		    DW_AT_location {
-			piece 3
-			const4u 0xf1927314
-			stack_value
-			piece 4
-			piece 2
+			DW_OP_piece 3
+			DW_OP_const4u 0xf1927314
+			DW_OP_stack_value
+			DW_OP_piece 4
+			DW_OP_piece 2
 		    } SPECIAL_expr
 		}
 		# Implicit location: immediate value.
@@ -153,7 +153,7 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name def_implicit_s
 		    DW_AT_type :$struct_s_label
 		    DW_AT_location {
-			implicit_value 0x12 0x34 0x56 0x78
+			DW_OP_implicit_value 0x12 0x34 0x56 0x78
 		    } SPECIAL_expr
 		}
 		# Implicit location: immediate value for whole array, with
@@ -162,7 +162,7 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name def_implicit_a
 		    DW_AT_type :$array_a9_label
 		    DW_AT_location {
-			implicit_value 0x1 0x12 0x23 0x34 0x45 \
+			DW_OP_implicit_value 0x1 0x12 0x23 0x34 0x45 \
 			    0x56 0x67 0x78 0x89 0x9a 0xab
 		    } SPECIAL_expr
 		}
@@ -170,26 +170,26 @@ Dwarf::assemble $asm_file {
 		DW_TAG_variable {
 		    DW_AT_name implicit_a_ptr
 		    DW_AT_type :$char_ptr_label
-		    DW_AT_location [subst {
-			implicit_pointer $implicit_a_label 5
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_implicit_pointer $implicit_a_label 5
+		    } SPECIAL_expr
 		}
 		# Stack-value location.
 		stack_b_label: DW_TAG_variable {
 		    DW_AT_name def_stack_b
 		    DW_AT_type :$struct_t_label
 		    DW_AT_location {
-			const4u 0x1a2b3c4d
-			stack_value
+			DW_OP_const4u 0x1a2b3c4d
+			DW_OP_stack_value
 		    } SPECIAL_expr
 		}
 		# Implicit pointer into stack value.
 		DW_TAG_variable {
 		    DW_AT_name implicit_b_ptr
 		    DW_AT_type :$char_ptr_label
-		    DW_AT_location [subst {
-			implicit_pointer $stack_b_label 1
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_implicit_pointer $stack_b_label 1
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/opt-out-not-implptr.exp b/gdb/testsuite/gdb.dwarf2/opt-out-not-implptr.exp
index 8c60d031e82..e4bc3ddb117 100644
--- a/gdb/testsuite/gdb.dwarf2/opt-out-not-implptr.exp
+++ b/gdb/testsuite/gdb.dwarf2/opt-out-not-implptr.exp
@@ -66,21 +66,21 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name i64_noptr
 		DW_AT_type :$i64_array
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_constu $::c64
 		    DW_OP_stack_value
 		    DW_OP_piece 8
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name i32_noptr
 		DW_AT_type :$i32_array
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_constu $::c32
 		    DW_OP_stack_value
 		    DW_OP_piece 4
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/staticvirtual.exp b/gdb/testsuite/gdb.dwarf2/staticvirtual.exp
index 9fa1a2b1c0d..792b8519577 100644
--- a/gdb/testsuite/gdb.dwarf2/staticvirtual.exp
+++ b/gdb/testsuite/gdb.dwarf2/staticvirtual.exp
@@ -38,7 +38,9 @@ Dwarf::assemble $asm_file {
 		    DW_AT_name ~S
 		    DW_AT_external 1 flag
 		    DW_AT_virtuality @DW_VIRTUALITY_virtual
-		    DW_AT_vtable_elem_location {const1u 1} SPECIAL_expr
+		    DW_AT_vtable_elem_location {
+			DW_OP_const1u 1
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
index 87ef2b2243e..93b01fca428 100644
--- a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
+++ b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
@@ -79,7 +79,7 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name a
 		DW_AT_type :$int_type_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr $exec_mask_var
 		    DW_OP_deref_size $int_size
 
@@ -91,7 +91,7 @@ Dwarf::assemble $asm_file {
 		    DW_OP_skip 3
 		    DW_OP_bregx $dwarf_regnum 0
 		    DW_OP_stack_value
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp
index 9f797a5843c..0c334427ab5 100644
--- a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp
+++ b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp
@@ -79,24 +79,24 @@ Dwarf::assemble $asm_file {
 	    DW_TAG_variable {
 		DW_AT_name exec_mask
 		DW_AT_type :$int_type_label
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr $exec_mask_var
-		}] SPECIAL_expr
+		} SPECIAL_expr
 		DW_AT_external 1 flag
 	    }
 
 	    # add info for subprogram main
 	    DW_TAG_subprogram {
 		MACRO_AT_func { main }
-		DW_AT_frame_base [subst {
+		DW_AT_frame_base {
 		    DW_OP_regx $dwarf_regnum
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    } {
 		# define artificial variable a
 		DW_TAG_variable {
 		    DW_AT_name a
 		    DW_AT_type :$int_type_label
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_lit1
 			DW_OP_addr $exec_mask_var
 			DW_OP_deref_size $int_size
@@ -112,7 +112,7 @@ Dwarf::assemble $asm_file {
 			# conditional jump to DW_OP_drop
 			DW_OP_bra -9
 			DW_OP_stack_value
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		    DW_AT_external 1 flag
 		}
 	    }
diff --git a/gdb/testsuite/gdb.dwarf2/var-access.exp b/gdb/testsuite/gdb.dwarf2/var-access.exp
index 3276b28a81f..87604655d2b 100644
--- a/gdb/testsuite/gdb.dwarf2/var-access.exp
+++ b/gdb/testsuite/gdb.dwarf2/var-access.exp
@@ -166,64 +166,64 @@ Dwarf::assemble $asm_file {
 		DW_TAG_variable {
 		    DW_AT_name "a"
 		    DW_AT_type :$array_a8_label
-		    DW_AT_location [subst {
-			addr $buf_var
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $buf_var
+		    } SPECIAL_expr
 		}
 		# Memory pieces: two bytes from &buf[2], and two bytes
 		# from &buf[0].
 		DW_TAG_variable {
 		    DW_AT_name "s1"
 		    DW_AT_type :$struct_s_label
-		    DW_AT_location [subst {
-			addr $buf_var
-			plus_uconst 2
-			piece 2
-			addr $buf_var
-			piece 2
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_addr $buf_var
+			DW_OP_plus_uconst 2
+			DW_OP_piece 2
+			DW_OP_addr $buf_var
+			DW_OP_piece 2
+		    } SPECIAL_expr
 		}
 		# Register- and memory pieces: one byte each from r0,
 		# &buf[4], r1, and &buf[5].
 		DW_TAG_variable {
 		    DW_AT_name "s2"
 		    DW_AT_type :$struct_s_label
-		    DW_AT_location [subst {
-			regx [lindex $dwarf_regnum 0]
-			piece 1
-			addr "$buf_var + 4"
-			piece 1
-			regx [lindex $dwarf_regnum 1]
-			piece 1
-			addr "$buf_var + 5"
-			piece 1
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_regx [lindex $dwarf_regnum 0]
+			DW_OP_piece 1
+			DW_OP_addr "$buf_var + 4"
+			DW_OP_piece 1
+			DW_OP_regx [lindex $dwarf_regnum 1]
+			DW_OP_piece 1
+			DW_OP_addr "$buf_var + 5"
+			DW_OP_piece 1
+		    } SPECIAL_expr
 		}
 		# Memory pieces for bitfield access: 8 bytes optimized
 		# out, 3 bytes from &buf[3], and 1 byte from &buf[1].
 		DW_TAG_variable {
 		    DW_AT_name "st1"
 		    DW_AT_type :$struct_st_label
-		    DW_AT_location [subst {
-			piece 8
-			addr "$buf_var + 3"
-			piece 3
-			addr "$buf_var + 1"
-			piece 1
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_piece 8
+			DW_OP_addr "$buf_var + 3"
+			DW_OP_piece 3
+			DW_OP_addr "$buf_var + 1"
+			DW_OP_piece 1
+		    } SPECIAL_expr
 		}
 		# Register pieces for bitfield access: 4 bytes optimized
 		# out, 3 bytes from r0, and 1 byte from r1.
 		DW_TAG_variable {
 		    DW_AT_name "t2"
 		    DW_AT_type :$struct_t_label
-		    DW_AT_location [subst {
-			piece 4
-			regx [lindex $dwarf_regnum 0]
-			piece 3
-			regx [lindex $dwarf_regnum 1]
-			piece 1
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_piece 4
+			DW_OP_regx [lindex $dwarf_regnum 0]
+			DW_OP_piece 3
+			DW_OP_regx [lindex $dwarf_regnum 1]
+			DW_OP_piece 1
+		    } SPECIAL_expr
 		}
 		# One piece per bitfield, using piece offsets: 32 bits of
 		# an implicit value, 9 bits of a stack value, 13 bits of
@@ -231,17 +231,17 @@ Dwarf::assemble $asm_file {
 		DW_TAG_variable {
 		    DW_AT_name "t3"
 		    DW_AT_type :$struct_t_label
-		    DW_AT_location [subst {
-			implicit_value 0x12 0x34 0x56 0x78 0x9a
-			bit_piece 32 4
-			const2s -280
-			stack_value
-			bit_piece 9 2
-			regx [lindex $dwarf_regnum 0]
-			bit_piece 13 14
-			addr $buf_var
-			bit_piece 10 42
-		    }] SPECIAL_expr
+		    DW_AT_location {
+			DW_OP_implicit_value 0x12 0x34 0x56 0x78 0x9a
+			DW_OP_bit_piece 32 4
+			DW_OP_const2s -280
+			DW_OP_stack_value
+			DW_OP_bit_piece 9 2
+			DW_OP_regx [lindex $dwarf_regnum 0]
+			DW_OP_bit_piece 13 14
+			DW_OP_addr $buf_var
+			DW_OP_bit_piece 10 42
+		    } SPECIAL_expr
 		}
 	    }
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/varval.exp b/gdb/testsuite/gdb.dwarf2/varval.exp
index 97c4ef87907..b8f532b2e65 100644
--- a/gdb/testsuite/gdb.dwarf2/varval.exp
+++ b/gdb/testsuite/gdb.dwarf2/varval.exp
@@ -74,9 +74,9 @@ proc setup_exec { arg_bad } {
 		    DW_AT_name "var_a"
 		    DW_AT_type :${int_label}
 		    DW_AT_external 1 DW_FORM_flag
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol "var_a"]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 
 		var_a_abstract_label: DW_TAG_variable {
@@ -88,9 +88,9 @@ proc setup_exec { arg_bad } {
 		    DW_AT_name "var_b"
 		    DW_AT_type :${int_label}
 		    DW_AT_external 1 DW_FORM_flag
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol "var_b"]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 
 		var_c_label: DW_TAG_variable {
@@ -104,9 +104,9 @@ proc setup_exec { arg_bad } {
 		    DW_AT_name "var_p"
 		    DW_AT_type :${ptr_label}
 		    DW_AT_external 1 DW_FORM_flag
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol "var_p"]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 
 		if { $bad } {
@@ -166,17 +166,17 @@ proc setup_exec { arg_bad } {
 		    DW_AT_name "var_s"
 		    DW_AT_type :${struct_label}
 		    DW_AT_external 1 DW_FORM_flag
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol "var_s"]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 
 		var_untyped_label: DW_TAG_variable {
 		    DW_AT_name "var_untyped"
 		    DW_AT_external 1 DW_FORM_flag
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol "var_b"]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 
 		int_array_label: DW_TAG_array_type {
@@ -202,9 +202,9 @@ proc setup_exec { arg_bad } {
 		    DW_AT_name "varval3"
 		    DW_AT_external 1 DW_FORM_flag
 		    DW_AT_type :${int_array_of_1_label}
-		    DW_AT_location [subst {
+		    DW_AT_location {
 			DW_OP_addr [gdb_target_symbol "var_a"]
-		    }] SPECIAL_expr
+		    } SPECIAL_expr
 		}
 
 		DW_TAG_subprogram {
@@ -215,39 +215,39 @@ proc setup_exec { arg_bad } {
 		    varval_label: DW_TAG_variable {
 			DW_AT_name "varval"
 			DW_AT_type :${int_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_a_label}
 			    DW_OP_const1s 0
 			    DW_OP_or
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    varval2_label: DW_TAG_variable {
 			DW_AT_name "varval2"
 			DW_AT_type :${int_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_a_abstract_label}
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    var_a_concrete_label: DW_TAG_variable {
 			DW_AT_abstract_origin :${var_a_abstract_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_addr [gdb_target_symbol "var_a"]
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    DW_TAG_variable {
 			DW_AT_name "constval"
 			DW_AT_type :${int_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_c_label}
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    DW_TAG_variable {
 			DW_AT_name "mixedval"
 			DW_AT_type :${int_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_c_label}
 			    DW_OP_GNU_variable_value ${var_b_label}
 			    DW_OP_div
@@ -258,40 +258,40 @@ proc setup_exec { arg_bad } {
 			    DW_OP_GNU_variable_value ${varval_label}
 			    DW_OP_minus
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    DW_TAG_variable {
 			DW_AT_name "pointerval"
 			DW_AT_type :${ptr_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_p_label}
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    if { $bad } {
 			DW_TAG_variable {
 			    DW_AT_name "badval"
 			    DW_AT_type :${int_label}
-			    DW_AT_location [subst {
+			    DW_AT_location {
 				DW_OP_GNU_variable_value ${var_bad_label}
 				DW_OP_stack_value
-			    }] SPECIAL_expr
+			    } SPECIAL_expr
 			}
 		    }
 		    DW_TAG_variable {
 			DW_AT_name "structval"
 			DW_AT_type :${struct_label}
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_s_label}
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    DW_TAG_variable {
 			DW_AT_name "untypedval"
-			DW_AT_location [subst {
+			DW_AT_location {
 			    DW_OP_GNU_variable_value ${var_untyped_label}
 			    DW_OP_stack_value
-			}] SPECIAL_expr
+			} SPECIAL_expr
 		    }
 		    if { $bad } {
 			DW_TAG_variable {
@@ -303,10 +303,10 @@ proc setup_exec { arg_bad } {
 			}
 			DW_TAG_variable {
 			    DW_AT_name "bad_die_val2"
-			    DW_AT_location [subst {
+			    DW_AT_location {
 				DW_OP_GNU_variable_value ${ptr_label}+1
 				DW_OP_stack_value
-			    }] SPECIAL_expr
+			    } SPECIAL_expr
 			}
 		    }
 		}
diff --git a/gdb/testsuite/gdb.dwarf2/void-type.exp b/gdb/testsuite/gdb.dwarf2/void-type.exp
index 963410f93e6..a747a7133e7 100644
--- a/gdb/testsuite/gdb.dwarf2/void-type.exp
+++ b/gdb/testsuite/gdb.dwarf2/void-type.exp
@@ -74,18 +74,18 @@ Dwarf::assemble $asm_file {
 		DW_AT_name "var_a"
 		DW_AT_type :$int_type
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "var_a"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 
 	    DW_TAG_variable {
 		DW_AT_name "var_ptr"
 		DW_AT_type :$ptr_type
 		DW_AT_external 1 DW_FORM_flag
-		DW_AT_location [subst {
+		DW_AT_location {
 		    DW_OP_addr [gdb_target_symbol "var_ptr"]
-		}] SPECIAL_expr
+		} SPECIAL_expr
 	    }
 	}
     }
diff --git a/gdb/testsuite/gdb.python/make-visualizer.exp b/gdb/testsuite/gdb.python/make-visualizer.exp
index 5794bbda8a2..2c6b40d4b9c 100644
--- a/gdb/testsuite/gdb.python/make-visualizer.exp
+++ b/gdb/testsuite/gdb.python/make-visualizer.exp
@@ -98,9 +98,9 @@ Dwarf::assemble $asm_file {
 	    # Used for testing synthetic pointers.
 	    DW_TAG_variable {
 		DW_AT_name synthptr
-		DW_AT_location [subst {
-		    GNU_implicit_pointer $variable_label 0
-		}] SPECIAL_expr
+		DW_AT_location {
+		    DW_OP_GNU_implicit_pointer $variable_label 0
+		} SPECIAL_expr
 		DW_AT_type :$pointer_label
 	    }
 
diff --git a/gdb/testsuite/gdb.trace/entry-values.exp b/gdb/testsuite/gdb.trace/entry-values.exp
index f6c4c5c1b6f..6c227089453 100644
--- a/gdb/testsuite/gdb.trace/entry-values.exp
+++ b/gdb/testsuite/gdb.trace/entry-values.exp
@@ -137,15 +137,15 @@ Dwarf::assemble $asm_file {
 		    GNU_call_site_parameter {
 			DW_AT_location {DW_OP_reg0} SPECIAL_expr
 			DW_AT_GNU_call_site_value {
-			    addr global1
-			    deref_size 4
+			    DW_OP_addr global1
+			    DW_OP_deref_size 4
 			} SPECIAL_expr
 		    }
 		    GNU_call_site_parameter {
 			DW_AT_location {DW_OP_reg1} SPECIAL_expr
 			DW_AT_GNU_call_site_value {
-			    addr global2
-			    deref_size 4
+			    DW_OP_addr global2
+			    DW_OP_deref_size 4
 			} SPECIAL_expr
 		    }
 		}
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 95e33c1b310..55f33d47797 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -488,10 +488,9 @@ proc get_func_info { name {options {debug}} } {
 # section automatically.
 #
 # If FORM is 'SPECIAL_expr', then VALUE is treated as a location
-# expression.  The effective form is then DW_FORM_block or DW_FORM_exprloc
-# for DWARF version >= 4, and VALUE is passed to the (internal)
-# '_location' proc to be translated.
-# This proc implements a miniature DW_OP_ assembler.
+# expression.  The effective form is then DW_FORM_block or
+# DW_FORM_exprloc for DWARF version >= 4, and VALUE is treated as
+# code, using DW_OP_* procs and evaluated in the appropriate scope.
 #
 # If FORM is not given, it is guessed:
 # * If VALUE starts with the "@" character, the rest of VALUE is
@@ -521,8 +520,6 @@ namespace eval Dwarf {
     variable _constants
     # DW_FORM short names.
     variable _FORM
-    # DW_OP short names.
-    variable _OP
 
     # The current output file.
     variable _output_file
@@ -591,10 +588,15 @@ namespace eval Dwarf {
     # code should be evaluated.
     variable _level
 
+    # Variables used when processing a location expression.  These are
+    # set by _location and may be used by the various DW_OP-handlers.
+    variable _loc_dwarf_version
+    variable _loc_addr_size
+    variable _loc_offset_size
+
     proc _process_one_constant {name value} {
 	variable _constants
 	variable _FORM
-	variable _OP
 
 	set _constants($name) $value
 
@@ -643,7 +645,18 @@ namespace eval Dwarf {
 	    }
 
 	    OP {
-		set _OP($name2) $name
+		set handler _handle_default_OP
+		if {[llength [info procs _handle_DW_OP_$name2]] > 0} {
+		    set handler _handle_DW_OP_$name2
+		}
+		# Each DW_OP_* proc emits the opcode and then
+		# delegates to the argument handler.
+		# tclint-disable-next-line command-args
+		proc $name {args} [format {
+		    variable _constants
+		    _op .byte $_constants(%s) %s
+		    %s {*}$args
+		} $name $name $handler]
 	    }
 
 	    default {
@@ -1217,209 +1230,211 @@ namespace eval Dwarf {
 	}
     }
 
-    # This is a miniature assembler for location expressions.  It is
-    # suitable for use in the attributes to a DIE.  Its output is
-    # prefixed with "=" to make it automatically use DW_FORM_block.
     #
-    # BODY is split by lines, and each line is taken to be a list.
-    #
-    # DWARF_VERSION is the DWARF version for the section where the location
-    # description is found.
+    # Handlers for DW_OP_* opcodes.
     #
-    # ADDR_SIZE is the length in bytes (4 or 8) of an address on the target
-    # machine (typically found in the header of the section where the location
-    # description is found).
+    # A handler is only needed if the opcode requires a parameter, or
+    # some sort of special handling.  Generic code handles emitting
+    # the actual opcode itself, so a handler should not do this.
     #
-    # OFFSET_SIZE is the length in bytes (4 or 8) of an offset into a DWARF
-    # section.  This typically depends on whether 32-bit or 64-bit DWARF is
-    # used, as indicated in the header of the section where the location
-    # description is found.
+    # Handlers are found by name when parsing the .def file.  If a
+    # handler isn't found, the default (_handle_default_OP) is used.
     #
-    # Each list's first element is the opcode, either short or long
-    # forms are accepted.
-    # FIXME argument handling
-    # FIXME move docs
-    proc _location { body dwarf_version addr_size offset_size } {
-	variable _constants
 
-	set collected_lines ""
-	foreach line [split $body \n] {
-	    # Ignore blank lines, and allow embedded comments.
-	    if {  [regexp -- {^[ \t]*$} $line] || [regexp -- {^[ \t]*#} $line] } {
-		continue
-	    }
-	    if { $collected_lines != "" } {
-		set line "$collected_lines\n$line"
-		set collected_lines ""
-	    }
-	    if { ! [info complete $line] } {
-		set collected_lines $line
-		continue
-	    }
-	    set opcode [_map_name [lindex $line 0] _OP]
-	    _op .byte $_constants($opcode) $opcode
+    proc _handle_DW_OP_addr {size} {
+	variable _loc_addr_size
+	_op .${_loc_addr_size}byte $size
+    }
 
-	    array unset argvec *
-	    switch -exact -- $opcode {
-		DW_OP_addr {
-		    _get_args $line $opcode size
-		    _op .${addr_size}byte $argvec(size)
-		}
+    proc _handle_DW_OP_GNU_addr_index {symbol} {
+	variable _debug_addr_index
+	variable _cu_addr_size
 
-		DW_OP_GNU_addr_index {
-		    variable _debug_addr_index
-		    variable _cu_addr_size
+	_op .uleb128 ${_debug_addr_index}
+	incr _debug_addr_index
 
-		    _op .uleb128 ${_debug_addr_index}
-		    incr _debug_addr_index
+	_defer_output .debug_addr {
+	    _op .${_cu_addr_size}byte $symbol
+	}
+    }
 
-		    _defer_output .debug_addr {
-			_op .${_cu_addr_size}byte [lindex $line 1]
-		    }
-		}
+    proc _handle_DW_OP_regx {register} {
+	_op .uleb128 $register
+    }
 
-		DW_OP_regx {
-		    _get_args $line $opcode register
-		    _op .uleb128 $argvec(register)
-		}
+    proc _handle_DW_OP_pick {const} {
+	_op .byte $const
+    }
+    proc _handle_DW_OP_const1u {const} {
+	_op .byte $const
+    }
+    proc _handle_DW_OP_const1s {const} {
+	_op .byte $const
+    }
 
-		DW_OP_pick -
-		DW_OP_const1u -
-		DW_OP_const1s {
-		    _get_args $line $opcode const
-		    _op .byte $argvec(const)
-		}
+    proc _handle_DW_OP_const2u {const} {
+	_op .2byte $const
+    }
+    proc _handle_DW_OP_const2s {const} {
+	_op .2byte $const
+    }
 
-		DW_OP_const2u -
-		DW_OP_const2s {
-		    _get_args $line $opcode const
-		    _op .2byte $argvec(const)
-		}
+    proc _handle_DW_OP_const4u {const} {
+	_op .4byte $const
+    }
+    proc _handle_DW_OP_const4s {const} {
+	_op .4byte $const
+    }
 
-		DW_OP_const4u -
-		DW_OP_const4s {
-		    _get_args $line $opcode const
-		    _op .4byte $argvec(const)
-		}
+    proc _handle_DW_OP_const8u {const} {
+	_op .8byte $const
+    }
+    proc _handle_DW_OP_const8s {const} {
+	_op .8byte $const
+    }
 
-		DW_OP_const8u -
-		DW_OP_const8s {
-		    _get_args $line $opcode const
-		    _op .8byte $argvec(const)
-		}
+    proc _handle_DW_OP_constu {const} {
+	_op .uleb128 $const
+    }
+    proc _handle_DW_OP_consts {const} {
+	_op .sleb128 $const
+    }
 
-		DW_OP_constu {
-		    _get_args $line $opcode const
-		    _op .uleb128 $argvec(const)
-		}
-		DW_OP_consts {
-		    _get_args $line $opcode const
-		    _op .sleb128 $argvec(const)
-		}
+    proc _handle_DW_OP_plus_uconst {const} {
+	_op .uleb128 $const
+    }
 
-		DW_OP_plus_uconst {
-		    _get_args $line $opcode const
-		    _op .uleb128 $argvec(const)
-		}
+    proc _handle_DW_OP_piece {size} {
+	_op .uleb128 $size
+    }
 
-		DW_OP_piece {
-		    _get_args $line $opcode size
-		    _op .uleb128 $argvec(size)
-		}
+    proc _handle_DW_OP_bit_piece {size offset} {
+	_op .uleb128 $size
+	_op .uleb128 $offset
+    }
 
-		DW_OP_bit_piece {
-		    _get_args $line $opcode size offset
-		    _op .uleb128 $argvec(size)
-		    _op .uleb128 $argvec(offset)
-		}
+    proc _handle_DW_OP_skip {label} {
+	_op .2byte $label
+    }
+    proc _handle_DW_OP_bra {label} {
+	_op .2byte $label
+    }
 
-		DW_OP_skip -
-		DW_OP_bra {
-		    _get_args $line $opcode label
-		    _op .2byte $argvec(label)
-		}
+    proc _handle_DW_OP_entry_value {body} {
+	variable _loc_dwarf_version
+	variable _loc_addr_size
+	variable _loc_offset_size
 
-		DW_OP_entry_value {
-		    _get_args $line $opcode body
-		    set l1 [new_label "expr_start"]
-		    set l2 [new_label "expr_end"]
-		    _op .uleb128 "$l2 - $l1" "expression"
-		    define_label $l1
-		    _location $argvec(body) $dwarf_version $addr_size \
-			$offset_size
-		    define_label $l2
+	set l1 [new_label "expr_start"]
+	set l2 [new_label "expr_end"]
+	_op .uleb128 "$l2 - $l1" "expression"
+	define_label $l1
+	variable _level
+	uplevel $_level [list _location $body $_loc_dwarf_version \
+			     $_loc_addr_size $_loc_offset_size]
+	define_label $l2
+    }
+
+    proc _handle_DW_OP_implicit_value {args} {
+	set l1 [new_label "value_start"]
+	set l2 [new_label "value_end"]
+	_op .uleb128 "$l2 - $l1"
+	define_label $l1
+	foreach value $args {
+	    switch -regexp -- $value {
+		{^0x[[:xdigit:]]{1,2}$} {_op .byte $value}
+		{^0x[[:xdigit:]]{4}$} {_op .2byte $value}
+		{^0x[[:xdigit:]]{8}$} {_op .4byte $value}
+		{^0x[[:xdigit:]]{16}$} {_op .8byte $value}
+		default {
+		    error "bad value '$value' in DW_OP_implicit_value"
 		}
+	    }
+	}
+	define_label $l2
+    }
 
-		DW_OP_implicit_value {
-		    set l1 [new_label "value_start"]
-		    set l2 [new_label "value_end"]
-		    _op .uleb128 "$l2 - $l1"
-		    define_label $l1
-		    foreach value [lrange $line 1 end] {
-			switch -regexp -- $value {
-			    {^0x[[:xdigit:]]{1,2}$} {_op .byte $value}
-			    {^0x[[:xdigit:]]{4}$} {_op .2byte $value}
-			    {^0x[[:xdigit:]]{8}$} {_op .4byte $value}
-			    {^0x[[:xdigit:]]{16}$} {_op .8byte $value}
-			    default {
-				error "bad value '$value' in DW_OP_implicit_value"
-			    }
-			}
-		    }
-		    define_label $l2
-		}
+    proc _handle_DW_OP_implicit_pointer {label offset} {
+	variable _loc_dwarf_version
+	variable _loc_addr_size
+	variable _loc_offset_size
+	# Here label is a section offset.
+	if { $_loc_dwarf_version == 2 } {
+	    _op .${_loc_addr_size}byte $label
+	} else {
+	    _op_offset $_loc_offset_size $label
+	}
+	_op .sleb128 $offset
+    }
 
-		DW_OP_implicit_pointer -
-		DW_OP_GNU_implicit_pointer {
-		    _get_args $line $opcode label offset
+    proc _handle_DW_OP_GNU_implicit_pointer {label offset} {
+	_handle_DW_OP_implicit_pointer $label $offset
+    }
 
-		    # Here label is a section offset.
-		    if { $dwarf_version == 2 } {
-			_op .${addr_size}byte $argvec(label)
-		    } else {
-			_op_offset $offset_size $argvec(label)
-		    }
-		    _op .sleb128 $argvec(offset)
-		}
+    proc _handle_DW_OP_GNU_variable_value {label} {
+	variable _loc_addr_size
+	variable _loc_offset_size
+	variable _loc_dwarf_version
+	# Here label is a section offset.
+	if { $_loc_dwarf_version == 2 } {
+	    _op .${_loc_addr_size}byte $label
+	} else {
+	    _op_offset $_loc_offset_size $label
+	}
+    }
 
-		DW_OP_GNU_variable_value {
-		    _get_args $line $opcode label
+    proc _handle_DW_OP_deref_size {size} {
+	_op .byte $size
+    }
 
-		    # Here label is a section offset.
-		    if { $dwarf_version == 2 } {
-			_op .${addr_size}byte $argvec(label)
-		    } else {
-			_op_offset $offset_size $argvec(label)
-		    }
-		}
+    proc _handle_DW_OP_bregx {register offset} {
+	_op .uleb128 $register
+	_op .sleb128 $offset
+    }
 
-		DW_OP_deref_size {
-		    _get_args $line $opcode size
-		    _op .byte $argvec(size)
-		}
+    proc _handle_DW_OP_fbreg {offset} {
+	_op .sleb128 $offset
+    }
 
-		DW_OP_bregx {
-		    _get_args $line $opcode register offset
-		    _op .uleb128 $argvec(register)
-		    _op .sleb128 $argvec(offset)
-		}
+    proc _handle_DW_OP_fbreg {reg} {
+	_op .sleb128 $reg
+    }
 
-		DW_OP_fbreg {
-		    _get_args $line $opcode offset
-		    _op .sleb128 $argvec(offset)
-		}
+    proc _handle_default_OP {} {
+	# Do nothing; if arguments are passed, Tcl will cause an
+	# error.
+    }
 
-		DW_OP_fbreg {
-		    _op .sleb128 [lindex $line 1]
-		}
+    # This is a miniature assembler for location expressions.  It is
+    # suitable for use in the attributes to a DIE.
+    #
+    # BODY is evaluated as code in the appropriate scope.
+    #
+    # DWARF_VERSION is the DWARF version for the section where the location
+    # description is found.
+    #
+    # ADDR_SIZE is the length in bytes (4 or 8) of an address on the target
+    # machine (typically found in the header of the section where the location
+    # description is found).
+    #
+    # OFFSET_SIZE is the length in bytes (4 or 8) of an offset into a DWARF
+    # section.  This typically depends on whether 32-bit or 64-bit DWARF is
+    # used, as indicated in the header of the section where the location
+    # description is found.
+    #
+    # FIXME move docs
+    proc _location { body dwarf_version addr_size offset_size } {
+	variable _loc_dwarf_version
+	set _loc_dwarf_version $dwarf_version
 
-		default {
-		    if {[llength $line] > 1} {
-			error "Unimplemented: operands in location for $opcode"
-		    }
-		}
-	    }
-	}
+	variable _loc_addr_size
+	set _loc_addr_size $addr_size
+
+	variable _loc_offset_size
+	set _loc_offset_size $offset_size
+
+	variable _level
+	uplevel $_level $body
     }
 
     # Return a label that references the current position in the


More information about the Gdb-cvs mailing list