Author: Joel E. Denny
Date: 2022-09-21T11:32:05-04:00
New Revision: 28412d1800e391c5ba8e7607bb15c74b106d581b

URL: 
https://github.com/llvm/llvm-project/commit/28412d1800e391c5ba8e7607bb15c74b106d581b
DIFF: 
https://github.com/llvm/llvm-project/commit/28412d1800e391c5ba8e7607bb15c74b106d581b.diff

LOG: [lit] Implement DEFINE and REDEFINE directives

These directives define per-test lit substitutions.  The concept was
discussed at
<https://discourse.llvm.org/t/iterating-lit-run-lines/62596/10>.

For example, the following directives can be inserted into a test file
to define `%{cflags}` and `%{fcflags}` substitutions with empty
initial values, which serve as the parameters of another newly defined
`%{check}` substitution:

```
// DEFINE: %{cflags} =
// DEFINE: %{fcflags} =

// DEFINE: %{check} = %clang_cc1 %{cflags} -emit-llvm -o - %s | \
// DEFINE:            FileCheck %{fcflags} %s
```

The following directives then redefine the parameters before each use
of `%{check}`:

```
// REDEFINE: %{cflags} = -foo
// REDEFINE: %{fcflags} = -check-prefix=FOO
// RUN: %{check}

// REDEFINE: %{cflags} = -bar
// REDEFINE: %{fcflags} = -check-prefix=BAR
// RUN: %{check}
```

Of course, `%{check}` would typically be more elaborate, increasing
the benefit of the reuse.

One issue is that the strings `DEFINE:` and `REDEFINE:` already appear
in 5 tests.  This patch adjusts those tests not to use those strings.
Our prediction is that, in the vast majority of cases, if a test
author mistakenly uses one of those strings for another purpose, the
text appearing after the string will not happen to have the syntax
required for these directives.  Thus, the test author will discover
the mistake immediately when lit reports the syntax error.

This patch also expands the documentation on existing lit substitution
behavior.

Reviewed By: jhenderson, MaskRay, awarzynski

Differential Revision: https://reviews.llvm.org/D132513

Added: 
    llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/before-name.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/between-name-equals.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-empty.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-dot.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-equals.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-newline.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-number.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-ws.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/empty.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-equals.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-name.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/ws-only.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/empty.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/end-in-double-backslash.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-bad-redefine.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-continuation.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-redefine.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-run.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-bad-define.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-continuation.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-define.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-run.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-define.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-redefine.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/ws-only.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-config.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-test.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-inside-pattern.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-exact.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-once-exact.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-prefixes-pattern.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-suffixes-pattern.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-inside-pattern.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-exact.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-once-exact.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-none.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-prefixes-pattern.txt
    
llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-suffixes-pattern.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/location-range.txt
    llvm/utils/lit/tests/Inputs/shtest-define/errors/no-run.txt
    llvm/utils/lit/tests/Inputs/shtest-define/examples/param-subst.txt
    llvm/utils/lit/tests/Inputs/shtest-define/expansion-order.txt
    llvm/utils/lit/tests/Inputs/shtest-define/line-number-substitutions.txt
    llvm/utils/lit/tests/Inputs/shtest-define/lit.cfg
    llvm/utils/lit/tests/Inputs/shtest-define/name-chars.txt
    llvm/utils/lit/tests/Inputs/shtest-define/recursiveExpansionLimit.txt
    llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-0.txt
    llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-1.txt
    llvm/utils/lit/tests/Inputs/shtest-define/value-equals.txt
    llvm/utils/lit/tests/Inputs/shtest-define/value-escaped.txt
    llvm/utils/lit/tests/Inputs/shtest-define/ws-and-continuations.txt
    llvm/utils/lit/tests/Inputs/shtest-shell/continuations.txt
    llvm/utils/lit/tests/shtest-define.py

Modified: 
    clang/test/CodeGen/attr-noundef.cpp
    clang/test/CodeGen/indirect-noundef.cpp
    clang/test/Preprocessor/init.c
    llvm/docs/CommandGuide/lit.rst
    llvm/docs/TestingGuide.rst
    llvm/test/tools/llvm-cvtres/help.test
    llvm/test/tools/yaml2obj/ELF/custom-null-section.yaml
    llvm/utils/lit/lit/TestRunner.py
    llvm/utils/lit/tests/Inputs/testrunner-custom-parsers/test.txt
    llvm/utils/lit/tests/shtest-keyword-parse-errors.py
    llvm/utils/lit/tests/shtest-shell.py
    llvm/utils/lit/tests/unit/TestRunner.py

Removed: 
    


################################################################################
diff  --git a/clang/test/CodeGen/attr-noundef.cpp 
b/clang/test/CodeGen/attr-noundef.cpp
index f0570a5390a26..ab167281817eb 100644
--- a/clang/test/CodeGen/attr-noundef.cpp
+++ b/clang/test/CodeGen/attr-noundef.cpp
@@ -15,10 +15,10 @@ struct Trivial {
 };
 Trivial ret_trivial() { return {}; }
 void pass_trivial(Trivial e) {}
-// CHECK-INTEL: [[DEFINE:define( dso_local)?]] i32 @{{.*}}ret_trivial
-// CHECK-AARCH: [[DEFINE:define( dso_local)?]] i32 @{{.*}}ret_trivial
-// CHECK-INTEL: [[DEFINE]] void @{{.*}}pass_trivial{{.*}}(i32 %
-// CHECK-AARCH: [[DEFINE]] void @{{.*}}pass_trivial{{.*}}(i64 %
+// CHECK-INTEL: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial
+// CHECK-AARCH: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial
+// CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 %
+// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 %
 
 struct NoCopy {
   int a;
@@ -26,16 +26,16 @@ struct NoCopy {
 };
 NoCopy ret_nocopy() { return {}; }
 void pass_nocopy(NoCopy e) {}
-// CHECK: [[DEFINE]] void 
@{{.*}}ret_nocopy{{.*}}(%"struct.check_structs::NoCopy"* noalias 
sret({{[^)]+}}) align 4 %
-// CHECK: [[DEFINE]] void 
@{{.*}}pass_nocopy{{.*}}(%"struct.check_structs::NoCopy"* noundef %
+// CHECK: [[DEF]] void 
@{{.*}}ret_nocopy{{.*}}(%"struct.check_structs::NoCopy"* noalias 
sret({{[^)]+}}) align 4 %
+// CHECK: [[DEF]] void 
@{{.*}}pass_nocopy{{.*}}(%"struct.check_structs::NoCopy"* noundef %
 
 struct Huge {
   int a[1024];
 };
 Huge ret_huge() { return {}; }
 void pass_huge(Huge h) {}
-// CHECK: [[DEFINE]] void @{{.*}}ret_huge{{.*}}(%"struct.check_structs::Huge"* 
noalias sret({{[^)]+}}) align 4 %
-// CHECK: [[DEFINE]] void 
@{{.*}}pass_huge{{.*}}(%"struct.check_structs::Huge"* noundef
+// CHECK: [[DEF]] void @{{.*}}ret_huge{{.*}}(%"struct.check_structs::Huge"* 
noalias sret({{[^)]+}}) align 4 %
+// CHECK: [[DEF]] void @{{.*}}pass_huge{{.*}}(%"struct.check_structs::Huge"* 
noundef
 } // namespace check_structs
 
 //************ Passing unions by value
@@ -47,10 +47,10 @@ union Trivial {
 };
 Trivial ret_trivial() { return {}; }
 void pass_trivial(Trivial e) {}
-// CHECK-INTEL: [[DEFINE]] i32 @{{.*}}ret_trivial
-// CHECK-AARCH: [[DEFINE]] i32 @{{.*}}ret_trivial
-// CHECK-INTEL: [[DEFINE]] void @{{.*}}pass_trivial{{.*}}(i32 %
-// CHECK-AARCH: [[DEFINE]] void @{{.*}}pass_trivial{{.*}}(i64 %
+// CHECK-INTEL: [[DEF]] i32 @{{.*}}ret_trivial
+// CHECK-AARCH: [[DEF]] i32 @{{.*}}ret_trivial
+// CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 %
+// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 %
 
 union NoCopy {
   int a;
@@ -58,8 +58,8 @@ union NoCopy {
 };
 NoCopy ret_nocopy() { return {}; }
 void pass_nocopy(NoCopy e) {}
-// CHECK: [[DEFINE]] void 
@{{.*}}ret_nocopy{{.*}}(%"union.check_unions::NoCopy"* noalias sret({{[^)]+}}) 
align 4 %
-// CHECK: [[DEFINE]] void 
@{{.*}}pass_nocopy{{.*}}(%"union.check_unions::NoCopy"* noundef %
+// CHECK: [[DEF]] void @{{.*}}ret_nocopy{{.*}}(%"union.check_unions::NoCopy"* 
noalias sret({{[^)]+}}) align 4 %
+// CHECK: [[DEF]] void @{{.*}}pass_nocopy{{.*}}(%"union.check_unions::NoCopy"* 
noundef %
 } // namespace check_unions
 
 //************ Passing `this` pointers
@@ -100,9 +100,9 @@ i32x3 ret_vec() {
 void pass_vec(i32x3 v) {
 }
 
-// CHECK: [[DEFINE]] noundef <3 x i32> @{{.*}}ret_vec{{.*}}()
-// CHECK-INTEL: [[DEFINE]] void @{{.*}}pass_vec{{.*}}(<3 x i32> noundef %
-// CHECK-AARCH: [[DEFINE]] void @{{.*}}pass_vec{{.*}}(<4 x i32> %
+// CHECK: [[DEF]] noundef <3 x i32> @{{.*}}ret_vec{{.*}}()
+// CHECK-INTEL: [[DEF]] void @{{.*}}pass_vec{{.*}}(<3 x i32> noundef %
+// CHECK-AARCH: [[DEF]] void @{{.*}}pass_vec{{.*}}(<4 x i32> %
 } // namespace check_vecs
 
 //************ Passing exotic types
@@ -145,23 +145,23 @@ void pass_large_BitInt(_BitInt(127) e) {
 }
 
 // Pointers to arrays/functions are always noundef
-// CHECK: [[DEFINE]] noundef [32 x i32]* @{{.*}}ret_arrptr{{.*}}()
-// CHECK: [[DEFINE]] noundef i32 (i32)* @{{.*}}ret_fnptr{{.*}}()
+// CHECK: [[DEF]] noundef [32 x i32]* @{{.*}}ret_arrptr{{.*}}()
+// CHECK: [[DEF]] noundef i32 (i32)* @{{.*}}ret_fnptr{{.*}}()
 
 // Pointers to members are never noundef
-// CHECK: [[DEFINE]] i64 @{{.*}}ret_mdptr{{.*}}()
-// CHECK-INTEL: [[DEFINE]] { i64, i64 } @{{.*}}ret_mfptr{{.*}}()
-// CHECK-AARCH: [[DEFINE]] [2 x i64] @{{.*}}ret_mfptr{{.*}}()
+// CHECK: [[DEF]] i64 @{{.*}}ret_mdptr{{.*}}()
+// CHECK-INTEL: [[DEF]] { i64, i64 } @{{.*}}ret_mfptr{{.*}}()
+// CHECK-AARCH: [[DEF]] [2 x i64] @{{.*}}ret_mfptr{{.*}}()
 
 // nullptr_t is never noundef
-// CHECK: [[DEFINE]] i8* @{{.*}}ret_npt{{.*}}()
-// CHECK: [[DEFINE]] void @{{.*}}pass_npt{{.*}}(i8* %
+// CHECK: [[DEF]] i8* @{{.*}}ret_npt{{.*}}()
+// CHECK: [[DEF]] void @{{.*}}pass_npt{{.*}}(i8* %
 
 // TODO: for now, ExtInt is only noundef if it is sign/zero-extended
-// CHECK-INTEL: [[DEFINE]] noundef signext i3 @{{.*}}ret_BitInt{{.*}}()
-// CHECK-AARCH: [[DEFINE]] i3 @{{.*}}ret_BitInt{{.*}}()
-// CHECK-INTEL: [[DEFINE]] void @{{.*}}pass_BitInt{{.*}}(i3 noundef signext %
-// CHECK-AARCH: [[DEFINE]] void @{{.*}}pass_BitInt{{.*}}(i3 %
-// CHECK-INTEL: [[DEFINE]] void @{{.*}}pass_large_BitInt{{.*}}(i64 %{{.*}}, 
i64 %
-// CHECK-AARCH: [[DEFINE]] void @{{.*}}pass_large_BitInt{{.*}}(i127 %
+// CHECK-INTEL: [[DEF]] noundef signext i3 @{{.*}}ret_BitInt{{.*}}()
+// CHECK-AARCH: [[DEF]] i3 @{{.*}}ret_BitInt{{.*}}()
+// CHECK-INTEL: [[DEF]] void @{{.*}}pass_BitInt{{.*}}(i3 noundef signext %
+// CHECK-AARCH: [[DEF]] void @{{.*}}pass_BitInt{{.*}}(i3 %
+// CHECK-INTEL: [[DEF]] void @{{.*}}pass_large_BitInt{{.*}}(i64 %{{.*}}, i64 %
+// CHECK-AARCH: [[DEF]] void @{{.*}}pass_large_BitInt{{.*}}(i127 %
 } // namespace check_exotic

