ART: New types of Checker assertions

Checker now supports positive out-of-order assertions (CHECK-DAG),
which are useful for matching dependency graphs, and negative
assertions (CHECK-NOT) to test element removal.

ConstantFolding tests are rewritten using -DAG checks and Inliner
tests are added.

Change-Id: I5afb665f532b24683624b6d21ef4377cb441d731
diff --git a/compiler/optimizing/test/ConstantFolding.java b/compiler/optimizing/test/ConstantFolding.java
index 7fac5a9..92f2a77 100644
--- a/compiler/optimizing/test/ConstantFolding.java
+++ b/compiler/optimizing/test/ConstantFolding.java
@@ -22,13 +22,13 @@
    */
 
   // CHECK-START: int ConstantFolding.IntNegation() constant_folding (before)
-  // CHECK:   [[Const42:i[0-9]+]]  IntConstant 42
-  // CHECK:   [[Neg:i[0-9]+]]      Neg [ [[Const42]] ]
-  // CHECK:                        Return [ [[Neg]] ]
+  // CHECK-DAG:     [[Const42:i[0-9]+]]  IntConstant 42
+  // CHECK-DAG:     [[Neg:i[0-9]+]]      Neg [ [[Const42]] ]
+  // CHECK-DAG:                          Return [ [[Neg]] ]
 
   // CHECK-START: int ConstantFolding.IntNegation() constant_folding (after)
