]> gcc.gnu.org Git - gcc.git/commitdiff
Split pointer ibased range operators to range-op-ptr.cc
authorAndrew MacLeod <amacleod@redhat.com>
Sat, 10 Jun 2023 20:17:51 +0000 (16:17 -0400)
committerAndrew MacLeod <amacleod@redhat.com>
Mon, 12 Jun 2023 14:48:31 +0000 (10:48 -0400)
MOve the pointer table and all pointer specific operators into a
new file for pointers.

* Makefile.in (OBJS): Add range-op-ptr.o.
* range-op-mixed.h (update_known_bitmask): Move prototype here.
(minus_op1_op2_relation_effect): Move prototype here.
(wi_includes_zero_p): Move function to here.
(wi_zero_p): Ditto.
* range-op.cc (update_known_bitmask): Remove static.
(wi_includes_zero_p): Move to header.
(wi_zero_p): Move to header.
(minus_op1_op2_relation_effect): Remove static.
(operator_pointer_diff): Move class and routines to range-op-ptr.cc.
(pointer_plus_operator): Ditto.
(pointer_min_max_operator): Ditto.
(pointer_and_operator): Ditto.
(pointer_or_operator): Ditto.
(pointer_table): Ditto.
(range_op_table::initialize_pointer_ops): Ditto.
* range-op-ptr.cc: New.

gcc/Makefile.in
gcc/range-op-mixed.h
gcc/range-op-ptr.cc [new file with mode: 0644]
gcc/range-op.cc

index 0c02f31298586aa7bd2de6d71aa8127a6ada8212..4be82e83b9ea2bd9cedc26a71c24714b16b41dce 100644 (file)
@@ -1588,6 +1588,7 @@ OBJS = \
        range.o \
        range-op.o \
        range-op-float.o \
+       range-op-ptr.o \
        read-md.o \
        read-rtl.o \
        read-rtl-function.o \
index cd137acd0e6f594171740bc1c7827e1d9f2ace6e..b188f5a516ecae7e40bad881a1e6d3502802bdde 100644 (file)
@@ -22,6 +22,31 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_RANGE_OP_MIXED_H
 #define GCC_RANGE_OP_MIXED_H
 
+void update_known_bitmask (irange &, tree_code, const irange &, const irange &);
+bool minus_op1_op2_relation_effect (irange &lhs_range, tree type,
+                                   const irange &, const irange &,
+                                   relation_kind rel);
+
+
+// Return TRUE if 0 is within [WMIN, WMAX].
+
+inline bool
+wi_includes_zero_p (tree type, const wide_int &wmin, const wide_int &wmax)
+{
+  signop sign = TYPE_SIGN (type);
+  return wi::le_p (wmin, 0, sign) && wi::ge_p (wmax, 0, sign);
+}
+
+// Return TRUE if [WMIN, WMAX] is the singleton 0.
+
+inline bool
+wi_zero_p (tree type, const wide_int &wmin, const wide_int &wmax)
+{
+  unsigned prec = TYPE_PRECISION (type);
+  return wmin == wmax && wi::eq_p (wmin, wi::zero (prec));
+}
+
+
 enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL };
 bool_range_state get_bool_state (vrange &r, const vrange &lhs, tree val_type);
 