diff  --git a/clang/test/CodeGen/indirect-noundef.cpp 
b/clang/test/CodeGen/indirect-noundef.cpp
index 0f63daac044c3..1d82aa60ad1e4 100644
--- a/clang/test/CodeGen/indirect-noundef.cpp
+++ b/clang/test/CodeGen/indirect-noundef.cpp
@@ -13,9 +13,9 @@ int (*indirect_callee_int_ptr)(int);
 // CHECK: @indirect_callee_union_ptr = [[GLOBAL]] i32 (i32)*
 union u1 (*indirect_callee_union_ptr)(union u1);
 
-// CHECK: [[DEFINE:define( dso_local)?]] noundef i32 
@{{.*}}indirect_callee_int{{.*}}(i32 noundef %
+// CHECK: [[DEF:define( dso_local)?]] noundef i32 
@{{.*}}indirect_callee_int{{.*}}(i32 noundef %
 int indirect_callee_int(int a) { return a; }
-// CHECK: [[DEFINE]] i32 @{{.*}}indirect_callee_union{{.*}}(i32 %
+// CHECK: [[DEF]] i32 @{{.*}}indirect_callee_union{{.*}}(i32 %
 union u1 indirect_callee_union(union u1 a) {
   return a;
 }

diff  --git a/clang/test/Preprocessor/init.c b/clang/test/Preprocessor/init.c
index 028f3448f8bda..8e3c87e7675f3 100644
--- a/clang/test/Preprocessor/init.c
+++ b/clang/test/Preprocessor/init.c
@@ -1396,13 +1396,13 @@
 // SPARC64-OBSD:#define __UINTMAX_C_SUFFIX__ ULL
 // SPARC64-OBSD:#define __UINTMAX_TYPE__ long long unsigned int
 //
-// RUN: %clang_cc1 -E -dM -ffreestanding -triple=x86_64-pc-kfreebsd-gnu < 
/dev/null | FileCheck -match-full-lines -check-prefix KFREEBSD-DEFINE %s
-// KFREEBSD-DEFINE:#define __FreeBSD_kernel__ 1
-// KFREEBSD-DEFINE:#define __GLIBC__ 1
+// RUN: %clang_cc1 -E -dM -ffreestanding -triple=x86_64-pc-kfreebsd-gnu < 
/dev/null | FileCheck -match-full-lines -check-prefix KFREEBSD-DEF %s
+// KFREEBSD-DEF:#define __FreeBSD_kernel__ 1
+// KFREEBSD-DEF:#define __GLIBC__ 1
 //
-// RUN: %clang_cc1 -E -dM -ffreestanding -triple=i686-pc-kfreebsd-gnu < 
/dev/null | FileCheck -match-full-lines -check-prefix KFREEBSDI686-DEFINE %s
-// KFREEBSDI686-DEFINE:#define __FreeBSD_kernel__ 1
-// KFREEBSDI686-DEFINE:#define __GLIBC__ 1
+// RUN: %clang_cc1 -E -dM -ffreestanding -triple=i686-pc-kfreebsd-gnu < 
/dev/null | FileCheck -match-full-lines -check-prefix KFREEBSDI686-DEF %s
+// KFREEBSDI686-DEF:#define __FreeBSD_kernel__ 1
+// KFREEBSDI686-DEF:#define __GLIBC__ 1
 //
 // RUN: %clang_cc1 -x c++ -triple i686-pc-linux-gnu -fobjc-runtime=gcc -E -dM 
< /dev/null | FileCheck -match-full-lines -check-prefix GNUSOURCE %s
 // RUN: %clang_cc1 -x c++ -triple sparc-rtems-elf -E -dM < /dev/null | 
FileCheck -match-full-lines -check-prefix GNUSOURCE %s

diff  --git a/llvm/docs/CommandGuide/lit.rst b/llvm/docs/CommandGuide/lit.rst
index b9d8675ef459b..e9bd46e84769a 100644
--- a/llvm/docs/CommandGuide/lit.rst
+++ b/llvm/docs/CommandGuide/lit.rst
@@ -562,14 +562,6 @@ Other substitutions are provided that are variations on 
this base set and
 further substitution patterns can be defined by each test module. See the
 modules :ref:`local-configuration-files`.
 
-By default, substitutions are expanded exactly once, so that if e.g. a
-substitution ``%build`` is defined in top of another substitution ``%cxx``,
-``%build`` will expand to ``%cxx`` textually, not to what ``%cxx`` expands to.
-However, if the ``recursiveExpansionLimit`` property of the ``TestingConfig``
-is set to a non-negative integer, substitutions will be expanded recursively
-until that limit is reached. It is an error if the limit is reached and
-expanding substitutions again would yield a 
diff erent result.
-
 More detailed information on substitutions can be found in the
 :doc:`../TestingGuide`.
 

diff  --git a/llvm/docs/TestingGuide.rst b/llvm/docs/TestingGuide.rst
index 4bbc0972cdafb..cd18cb4a4a5a1 100644
--- a/llvm/docs/TestingGuide.rst
+++ b/llvm/docs/TestingGuide.rst
@@ -619,6 +619,15 @@ RUN lines:
  ``%else %{<else branch>%}`` is optional and treated like ``%else %{%}``
  if not present.
 
+``%(line)``, ``%(line+<number>)``, ``%(line-<number>)``
+
+  The number of the line where this substitution is used, with an
+  optional integer offset.  These expand only if they appear
+  immediately in ``RUN:``, ``DEFINE:``, and ``REDEFINE:`` directives.
+  Occurrences in substitutions defined elsewhere are never expanded.
+  For example, this can be used in tests with multiple RUN lines,
+  which reference the test file's line numbers.
+
 **LLVM-specific substitutions:**
 
 ``%shlibext``
@@ -633,12 +642,6 @@ RUN lines:
 
    Example: ``.exe`` (Windows), empty on Linux.
 
-``%(line)``, ``%(line+<number>)``, ``%(line-<number>)``
-   The number of the line where this substitution is used, with an optional
-   integer offset. This can be used in tests with multiple RUN lines, which
-   reference test file's line numbers.
-
-
 **Clang-specific substitutions:**
 
 ``%clang``
@@ -670,8 +673,199 @@ RUN lines:
    output affects test results.  It's usually easy to tell: just look for
    redirection or piping of the ``FileCheck`` call's stdout or stderr.
 
-To add more substitutions, look at ``test/lit.cfg`` or ``lit.local.cfg``.
+.. _Test-specific substitutions:
+
+**Test-specific substitutions:**
+
+Additional substitutions can be defined as follows:
+
+- Lit configuration files (e.g., ``lit.cfg`` or ``lit.local.cfg``) can define
+  substitutions for all tests in a test directory.  They do so by extending the
+  substitution list, ``config.substitutions``.  Each item in the list is a 
tuple
+  consisting of a pattern and its replacement, which lit applies using python's
+  ``re.sub`` function.
+- To define substitutions within a single test file, lit supports the
+  ``DEFINE:`` and ``REDEFINE:`` directives, described in detail below.  So that
+  they have no effect on other test files, these directives modify a copy of 
the
+  substitution list that is produced by lit configuration files.
+
+For example, the following directives can be inserted into a test file to 
define
+``%{cflags}`` and ``%{fcflags}`` substitutions with empty initial values, which
+serve as the parameters of another newly defined ``%{check}`` substitution:
+
+.. code-block:: llvm
+
+    ; DEFINE: %{cflags} =
+    ; DEFINE: %{fcflags} =
+
+    ; DEFINE: %{check} =                                                  \
+    ; DEFINE:   %clang_cc1 -verify -fopenmp -fopenmp-version=51 %{cflags} \
+    ; DEFINE:              -emit-llvm -o - %s |                           \
+    ; DEFINE:     FileCheck %{fcflags} %s
+
+Alternatively, the above substitutions can be defined in a lit configuration
+file to be shared with other test files.  Either way, the test file can then
+specify directives like the following to redefine the parameter substitutions 
as
+desired before each use of ``%{check}`` in a ``RUN:`` line:
+
+.. code-block:: llvm
+
+    ; REDEFINE: %{cflags} = -triple x86_64-apple-darwin10.6.0 -fopenmp-simd
+    ; REDEFINE: %{fcflags} = -check-prefix=SIMD
+    ; RUN: %{check}
+
+    ; REDEFINE: %{cflags} = -triple x86_64-unknown-linux-gnu -fopenmp-simd
+    ; REDEFINE: %{fcflags} = -check-prefix=SIMD
+    ; RUN: %{check}
+
+    ; REDEFINE: %{cflags} = -triple x86_64-apple-darwin10.6.0
+    ; REDEFINE: %{fcflags} = -check-prefix=NO-SIMD
+    ; RUN: %{check}
+
+    ; REDEFINE: %{cflags} = -triple x86_64-unknown-linux-gnu
+    ; REDEFINE: %{fcflags} = -check-prefix=NO-SIMD
+    ; RUN: %{check}
+
+Besides providing initial values, the initial ``DEFINE:`` directives for the
+parameter substitutions in the above example serve a second purpose: they
+establish the substitution order so that both ``%{check}`` and its parameters
+expand as desired.  There's a simple way to remember the required definition
+order in a test file: define a substitution before any substitution that might
+refer to it.
+
+In general, substitution expansion behaves as follows:
+
+- Upon arriving at each ``RUN:`` line, lit expands all substitutions in that
+  ``RUN:`` line using their current values from the substitution list.  No
+  substitution expansion is performed immediately at ``DEFINE:`` and
+  ``REDEFINE:`` directives except ``%(line)``, ``%(line+<number>)``, and
+  ``%(line-<number>)``.
+- When expanding substitutions in a ``RUN:`` line, lit makes only one pass
+  through the substitution list by default.  In this case, a substitution must
+  have been inserted earlier in the substitution list than any substitution
+  appearing in its value in order for the latter to expand.  (For greater
+  flexibility, you can enable multiple passes through the substitution list by
+  setting `recursiveExpansionLimit`_ in a lit configuration file.)
+- While lit configuration files can insert anywhere in the substitution list,
+  the insertion behavior of the ``DEFINE:`` and ``REDEFINE:`` directives is
+  specified below and is designed specifically for the use case presented in 
the
+  example above.
+- Defining a substitution in terms of itself, whether directly or via other
+  substitutions, should be avoided.  It usually produces an infinitely 
recursive
+  definition that cannot be fully expanded.  It does *not* define the
+  substitution in terms of its previous value, even when using ``REDEFINE:``.
+
+The relationship between the ``DEFINE:`` and ``REDEFINE:`` directive is
+analogous to the relationship between a variable declaration and variable
+assignment in many programming languages:
+
+- ``DEFINE: %{name} = value``
+
+   This directive assigns the specified value to a new substitution whose
+   pattern is ``%{name}``, or it reports an error if there is already a
+   substitution whose pattern contains ``%{name}`` because that could produce
+   confusing expansions (e.g., a lit configuration file might define a
+   substitution with the pattern ``%{name}\[0\]``).  The new substitution is
+   inserted at the start of the substitution list so that it will expand first.
+   Thus, its value can contain any substitution previously defined, whether in
+   the same test file or in a lit configuration file, and both will expand.
+
+- ``REDEFINE: %{name} = value``
+
+   This directive assigns the specified value to an existing substitution whose
+   pattern is ``%{name}``, or it reports an error if there are no substitutions
+   with that pattern or if there are multiple substitutions whose patterns
+   contain ``%{name}``.  The substitution's current position in the 
substitution
+   list does not change so that expansion order relative to other existing
+   substitutions is preserved.
+
+The following properties apply to both the ``DEFINE:`` and ``REDEFINE:``
+directives:
+
+- **Substitution name**: In the directive, whitespace immediately before or
+  after ``%{name}`` is optional and discarded.  ``%{name}`` must start with
+  ``%{``, it must end with ``}``, and the rest must start with a letter or
+  underscore and contain only alphanumeric characters, hyphens, underscores, 
and
+  colons.  This syntax has a few advantages:
+
+    - It is impossible for ``%{name}`` to contain sequences that are special in
+      python's ``re.sub`` patterns.  Otherwise, attempting to specify
+      ``%{name}`` as a substitution pattern in a lit configuration file could
+      produce confusing expansions.
+    - The braces help avoid the possibility that another substitution's pattern
+      will match part of ``%{name}`` or vice-versa, producing confusing
+      expansions.  However, the patterns of substitutions defined by lit
+      configuration files and by lit itself are not restricted to this form, so
+      overlaps are still theoretically possible.
+
+- **Substitution value**: The value includes all text from the first
+  non-whitespace character after ``=`` to the last non-whitespace character.  
If
+  there is no non-whitespace character after ``=``, the value is the empty
+  string.  Escape sequences that can appear in python ``re.sub`` replacement
+  strings are treated as plain text in the value.
+- **Line continuations**: If the last non-whitespace character on the line 
after
+  ``:`` is ``\``, then the next directive must use the same directive keyword
+  (e.g., ``DEFINE:``) , and it is an error if there is no additional directive.
+  That directive serves as a continuation.  That is, before following the rules
+  above to parse the text after ``:`` in either directive, lit joins that text
+  together to form a single directive, replaces the ``\`` with a single space,
+  and removes any other whitespace that is now adjacent to that space.  A
+  continuation can be continued in the same manner.  A continuation containing
+  only whitespace after its ``:`` is an error.
+
+.. _recursiveExpansionLimit:
+
+**recursiveExpansionLimit:**
+
+As described in the previous section, when expanding substitutions in a 
``RUN:``
+line, lit makes only one pass through the substitution list by default.  Thus,
+if substitutions are not defined in the proper order, some will remain in the
+``RUN:`` line unexpanded.  For example, the following directives refer to
+``%{inner}`` within ``%{outer}`` but do not define ``%{inner}`` until after
+``%{outer}``:
+
+.. code-block:: llvm
+
+    ; By default, this definition order does not enable full expansion.
+
+    ; DEFINE: %{outer} = %{inner}
+    ; DEFINE: %{inner} = expanded
+
+    ; RUN: echo '%{outer}'
+
+``DEFINE:`` inserts substitutions at the start of the substitution list, so
+``%{inner}`` expands first but has no effect because the original ``RUN:`` line
+does not contain ``%{inner}``.  Next, ``%{outer}`` expands, and the output of
+the ``echo`` command becomes:
+
+.. code-block:: shell
+
+    %{inner}
+
+Of course, one way to fix this simple case is to reverse the definitions of
+``%{outer}`` and ``%{inner}``.  However, if a test has a complex set of
+substitutions that can all reference each other, there might not exist a
+sufficient substitution order.
+
+To address such use cases, lit configuration files support
+``config.recursiveExpansionLimit``, which can be set to a non-negative integer
+to specify the maximum number of passes through the substitution list.  Thus, 
in
+the above example, setting the limit to 2 would cause lit to make a second pass
+that expands ``%{inner}`` in the ``RUN:`` line, and the output from the 
``echo``
+command when then be:
+
+.. code-block:: shell
+
+    expanded
+
+To improve performance, lit will stop making passes when it notices the 
``RUN:``
+line has stopped changing.  In the above example, setting the limit higher than
+2 is thus harmless.
 
+To facilitate debugging, after reaching the limit, lit will make one extra pass
+and report an error if the ``RUN:`` line changes again.  In the above example,
+setting the limit to 1 will thus cause lit to report an error instead of
+producing incorrect output.
 
 Options
 -------

diff  --git a/llvm/test/tools/llvm-cvtres/help.test 
b/llvm/test/tools/llvm-cvtres/help.test
index a3817e9725ae5..f504e063ac152 100644
--- a/llvm/test/tools/llvm-cvtres/help.test
+++ b/llvm/test/tools/llvm-cvtres/help.test
@@ -4,7 +4,7 @@
 ; HELP_TEST:     OVERVIEW: Resource Converter
 ; HELP_TEST-DAG:  USAGE: llvm-cvtres [options] file...
 ; HELP_TEST-DAG:  OPTIONS:
-; HELP_TEST-NEXT:   /DEFINE:symbol Not implemented
+; HELP_TEST-NEXT:   /{{DEFINE}}:symbol Not implemented
 ; HELP_TEST-NEXT:   /FOLDDUPS: Not implemented
 ; HELP_TEST-NEXT:   /HELP Display available options
 ; HELP_TEST-NEXT:   /MACHINE:{ARM|ARM64|EBC|IA64|X64|X86}

diff  --git a/llvm/test/tools/yaml2obj/ELF/custom-null-section.yaml 
b/llvm/test/tools/yaml2obj/ELF/custom-null-section.yaml
index f6f349318fe40..73f08f97fc4fc 100644
--- a/llvm/test/tools/yaml2obj/ELF/custom-null-section.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/custom-null-section.yaml
@@ -42,11 +42,11 @@ Sections:
 ## Check we can redefine fields of the first SHT_NULL section.
 
 # RUN: yaml2obj --docnum=3 %s -o %t3
-# RUN: llvm-readelf --sections %t3 | FileCheck %s --check-prefix=REDEFINE
+# RUN: llvm-readelf --sections %t3 | FileCheck %s --check-prefix=REDEF
 
-# REDEFINE:      Section Headers:
-# REDEFINE-NEXT:  [Nr] Name Type Address          Off    Size   ES Flg Lk Inf 
Al
-# REDEFINE-NEXT:  [ 0] .foo NULL 0000000000000006 000000 000002 03   A 4   5  1
+# REDEF:      Section Headers:
+# REDEF-NEXT:  [Nr] Name Type Address          Off    Size   ES Flg Lk Inf Al
+# REDEF-NEXT:  [ 0] .foo NULL 0000000000000006 000000 000002 03   A 4   5  1
 
 --- !ELF
 FileHeader:

diff  --git a/llvm/utils/lit/lit/TestRunner.py 
b/llvm/utils/lit/lit/TestRunner.py
index 0242e0b75af33..300c17bfea3aa 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -1218,6 +1218,203 @@ def memoized(x):
 def _caching_re_compile(r):
     return re.compile(r)
 
+class ExpandableScriptDirective(object):
+    """
+    Common interface for lit directives for which any lit substitutions must be
+    expanded to produce the shell script.  It includes directives (e.g., 
'RUN:')
+    specifying shell commands that might have lit substitutions to be expanded.
+    It also includes lit directives (e.g., 'DEFINE:') that adjust 
substitutions.
+
+    start_line_number: The directive's starting line number.
+    end_line_number: The directive's ending line number, which is
+        start_line_number if the directive has no line continuations.
+    keyword: The keyword that specifies the directive.  For example, 'RUN:'.
+    """
+
+    def __init__(self, start_line_number, end_line_number, keyword):
+        # Input line number where the directive starts.
+        self.start_line_number = start_line_number
+        # Input line number where the directive ends.
+        self.end_line_number = end_line_number
+        # The keyword used to indicate the directive.
+        self.keyword = keyword
+
+    def add_continuation(self, line_number, keyword, line):
+        """
+        Add a continuation line to this directive and return True, or do 
nothing
+        and return False if the specified line is not a continuation for this
+        directive (e.g., previous line does not end in '\', or keywords do not
+        match).
+
+        line_number: The line number for the continuation line.
+        keyword: The keyword that specifies the continuation line.  For 
example,
+            'RUN:'.
+        line: The content of the continuation line after the keyword.
+        """
+        assert False, "expected method to be called on derived class"
+
+    def needs_continuation(self):
+        """
+        Does this directive require a continuation line?
+
+        '\' is documented as indicating a line continuation even if whitespace
+        separates it from the newline.  It looks like a line continuation, and
+        it would be confusing if it didn't behave as one.
+        """
+        assert False, "expected method to be called on derived class"
+
+    def get_location(self):
+        """
+        Get a phrase describing the line or range of lines so far included by
+        this directive and any line continuations.
+        """
+        if self.start_line_number == self.end_line_number:
+            return f'at line {self.start_line_number}'
+        return f'from line {self.start_line_number} to {self.end_line_number}'
+
+class CommandDirective(ExpandableScriptDirective):
+    """
+    A lit directive taking a shell command line.  For example,
+    'RUN: echo hello world'.
+
+    command: The content accumulated so far from the directive and its
+        continuation lines.
+    """
+
+    def __init__(self, start_line_number, end_line_number, keyword, line):
+        super().__init__(start_line_number, end_line_number, keyword)
+        self.command = line.rstrip()
+
+    def add_continuation(self, line_number, keyword, line):
+        if keyword != self.keyword or not self.needs_continuation():
+            return False
+        self.command = self.command[:-1] + line.rstrip()
+        self.end_line_number = line_number
+        return True
+
+    def needs_continuation(self):
+        # Trailing whitespace is stripped immediately when each line is added,
+        # so '\' is never hidden here.
+        return self.command[-1] == '\\'
+
+class SubstDirective(ExpandableScriptDirective):
+    """
+    A lit directive taking a substitution definition or redefinition.  For
+    example, 'DEFINE: %{name} = value'.
+
+    new_subst: True if this directive defines a new substitution.  False if it
+        redefines an existing substitution.
+    body: The unparsed content accumulated so far from the directive and its
+        continuation lines.
+    name: The substitution's name, or None if more continuation lines are still
+        required.
+    value: The substitution's value, or None if more continuation lines are
+        still required.
+    """
+
+    def __init__(self, start_line_number, end_line_number, keyword, new_subst,
+                 line):
+        super().__init__(start_line_number, end_line_number, keyword)
+        self.new_subst = new_subst
+        self.body = line
+        self.name = None
+        self.value = None
+        self._parse_body()
+
+    def add_continuation(self, line_number, keyword, line):
+        if keyword != self.keyword or not self.needs_continuation():
+            return False
+        if not line.strip():
+            raise ValueError("Substitution's continuation is empty")
+        # Append line.  Replace the '\' and any adjacent whitespace with a
+        # single space.
+        self.body = self.body.rstrip()[:-1].rstrip() + ' ' + line.lstrip()
+        self.end_line_number = line_number
+        self._parse_body()
+        return True
+
+    def needs_continuation(self):
+        return self.body.rstrip()[-1:] == '\\'
+
+    def _parse_body(self):
+        """
+        If no more line continuations are required, parse all the directive's
+        accumulated lines in order to identify the substitution's name and full
+        value, and raise an exception if invalid.
+        """
+        if self.needs_continuation():
+            return
+
+        # Extract the left-hand side and value, and discard any whitespace
+        # enclosing each.
+        parts = self.body.split('=', 1)
+        if len(parts) == 1:
+            raise ValueError("Substitution's definition does not contain '='")
+        self.name = parts[0].strip()
+        self.value = parts[1].strip()
+
+        # Check the substitution's name.
+        #
+        # Do not extend this to permit '.' or any sequence that's special in a
+        # python pattern.  We could escape that automatically for
+        # DEFINE/REDEFINE directives in test files.  However, lit configuration
+        # file authors would still have to remember to escape them manually in
+        # substitution names but not in values.  Moreover, the manually chosen
+        # and automatically chosen escape sequences would have to be consistent
+        # (e.g., '\.' vs. '[.]') in order for REDEFINE to successfully redefine
+        # a substitution previously defined by a lit configuration file.  All
+        # this seems too error prone and confusing to be worthwhile.  If you
+        # want your name to express structure, use ':' instead of '.'.
+        #
+        # Actually, '{' and '}' are special if they contain only digits 
possibly
+        # separated by a comma.  Requiring a leading letter avoids that.
+        if not re.fullmatch(r'%{[_a-zA-Z][-_:0-9a-zA-Z]*}', self.name):
+            raise ValueError(
+                f"Substitution name '{self.name}' is malformed as it must "
+                f"start with '%{{', it must end with '}}', and the rest must "
+                f"start with a letter or underscore and contain only "
+                f"alphanumeric characters, hyphens, underscores, and colons")
+
+    def adjust_substitutions(self, substitutions):
+        """
+        Modify the specified substitution list as specified by this directive.
+        """
+        assert not self.needs_continuation(), \
+               "expected directive continuations to be parsed before applying"
+        value_repl = self.value.replace('\\', '\\\\')
+        existing = [i for i, subst in enumerate(substitutions)
+                    if self.name in subst[0]]
+        existing_res = ''.join("\nExisting pattern: " + substitutions[i][0]
+                               for i in existing)
+        if self.new_subst:
+            if existing:
+                raise ValueError(
+                    f"Substitution whose pattern contains '{self.name}' is "
+                    f"already defined before '{self.keyword}' directive "
+                    f"{self.get_location()}"
+                    f"{existing_res}")
+            substitutions.insert(0, (self.name, value_repl))
+            return
+        if len(existing) > 1:
+            raise ValueError(
+                f"Multiple substitutions whose patterns contain '{self.name}' "
+                f"are defined before '{self.keyword}' directive "
+                f"{self.get_location()}"
+                f"{existing_res}")
+        if not existing:
+            raise ValueError(
+                f"No substitution for '{self.name}' is defined before "
+                f"'{self.keyword}' directive {self.get_location()}")
+        if substitutions[existing[0]][0] != self.name:
+            raise ValueError(
+                f"Existing substitution whose pattern contains '{self.name}' "
+                f"does not have the pattern specified by '{self.keyword}' "
+                f"directive {self.get_location()}\n"
+                f"Expected pattern: {self.name}"
+                f"{existing_res}")
+        substitutions[existing[0]] = (self.name, value_repl)
+
+
 def applySubstitutions(script, substitutions, conditions={},
                        recursion_limit=None):
     """
@@ -1363,8 +1560,20 @@ def processLineToFixedPoint(ln):
         return processed
 
     process = processLine if recursion_limit is None else 
processLineToFixedPoint
-    
-    return [unescapePercents(process(ln)) for ln in script]
+    output = []
+    for directive in script:
+        if isinstance(directive, SubstDirective):
+            directive.adjust_substitutions(substitutions)
+        else:
+            if isinstance(directive, CommandDirective):
+                line = directive.command
+            else:
+                # Can come from preamble_commands.
+                assert isinstance(directive, str)
+                line = directive
+            output.append(unescapePercents(process(line)))
+
+    return output
 
 
 class ParserKind(object):
@@ -1379,6 +1588,10 @@ class ParserKind(object):
         boolean expressions. Ex 'XFAIL:'
     INTEGER: A keyword taking a single integer. Ex 'ALLOW_RETRIES:'
     CUSTOM: A keyword with custom parsing semantics.
+    DEFINE: A keyword taking a new lit substitution definition. Ex
+        'DEFINE: %{name}=value'
+    REDEFINE: A keyword taking a lit substitution redefinition. Ex
+        'REDEFINE: %{name}=value'
     """
     TAG = 0
     COMMAND = 1
@@ -1386,6 +1599,8 @@ class ParserKind(object):
     BOOLEAN_EXPR = 3
     INTEGER = 4
     CUSTOM = 5
+    DEFINE = 6
+    REDEFINE = 7
 
     @staticmethod
     def allowedKeywordSuffixes(value):
@@ -1394,7 +1609,9 @@ def allowedKeywordSuffixes(value):
                  ParserKind.LIST:         [':'],
                  ParserKind.BOOLEAN_EXPR: [':'],
                  ParserKind.INTEGER:      [':'],
-                 ParserKind.CUSTOM:       [':', '.']
+                 ParserKind.CUSTOM:       [':', '.'],
+                 ParserKind.DEFINE:       [':'],
+                 ParserKind.REDEFINE:     [':']
                } [value]
 
     @staticmethod
@@ -1404,7 +1621,9 @@ def str(value):
                  ParserKind.LIST:         'LIST',
                  ParserKind.BOOLEAN_EXPR: 'BOOLEAN_EXPR',
                  ParserKind.INTEGER:      'INTEGER',
-                 ParserKind.CUSTOM:       'CUSTOM'
+                 ParserKind.CUSTOM:       'CUSTOM',
+                 ParserKind.DEFINE:       'DEFINE',
+                 ParserKind.REDEFINE:     'REDEFINE'
                } [value]
 
 
