Bug 990764 - Add MOZ_ASSERT_UNREACHABLE and MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE. r=Waldo
authorChris Peterson <cpeterson@mozilla.com>
Sat, 05 Apr 2014 18:07:09 -0700
changeset 182299 5153021485badc4e0cadb12b3835439cf0352b8a
parent 182298 10bf5fc4bfc3b839a37925a440f1b67363860025
child 182300 335bd3cd08025f412bf4f26ab43a5bb74e8f0755
push id26755
push usercbook@mozilla.com
push dateFri, 09 May 2014 11:54:47 +0000
treeherdermozilla-central@5700a88f895f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs990764
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 990764 - Add MOZ_ASSERT_UNREACHABLE and MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE. r=Waldo
mfbt/Assertions.h
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -377,16 +377,26 @@ void ValidateAssertConditionType()
 
 #ifdef DEBUG
 #  define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
 #else
 #  define MOZ_ASSERT(...) do { } while(0)
 #endif /* DEBUG */
 
 /*
+ * MOZ_NIGHTLY_ASSERT is defined for both debug and release builds on the
+ * Nightly channel, but only debug builds on Aurora, Beta, and Release.
+ */
+#if defined(NIGHTLY_BUILD)
+#  define MOZ_NIGHTLY_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
+#else
+#  define MOZ_NIGHTLY_ASSERT(...) MOZ_ASSERT(__VA_ARGS__)
+#endif
+
+/*
  * MOZ_ASSERT_IF(cond1, cond2) is equivalent to MOZ_ASSERT(cond2) if cond1 is
  * true.
  *
  *   MOZ_ASSERT_IF(isPrime(num), num == 2 || isOdd(num));
  *
  * As with MOZ_ASSERT, MOZ_ASSERT_IF has effect only in debug builds.  It is
  * designed to catch bugs during debugging, not "in the field".
  */
@@ -398,19 +408,18 @@ void ValidateAssertConditionType()
      } while (0)
 #else
 #  define MOZ_ASSERT_IF(cond, expr)  do { } while (0)
 #endif
 
 /*
  * MOZ_ASSUME_UNREACHABLE_MARKER() expands to an expression which states that it is
  * undefined behavior for execution to reach this point.  No guarantees are made
- * about what will happen if this is reached at runtime.  Most code should
- * probably use the higher level MOZ_ASSUME_UNREACHABLE, which uses this when
- * appropriate.
+ * about what will happen if this is reached at runtime.  Most code should use
+ * MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE because it has extra asserts.
  */
 #if defined(__clang__)
 #  define MOZ_ASSUME_UNREACHABLE_MARKER() __builtin_unreachable()
 #elif defined(__GNUC__)
    /*
     * __builtin_unreachable() was implemented in gcc 4.5.  If we don't have
     * that, call a noreturn function; abort() will do nicely.  Qualify the call
     * in C++ in case there's another abort() visible in local scope.
@@ -430,31 +439,32 @@ void ValidateAssertConditionType()
 #  ifdef __cplusplus
 #    define MOZ_ASSUME_UNREACHABLE_MARKER() ::abort()
 #  else
 #    define MOZ_ASSUME_UNREACHABLE_MARKER() abort()
 #  endif
 #endif
 
 /*
- * MOZ_ASSUME_UNREACHABLE([reason]) tells the compiler that it can assume that
- * the macro call cannot be reached during execution.  This lets the compiler
- * generate better-optimized code under some circumstances, at the expense of
- * the program's behavior being undefined if control reaches the
- * MOZ_ASSUME_UNREACHABLE.
+ * MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE([reason]) tells the compiler that it
+ * can assume that the macro call cannot be reached during execution.  This lets
+ * the compiler generate better-optimized code under some circumstances, at the
+ * expense of the program's behavior being undefined if control reaches the
+ * MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE.
  *
  * In Gecko, you probably should not use this macro outside of performance- or
  * size-critical code, because it's unsafe.  If you don't care about code size
  * or performance, you should probably use MOZ_ASSERT or MOZ_CRASH.
  *
  * SpiderMonkey is a different beast, and there it's acceptable to use
- * MOZ_ASSUME_UNREACHABLE more widely.
+ * MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE more widely.
  *
- * Note that MOZ_ASSUME_UNREACHABLE is noreturn, so it's valid not to return a
- * value following a MOZ_ASSUME_UNREACHABLE call.
+ * Note that MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE is noreturn, so it's valid
+ * not to return a value following a MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE
+ * call.
  *
  * Example usage:
  *
  *   enum ValueType {
  *     VALUE_STRING,
  *     VALUE_INT,
  *     VALUE_FLOAT
  *   };
@@ -464,29 +474,42 @@ void ValidateAssertConditionType()
  *     // We know for sure that type is either INT or FLOAT, and we want this
  *     // code to run as quickly as possible.
  *     switch (type) {
  *     case VALUE_INT:
  *       return *(int*) value;
  *     case VALUE_FLOAT:
  *       return (int) *(float*) value;
  *     default:
- *       MOZ_ASSUME_UNREACHABLE("can only handle VALUE_INT and VALUE_FLOAT");
+ *       MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected ValueType");
  *     }
  *   }
  */
-#if defined(DEBUG)
-#  define MOZ_ASSUME_UNREACHABLE(...) \
-     do { \
-       MOZ_ASSERT(false, "MOZ_ASSUME_UNREACHABLE(" __VA_ARGS__ ")"); \
-       MOZ_ASSUME_UNREACHABLE_MARKER(); \
-     } while (0)
-#else
-#  define MOZ_ASSUME_UNREACHABLE(reason)  MOZ_ASSUME_UNREACHABLE_MARKER()
-#endif
+
+/*
+ * Assert in all debug builds plus the Nightly channel's release builds. Take
+ * this extra testing precaution because hitting MOZ_ASSUME_UNREACHABLE_MARKER
+ * could trigger exploitable undefined behavior.
+ */
+#define MOZ_ASSERT_UNREACHABLE(reason) \
+   MOZ_NIGHTLY_ASSERT(false, "MOZ_ASSERT_UNREACHABLE: " reason)
+
+#define MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(reason) \
+   do { \
+     MOZ_ASSERT_UNREACHABLE(reason); \
+     MOZ_ASSUME_UNREACHABLE_MARKER(); \
+   } while (0)
+
+/*
+ * TODO: Bug 990764: Audit all MOZ_ASSUME_UNREACHABLE calls and replace them
+ * with MOZ_ASSERT_UNREACHABLE, MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE, or
+ * MOZ_CRASH. For now, preserve the macro's same meaning of unreachable.
+ */
+#define MOZ_ASSUME_UNREACHABLE(reason) \
+   MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(reason)
 
 /*
  * MOZ_ALWAYS_TRUE(expr) and MOZ_ALWAYS_FALSE(expr) always evaluate the provided
  * expression, in debug builds and in release builds both.  Then, in debug
  * builds only, the value of the expression is asserted either true or false
  * using MOZ_ASSERT.
  */
 #ifdef DEBUG