diff --git a/gcc/range-op-ptr.cc b/gcc/range-op-ptr.cc
new file mode 100644 (file)
index 0000000..55c37cc
--- /dev/null
@@ -0,0 +1,286 @@
+/* Code for range operators.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   Contributed by Andrew MacLeod <amacleod@redhat.com>
+   and Aldy Hernandez <aldyh@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "insn-codes.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple.h"
+#include "cfghooks.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "optabs-tree.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic-core.h"
+#include "flags.h"
+#include "fold-const.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "cfganal.h"
+#include "gimple-iterator.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-walk.h"
+#include "tree-cfg.h"
+#include "wide-int.h"
+#include "value-relation.h"
+#include "range-op.h"
+#include "tree-ssa-ccp.h"
+#include "range-op-mixed.h"
+
+class pointer_plus_operator : public range_operator
+{
+  using range_operator::op2_range;
+public:
+  virtual void wi_fold (irange &r, tree type,
+                       const wide_int &lh_lb,
+                       const wide_int &lh_ub,
+                       const wide_int &rh_lb,
+                       const wide_int &rh_ub) const;
+  virtual bool op2_range (irange &r, tree type,
+                         const irange &lhs,
+                         const irange &op1,
+                         relation_trio = TRIO_VARYING) const;
+  void update_bitmask (irange &r, const irange &lh, const irange &rh) const
+    { update_known_bitmask (r, POINTER_PLUS_EXPR, lh, rh); }
+} op_pointer_plus;
+
+void
+pointer_plus_operator::wi_fold (irange &r, tree type,
+                               const wide_int &lh_lb,
+                               const wide_int &lh_ub,
+                               const wide_int &rh_lb,
+                               const wide_int &rh_ub) const
+{
+  // Check for [0,0] + const, and simply return the const.
+  if (lh_lb == 0 && lh_ub == 0 && rh_lb == rh_ub)
+    {
+      r.set (type, rh_lb, rh_lb);
+      return;
+    }
+
+  // For pointer types, we are really only interested in asserting
+  // whether the expression evaluates to non-NULL.
+  //
+  // With -fno-delete-null-pointer-checks we need to be more
+  // conservative.  As some object might reside at address 0,
+  // then some offset could be added to it and the same offset
+  // subtracted again and the result would be NULL.
+  // E.g.
+  // static int a[12]; where &a[0] is NULL and
+  // ptr = &a[6];
+  // ptr -= 6;
+  // ptr will be NULL here, even when there is POINTER_PLUS_EXPR
+  // where the first range doesn't include zero and the second one
+  // doesn't either.  As the second operand is sizetype (unsigned),
+  // consider all ranges where the MSB could be set as possible
+  // subtractions where the result might be NULL.
+  if ((!wi_includes_zero_p (type, lh_lb, lh_ub)
+       || !wi_includes_zero_p (type, rh_lb, rh_ub))
+      && !TYPE_OVERFLOW_WRAPS (type)
+      && (flag_delete_null_pointer_checks
+         || !wi::sign_mask (rh_ub)))
+    r = range_nonzero (type);
+  else if (lh_lb == lh_ub && lh_lb == 0
+          && rh_lb == rh_ub && rh_lb == 0)
+    r = range_zero (type);
+  else
+   r.set_varying (type);
+}
+
+bool
+pointer_plus_operator::op2_range (irange &r, tree type,
+                                 const irange &lhs ATTRIBUTE_UNUSED,
+                                 const irange &op1 ATTRIBUTE_UNUSED,
+                                 relation_trio trio) const
+{
+  relation_kind rel = trio.lhs_op1 ();
+  r.set_varying (type);
+
+  // If the LHS and OP1 are equal, the op2 must be zero.
+  if (rel == VREL_EQ)
+    r.set_zero (type);
+  // If the LHS and OP1 are not equal, the offset must be non-zero.
+  else if (rel == VREL_NE)
+    r.set_nonzero (type);
+  else
+    return false;
+  return true;
+}
+
+class pointer_min_max_operator : public range_operator
+{
+public:
+  virtual void wi_fold (irange & r, tree type,
+                       const wide_int &lh_lb, const wide_int &lh_ub,
+                       const wide_int &rh_lb, const wide_int &rh_ub) const;
+} op_ptr_min_max;
+
+void
+pointer_min_max_operator::wi_fold (irange &r, tree type,
+                                  const wide_int &lh_lb,
+                                  const wide_int &lh_ub,
+                                  const wide_int &rh_lb,
+                                  const wide_int &rh_ub) const
+{
+  // For MIN/MAX expressions with pointers, we only care about
+  // nullness.  If both are non null, then the result is nonnull.
+  // If both are null, then the result is null.  Otherwise they
+  // are varying.
+  if (!wi_includes_zero_p (type, lh_lb, lh_ub)
+      && !wi_includes_zero_p (type, rh_lb, rh_ub))
+    r = range_nonzero (type);
+  else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub))
+    r = range_zero (type);
+  else
+    r.set_varying (type);
+}
+
+
+class pointer_and_operator : public range_operator
+{
+public:
+  virtual void wi_fold (irange &r, tree type,
+                       const wide_int &lh_lb, const wide_int &lh_ub,
+                       const wide_int &rh_lb, const wide_int &rh_ub) const;
+} op_pointer_and;
+
+void
+pointer_and_operator::wi_fold (irange &r, tree type,
+                              const wide_int &lh_lb,
+                              const wide_int &lh_ub,
+                              const wide_int &rh_lb ATTRIBUTE_UNUSED,
+                              const wide_int &rh_ub ATTRIBUTE_UNUSED) const
+{
+  // For pointer types, we are really only interested in asserting
+  // whether the expression evaluates to non-NULL.
+  if (wi_zero_p (type, lh_lb, lh_ub) || wi_zero_p (type, lh_lb, lh_ub))
+    r = range_zero (type);
+  else
+    r.set_varying (type);
+}
+
+
+class pointer_or_operator : public range_operator
+{
+  using range_operator::op1_range;
+  using range_operator::op2_range;
+public:
+  virtual bool op1_range (irange &r, tree type,
+                         const irange &lhs,
+                         const irange &op2,
+                         relation_trio rel = TRIO_VARYING) const;
+  virtual bool op2_range (irange &r, tree type,
+                         const irange &lhs,
+                         const irange &op1,
+                         relation_trio rel = TRIO_VARYING) const;
+  virtual void wi_fold (irange &r, tree type,
+                       const wide_int &lh_lb, const wide_int &lh_ub,
+                       const wide_int &rh_lb, const wide_int &rh_ub) const;
+} op_pointer_or;
+
+bool
+pointer_or_operator::op1_range (irange &r, tree type,
+                               const irange &lhs,
+                               const irange &op2 ATTRIBUTE_UNUSED,
+                               relation_trio) const
+{
+  if (lhs.undefined_p ())
+    return false;
+  if (lhs.zero_p ())
+    {
+      r.set_zero (type);
+      return true;
+    }
+  r.set_varying (type);
+  return true;
+}
+
+bool
+pointer_or_operator::op2_range (irange &r, tree type,
+                               const irange &lhs,
+                               const irange &op1,
+                               relation_trio) const
+{
+  return pointer_or_operator::op1_range (r, type, lhs, op1);
+}
+
+void
+pointer_or_operator::wi_fold (irange &r, tree type,
+                             const wide_int &lh_lb,
+                             const wide_int &lh_ub,
+                             const wide_int &rh_lb,
+                             const wide_int &rh_ub) const
+{
+  // For pointer types, we are really only interested in asserting
+  // whether the expression evaluates to non-NULL.
+  if (!wi_includes_zero_p (type, lh_lb, lh_ub)
+      && !wi_includes_zero_p (type, rh_lb, rh_ub))
+    r = range_nonzero (type);
+  else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub))
+    r = range_zero (type);
+  else
+    r.set_varying (type);
+}
+
+class operator_pointer_diff : public range_operator
+{
+  virtual bool op1_op2_relation_effect (irange &lhs_range,
+                                       tree type,
+                                       const irange &op1_range,
+                                       const irange &op2_range,
+                                       relation_kind rel) const;
+  void update_bitmask (irange &r, const irange &lh, const irange &rh) const
+    { update_known_bitmask (r, POINTER_DIFF_EXPR, lh, rh); }
+} op_pointer_diff;
+
+bool
+operator_pointer_diff::op1_op2_relation_effect (irange &lhs_range, tree type,
+                                               const irange &op1_range,
+                                               const irange &op2_range,
+                                               relation_kind rel) const
+{
+  return minus_op1_op2_relation_effect (lhs_range, type, op1_range, op2_range,
+                                       rel);
+}
+
+// When PRANGE is implemented, these are all the opcodes which are currently
+// expecting routines with PRANGE signatures.
+
+pointer_table::pointer_table ()
+{
+  set (BIT_AND_EXPR, op_pointer_and);
+  set (BIT_IOR_EXPR, op_pointer_or);
+  set (MIN_EXPR, op_ptr_min_max);
+  set (MAX_EXPR, op_ptr_min_max);
+}
+
+// Initialize any pointer operators to the primary table
+
+void
+range_op_table::initialize_pointer_ops ()
+{
+  set (POINTER_PLUS_EXPR, op_pointer_plus);
+  set (POINTER_DIFF_EXPR, op_pointer_diff);
+}
index e83f627a722bb2984df40e6e135064fd8bd32f94..e0cd1b1079001b62f100c56b28d4b5b36494c7da 100644 (file)
@@ -381,7 +381,7 @@ irange_to_masked_value (const irange &r, widest_int &value, widest_int &mask)
 // Update the known bitmasks in R when applying the operation CODE to
 // LH and RH.
 
-static void
+void
 update_known_bitmask (irange &r, tree_code code,
                      const irange &lh, const irange &rh)
 {
@@ -444,24 +444,6 @@ get_shift_range (irange &r, tree type, const irange &op)
   return true;
 }
 
-// Return TRUE if 0 is within [WMIN, WMAX].
-
-static inline bool
-wi_includes_zero_p (tree type, const wide_int &wmin, const wide_int &wmax)
-{
-  signop sign = TYPE_SIGN (type);
-  return wi::le_p (wmin, 0, sign) && wi::ge_p (wmax, 0, sign);
-}
-
-// Return TRUE if [WMIN, WMAX] is the singleton 0.
-
-static inline bool
-wi_zero_p (tree type, const wide_int &wmin, const wide_int &wmax)
-{
-  unsigned prec = TYPE_PRECISION (type);
-  return wmin == wmax && wi::eq_p (wmin, wi::zero (prec));
-}
-
 // Default wide_int fold operation returns [MIN, MAX].
 
 void
@@ -1844,7 +1826,7 @@ operator_minus::lhs_op1_relation (const irange &, const irange &op1,
 // LHS of the expression.  If so, apply it to LHS_RANGE.  This is a helper
 // function for both MINUS_EXPR and POINTER_DIFF_EXPR.
 
-static bool
+bool
 minus_op1_op2_relation_effect (irange &lhs_range, tree type,
                               const irange &op1_range ATTRIBUTE_UNUSED,
                               const irange &op2_range ATTRIBUTE_UNUSED,
@@ -1951,29 +1933,6 @@ operator_minus::op2_range (irange &r, tree type,
   return fold_range (r, type, op1, lhs);
 }
 
-
-class operator_pointer_diff : public range_operator
-{
-  virtual bool op1_op2_relation_effect (irange &lhs_range,
-                                       tree type,
-                                       const irange &op1_range,
-                                       const irange &op2_range,
-                                       relation_kind rel) const;
-  void update_bitmask (irange &r, const irange &lh, const irange &rh) const
-    { update_known_bitmask (r, POINTER_DIFF_EXPR, lh, rh); }
-} op_pointer_diff;
-
-bool
-operator_pointer_diff::op1_op2_relation_effect (irange &lhs_range, tree type,
-                                               const irange &op1_range,
-                                               const irange &op2_range,
-                                               relation_kind rel) const
-{
-  return minus_op1_op2_relation_effect (lhs_range, type, op1_range, op2_range,
-                                       rel);
-}
-
-
 void
 operator_min::update_bitmask (irange &r, const irange &lh,
                              const irange &rh) const
@@ -4319,202 +4278,6 @@ operator_addr_expr::op1_range (irange &r, tree type,
 {
   return operator_addr_expr::fold_range (r, type, lhs, op2);
 }
-
-
-class pointer_plus_operator : public range_operator
-{
-  using range_operator::op2_range;
-public:
-  virtual void wi_fold (irange &r, tree type,
-                       const wide_int &lh_lb,
-                       const wide_int &lh_ub,
-                       const wide_int &rh_lb,
-                       const wide_int &rh_ub) const;
-  virtual bool op2_range (irange &r, tree type,
-                         const irange &lhs,
-                         const irange &op1,
-                         relation_trio = TRIO_VARYING) const;
-  void update_bitmask (irange &r, const irange &lh, const irange &rh) const
-    { update_known_bitmask (r, POINTER_PLUS_EXPR, lh, rh); }
-} op_pointer_plus;
-
-void
-pointer_plus_operator::wi_fold (irange &r, tree type,
-                               const wide_int &lh_lb,
-                               const wide_int &lh_ub,
-                               const wide_int &rh_lb,
-                               const wide_int &rh_ub) const
-{
-  // Check for [0,0] + const, and simply return the const.
-  if (lh_lb == 0 && lh_ub == 0 && rh_lb == rh_ub)
-    {
-      r.set (type, rh_lb, rh_lb);
-      return;
-    }
-
-  // For pointer types, we are really only interested in asserting
-  // whether the expression evaluates to non-NULL.
-  //
-  // With -fno-delete-null-pointer-checks we need to be more
-  // conservative.  As some object might reside at address 0,
-  // then some offset could be added to it and the same offset
-  // subtracted again and the result would be NULL.
-  // E.g.
-  // static int a[12]; where &a[0] is NULL and
-  // ptr = &a[6];
-  // ptr -= 6;
-  // ptr will be NULL here, even when there is POINTER_PLUS_EXPR
-  // where the first range doesn't include zero and the second one
-  // doesn't either.  As the second operand is sizetype (unsigned),
-  // consider all ranges where the MSB could be set as possible
-  // subtractions where the result might be NULL.
-  if ((!wi_includes_zero_p (type, lh_lb, lh_ub)
-       || !wi_includes_zero_p (type, rh_lb, rh_ub))
-      && !TYPE_OVERFLOW_WRAPS (type)
-      && (flag_delete_null_pointer_checks
-         || !wi::sign_mask (rh_ub)))
-    r = range_nonzero (type);
-  else if (lh_lb == lh_ub && lh_lb == 0
-          && rh_lb == rh_ub && rh_lb == 0)
-    r = range_zero (type);
-  else
-   r.set_varying (type);
-}
-
-bool
-pointer_plus_operator::op2_range (irange &r, tree type,
-                                 const irange &lhs ATTRIBUTE_UNUSED,
-                                 const irange &op1 ATTRIBUTE_UNUSED,
-                                 relation_trio trio) const
-{
-  relation_kind rel = trio.lhs_op1 ();
-  r.set_varying (type);
-
-  // If the LHS and OP1 are equal, the op2 must be zero.
-  if (rel == VREL_EQ)
-    r.set_zero (type);
-  // If the LHS and OP1 are not equal, the offset must be non-zero.
-  else if (rel == VREL_NE)
-    r.set_nonzero (type);
-  else
-    return false;
-  return true;
-}
-
-class pointer_min_max_operator : public range_operator
-{
-public:
-  virtual void wi_fold (irange & r, tree type,
-                       const wide_int &lh_lb, const wide_int &lh_ub,
-                       const wide_int &rh_lb, const wide_int &rh_ub) const;
-} op_ptr_min_max;
-
-void
-pointer_min_max_operator::wi_fold (irange &r, tree type,
-                                  const wide_int &lh_lb,
-                                  const wide_int &lh_ub,
-                                  const wide_int &rh_lb,
-                                  const wide_int &rh_ub) const
-{
-  // For MIN/MAX expressions with pointers, we only care about
-  // nullness.  If both are non null, then the result is nonnull.
-  // If both are null, then the result is null.  Otherwise they
-  // are varying.
-  if (!wi_includes_zero_p (type, lh_lb, lh_ub)
-      && !wi_includes_zero_p (type, rh_lb, rh_ub))
-    r = range_nonzero (type);
-  else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub))
-    r = range_zero (type);
-  else
-    r.set_varying (type);
-}
-
-
-class pointer_and_operator : public range_operator
-{
-public:
-  virtual void wi_fold (irange &r, tree type,
-                       const wide_int &lh_lb, const wide_int &lh_ub,
-                       const wide_int &rh_lb, const wide_int &rh_ub) const;
-} op_pointer_and;
-
-void
-pointer_and_operator::wi_fold (irange &r, tree type,
-                              const wide_int &lh_lb,
-                              const wide_int &lh_ub,
-                              const wide_int &rh_lb ATTRIBUTE_UNUSED,
-                              const wide_int &rh_ub ATTRIBUTE_UNUSED) const
-{
-  // For pointer types, we are really only interested in asserting
-  // whether the expression evaluates to non-NULL.
-  if (wi_zero_p (type, lh_lb, lh_ub) || wi_zero_p (type, lh_lb, lh_ub))
-    r = range_zero (type);
-  else 
-    r.set_varying (type);
-}
-
-
-class pointer_or_operator : public range_operator
-{
-  using range_operator::op1_range;
-  using range_operator::op2_range;
-public:
-  virtual bool op1_range (irange &r, tree type,
-                         const irange &lhs,
-                         const irange &op2,
-                         relation_trio rel = TRIO_VARYING) const;
-  virtual bool op2_range (irange &r, tree type,
-                         const irange &lhs,
-                         const irange &op1,
-                         relation_trio rel = TRIO_VARYING) const;
-  virtual void wi_fold (irange &r, tree type,
-                       const wide_int &lh_lb, const wide_int &lh_ub,
-                       const wide_int &rh_lb, const wide_int &rh_ub) const;
-} op_pointer_or;
-
-bool
-pointer_or_operator::op1_range (irange &r, tree type,
-                               const irange &lhs,
-                               const irange &op2 ATTRIBUTE_UNUSED,
-                               relation_trio) const
-{
-  if (lhs.undefined_p ())
-    return false;
-  if (lhs.zero_p ())
-    {
-      r.set_zero (type);
-      return true;
-    }
-  r.set_varying (type);
-  return true;
-}
-
-bool
-pointer_or_operator::op2_range (irange &r, tree type,
-                               const irange &lhs,
-                               const irange &op1,
-                               relation_trio) const
-{
-  return pointer_or_operator::op1_range (r, type, lhs, op1);
-}
-
-void
-pointer_or_operator::wi_fold (irange &r, tree type,
-                             const wide_int &lh_lb,
-                             const wide_int &lh_ub,
-                             const wide_int &rh_lb,
-                             const wide_int &rh_ub) const
-{
-  // For pointer types, we are really only interested in asserting
-  // whether the expression evaluates to non-NULL.
-  if (!wi_includes_zero_p (type, lh_lb, lh_ub)
-      && !wi_includes_zero_p (type, rh_lb, rh_ub))
-    r = range_nonzero (type);
-  else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub))
-    r = range_zero (type);
-  else
-    r.set_varying (type);
-}
 \f
 // Initialize any integral operators to the primary table
 
@@ -4537,23 +4300,6 @@ range_op_table::initialize_integral_ops ()
   set (ABSU_EXPR, op_absu);
 }
 
-pointer_table::pointer_table ()
-{
-  set (BIT_AND_EXPR, op_pointer_and);
-  set (BIT_IOR_EXPR, op_pointer_or);
-  set (MIN_EXPR, op_ptr_min_max);
-  set (MAX_EXPR, op_ptr_min_max);
-}
-
-// Initialize any pointer operators to the primary table
-
-void
-range_op_table::initialize_pointer_ops ()
-{
-  set (POINTER_PLUS_EXPR, op_pointer_plus);
-  set (POINTER_DIFF_EXPR, op_pointer_diff);
-}
-
 #if CHECKING_P
 #include "selftest.h"
 
This page took 0.090719 seconds and 5 git commands to generate.