@@ -1454,6 +1673,15 @@ def __init__(self, keyword, kind, parser=None, 
initial_value=None):
             if parser is None:
                 raise ValueError("ParserKind.CUSTOM requires a custom parser")
             self.parser = parser
+        elif kind == ParserKind.DEFINE:
+            self.parser = lambda line_number, line, output: \
+                                 self._handleSubst(line_number, line, output,
+                                                   self.keyword, 
new_subst=True)
+        elif kind == ParserKind.REDEFINE:
+            self.parser = lambda line_number, line, output: \
+                                 self._handleSubst(line_number, line, output,
+                                                   self.keyword,
+                                                   new_subst=False)
         else:
             raise ValueError("Unknown kind '%s'" % kind)
 
@@ -1474,23 +1702,25 @@ def _handleTag(line_number, line, output):
         return (not line.strip() or output)
 
     @staticmethod
-    def _handleCommand(line_number, line, output, keyword):
-        """A helper for parsing COMMAND type keywords"""
-        # Trim trailing whitespace.
-        line = line.rstrip()
-        # Substitute line number expressions
+    def _substituteLineNumbers(line_number, line):
         line = re.sub(r'%\(line\)', str(line_number), line)
-
         def replace_line_number(match):
             if match.group(1) == '+':
                 return str(line_number + int(match.group(2)))
             if match.group(1) == '-':
                 return str(line_number - int(match.group(2)))