-  // CHECK:   [[ConstN42:i[0-9]+]] IntConstant -42
-  // CHECK:                        Return [ [[ConstN42]] ]
+  // CHECK-DAG:     [[ConstN42:i[0-9]+]] IntConstant -42
+  // CHECK-DAG:                          Return [ [[ConstN42]] ]
 
   public static int IntNegation() {
     int x, y;
@@ -43,14 +43,14 @@
    */
 
   // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (before)
-  // CHECK:   [[Const1:i[0-9]+]]   IntConstant 1
-  // CHECK:   [[Const2:i[0-9]+]]   IntConstant 2
-  // CHECK:   [[Add:i[0-9]+]]      Add [ [[Const1]] [[Const2]] ]
-  // CHECK:                        Return [ [[Add]] ]
+  // CHECK-DAG:     [[Const1:i[0-9]+]]  IntConstant 1
+  // CHECK-DAG:     [[Const2:i[0-9]+]]  IntConstant 2
+  // CHECK-DAG:     [[Add:i[0-9]+]]     Add [ [[Const1]] [[Const2]] ]
+  // CHECK-DAG:                         Return [ [[Add]] ]
 
   // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (after)
-  // CHECK:   [[Const3:i[0-9]+]]   IntConstant 3
-  // CHECK:                        Return [ [[Const3]] ]
+  // CHECK-DAG:     [[Const3:i[0-9]+]]  IntConstant 3
+  // CHECK-DAG:                         Return [ [[Const3]] ]
 
   public static int IntAddition1() {
     int a, b, c;
@@ -66,18 +66,18 @@
   */
 
   // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (before)
-  // CHECK:   [[Const1:i[0-9]+]]   IntConstant 1
-  // CHECK:   [[Const2:i[0-9]+]]   IntConstant 2
-  // CHECK:   [[Const5:i[0-9]+]]   IntConstant 5
-  // CHECK:   [[Const6:i[0-9]+]]   IntConstant 6
-  // CHECK:   [[Add1:i[0-9]+]]     Add [ [[Const1]] [[Const2]] ]
-  // CHECK:   [[Add2:i[0-9]+]]     Add [ [[Const5]] [[Const6]] ]
-  // CHECK:   [[Add3:i[0-9]+]]     Add [ [[Add1]] [[Add2]] ]
-  // CHECK:                        Return [ [[Add3]] ]
+  // CHECK-DAG:     [[Const1:i[0-9]+]]  IntConstant 1
+  // CHECK-DAG:     [[Const2:i[0-9]+]]  IntConstant 2
+  // CHECK-DAG:     [[Const5:i[0-9]+]]  IntConstant 5
+  // CHECK-DAG:     [[Const6:i[0-9]+]]  IntConstant 6
+  // CHECK-DAG:     [[Add1:i[0-9]+]]    Add [ [[Const1]] [[Const2]] ]
+  // CHECK-DAG:     [[Add2:i[0-9]+]]    Add [ [[Const5]] [[Const6]] ]
+  // CHECK-DAG:     [[Add3:i[0-9]+]]    Add [ [[Add1]] [[Add2]] ]
+  // CHECK-DAG:                         Return [ [[Add3]] ]
 
   // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (after)
-  // CHECK:   [[Const14:i[0-9]+]]  IntConstant 14
-  // CHECK:                        Return [ [[Const14]] ]
+  // CHECK-DAG:     [[Const14:i[0-9]+]] IntConstant 14
+  // CHECK-DAG:                         Return [ [[Const14]] ]
 
   public static int IntAddition2() {
     int a, b, c;
@@ -97,14 +97,14 @@
    */
 
   // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (before)
-  // CHECK:   [[Const5:i[0-9]+]]   IntConstant 5
-  // CHECK:   [[Const2:i[0-9]+]]   IntConstant 2
-  // CHECK:   [[Sub:i[0-9]+]]      Sub [ [[Const5]] [[Const2]] ]
-  // CHECK:                        Return [ [[Sub]] ]
+  // CHECK-DAG:     [[Const5:i[0-9]+]]  IntConstant 5
+  // CHECK-DAG:     [[Const2:i[0-9]+]]  IntConstant 2
+  // CHECK-DAG:     [[Sub:i[0-9]+]]     Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:                         Return [ [[Sub]] ]
 
   // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (after)
-  // CHECK:   [[Const3:i[0-9]+]]   IntConstant 3
-  // CHECK:                        Return [ [[Const3]] ]
+  // CHECK-DAG:     [[Const3:i[0-9]+]]  IntConstant 3
+  // CHECK-DAG:                         Return [ [[Const3]] ]
 
   public static int IntSubtraction() {
     int a, b, c;
@@ -120,14 +120,14 @@
    */
 
   // CHECK-START: long ConstantFolding.LongAddition() constant_folding (before)
-  // CHECK:   [[Const1:j[0-9]+]]   LongConstant 1
-  // CHECK:   [[Const2:j[0-9]+]]   LongConstant 2
-  // CHECK:   [[Add:j[0-9]+]]      Add [ [[Const1]] [[Const2]] ]
-  // CHECK:                        Return [ [[Add]] ]
+  // CHECK-DAG:     [[Const1:j[0-9]+]]  LongConstant 1
+  // CHECK-DAG:     [[Const2:j[0-9]+]]  LongConstant 2
+  // CHECK-DAG:     [[Add:j[0-9]+]]     Add [ [[Const1]] [[Const2]] ]
+  // CHECK-DAG:                         Return [ [[Add]] ]
 
   // CHECK-START: long ConstantFolding.LongAddition() constant_folding (after)
-  // CHECK:   [[Const3:j[0-9]+]]   LongConstant 3
-  // CHECK:                        Return [ [[Const3]] ]
+  // CHECK-DAG:     [[Const3:j[0-9]+]]  LongConstant 3
+  // CHECK-DAG:                         Return [ [[Const3]] ]
 
   public static long LongAddition() {
     long a, b, c;
@@ -143,14 +143,14 @@
    */
 
   // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (before)
-  // CHECK:   [[Const5:j[0-9]+]]   LongConstant 5
-  // CHECK:   [[Const2:j[0-9]+]]   LongConstant 2
-  // CHECK:   [[Sub:j[0-9]+]]      Sub [ [[Const5]] [[Const2]] ]
-  // CHECK:                        Return [ [[Sub]] ]
+  // CHECK-DAG:     [[Const5:j[0-9]+]]  LongConstant 5
+  // CHECK-DAG:     [[Const2:j[0-9]+]]  LongConstant 2
+  // CHECK-DAG:     [[Sub:j[0-9]+]]     Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:                         Return [ [[Sub]] ]
 
   // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (after)
-  // CHECK:   [[Const3:j[0-9]+]]   LongConstant 3
-  // CHECK:                        Return [ [[Const3]] ]
+  // CHECK-DAG:     [[Const3:j[0-9]+]]  LongConstant 3
+  // CHECK-DAG:                         Return [ [[Const3]] ]
 
   public static long LongSubtraction() {
     long a, b, c;
@@ -165,14 +165,14 @@
    */
 
   // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (before)
-  // CHECK:   [[Const5:i[0-9]+]]   IntConstant 5
-  // CHECK:   [[Const2:i[0-9]+]]   IntConstant 2
-  // CHECK:   [[Cond:z[0-9]+]]     GreaterThanOrEqual [ [[Const5]] [[Const2]] ]
-  // CHECK:                        If [ [[Cond]] ]
+  // CHECK-DAG:     [[Const5:i[0-9]+]]  IntConstant 5
+  // CHECK-DAG:     [[Const2:i[0-9]+]]  IntConstant 2
+  // CHECK-DAG:     [[Cond:z[0-9]+]]    GreaterThanOrEqual [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:                         If [ [[Cond]] ]
 
   // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (after)
-  // CHECK:   [[Const1:i[0-9]+]]   IntConstant 1
-  // CHECK:                        If [ [[Const1]] ]
+  // CHECK-DAG:     [[Const1:i[0-9]+]]  IntConstant 1
+  // CHECK-DAG:                         If [ [[Const1]] ]
 
   public static int StaticCondition() {
     int a, b, c;
@@ -195,18 +195,18 @@
    */
 
   // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (before)
-  // CHECK:   [[Const5:i[0-9]+]]   IntConstant 5
-  // CHECK:   [[Const2:i[0-9]+]]   IntConstant 2
-  // CHECK:   [[Add:i[0-9]+]]      Add [ [[Const5]] [[Const2]] ]
-  // CHECK:   [[Phi:i[0-9]+]]      Phi [ [[Add]] [[Sub:i[0-9]+]] ]
-  // CHECK:                        Return [ [[Phi]] ]
-  // CHECK:   [[Sub]]              Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Const2:i[0-9]+]]  IntConstant 2
+  // CHECK-DAG:     [[Const5:i[0-9]+]]  IntConstant 5
+  // CHECK-DAG:     [[Add:i[0-9]+]]     Add [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Sub:i[0-9]+]]     Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Phi:i[0-9]+]]     Phi [ [[Add]] [[Sub]] ]
+  // CHECK-DAG:                         Return [ [[Phi]] ]
 
   // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (after)
-  // CHECK:   [[Const7:i[0-9]+]]   IntConstant 7
-  // CHECK:   [[Phi:i[0-9]+]]      Phi [ [[Const7]] [[Const3:i[0-9]+]] ]
-  // CHECK:                        Return [ [[Phi]] ]
-  // CHECK:   [[Const3]]           IntConstant 3
+  // CHECK-DAG:     [[Const3:i[0-9]+]]  IntConstant 3
+  // CHECK-DAG:     [[Const7:i[0-9]+]]  IntConstant 7
+  // CHECK-DAG:     [[Phi:i[0-9]+]]     Phi [ [[Const7]] [[Const3]] ]
+  // CHECK-DAG:                         Return [ [[Phi]] ]
 
   public static int JumpsAndConditionals(boolean cond) {
     int a, b, c;
diff --git a/compiler/optimizing/test/Inliner.java b/compiler/optimizing/test/Inliner.java
new file mode 100644
index 0000000..6aa7f8d
--- /dev/null
+++ b/compiler/optimizing/test/Inliner.java
@@ -0,0 +1,186 @@
+public class Inliner {
+
+  // CHECK-START: void Inliner.InlineVoid() inliner (before)
+  // CHECK-DAG:     [[Const42:i[0-9]+]] IntConstant 42
+  // CHECK-DAG:                         InvokeStaticOrDirect
+  // CHECK-DAG:                         InvokeStaticOrDirect [ [[Const42]] ]
+
+  // CHECK-START: void Inliner.InlineVoid() inliner (after)
+  // CHECK-NOT:                         InvokeStaticOrDirect
+
+  public static void InlineVoid() {
+    returnVoid();
+    returnVoidWithOneParameter(42);
+  }
+
+  // CHECK-START: int Inliner.InlineParameter(int) inliner (before)
+  // CHECK-DAG:     [[Param:i[0-9]+]]  ParameterValue
+  // CHECK-DAG:     [[Result:i[0-9]+]] InvokeStaticOrDirect [ [[Param]] ]
+  // CHECK-DAG:                        Return [ [[Result]] ]
+
+  // CHECK-START: int Inliner.InlineParameter(int) inliner (after)
+  // CHECK-DAG:     [[Param:i[0-9]+]]  ParameterValue
+  // CHECK-DAG:                        Return [ [[Param]] ]
+
+  public static int InlineParameter(int a) {
+    return returnParameter(a);
+  }
+
+  // CHECK-START: long Inliner.InlineWideParameter(long) inliner (before)
+  // CHECK-DAG:     [[Param:j[0-9]+]]  ParameterValue
+  // CHECK-DAG:     [[Result:j[0-9]+]] InvokeStaticOrDirect [ [[Param]] ]
+  // CHECK-DAG:                        Return [ [[Result]] ]
+
+  // CHECK-START: long Inliner.InlineWideParameter(long) inliner (after)
+  // CHECK-DAG:     [[Param:j[0-9]+]]  ParameterValue
+  // CHECK-DAG:                        Return [ [[Param]] ]
+
+  public static long InlineWideParameter(long a) {
+    return returnWideParameter(a);
+  }
+
+  // CHECK-START: java.lang.Object Inliner.InlineReferenceParameter(java.lang.Object) inliner (before)
+  // CHECK-DAG:     [[Param:l[0-9]+]]  ParameterValue
+  // CHECK-DAG:     [[Result:l[0-9]+]] InvokeStaticOrDirect [ [[Param]] ]
+  // CHECK-DAG:                        Return [ [[Result]] ]
+
+  // CHECK-START: java.lang.Object Inliner.InlineReferenceParameter(java.lang.Object) inliner (after)
+  // CHECK-DAG:     [[Param:l[0-9]+]]  ParameterValue
+  // CHECK-DAG:                        Return [ [[Param]] ]
+
+  public static Object InlineReferenceParameter(Object o) {
+    return returnReferenceParameter(o);
+  }
+
+  // CHECK-START: int Inliner.InlineInt() inliner (before)
+  // CHECK-DAG:     [[Result:i[0-9]+]] InvokeStaticOrDirect
+  // CHECK-DAG:                        Return [ [[Result]] ]
+
+  // CHECK-START: int Inliner.InlineInt() inliner (after)
+  // CHECK-DAG:     [[Const4:i[0-9]+]] IntConstant 4
+  // CHECK-DAG:                        Return [ [[Const4]] ]
+
+  public static int InlineInt() {
+    return returnInt();
+  }
+
+  // CHECK-START: long Inliner.InlineWide() inliner (before)
+  // CHECK-DAG:     [[Result:j[0-9]+]] InvokeStaticOrDirect
+  // CHECK-DAG:                        Return [ [[Result]] ]
+
+  // CHECK-START: long Inliner.InlineWide() inliner (after)
+  // CHECK-DAG:     [[Const8:j[0-9]+]] LongConstant 8
+  // CHECK-DAG:                        Return [ [[Const8]] ]
+
+  public static long InlineWide() {
+    return returnWide();
+  }
+
+  // CHECK-START: int Inliner.InlineAdd() inliner (before)
+  // CHECK-DAG:     [[Const3:i[0-9]+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i[0-9]+]] IntConstant 5
+  // CHECK-DAG:     [[Result:i[0-9]+]] InvokeStaticOrDirect
+  // CHECK-DAG:                        Return [ [[Result]] ]
+
+  // CHECK-START: int Inliner.InlineAdd() inliner (after)
+  // CHECK-DAG:     [[Const3:i[0-9]+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i[0-9]+]] IntConstant 5
+  // CHECK-DAG:     [[Add:i[0-9]+]]    Add [ [[Const3]] [[Const5]] ]
+  // CHECK-DAG:                        Return [ [[Add]] ]
+
+  public static int InlineAdd() {
+    return returnAdd(3, 5);
+  }
+
+  // CHECK-START: int Inliner.InlineFieldAccess() inliner (before)
+  // CHECK-DAG:     [[After:i[0-9]+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                        Return [ [[After]] ]
+
+  // CHECK-START: int Inliner.InlineFieldAccess() inliner (after)
+  // CHECK-DAG:     [[Const1:i[0-9]+]] IntConstant 1
+  // CHECK-DAG:     [[Before:i[0-9]+]] StaticFieldGet
+  // CHECK-DAG:     [[After:i[0-9]+]]  Add [ [[Before]] [[Const1]] ]
+  // CHECK-DAG:                        StaticFieldSet [ {{l[0-9]+}} [[After]] ]
+  // CHECK-DAG:                        Return [ [[After]] ]
+
+  // CHECK-START: int Inliner.InlineFieldAccess() inliner (after)
+  // CHECK-NOT:                        InvokeStaticOrDirect
+
+  public static int InlineFieldAccess() {
+    return incCounter();
+  }
+
+  // CHECK-START: int Inliner.InlineWithControlFlow(boolean) inliner (before)
+  // CHECK-DAG:     [[Const1:i[0-9]+]] IntConstant 1
+  // CHECK-DAG:     [[Const3:i[0-9]+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i[0-9]+]] IntConstant 5
+  // CHECK-DAG:     [[Add:i[0-9]+]]    InvokeStaticOrDirect [ [[Const1]] [[Const3]] ]
+  // CHECK-DAG:     [[Sub:i[0-9]+]]    InvokeStaticOrDirect [ [[Const5]] [[Const3]] ]
+  // CHECK-DAG:     [[Phi:i[0-9]+]]    Phi [ [[Add]] [[Sub]] ]
+  // CHECK-DAG:                        Return [ [[Phi]] ]
+
+  // CHECK-START: int Inliner.InlineWithControlFlow(boolean) inliner (after)
+  // CHECK-DAG:     [[Const1:i[0-9]+]] IntConstant 1
+  // CHECK-DAG:     [[Const3:i[0-9]+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i[0-9]+]] IntConstant 5
+  // CHECK-DAG:     [[Add:i[0-9]+]]    Add [ [[Const1]] [[Const3]] ]
+  // CHECK-DAG:     [[Sub:i[0-9]+]]    Sub [ [[Const5]] [[Const3]] ]
+  // CHECK-DAG:     [[Phi:i[0-9]+]]    Phi [ [[Add]] [[Sub]] ]
+  // CHECK-DAG:                        Return [ [[Phi]] ]
+
+  public static int InlineWithControlFlow(boolean cond) {
+    int x, const1, const3, const5;
+    const1 = 1;
+    const3 = 3;
+    const5 = 5;
+    if (cond) {
+      x = returnAdd(const1, const3);
+    } else {
+      x = returnSub(const5, const3);
+    }
+    return x;
+  }
+
+
+  private static void returnVoid() {
+    return;
+  }
+
+  private static void returnVoidWithOneParameter(int a) {
+    return;
+  }
+
+  private static int returnParameter(int a) {
+    return a;
+  }
+
+  private static long returnWideParameter(long a) {
+    return a;
+  }
+
+  private static Object returnReferenceParameter(Object o) {
+    return o;
+  }
+
+  private static int returnInt() {
+    return 4;
+  }
+
+  private static long returnWide() {
+    return 8L;
+  }
+
+  private static int returnAdd(int a, int b) {
+    return a + b;
+  }
+
+  private static int returnSub(int a, int b) {
+    return a - b;
+  }
+
+  private static int counter = 42;
+
+  private static int incCounter() {
+    return ++counter;
+  }
+}
diff --git a/tools/checker.py b/tools/checker.py
index 82a1e6b..74c6d61 100755
--- a/tools/checker.py
+++ b/tools/checker.py
@@ -20,9 +20,9 @@
 # against a set of assertions specified alongside the tests.
 #
 # Tests are written in Java, turned into DEX and compiled with the Optimizing
-# compiler. "Check lines" are comments in the Java file which begin with prefix
-# 'CHECK' followed by a pattern that the engine attempts to match in the
-# compiler-generated output.
+# compiler. "Check lines" are assertions formatted as comments of the Java file.
+# They begin with prefix 'CHECK' followed by a pattern that the engine attempts
+# to match in the compiler-generated output.
 #
 # Assertions are tested in groups which correspond to the individual compiler
 # passes. Each group of check lines therefore must start with a 'CHECK-START'
@@ -30,7 +30,23 @@
 # name must exactly match one of the groups recognized in the output (they can
 # be listed with the '--list-groups' command-line flag).
 #
-# Check line patterns are treated as plain text rather than regular expressions
+# Matching of check lines is carried out in the order of appearance in the
+# source file. There are three types of check lines:
+#  - CHECK:     Must match an output line which appears in the output group
+#               later than lines matched against any preceeding checks. Output
+#               lines must therefore match the check lines in the same order.
+#               These are referred to as "in-order" checks in the code.
+#  - CHECK-DAG: Must match an output line which appears in the output group
+#               later than lines matched against any preceeding in-order checks.
+#               In other words, the order of output lines does not matter
+#               between consecutive DAG checks.
+#  - CHECK-NOT: Must not match any output line which appear in the output group
+#               later than lines matched against any preceeding checks and
+#               earlier than lines matched against any subsequent checks.
+#               Surrounding non-negative checks (or boundaries of the group)
+#               therefore create a scope within which the assertion is verified.
+#
+# Check-line patterns are treated as plain text rather than regular expressions
 # but are whitespace agnostic.
 #
 # Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If
@@ -115,13 +131,16 @@
      more regex elements. Matching against an output line is successful only
      if all regex elements can be matched in the given order."""
 
-  def __init__(self, lineContent, lineNo=-1):
-    lineContent = lineContent.strip()
+  class Variant(object):
+    """Supported types of assertions."""
+    InOrder, DAG, Not = range(3)
 
+  def __init__(self, content, variant=Variant.InOrder, lineNo=-1):
+    self.content = content.strip()
+    self.variant = variant
     self.lineNo = lineNo
-    self.content = lineContent
 
-    self.lineParts = self.__parse(lineContent)
+    self.lineParts = self.__parse(self.content)
     if not self.lineParts:
       raise Exception("Empty check line")
 
@@ -180,7 +199,11 @@
       elif self.__isMatchAtStart(matchVariable):
         var = line[0:matchVariable.end()]
         line = line[matchVariable.end():]
-        lineParts.append(CheckElement.parseVariable(var))
+        elem = CheckElement.parseVariable(var)
+        if self.variant == CheckLine.Variant.Not and elem.variant == CheckElement.Variant.VarDef:
+          raise Exception("CHECK-NOT check lines cannot define variables " +
+                          "(line " + str(self.lineNo) + ")")
+        lineParts.append(elem)
       else:
         # If we're not currently looking at a special marker, this is a plain
         # text match all the way until the first special marker (or the end
@@ -267,44 +290,101 @@
   def __headAndTail(self, list):
     return list[0], list[1:]
 
-  # The driver of matching inside a group. It simultaneously reads lines from
-  # the output and check groups and attempts to match them against each other
-  # in the correct order.
-  def match(self, outputGroup):
-    readOutputLines = 0
-    lastMatch = 0
+  # Splits a list of check lines at index 'i' such that lines[i] is the first
+  # element whose variant is not equal to the given parameter.
+  def __splitByVariant(self, lines, variant):
+    i = 0
+    while i < len(lines) and lines[i].variant == variant:
+      i += 1
+    return lines[:i], lines[i:]
 
-    # Check and output lines which remain to be matched.
+  # Extracts the first sequence of check lines which are independent of each
+  # other's match location, i.e. either consecutive DAG lines or a single
+  # InOrder line. Any Not lines preceeding this sequence are also extracted.
+  def __nextIndependentChecks(self, checkLines):
+    notChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.Not)
+    if not checkLines:
+      return notChecks, [], []
+
+    head, tail = self.__headAndTail(checkLines)
+    if head.variant == CheckLine.Variant.InOrder:
+      return notChecks, [head], tail
+    else:
+      assert head.variant == CheckLine.Variant.DAG
+      independentChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.DAG)
+      return notChecks, independentChecks, checkLines
+
+  # If successful, returns the line number of the first output line matching the
+  # check line and the updated variable state. Otherwise returns -1 and None,
+  # respectively. The 'lineFilter' parameter can be used to supply a list of
+  # line numbers (counting from 1) which should be skipped.
+  def __findFirstMatch(self, checkLine, outputLines, lineFilter, varState):
+    matchLineNo = 0
+    for outputLine in outputLines:
+      matchLineNo += 1
+      if matchLineNo in lineFilter:
+        continue
+      newVarState = checkLine.match(outputLine, varState)
+      if newVarState is not None:
+        return matchLineNo, newVarState
+    return -1, None
+
+  # Matches the given positive check lines against the output in order of
+  # appearance. Variable state is propagated but the scope of the search remains
+  # the same for all checks. Each output line can only be matched once.
+  # If all check lines are matched, the resulting variable state is returned
+  # together with the remaining output. The function also returns output lines
+  # which appear before either of the matched lines so they can be tested
+  # against Not checks.
+  def __matchIndependentChecks(self, checkLines, outputLines, varState):
+    # If no checks are provided, skip over the entire output.
+    if not checkLines:
+      return outputLines, varState, []
+
+    # Keep track of which lines have been matched.
+    matchedLines = []
+
+    # Find first unused output line which matches each check line.
+    for checkLine in checkLines:
+      matchLineNo, varState = self.__findFirstMatch(checkLine, outputLines, matchedLines, varState)
+      if varState is None:
+        raise Exception("Could not match line " + str(checkLine))
+      matchedLines.append(matchLineNo)
+
+    # Return new variable state and the output lines which lie outside the
+    # match locations of this independent group.
+    preceedingLines = outputLines[:min(matchedLines)-1]
+    remainingLines = outputLines[max(matchedLines):]
+    return preceedingLines, remainingLines, varState
+
+  # Makes sure that the given check lines do not match any of the given output
+  # lines. Variable state does not change.
+  def __matchNotLines(self, checkLines, outputLines, varState):
+    for checkLine in checkLines:
+      assert checkLine.variant == CheckLine.Variant.Not
+      matchLineNo, varState = self.__findFirstMatch(checkLine, outputLines, [], varState)
+      if varState is not None:
+        raise Exception("CHECK-NOT line " + str(checkLine) + " matches output")
+
+  # Matches the check lines in this group against an output group. It is
+  # responsible for running the checks in the right order and scope, and
+  # for propagating the variable state between the check lines.
+  def match(self, outputGroup):
+    varState = {}
     checkLines = self.lines
     outputLines = outputGroup.body
-    varState = {}
 
-    # Retrieve the next check line.
     while checkLines:
-      checkLine, checkLines = self.__headAndTail(checkLines)
-      foundMatch = False
-
-      # Retrieve the next output line.
-      while outputLines:
-        outputLine, outputLines = self.__headAndTail(outputLines)
-        readOutputLines += 1
-
-        # Try to match the current lines against each other. If successful,
-        # save the new state of variables and continue to the next check line.
-        newVarState = checkLine.match(outputLine, varState)
-        if newVarState is not None:
-          varState = newVarState
-          lastMatch = readOutputLines
-          foundMatch = True
-          break
-      if not foundMatch:
-        raise Exception("Could not match check line \"" + checkLine.content + "\" from line " +
-                        str(lastMatch+1) + " of the output. [vars=" + str(varState) + "]")
-
-  @staticmethod
-  def parse(name, lines):
-    return CheckGroup(name, list(map(lambda line: CheckLine(line), lines)))
-
+      # Extract the next sequence of location-independent checks to be matched.
+      notChecks, independentChecks, checkLines = self.__nextIndependentChecks(checkLines)
+      # Match the independent checks.
+      notOutput, outputLines, newVarState = \
+          self.__matchIndependentChecks(independentChecks, outputLines, varState)
+      # Run the Not checks against the output lines which lie between the last
+      # two independent groups or the bounds of the output.
+      self.__matchNotLines(notChecks, notOutput, varState)
+      # Update variable state.
+      varState = newVarState
 
 class OutputGroup(CommonEqualityMixin):
   """Represents a named part of the test output against which a check group of
@@ -378,20 +458,35 @@
       return None
 
   def _processLine(self, line, lineNo):
+    # Lines beginning with 'CHECK-START' start a new check group.
     startLine = self._extractLine(self.prefix + "-START", line)
     if startLine is not None:
-      # Line starts with the CHECK-START keyword, start a new group
-      return (None, startLine)
-    else:
-      # Otherwise try to parse it as a standard CHECK line. If unsuccessful,
-      # _extractLine will return None and the line will be ignored.
-      return (self._extractLine(self.prefix, line), None)
+      return None, startLine
+
+    # Lines starting only with 'CHECK' are matched in order.
+    plainLine = self._extractLine(self.prefix, line)
+    if plainLine is not None:
+      return (plainLine, CheckLine.Variant.InOrder), None
+
+    # 'CHECK-DAG' lines are no-order assertions.
+    dagLine = self._extractLine(self.prefix + "-DAG", line)
+    if dagLine is not None:
+      return (dagLine, CheckLine.Variant.DAG), None
+
+    # 'CHECK-NOT' lines are no-order negative assertions.
+    notLine = self._extractLine(self.prefix + "-NOT", line)
+    if notLine is not None:
+      return (notLine, CheckLine.Variant.Not), None
+
+    # Other lines are ignored.
+    return None, None
 
   def _exceptionLineOutsideGroup(self, line, lineNo):
     raise Exception("Check file line lies outside a group (line " + str(lineNo) + ")")
 
   def _processGroup(self, name, lines):
-    return CheckGroup.parse(name, lines)
+    checkLines = list(map(lambda line: CheckLine(line[0], line[1]), lines))
+    return CheckGroup(name, checkLines)
 
   def match(self, outputFile, printInfo=False):
     for checkGroup in self.groups:
diff --git a/tools/checker_test.py b/tools/checker_test.py
index f69f9e3..8947d8a 100755
--- a/tools/checker_test.py
+++ b/tools/checker_test.py
@@ -64,6 +64,9 @@
   def __parsesTo(self, string, expected):
     self.assertEqual(expected, self.__getRegex(self.__tryParse(string)))
 
+  def __tryParseNot(self, string):
+    return checker.CheckLine(string, checker.CheckLine.Variant.UnorderedNot)
+
   def __parsesPattern(self, string, pattern):
     line = self.__tryParse(string)
     self.assertEqual(1, len(line.lineParts))
@@ -163,6 +166,9 @@
     self.__parsesTo("{{abc}}{{def}}", "(abc)(def)")
     self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)")
 
+  def test_NoVarDefsInNotChecks(self):
+    with self.assertRaises(Exception):
+      self.__tryParseNot("[[ABC:abc]]")
 
 class TestCheckLine_Match(unittest.TestCase):
   def __matchSingle(self, checkString, outputString, varState={}):
@@ -228,9 +234,23 @@
     self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA")
 
 
+CheckVariant = checker.CheckLine.Variant
+
+def prepareSingleCheck(line):
+  if isinstance(line, str):
+    return checker.CheckLine(line)
+  else:
+    return checker.CheckLine(line[0], line[1])
+
+def prepareChecks(lines):
+  if isinstance(lines, str):
+    lines = lines.splitlines()
+  return list(map(lambda line: prepareSingleCheck(line), lines))
+
+
 class TestCheckGroup_Match(unittest.TestCase):
-  def __matchMulti(self, checkString, outputString):
-    checkGroup = checker.CheckGroup.parse("MyGroup", checkString.splitlines())
+  def __matchMulti(self, checkLines, outputString):
+    checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines))
     outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines())
     return checkGroup.match(outputGroup)
 
@@ -271,14 +291,62 @@
                          ### 1234 ###""");
 
   def test_Ordering(self):
-    self.__matchMulti("""foo
-                         bar""",
+    self.__matchMulti([("foo", CheckVariant.InOrder),
+                       ("bar", CheckVariant.InOrder)],
                       """foo
                          bar""")
-    self.__notMatchMulti("""foo
-                            bar""",
+    self.__notMatchMulti([("foo", CheckVariant.InOrder),
+                          ("bar", CheckVariant.InOrder)],
                          """bar
                             foo""")
+    self.__matchMulti([("abc", CheckVariant.DAG),
+                       ("def", CheckVariant.DAG)],
+                      """abc
+                         def""")
+    self.__matchMulti([("abc", CheckVariant.DAG),
+                       ("def", CheckVariant.DAG)],
+                      """def
+                         abc""")
+    self.__matchMulti([("foo", CheckVariant.InOrder),
+                       ("abc", CheckVariant.DAG),
+                       ("def", CheckVariant.DAG),
+                       ("bar", CheckVariant.InOrder)],
+                      """foo
+                         def
+                         abc
+                         bar""")
+    self.__notMatchMulti([("foo", CheckVariant.InOrder),
+                          ("abc", CheckVariant.DAG),
+                          ("def", CheckVariant.DAG),
+                          ("bar", CheckVariant.InOrder)],
+                         """foo
+                            abc
+                            bar""")
+    self.__notMatchMulti([("foo", CheckVariant.InOrder),
+                          ("abc", CheckVariant.DAG),
+                          ("def", CheckVariant.DAG),
+                          ("bar", CheckVariant.InOrder)],
+                         """foo
+                            def
+                            bar""")
+
+  def test_NotAssertions(self):
+    self.__matchMulti([("foo", CheckVariant.Not)],
+                      """abc
+                         def""")
+    self.__notMatchMulti([("foo", CheckVariant.Not)],
+                         """abc foo
+                            def""")
+
+  def test_LineOnlyMatchesOnce(self):
+    self.__matchMulti([("foo", CheckVariant.DAG),
+                       ("foo", CheckVariant.DAG)],
+                       """foo
+                          foo""")
+    self.__notMatchMulti([("foo", CheckVariant.DAG),
+                          ("foo", CheckVariant.DAG)],
+                          """foo
+                             bar""")
 
 class TestOutputFile_Parse(unittest.TestCase):
   def __parsesTo(self, string, expected):
@@ -355,7 +423,7 @@
     self.__parsesTo("""// CHECK-START: Example Group
                        // CHECK:  foo
                        // CHECK:    bar""",
-                    [ checker.CheckGroup.parse("Example Group", [ "foo", "bar" ]) ])
+                    [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ])
 
   def test_MultipleGroups(self):
     self.__parsesTo("""// CHECK-START: Example Group1
@@ -364,8 +432,20 @@
                        // CHECK-START: Example Group2
                        // CHECK: abc
                        // CHECK: def""",
-                    [ checker.CheckGroup.parse("Example Group1", [ "foo", "bar" ]),
-                      checker.CheckGroup.parse("Example Group2", [ "abc", "def" ]) ])
+                    [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])),
+                      checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ])
+
+  def test_CheckVariants(self):
+    self.__parsesTo("""// CHECK-START: Example Group
+                       // CHECK:     foo
+                       // CHECK-NOT: bar
+                       // CHECK-DAG: abc
+                       // CHECK-DAG: def""",
+                    [ checker.CheckGroup("Example Group",
+                                         prepareChecks([ ("foo", CheckVariant.InOrder),
+                                                         ("bar", CheckVariant.Not),
+                                                         ("abc", CheckVariant.DAG),
+                                                         ("def", CheckVariant.DAG) ])) ])
 
 if __name__ == '__main__':
   unittest.main()