-        line = re.sub(r'%\(line *([\+-]) *(\d+)\)', replace_line_number, line)
-        # Collapse lines with trailing '\\'.
-        if output and output[-1][-1] == '\\':
-            output[-1] = output[-1][:-1] + line
-        else:
+        return re.sub(r'%\(line *([\+-]) *(\d+)\)', replace_line_number, line)
+
+    @classmethod
+    def _handleCommand(cls, line_number, line, output, keyword):
+        """A helper for parsing COMMAND type keywords"""
+        # Substitute line number expressions.
+        line = cls._substituteLineNumbers(line_number, line)
+
+        # Collapse lines with trailing '\\', or add line with line number to
+        # start a new pipeline.
+        if not output or not output[-1].add_continuation(line_number, keyword,
+                                                         line):
             if output is None:
                 output = []
             pdbg = "%dbg({keyword} at line {line_number})".format(
@@ -1501,7 +1731,8 @@ def replace_line_number(match):
             line = "{pdbg} {real_command}".format(
                 pdbg=pdbg,
                 real_command=line)
-            output.append(line)
+            output.append(CommandDirective(line_number, line_number, keyword,
+                                           line))
         return output
 
     @staticmethod
@@ -1541,6 +1772,18 @@ def _handleBooleanExpr(line_number, line, output):
                 BooleanExpression.evaluate(s, [])
         return output
 
+    @classmethod
+    def _handleSubst(cls, line_number, line, output, keyword, new_subst):
+        """A parser for DEFINE and REDEFINE type keywords"""
+        line = cls._substituteLineNumbers(line_number, line)
+        if output and output[-1].add_continuation(line_number, keyword, line):
+            return output
+        if output is None:
+            output = []
+        output.append(SubstDirective(line_number, line_number, keyword,
+                                     new_subst, line))
+        return output
+
 
 def _parseKeywords(sourcepath, additional_parsers=[],
                    require_script=True):
@@ -1548,8 +1791,8 @@ def _parseKeywords(sourcepath, additional_parsers=[],
 
     Scan an LLVM/Clang style integrated test script and extract all the lines
     pertaining to a special parser. This includes 'RUN', 'XFAIL', 'REQUIRES',
-    'UNSUPPORTED' and 'ALLOW_RETRIES', as well as other specified custom
-    parsers.
+    'UNSUPPORTED', 'ALLOW_RETRIES', 'END', 'DEFINE', 'REDEFINE', as well as
+    other specified custom parsers.
 
     Returns a dictionary mapping each custom parser to its value after
     parsing the test.
@@ -1562,7 +1805,11 @@ def _parseKeywords(sourcepath, additional_parsers=[],
         IntegratedTestKeywordParser('REQUIRES:', ParserKind.BOOLEAN_EXPR),
         IntegratedTestKeywordParser('UNSUPPORTED:', ParserKind.BOOLEAN_EXPR),
         IntegratedTestKeywordParser('ALLOW_RETRIES:', ParserKind.INTEGER),
-        IntegratedTestKeywordParser('END.', ParserKind.TAG)
+        IntegratedTestKeywordParser('END.', ParserKind.TAG),
+        IntegratedTestKeywordParser('DEFINE:', ParserKind.DEFINE,
+                                    initial_value=script),
+        IntegratedTestKeywordParser('REDEFINE:', ParserKind.REDEFINE,
+                                    initial_value=script)
     ]
     keyword_parsers = {p.keyword: p for p in builtin_parsers}
 
@@ -1586,12 +1833,21 @@ def _parseKeywords(sourcepath, additional_parsers=[],
             break
 
     # Verify the script contains a run line.
-    if require_script and not script:
+    if require_script and not any(isinstance(directive, CommandDirective)
+                                  for directive in script):
         raise ValueError("Test has no 'RUN:' line")
 
-    # Check for unterminated run lines.
-    if script and script[-1][-1] == '\\':
-        raise ValueError("Test has unterminated 'RUN:' lines (with '\\')")
+    # Check for unterminated run or subst lines.
+    #
+    # If, after a line continuation for one kind of directive (e.g., 'RUN:',
+    # 'DEFINE:', 'REDEFINE:') in script, the next directive in script is a
+    # 
diff erent kind, then the '\\' remains on the former, and we report it
+    # here.
+    for directive in script:
+        if directive.needs_continuation():
+            raise ValueError(f"Test has unterminated '{directive.keyword}' "
+                             f"directive (with '\\') "
+                             f"{directive.get_location()}")
 
     # Check boolean expressions for unterminated lines.
     for key in keyword_parsers:
@@ -1631,6 +1887,8 @@ def parseIntegratedTestScript(test, additional_parsers=[],
     except ValueError as e:
         return lit.Test.Result(Test.UNRESOLVED, str(e))
     script = parsed['RUN:'] or []
+    assert parsed['DEFINE:'] == script
+    assert parsed['REDEFINE:'] == script
     test.xfails += parsed['XFAIL:'] or []
     test.requires += parsed['REQUIRES:'] or []
     test.unsupported += parsed['UNSUPPORTED:'] or []

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/before-name.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/before-name.txt
new file mode 100644
index 0000000000000..2ef47047907c3
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/before-name.txt
@@ -0,0 +1,7 @@
+# DEFINE: foo %{name}=value
+# RUN: echo %{name}
+
+#      CHECK: Substitution name 'foo %{name}' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/between-name-equals.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/between-name-equals.txt
new file mode 100644
index 0000000000000..dae61298be9e2
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/between-name-equals.txt
@@ -0,0 +1,9 @@
+# ':' is treated like part of the name, so the name is bad.
+
+# DEFINE: %{name}:=value
+# RUN: echo %{name}
+
+#      CHECK: Substitution name '%{name}:' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-empty.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-empty.txt
new file mode 100644
index 0000000000000..c6c7ececa8bee
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-empty.txt
@@ -0,0 +1,7 @@
+# DEFINE: %{} = value
+# RUN: echo %{}
+
+#      CHECK: Substitution name '%{}' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-dot.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-dot.txt
new file mode 100644
index 0000000000000..c0528c695d78e
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-dot.txt
@@ -0,0 +1,12 @@
+# In python's re.sub patterns, '.' means any character.  If we permitted that
+# in a substitution name (and escaped it before recording it as a 
substitution),
+# it would be confusing if someone tried to use the same name in a lit
+# configuration file, where the pattern isn't escaped automatically.
+
+# DEFINE: %{foo.bar} = value
+# RUN: echo %{foo.bar}
+
+#      CHECK: Substitution name '%{foo.bar}' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-equals.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-equals.txt
new file mode 100644
index 0000000000000..1029665015e50
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-equals.txt
@@ -0,0 +1,9 @@
+# The first equals counts as the equals.
+
+# DEFINE: %{foo=bar} = value
+# RUN: echo %{foo=bar}
+
+#      CHECK: Substitution name '%{foo' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-newline.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-newline.txt
new file mode 100644
index 0000000000000..9138beb9ef7df
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-newline.txt
@@ -0,0 +1,8 @@
+# DEFINE:%{foo\
+# DEFINE:bar} = value
+# RUN: echo %{foo bar}
+
+#      CHECK: Substitution name '%{foo bar}' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-number.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-number.txt
new file mode 100644
index 0000000000000..72f96f3ec6c23
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-number.txt
@@ -0,0 +1,12 @@
+# In python's re.sub patterns, '%{3}' means three '%%%'.  If we permitted that
+# as a substitution name (and escaped it before recording it as a
+# substitution), it would be confusing if someone tried to use the same name in
+# a lit configuration file, where the pattern isn't escaped automatically.
+
+# DEFINE: %{3} = value
+# RUN: echo %{3}
+
+#      CHECK: Substitution name '%{3}' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-ws.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-ws.txt
new file mode 100644
index 0000000000000..a64531b7b556b
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/braces-with-ws.txt
@@ -0,0 +1,7 @@
+# DEFINE: %{foo bar} = value
+# RUN: echo %{foo bar}
+
+#      CHECK: Substitution name '%{foo bar}' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/empty.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/empty.txt
new file mode 100644
index 0000000000000..7fec3c49c117f
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/empty.txt
@@ -0,0 +1,7 @@
+# DEFINE:
+# RUN: echo Hello World
+
+#      CHECK: Substitution's definition does not contain '='
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-equals.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-equals.txt
new file mode 100644
index 0000000000000..c57283bcec5f5
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-equals.txt
@@ -0,0 +1,7 @@
+# DEFINE: %{name}: value
+# RUN: echo %{name}
+
+#      CHECK: Substitution's definition does not contain '='
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-name.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-name.txt
new file mode 100644
index 0000000000000..a39681f1f5198
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/no-name.txt
@@ -0,0 +1,7 @@
+# DEFINE:=value
+# RUN: echo Hello World
+
+#      CHECK: Substitution name '' is malformed {{.*}}
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/ws-only.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/ws-only.txt
new file mode 100644
index 0000000000000..eee0c77946880
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/assignment/ws-only.txt
@@ -0,0 +1,8 @@
+#        v~ whitespace intentional
+# DEFINE:  
+# RUN: echo Hello World
+
+#      CHECK: Substitution's definition does not contain '='
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/empty.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/empty.txt
new file mode 100644
index 0000000000000..6a85adc04fc68
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/empty.txt
@@ -0,0 +1,10 @@
+# Surely an empty substitution continuation is an accident, so it's an error.
+
+# DEFINE: %{foo} = foo \
+# DEFINE:
+# RUN: echo '%{foo}'
+
+#      CHECK: Substitution's continuation is empty
+# CHECK-NEXT: in DEFINE: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/end-in-double-backslash.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/end-in-double-backslash.txt
new file mode 100644
index 0000000000000..acb51cfd1f619
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/end-in-double-backslash.txt
@@ -0,0 +1,11 @@
+# This seems like a way to end a value with a backslash, but it requires an
+# empty substitution continuation, which isn't permitted.
+
+# DEFINE: %{foo} = \\
+# DEFINE:
+# RUN: echo '%{foo}'
+
+#      CHECK: Substitution's continuation is empty
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-bad-redefine.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-bad-redefine.txt
new file mode 100644
index 0000000000000..0083ccbf2e886
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-bad-redefine.txt
@@ -0,0 +1,13 @@
+# You cannot continue a DEFINE with a REDEFINE.  In this case, the REDEFINE
+# is wrong on its own because it's written as a continuation, so the bad
+# REDEFINE is reported.
+
+# DEFINE: %{foo}=echo \
+# REDEFINE: Hello World
+# DEFINE: too late to continue
+# RUN: %{foo}
+
+#      CHECK: Substitution's definition does not contain '='
+# CHECK-NEXT: in {{REDEFINE}}: directive on test line [[#@LINE-5]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-continuation.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-continuation.txt
new file mode 100644
index 0000000000000..40bab49f388a4
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-continuation.txt
@@ -0,0 +1,9 @@
+# Check a define's continuation line that is unterminated.
+
+# RUN: echo "Don't complain about no RUN lines."
+# DEFINE: %{foo} = foo \
+# DEFINE:          bar \
+
+# CHECK: Test has unterminated '{{DEFINE}}:' directive (with '\') from line 
[[#@LINE-3]] to [[#@LINE-2]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-redefine.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-redefine.txt
new file mode 100644
index 0000000000000..327b72df5b189
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-redefine.txt
@@ -0,0 +1,13 @@
+# You cannot continue a DEFINE with a REDEFINE.  In this case, the REDEFINE
+# looks fine on its own, so the unterminated DEFINE is reported.
+
+# DEFINE: %{name}=x
+# DEFINE: %{value}=3
+# DEFINE: %{deceptive-continue}=echo \
+# REDEFINE: %{name}=%{value}
+# DEFINE: %{too-late-to-continue}=
+# RUN: %{deceptive-continue}
+
+# CHECK: Test has unterminated '{{DEFINE}}:' directive (with '\') at line 
[[#@LINE-5]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-run.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-run.txt
new file mode 100644
index 0000000000000..65fe09c84688d
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define-run.txt
@@ -0,0 +1,9 @@
+# You cannot continue a DEFINE with a RUN.
+
+# DEFINE: %{foo}=echo \
+# RUN: %{foo}
+# DEFINE: %{too-late-to-continue}=
+
+# CHECK: Test has unterminated '{{DEFINE}}:' directive (with '\') at line 
[[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define.txt
new file mode 100644
index 0000000000000..58a978a386534
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-define.txt
@@ -0,0 +1,8 @@
+# Simple case of unterminated define as last directive in the script.
+
+# RUN: echo "Don't complain about no RUN lines."
+# DEFINE: %{foo}=foo\
+
+# CHECK: Test has unterminated '{{DEFINE}}:' directive (with '\') at line 
[[#@LINE-2]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-bad-define.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-bad-define.txt
new file mode 100644
index 0000000000000..5d91231d84d5f
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-bad-define.txt
@@ -0,0 +1,13 @@
+# You cannot continue a REDEFINE with a DEFINE.  In this case, the DEFINE is
+# wrong on its own because it's written as a continuation, so the bad DEFINE is
+# reported.
+
+# REDEFINE: %{global:what}=echo \
+# DEFINE: Hello World
+# REDEFINE: too late to continue
+# RUN: %{foo}
+
+#      CHECK: Substitution's definition does not contain '='
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-5]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-continuation.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-continuation.txt
new file mode 100644
index 0000000000000..d697d1f5601c4
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-continuation.txt
@@ -0,0 +1,9 @@
+# Check a redefine's continuation line that is unterminated.
+
+# RUN: echo "Don't complain about no RUN lines."
+# REDEFINE: %{global:what} = foo \
+# REDEFINE:                  bar \
+
+# CHECK: Test has unterminated '{{REDEFINE}}:' directive (with '\') from line 
[[#@LINE-3]] to [[#@LINE-2]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-define.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-define.txt
new file mode 100644
index 0000000000000..a259e210b063d
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-define.txt
@@ -0,0 +1,13 @@
+# You cannot continue a REDEFINE with a DEFINE.  In this case, the DEFINE
+# looks fine on its own, so the unterminated REDEFINE is reported.
+
+# DEFINE: %{name}=x
+# DEFINE: %{value}=3
+# REDEFINE: %{global:what}=echo \
+# DEFINE: %{name}=%{value}
+# REDEFINE: %{too-late-to-continue}=
+# RUN: %{deceptive-continue}
+
+# CHECK: Test has unterminated '{{REDEFINE}}:' directive (with '\') at line 
[[#@LINE-5]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-run.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-run.txt
new file mode 100644
index 0000000000000..6385180719a20
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine-run.txt
@@ -0,0 +1,9 @@
+# You cannot continue a REDEFINE with a RUN.
+
+# REDEFINE: %{global:what}=echo \
+# RUN: %{global:what}
+# REDEFINE: %{too-late-to-continue}=
+
+# CHECK: Test has unterminated '{{REDEFINE}}:' directive (with '\') at line 
[[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine.txt
new file mode 100644
index 0000000000000..e6f9143a98613
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-redefine.txt
@@ -0,0 +1,8 @@
+# Simple case of unterminated redefine as last directive in the script.
+
+# RUN: echo "Don't complain about no RUN lines."
+# REDEFINE: %{global:what}=foo\
+
+# CHECK: Test has unterminated '{{REDEFINE}}:' directive (with '\') at line 
[[#@LINE-2]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-define.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-define.txt
new file mode 100644
index 0000000000000..870a707ad2c93
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-define.txt
@@ -0,0 +1,9 @@
+# You cannot continue a RUN with a DEFINE.
+
+# RUN: echo \
+# DEFINE: %{foo}=bar
+# RUN: too late to continue
+
+# CHECK: Test has unterminated '{{RUN}}:' directive (with '\') at line 
[[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-redefine.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-redefine.txt
new file mode 100644
index 0000000000000..986e1d53b4e4e
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/unterminated-run-redefine.txt
@@ -0,0 +1,9 @@
+# You cannot continue a RUN with a REDEFINE.
+
+# RUN: echo Hello \
+# REDEFINE: %{global:what}=bar
+# RUN: too late to continue
+
+# CHECK: Test has unterminated '{{RUN}}:' directive (with '\') at line 
[[#@LINE-4]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/ws-only.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/ws-only.txt
new file mode 100644
index 0000000000000..c4b063055d66a
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/continuation/ws-only.txt
@@ -0,0 +1,11 @@
+# Surely an empty substitution continuation is an accident, so it's an error.
+
+# DEFINE: %{foo} = foo \
+# DEFINE:  
+#        ^~ whitespace intentional
+# RUN: echo '%{foo}'
+
+#      CHECK: Substitution's continuation is empty
+# CHECK-NEXT: in {{DEFINE}}: directive on test line [[#@LINE-5]]
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-config.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-config.txt
new file mode 100644
index 0000000000000..56c9f6ac85578
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-config.txt
@@ -0,0 +1,8 @@
+# DEFINE: %{global:greeting}=Hello
+# RUN: %{global:echo}
+
+#      CHECK: ValueError: Substitution whose pattern contains 
'%{global:greeting}' is already defined before '{{DEFINE}}:' directive at line 
[[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: %{global:greeting}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-test.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-test.txt
new file mode 100644
index 0000000000000..922201ec45d2e
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-already-by-test.txt
@@ -0,0 +1,10 @@
+# DEFINE: %{foo}=bar
+# RUN: echo %{foo}
+# DEFINE: %{foo}=bar
+# RUN: echo %{foo}
+
+#      CHECK: ValueError: Substitution whose pattern contains '%{foo}' is 
already defined before '{{DEFINE}}:' directive at line [[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: %{foo}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-inside-pattern.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-inside-pattern.txt
new file mode 100644
index 0000000000000..0ed22d5029db2
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-inside-pattern.txt
@@ -0,0 +1,8 @@
+# DEFINE: %{global:inside} = @
+# RUN: echo '<%{global:inside}>'
+
+#      CHECK: ValueError: Substitution whose pattern contains 
'%{global:inside}' is already defined before '{{DEFINE}}:' directive at line 
[[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: <%{global:inside}>
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-exact.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-exact.txt
new file mode 100644
index 0000000000000..b222bc2f6230c
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-exact.txt
@@ -0,0 +1,9 @@
+# DEFINE: %{global:multiple-exact} = @
+# RUN: echo '%{global:multiple-exact}'
+
+#      CHECK: ValueError: Substitution whose pattern contains 
'%{global:multiple-exact}' is already defined before '{{DEFINE}}:' directive at 
line [[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: %{global:multiple-exact}
+# CHECK-NEXT: Existing pattern: %{global:multiple-exact}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-once-exact.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-once-exact.txt
new file mode 100644
index 0000000000000..2da630e027f4f
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-multiple-once-exact.txt
@@ -0,0 +1,9 @@
+# DEFINE: %{global:multiple-once-exact} = @
+# RUN: echo '%{global:multiple-once-exact}'
+
+#      CHECK: ValueError: Substitution whose pattern contains 
'%{global:multiple-once-exact}' is already defined before '{{DEFINE}}:' 
directive at line [[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: <%{global:multiple-once-exact}>
+# CHECK-NEXT: Existing pattern: %{global:multiple-once-exact}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-prefixes-pattern.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-prefixes-pattern.txt
new file mode 100644
index 0000000000000..a0c7402a3942a
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-prefixes-pattern.txt
@@ -0,0 +1,8 @@
+# DEFINE: %{global:prefix} = @
+# RUN: echo '%{global:prefix}(foo)'
+
+#      CHECK: ValueError: Substitution whose pattern contains 
'%{global:prefix}' is already defined before '{{DEFINE}}:' directive at line 
[[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: %{global:prefix}\((.*)\)
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-suffixes-pattern.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-suffixes-pattern.txt
new file mode 100644
index 0000000000000..3189271d3ebb1
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/define-suffixes-pattern.txt
@@ -0,0 +1,8 @@
+# DEFINE: %{global:suffix} = @
+# RUN: echo '@%{global:suffix}'
+
+#      CHECK: ValueError: Substitution whose pattern contains 
'%{global:suffix}' is already defined before '{{DEFINE}}:' directive at line 
[[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: @%{global:suffix}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-inside-pattern.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-inside-pattern.txt
new file mode 100644
index 0000000000000..7d308a3d52fe7
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-inside-pattern.txt
@@ -0,0 +1,9 @@
+# REDEFINE: %{global:inside} = @
+# RUN: echo '<%{global:inside}>'
+
+#      CHECK: ValueError: Existing substitution whose pattern contains 
'%{global:inside}' does not have the pattern specified by '{{REDEFINE}}:' 
directive at line [[#@LINE-3]]
+# CHECK-NEXT: Expected pattern: %{global:inside}
+# CHECK-NEXT: Existing pattern: <%{global:inside}>
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-exact.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-exact.txt
new file mode 100644
index 0000000000000..ee61c8aea2b8f
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-exact.txt
@@ -0,0 +1,13 @@
+# It's impossible to multiply define a local substitution, but a lit config 
file
+# substitution can be multiply defined.  The trouble is we then don't know 
which
+# definition to redefine locally.
+
+# REDEFINE: %{global:multiple-exact}=foo
+# RUN: echo %{global:multiple-exact}
+
+#      CHECK: ValueError: Multiple substitutions whose patterns contain 
'%{global:multiple-exact}' are defined before '{{REDEFINE}}:' directive at line 
[[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: %{global:multiple-exact}
+# CHECK-NEXT: Existing pattern: %{global:multiple-exact}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-once-exact.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-once-exact.txt
new file mode 100644
index 0000000000000..3fa86a78e8101
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-multiple-once-exact.txt
@@ -0,0 +1,12 @@
+# There's only one substitution whose pattern matches exactly, but there's
+# another partial match, and that can lead to confusing expansions.
+
+# REDEFINE: %{global:multiple-once-exact}=foo
+# RUN: echo %{global:multiple-once-exact}
+
+#      CHECK: ValueError: Multiple substitutions whose patterns contain 
'%{global:multiple-once-exact}' are defined before '{{REDEFINE}}:' directive at 
line [[#@LINE-3]]
+# CHECK-NEXT: Existing pattern: <%{global:multiple-once-exact}>
+# CHECK-NEXT: Existing pattern: %{global:multiple-once-exact}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-none.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-none.txt
new file mode 100644
index 0000000000000..28ad04c71e9bf
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-none.txt
@@ -0,0 +1,7 @@
+# REDEFINE: %{local}=foo
+# RUN: echo %{local}
+
+#     CHECK: ValueError: No substitution for '%{local}' is defined before 
'{{REDEFINE}}:' directive at line [[#@LINE-3]]
+# CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-prefixes-pattern.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-prefixes-pattern.txt
new file mode 100644
index 0000000000000..cc82a0e0942ea
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-prefixes-pattern.txt
@@ -0,0 +1,9 @@
+# REDEFINE: %{global:prefix} = @
+# RUN: echo '%{global:prefix}(foo)'
+
+#      CHECK: ValueError: Existing substitution whose pattern contains 
'%{global:prefix}' does not have the pattern specified by '{{REDEFINE}}:' 
directive at line [[#@LINE-3]]
+# CHECK-NEXT: Expected pattern: %{global:prefix}
+# CHECK-NEXT: Existing pattern: %{global:prefix}\((.*)\)
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-suffixes-pattern.txt
 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-suffixes-pattern.txt
new file mode 100644
index 0000000000000..edbc124169b9f
--- /dev/null
+++ 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/defined-check/redefine-suffixes-pattern.txt
@@ -0,0 +1,9 @@
+# REDEFINE: %{global:suffix} = @
+# RUN: echo '@%{global:suffix}'
+
+#      CHECK: ValueError: Existing substitution whose pattern contains 
'%{global:suffix}' does not have the pattern specified by '{{REDEFINE}}:' 
directive at line [[#@LINE-3]]
+# CHECK-NEXT: Expected pattern: %{global:suffix}
+# CHECK-NEXT: Existing pattern: @%{global:suffix}
+#  CHECK-NOT: Existing pattern:
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/errors/location-range.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/location-range.txt
new file mode 100644
index 0000000000000..a230298ffd735
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/location-range.txt
@@ -0,0 +1,8 @@
+# DEFINE: %{foo}=Hello World
+# DEFINE: %{foo}=Hello \
+# DEFINE:   World
+# RUN: echo '%{foo}'
+
+# CHECK: ValueError: Substitution whose pattern contains '%{foo}' is already 
defined before '{{DEFINE}}:' directive from line [[#@LINE-4]] to [[#@LINE-3]]
+
+# CHECK: Unresolved: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/errors/no-run.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/errors/no-run.txt
new file mode 100644
index 0000000000000..842b9c47557bf
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/errors/no-run.txt
@@ -0,0 +1,8 @@
+# DEFINE and REDEFINE are not sufficient.  There must be a RUN.
+
+# DEFINE: %{local:echo}=foo
+# REDEFINE: %{global:echo}=bar
+
+# CHECK: Test has no '{{RUN}}:' line
+
+# CHECK: Unresolved: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/examples/param-subst.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/examples/param-subst.txt
new file mode 100644
index 0000000000000..6dd9b16304e05
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/examples/param-subst.txt
@@ -0,0 +1,34 @@
+; This example originally appeared in TestingGuide.rst except here we've added
+; echo to the clang/FileCheck command line to be executed.
+
+; DEFINE: %{cflags} =
+; DEFINE: %{fcflags} =
+
+; DEFINE: %{check} =                                                           
\
+; DEFINE:   echo '                                                             
\
+; DEFINE:   %clang_cc1 -verify -fopenmp -fopenmp-version=51 %{cflags}          
\
+; DEFINE:              -emit-llvm -o - %s |                                    
\
+; DEFINE:     FileCheck %{fcflags} %s                                          
\
+; DEFINE:   '
+
+; REDEFINE: %{cflags} = -triple x86_64-apple-darwin10.6.0 -fopenmp-simd
+; REDEFINE: %{fcflags} = -check-prefix=SIMD
+; RUN: %{check}
+; CHECK: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -triple 
x86_64-apple-darwin10.6.0 -fopenmp-simd -emit-llvm -o - {{.*}} | FileCheck 
-check-prefix=SIMD {{.*}}
+
+; REDEFINE: %{cflags} = -triple x86_64-unknown-linux-gnu -fopenmp-simd
+; REDEFINE: %{fcflags} = -check-prefix=SIMD
+; RUN: %{check}
+; CHECK: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -triple 
x86_64-unknown-linux-gnu -fopenmp-simd -emit-llvm -o - {{.*}} | FileCheck 
-check-prefix=SIMD {{.*}}
+
+; REDEFINE: %{cflags} = -triple x86_64-apple-darwin10.6.0
+; REDEFINE: %{fcflags} = -check-prefix=NO-SIMD
+; RUN: %{check}
+; CHECK: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -triple 
x86_64-apple-darwin10.6.0 -emit-llvm -o - {{.*}} | FileCheck 
-check-prefix=NO-SIMD {{.*}}
+
+; REDEFINE: %{cflags} = -triple x86_64-unknown-linux-gnu
+; REDEFINE: %{fcflags} = -check-prefix=NO-SIMD
+; RUN: %{check}
+; CHECK: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -triple 
x86_64-unknown-linux-gnu -emit-llvm -o - {{.*}} | FileCheck 
-check-prefix=NO-SIMD {{.*}}
+
+; CHECK: Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/expansion-order.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/expansion-order.txt
new file mode 100644
index 0000000000000..3bf057151afb7
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/expansion-order.txt
@@ -0,0 +1,41 @@
+# Redefine the %{global:greeting} parameter of %{global:echo} before using it.
+# The necessary expansion order was established in the test suite config
+# (%{global:echo} before %{global:greeting}), and redefining the parameter
+# doesn't change that expansion order.  That order would be reversed if
+# %{global:greeting} were handled as a new substitution, preventing full
+# expansion.
+#
+# REDEFINE: %{global:greeting}=Hello
+# RUN: %{global:echo}
+# CHECK: GLOBAL: Hello World
+
+# We can redefine the test suite config's substitutions multiple times.  Again,
+# the expansion order remains the same (%{global:echo} before 
%{global:greeting}
+# before %{global:what}) but would be reversed if these were handled as new
+# substitutions, preventing full expansion.
+#
+# REDEFINE: %{global:greeting}=Goodbye %{global:what}
+# REDEFINE: %{global:what}=Sleep
+# RUN: %{global:echo}
+# CHECK: GLOBAL: Goodbye Sleep Sleep
+
+# A new local substitution is prepended to the substitution list so that it can
+# depend on all substitutions that were defined previously, including those 
from
+# the test suite config.
+#
+# DEFINE: %{local:greeting}=Hey %{global:what}
+# DEFINE: %{local:echo}=echo "LOCAL: %{local:greeting} %{global:what}"
+# RUN: %{local:echo}
+# CHECK: LOCAL: Hey Sleep Sleep
+
+# As for substitutions from the test suite config, redefining local
+# substitutions should not change the expansion order.  Again, the expansion
+# order would be reversed if these were instead handled as new substitutions,
+# preventing full expansion.
+#
+# REDEFINE: %{local:greeting}=So Long %{global:what}
+# REDEFINE: %{global:what}=World
+# RUN: %{local:echo}
+# CHECK: LOCAL: So Long World World
+
+# CHECK: Passed: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/line-number-substitutions.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/line-number-substitutions.txt
new file mode 100644
index 0000000000000..65f90792ff7bc
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/line-number-substitutions.txt
@@ -0,0 +1,32 @@
+# Does it work as expected directly in RUN lines?
+# RUN: echo %(line), %(line-1), %(line+2)
+# CHECK: [[#@LINE-1]], [[#@LINE-2]], [[#@LINE+1]]
+
+# %(line) substitutions refer to the original DEFINE/REDEFINE line not the RUN
+# line they eventually appear within.
+#
+# DEFINE: %{lines} = %(line)
+# RUN: echo '%{lines}'
+# CHECK: [[#@LINE-2]]
+#
+# REDEFINE: %{lines} = %(line),                                                
\
+# REDEFINE:            %(line),                                                
\
+# REDEFINE:            %(line)
+# RUN: echo '%{lines}'
+# CHECK: [[#@LINE-4]], [[#@LINE-3]], [[#@LINE-2]]
+
+# %(line+N) and %{line-N) should work too.
+#
+# DEFINE: %{lines-rel} = %(line+1),                                            
\
+# DEFINE:                %(line),                                              
\
+# DEFINE:                %(line-1)
+# RUN: echo '%{lines-rel}'
+# CHECK: [[#@LINE-3]], [[#@LINE-3]], [[#@LINE-3]]
+#
+# REDEFINE: %{lines-rel} = %(line+5),                                          
\
+# REDEFINE:                %(line+0),                                          
\
+# REDEFINE:                %(line-10)
+# RUN: echo '%{lines-rel}'
+# CHECK: [[#@LINE+1]], [[#@LINE-3]], [[#@LINE-12]]
+
+# CHECK: Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/lit.cfg 
b/llvm/utils/lit/tests/Inputs/shtest-define/lit.cfg
new file mode 100644
index 0000000000000..acdb27d005b3a
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/lit.cfg
@@ -0,0 +1,47 @@
+import lit.formats
+config.name = 'shtest-define'
+config.suffixes = ['.txt']
+config.test_format = lit.formats.ShTest()
+config.test_source_root = None
+config.test_exec_root = None
+
+# When config.recursiveExpansionLimit is not specified, it's important to
+# prepend substitutions before substitutions they might now or later (upon a
+# redefinition) depend upon.  For example, %{global:greeting} and 
%{global:what}
+# act as parameters for %{global:echo}, so we make sure the latter expands
+# before the former.  Moreover, some tests redefine %{global:greeting} in terms
+# of %{global:what}, so we make sure the former expands before the latter.
+# If we always insert at the beginning of the substitution list (as DEFINE
+# does), then the rule is simple: define a substitution before you refer to it.
+config.substitutions.insert(0, ('%{global:what}', 'World'))
+config.substitutions.insert(0, ('%{global:greeting}', ''))
+config.substitutions.insert(0,
+    ('%{global:echo}', "echo GLOBAL: %{global:greeting} %{global:what}"))
+
+# The following substitution definitions are confusing and should be avoided.
+# We define them here so we can test that 'DEFINE:' and 'REDEFINE:' directives
+# guard against the confusion they cause.
+
+# Even though each of '%{global:inside}', '%{global:prefix}', and
+# '%{global:suffix}' is not already the exact pattern of a substitution,
+# 'DEFINE:' and 'REDEFINE:' will refuse to (re)define a substitution with that
+# pattern because it is a substring of one of the following substitution's
+# patterns.
+config.substitutions.insert(0, ('<%{global:inside}>', '<@>'))
+config.substitutions.insert(0, (r'%{global:prefix}\((.*)\)', r'@(\g<1>)'))
+config.substitutions.insert(0, ('@%{global:suffix}', '@@'))
+
+# These cannot be redefined by 'REDEFINE:', which doesn't know which one to
+# redefine.
+config.substitutions.insert(0, ('%{global:multiple-exact}', 'first'))
+config.substitutions.insert(0, ('%{global:multiple-exact}', 'second'))
+
+# Even though '%{global:multiple-once-exact}' is the exact pattern of only one
+# existing substitution, 'REDEFINE:' will refuse to redefine that substitution
+# because that string is a substring of another substitution's pattern.
+config.substitutions.insert(0, ('%{global:multiple-once-exact}', '@'))
+config.substitutions.insert(0, ('<%{global:multiple-once-exact}>', '<@>'))
+
+recur = lit_config.params.get('recur', None)
+if recur:
+  config.recursiveExpansionLimit = int(recur)

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/name-chars.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/name-chars.txt
new file mode 100644
index 0000000000000..18a27cdd72fa6
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/name-chars.txt
@@ -0,0 +1,25 @@
+# DEFINE: %{abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789} = 
ok
+# RUN: echo '%{abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789}'
+# CHECK: ok
+
+# DEFINE: %{FooBar} = ok at %(line)
+# RUN: echo '%{FooBar}'
+# CHECK: ok at [[#@LINE - 2]]
+
+# DEFINE: %{fooBar} = ok at %(line)
+# RUN: echo '%{fooBar}'
+# CHECK: ok at [[#@LINE - 2]]
+
+# DEFINE: %{foo-bar-} = ok at %(line)
+# RUN: echo '%{foo-bar-}'
+# CHECK: ok at [[#@LINE - 2]]
+
+# DEFINE: %{foo:bar:} = ok at %(line)
+# RUN: echo '%{foo:bar:}'
+# CHECK: ok at [[#@LINE - 2]]
+
+# DEFINE: %{_foo_bar_} = ok at %(line)
+# RUN: echo '%{_foo_bar_}'
+# CHECK: ok at [[#@LINE - 2]]
+
+# CHECK: Passed: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/recursiveExpansionLimit.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/recursiveExpansionLimit.txt
new file mode 100644
index 0000000000000..eb5f0b918fd19
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/recursiveExpansionLimit.txt
@@ -0,0 +1,12 @@
+# These are defined in the wrong order for non-recursive expansion: %{inner} is
+# defined after it's referenced.
+
+# DEFINE: %{outer}=%{inner}
+# DEFINE: %{inner}=expanded
+
+# RUN: echo '%{outer}'
+
+# CHECK-NON-RECUR:%{inner}
+# CHECK-RECUR:expanded
+
+# CHECK: Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-0.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-0.txt
new file mode 100644
index 0000000000000..dfff9ddd4336f
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-0.txt
@@ -0,0 +1,22 @@
+# RUN: echo shared-substs-0.txt
+
+# Make sure we don't see modifications that other shared-substs-*.txt have made
+# to the test suite config's substitutions.
+#
+# RUN: %{global:echo}
+
+# Next, modify substs that would affect the above.  Verify they are set 
locally.
+#
+# REDEFINE: %{global:what}=LOCAL0:World
+# REDEFINE: %{global:greeting}=LOCAL0:Hello
+# REDEFINE: %{global:echo}=echo LOCAL0: %{global:greeting} %{global:what}
+# RUN: %{global:echo}
+
+# Finally, set a local that other shared-substs-*.txt also set to be sure
+# there's no redefinition complaint because they left it behind.  Verify it is
+# set locally.
+#
+# DEFINE: %{local:echo}=echo LOCAL0: subst
+# RUN: %{local:echo}
+
+# CHECK: Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-1.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-1.txt
new file mode 100644
index 0000000000000..723165a67100b
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/shared-substs-1.txt
@@ -0,0 +1,22 @@
+# RUN: echo shared-substs-1.txt
+
+# Make sure we don't see modifications that other shared-substs-*.txt have made
+# to the test suite config's substitutions.
+#
+# RUN: %{global:echo}
+
+# Next, modify substs that would affect the above.  Verify they are set 
locally.
+#
+# REDEFINE: %{global:what}=LOCAL1:World
+# REDEFINE: %{global:greeting}=LOCAL1:Hello
+# REDEFINE: %{global:echo}=echo LOCAL1: %{global:greeting} %{global:what}
+# RUN: %{global:echo}
+
+# Finally, set a local that other shared-substs-*.txt also set to be sure
+# there's no redefinition complaint because they left it behind.  Verify it is
+# set locally.
+#
+# DEFINE: %{local:echo}=echo LOCAL1: subst
+# RUN: %{local:echo}
+
+# CHECK: Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/value-equals.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/value-equals.txt
new file mode 100644
index 0000000000000..9d2e7197fb3a6
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/value-equals.txt
@@ -0,0 +1,22 @@
+# After the initial '=', the value can contain '=' with no special meaning.
+
+# DEFINE: %{equals} = FileCheck -check-prefixes=FOO,BAR
+# RUN: echo '%{equals}'
+# CHECK: FileCheck -check-prefixes=FOO,BAR
+#
+# REDEFINE: %{equals} == == =
+# RUN: echo '%{equals}'
+# CHECK: = == =
+
+# DEFINE: %{continue-equals} = FileCheck -strict-whitespace -match-full-lines \
+# DEFINE:                      -check-prefixes=FOO,BAR
+# RUN: echo '%{continue-equals}'
+# CHECK: FileCheck -strict-whitespace -match-full-lines -check-prefixes=FOO,BAR
+#
+# REDEFINE: %{continue-equals} = FileCheck -input-file=test.txt                
\
+# REDEFINE:                      -implicit-check-not=foobar                    
\
+# REDEFINE:                      -check-prefixes=FOO,BAR
+# RUN: echo '%{continue-equals}'
+# CHECK: FileCheck -input-file=test.txt -implicit-check-not=foobar 
-check-prefixes=FOO,BAR
+
+# CHECK: Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-define/value-escaped.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/value-escaped.txt
new file mode 100644
index 0000000000000..844d53ebc54ed
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/value-escaped.txt
@@ -0,0 +1,13 @@
+# Escape sequences that can appear in python re.sub replacement strings have no
+# special meaning in the value.
+
+# DEFINE: %{escape} = \g<0>\n
+# RUN: echo '%{escape}'
+# CHECK: \g<0>\n
+
+# REDEFINE: %{escape} = \n                                                     
\
+# REDEFINE:             \g<param>
+# RUN: echo '%{escape}'
+# CHECK: \n \g<param>
+
+# CHECK: Passed: 1

diff  --git 
a/llvm/utils/lit/tests/Inputs/shtest-define/ws-and-continuations.txt 
b/llvm/utils/lit/tests/Inputs/shtest-define/ws-and-continuations.txt
new file mode 100644
index 0000000000000..1259e511ba701
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-define/ws-and-continuations.txt
@@ -0,0 +1,135 @@
+# Empty values are permitted and reasonable, especially when just establishing
+# expansion order.
+#
+# DEFINE: %{empty}=
+# RUN: echo "'%{empty}'"
+# CHECK:''
+#
+# REDEFINE: %{empty}=
+# RUN: echo "'%{empty}'"
+# CHECK:''
+
+# A value consisting only of whitespace is trimmed to the empty string.
+#
+#               v~~ intentional whitespace
+# DEFINE: %{ws}=   
+# RUN: echo "'%{ws}'"
+# CHECK:''
+#
+#                 v intentional whitespace
+# REDEFINE: %{ws}= 
+# RUN: echo "'%{ws}'"
+# CHECK:''
+
+# Whitespace is not required around the name or value.
+#
+# DEFINE:%{no-whitespace}=abc
+# RUN: echo "'%{no-whitespace}'"
+# CHECK:'abc'
+#
+# REDEFINE:%{no-whitespace}=HelloWorld
+# RUN: echo "'%{no-whitespace}'"
+# CHECK:'HelloWorld'
+
+# Whitespace is not required between substitutions in a value.
+#
+# DEFINE: %{adjacent0} = foo
+# DEFINE: %{adjacent1} = bar
+# DEFINE: %{has-adjacent-substs} = %{adjacent0}%{adjacent1}
+# RUN: echo "'%{has-adjacent-substs}'"
+# CHECK:'foobar'
+#
+# REDEFINE: %{has-adjacent-substs} = %{adjacent0}%{adjacent1}%{adjacent0}
+# RUN: echo "'%{has-adjacent-substs}'"
+# CHECK:'foobarfoo'
+
+# Exact whitespace is preserved within the value, but whitespace enclosing the
+# name or value is discarded.  ('%{' and '}' are part of the name, and
+# whitespace in between isn't permitted.)
+#
+#                                       v~~ intentional whitespace
+# DEFINE:   %{whitespace}  =  abc    def   
+# RUN: echo "'%{whitespace}'"
+# CHECK:'abc    def'
+#                                      v intentional whitespace
+# REDEFINE: %{whitespace} = Hello World 
+# RUN: echo "'%{whitespace}'"
+# CHECK:'Hello World'
+
+# Line continuations in the value are permitted and collapse whitespace.
+#
+# DEFINE: %{continue} = abc\
+# DEFINE:def \
+# DEFINE:ghi\
+# DEFINE: jkl \
+# DEFINE: mno  \
+# DEFINE:  pqr 
+#             ^ intentional whitespace
+# RUN: echo "'%{continue}'"
+# CHECK:'abc def ghi jkl mno pqr'
+#
+# REDEFINE: %{continue} =  abc  \
+# REDEFINE: def
+# RUN: echo "'%{continue}'"
+# CHECK:'abc def'
+
+# Whitespace at the end of the line after a '\' is ignored, and it's treated as
+# a line continuation.  Otherwise, the behavior would be hard to understand
+# because it looks like a line continuation.
+#
+#                                   v~~~~~~~~~~~ intentional whitespace
+# DEFINE: %{ws-after-continue}=foo \            
+# DEFINE: bar                      \ 
+#                                   ^ intentional whitespace
+# DEFINE: baz
+# RUN: echo "'%{ws-after-continue}'"
+# CHECK:'foo bar baz'
+#
+#                                     v intentional whitespace
+# REDEFINE: %{ws-after-continue}=foo \ 
+# REDEFINE: bar                      \            
+#                                     ^~~~~~~~~~~~ intentional whitespace
+# REDEFINE: baz
+# RUN: echo "'%{ws-after-continue}'"
+# CHECK:'foo bar baz'
+
+# A line continuation is recognized anywhere.  It should be used only where
+# whitespace is permitted because it reduces to a single space.
+#
+# Directives with at least one non-whitespace character (could be '\') are
+# permitted even if they contribute nothing to the value.  There might be no
+# practical use, but check that it behaves as expected.
+#
+# DEFINE:\
+# DEFINE:%{blank-lines}\
+# DEFINE:\
+# DEFINE:=\
+# DEFINE:\
+# DEFINE:a
+# RUN: echo "'%{blank-lines}'"
+# CHECK:'a'
+#
+# REDEFINE:                  \
+# REDEFINE: %{blank-lines}   \
+# REDEFINE:                  \
+# REDEFINE:                = \
+# REDEFINE:                  \
+# REDEFINE:      a           \
+# REDEFINE:                  \
+# REDEFINE:      b           \
+# REDEFINE:                  \
+# REDEFINE:      c
+# RUN: echo "'%{blank-lines}'"
+# CHECK:'a b c'
+
+# The fourth DEFINE line is deceptive because it looks like a new substitution,
+# but it's actually a continuation of the previous value.
+#
+# DEFINE: %{name}=x
+# DEFINE: %{value}=3
+# DEFINE: %{deceptive-continue}=echo \
+# DEFINE: %{name}=%{value}
+# RUN: %{deceptive-continue}
+# CHECK:x=3
+
+# CHECK:{{ *}}Passed: 1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-shell/continuations.txt 
b/llvm/utils/lit/tests/Inputs/shtest-shell/continuations.txt
new file mode 100644
index 0000000000000..4872733b16abd
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-shell/continuations.txt
@@ -0,0 +1,17 @@
+RUN: echo 'foo' \
+RUN:      'bar' >> %t.out
+CHECK: foo bar
+
+RUN: echo 'foo' \
+RUN:      'bar' \
+RUN:      'baz' >> %t.out
+CHECK: foo bar baz
+
+#                 v~~ intentional whitespace
+RUN: echo 'foo'  \   
+RUN:      'bar'  \ 
+#                 ^ intentional whitespace
+RUN:      'baz' >> %t.out
+CHECK: foo bar baz
+
+RUN: FileCheck -match-full-lines -input-file=%t.out %s

diff  --git a/llvm/utils/lit/tests/Inputs/testrunner-custom-parsers/test.txt 
b/llvm/utils/lit/tests/Inputs/testrunner-custom-parsers/test.txt
index 5809af5477ceb..31e835d9bd4df 100644
--- a/llvm/utils/lit/tests/Inputs/testrunner-custom-parsers/test.txt
+++ b/llvm/utils/lit/tests/Inputs/testrunner-custom-parsers/test.txt
@@ -18,5 +18,10 @@
 //
 // MY_BOOL_UNTERMINATED: a \
 //
+// MY_DEFINE: %{name} = value one
+//
+// MY_REDEFINE: %{name} = value \
+// MY_REDEFINE:           two
+//
 // END.
 // MY_LIST: five

diff  --git a/llvm/utils/lit/tests/shtest-define.py 
b/llvm/utils/lit/tests/shtest-define.py
new file mode 100644
index 0000000000000..4bedda96e638d
--- /dev/null
+++ b/llvm/utils/lit/tests/shtest-define.py
@@ -0,0 +1,167 @@
+# We're using DEFINE/REDEFINE to help us write tests for DEFINE/REDEFINE.
+
+# RUN: echo '-- Available Tests --' > %t.tests.actual.txt
+
+# DEFINE: %{my-inputs} = %{inputs}/shtest-define
+
+# DEFINE: %{test} =
+# DEFINE: %{lit-pre} =
+# DEFINE: %{lit-args} =
+# DEFINE: %{fc-args} =
+# DEFINE: %{run-test} =                                                        
\
+# DEFINE:   %{lit-pre} %{lit} -va  %{lit-args} %{my-inputs}/%{test} 2>&1 |     
\
+# DEFINE:     FileCheck -match-full-lines %{fc-args} %{my-inputs}/%{test}
+# DEFINE: %{record-test} =                                                     
\
+# DEFINE:   echo '  shtest-define :: %{test}' >> %t.tests.actual.txt
+# DEFINE: %{run-and-record-test} = %{run-test} && %{record-test}
+
+# REDEFINE: %{lit-pre} = not
+#
+# REDEFINE: %{test} = errors/assignment/before-name.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/between-name-equals.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/braces-empty.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/braces-with-dot.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/braces-with-equals.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/braces-with-newline.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/braces-with-number.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/braces-with-ws.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/empty.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/no-equals.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/no-name.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/assignment/ws-only.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/empty.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/end-in-double-backslash.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-define-bad-redefine.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-define-continuation.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-define-redefine.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-define-run.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-define.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-redefine-bad-define.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = 
errors/continuation/unterminated-redefine-continuation.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-redefine-define.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-redefine-run.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-redefine.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-run-define.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/unterminated-run-redefine.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/continuation/ws-only.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-already-by-config.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-already-by-test.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-inside-pattern.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-multiple-exact.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-multiple-once-exact.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-prefixes-pattern.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/define-suffixes-pattern.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/redefine-inside-pattern.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/redefine-multiple-exact.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/redefine-multiple-once-exact.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/redefine-none.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/redefine-prefixes-pattern.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/defined-check/redefine-suffixes-pattern.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/location-range.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{test} = errors/no-run.txt
+# RUN: %{run-and-record-test}
+#
+# REDEFINE: %{lit-pre} =
+
+# REDEFINE: %{test} = examples/param-subst.txt
+# RUN: %{run-and-record-test}
+
+# REDEFINE: %{test} = expansion-order.txt
+# RUN: %{run-and-record-test}
+
+# REDEFINE: %{test} = line-number-substitutions.txt
+# RUN: %{run-and-record-test}
+
+# REDEFINE: %{test} = name-chars.txt
+# RUN: %{run-and-record-test}
+
+# REDEFINE: %{test} = recursiveExpansionLimit.txt
+#
+# REDEFINE: %{fc-args} = -check-prefix=CHECK-NON-RECUR
+# RUN: %{run-test}
+#
+# REDEFINE: %{lit-args} = -Drecur=2
+# REDEFINE: %{fc-args} = -check-prefix=CHECK-RECUR
+# RUN: %{run-test}
+#
+# RUN: %{record-test}
+# REDEFINE: %{lit-args} =
+# REDEFINE: %{fc-args} =
+
+# Check that per-test changes to substitutions don't affect other tests in the
+# same LIT invocation.
+#
+# RUN: %{lit} -va %{my-inputs}/shared-substs-*.txt 2>&1 |                      
\
+# RUN:   FileCheck -check-prefix=SHARED-SUBSTS -match-full-lines %s
+#
+# SHARED-SUBSTS: shared-substs-0.txt
+# SHARED-SUBSTS: GLOBAL: World
+# SHARED-SUBSTS: LOCAL0: LOCAL0:Hello LOCAL0:World
+# SHARED-SUBSTS: LOCAL0: subst
+#
+# SHARED-SUBSTS: shared-substs-1.txt
+# SHARED-SUBSTS: GLOBAL: World
+# SHARED-SUBSTS: LOCAL1: LOCAL1:Hello LOCAL1:World
+# SHARED-SUBSTS: LOCAL1: subst
+#
+# REDEFINE: %{test} = shared-substs-0.txt
+# RUN: %{record-test}
+# REDEFINE: %{test} = shared-substs-1.txt
+# RUN: %{record-test}
+
+# REDEFINE: %{test} = value-equals.txt
+# RUN: %{run-and-record-test}
+
+# REDEFINE: %{test} = value-escaped.txt
+# RUN: %{run-and-record-test}
+
+# REDEFINE: %{fc-args} = -strict-whitespace
+# REDEFINE: %{test} = ws-and-continuations.txt
+# RUN: %{run-and-record-test}
+# REDEFINE: %{fc-args} =
+
+# Make sure we didn't forget to run something.
+#
+# RUN: %{lit} --show-tests %{my-inputs} > %t.tests.expected.txt
+# RUN: 
diff  -u %t.tests.expected.txt %t.tests.actual.txt

diff  --git a/llvm/utils/lit/tests/shtest-keyword-parse-errors.py 
b/llvm/utils/lit/tests/shtest-keyword-parse-errors.py
index 0d4c693f9bd65..2b42d748b54db 100644
--- a/llvm/utils/lit/tests/shtest-keyword-parse-errors.py
+++ b/llvm/utils/lit/tests/shtest-keyword-parse-errors.py
@@ -12,4 +12,4 @@
 # CHECK:       {{^}}Test has more than one ALLOW_RETRIES lines{{$}}
 
 # CHECK-LABEL: UNRESOLVED: shtest-keyword-parse-errors :: unterminated-run.txt
-# CHECK:       {{^}}Test has unterminated 'RUN:' lines (with '\'){{$}}
+# CHECK:       {{^}}Test has unterminated 'RUN:' directive (with '\') at line 
1{{$}}

diff  --git a/llvm/utils/lit/tests/shtest-shell.py 
b/llvm/utils/lit/tests/shtest-shell.py
index 595960ce79e76..93f05dbd35d0d 100644
--- a/llvm/utils/lit/tests/shtest-shell.py
+++ b/llvm/utils/lit/tests/shtest-shell.py
@@ -42,6 +42,8 @@
 # CHECK: error: command failed with exit status: 127
 # CHECK: ***
 
+# CHECK: PASS: shtest-shell :: continuations.txt
+
 # CHECK: PASS: shtest-shell :: dev-null.txt
 
 # CHECK: FAIL: shtest-shell :: 
diff -b.txt

diff  --git a/llvm/utils/lit/tests/unit/TestRunner.py 
b/llvm/utils/lit/tests/unit/TestRunner.py
index 7022473c2d8ca..e8780502f4e72 100644
--- a/llvm/utils/lit/tests/unit/TestRunner.py
+++ b/llvm/utils/lit/tests/unit/TestRunner.py
@@ -62,7 +62,8 @@ def custom_parse(line_number, line, output):
             IntegratedTestKeywordParser("MY_RUN:", ParserKind.COMMAND),
             IntegratedTestKeywordParser("MY_CUSTOM:", ParserKind.CUSTOM,
                                         custom_parse),
-
+            IntegratedTestKeywordParser("MY_DEFINE:", ParserKind.DEFINE),
+            IntegratedTestKeywordParser("MY_REDEFINE:", ParserKind.REDEFINE),
         ]
 
     @staticmethod
@@ -105,8 +106,10 @@ def test_commands(self):
         cmd_parser = self.get_parser(parsers, 'MY_RUN:')
         value = cmd_parser.getValue()
         self.assertEqual(len(value), 2)  # there are only two run lines
-        self.assertEqual(value[0].strip(), "%dbg(MY_RUN: at line 4)  baz")
-        self.assertEqual(value[1].strip(), "%dbg(MY_RUN: at line 7)  foo  bar")
+        self.assertEqual(value[0].command.strip(),
+                         "%dbg(MY_RUN: at line 4)  baz")
+        self.assertEqual(value[1].command.strip(),
+                         "%dbg(MY_RUN: at line 7)  foo  bar")
 
     def test_boolean(self):
         parsers = self.make_parsers()
@@ -164,6 +167,26 @@ def test_custom(self):
         value = custom_parser.getValue()
         self.assertEqual(value, ['a', 'b', 'c'])
 
+    def test_defines(self):
+        parsers = self.make_parsers()
+        self.parse_test(parsers)
+        cmd_parser = self.get_parser(parsers, 'MY_DEFINE:')
+        value = cmd_parser.getValue()
+        self.assertEqual(len(value), 1) # there's only one MY_DEFINE directive
+        self.assertEqual(value[0].new_subst, True)
+        self.assertEqual(value[0].name, '%{name}')
+        self.assertEqual(value[0].value, 'value one')
+
+    def test_redefines(self):
+        parsers = self.make_parsers()
+        self.parse_test(parsers)
+        cmd_parser = self.get_parser(parsers, 'MY_REDEFINE:')
+        value = cmd_parser.getValue()
+        self.assertEqual(len(value), 1) # there's only one MY_REDEFINE 
directive
+        self.assertEqual(value[0].new_subst, False)
+        self.assertEqual(value[0].name, '%{name}')
+        self.assertEqual(value[0].value, 'value two')
+
     def test_bad_keywords(self):
         def custom_parse(line_number, line, output):
             return output


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to