Some pre-PR fixes.
diff --git a/BUILD b/BUILD
index 633825c..5bed646 100644
--- a/BUILD
+++ b/BUILD
@@ -109,6 +109,7 @@
 cc_library(
     name = "fastdecode",
     srcs = [
+        "upb/decode.h",
         "upb/decode_internal.h",
         "upb/decode_fast.c",
         "upb/decode_fast.h",
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index c627271..82d518c 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -82,6 +82,7 @@
   port
   /third_party/utf8_range)
 add_library(fastdecode
+  ../upb/decode.h
   ../upb/decode_internal.h
   ../upb/decode_fast.c
   ../upb/decode_fast.h
diff --git a/cmake/google/protobuf/descriptor.upb.c b/cmake/google/protobuf/descriptor.upb.c
index 588e431..007d651 100644
--- a/cmake/google/protobuf/descriptor.upb.c
+++ b/cmake/google/protobuf/descriptor.upb.c
@@ -23,7 +23,7 @@
 const upb_msglayout google_protobuf_FileDescriptorSet_msginit = {
   &google_protobuf_FileDescriptorSet_submsgs[0],
   &google_protobuf_FileDescriptorSet__fields[0],
-  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
+  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_FileDescriptorProto_submsgs[6] = {
@@ -53,7 +53,7 @@
 const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
   &google_protobuf_FileDescriptorProto_submsgs[0],
   &google_protobuf_FileDescriptorProto__fields[0],
-  UPB_SIZE(64, 128), 12, _UPB_MSGEXT_NONE, 12, 255,
+  0, UPB_SIZE(64, 128), 12, _UPB_MSGEXT_NONE, 12, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_DescriptorProto_submsgs[7] = {
@@ -82,7 +82,7 @@
 const upb_msglayout google_protobuf_DescriptorProto_msginit = {
   &google_protobuf_DescriptorProto_submsgs[0],
   &google_protobuf_DescriptorProto__fields[0],
-  UPB_SIZE(48, 96), 10, _UPB_MSGEXT_NONE, 10, 255,
+  0, UPB_SIZE(48, 96), 10, _UPB_MSGEXT_NONE, 10, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_DescriptorProto_ExtensionRange_submsgs[1] = {
@@ -98,7 +98,7 @@
 const upb_msglayout google_protobuf_DescriptorProto_ExtensionRange_msginit = {
   &google_protobuf_DescriptorProto_ExtensionRange_submsgs[0],
   &google_protobuf_DescriptorProto_ExtensionRange__fields[0],
-  UPB_SIZE(16, 24), 3, _UPB_MSGEXT_NONE, 3, 255,
+  0, UPB_SIZE(16, 24), 3, _UPB_MSGEXT_NONE, 3, 255,
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto_ReservedRange__fields[2] = {
@@ -109,7 +109,7 @@
 const upb_msglayout google_protobuf_DescriptorProto_ReservedRange_msginit = {
   NULL,
   &google_protobuf_DescriptorProto_ReservedRange__fields[0],
-  UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
+  0, UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_ExtensionRangeOptions_submsgs[1] = {
@@ -123,7 +123,7 @@
 const upb_msglayout google_protobuf_ExtensionRangeOptions_msginit = {
   &google_protobuf_ExtensionRangeOptions_submsgs[0],
   &google_protobuf_ExtensionRangeOptions__fields[0],
-  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_FieldDescriptorProto_submsgs[3] = {
@@ -149,7 +149,7 @@
 const upb_msglayout google_protobuf_FieldDescriptorProto_msginit = {
   &google_protobuf_FieldDescriptorProto_submsgs[0],
   &google_protobuf_FieldDescriptorProto__fields[0],
-  UPB_SIZE(72, 112), 11, _UPB_MSGEXT_NONE, 10, 255,
+  0, UPB_SIZE(72, 112), 11, _UPB_MSGEXT_NONE, 10, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_OneofDescriptorProto_submsgs[1] = {
@@ -164,7 +164,7 @@
 const upb_msglayout google_protobuf_OneofDescriptorProto_msginit = {
   &google_protobuf_OneofDescriptorProto_submsgs[0],
   &google_protobuf_OneofDescriptorProto__fields[0],
-  UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
+  0, UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumDescriptorProto_submsgs[3] = {
@@ -184,7 +184,7 @@
 const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
   &google_protobuf_EnumDescriptorProto_submsgs[0],
   &google_protobuf_EnumDescriptorProto__fields[0],
-  UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 5, 255,
+  0, UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 5, 255,
 };
 
 static const upb_msglayout_field google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[2] = {
@@ -195,7 +195,7 @@
 const upb_msglayout google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit = {
   NULL,
   &google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[0],
-  UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
+  0, UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumValueDescriptorProto_submsgs[1] = {
@@ -211,7 +211,7 @@
 const upb_msglayout google_protobuf_EnumValueDescriptorProto_msginit = {
   &google_protobuf_EnumValueDescriptorProto_submsgs[0],
   &google_protobuf_EnumValueDescriptorProto__fields[0],
-  UPB_SIZE(24, 32), 3, _UPB_MSGEXT_NONE, 3, 255,
+  0, UPB_SIZE(24, 32), 3, _UPB_MSGEXT_NONE, 3, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_ServiceDescriptorProto_submsgs[2] = {
@@ -228,7 +228,7 @@
 const upb_msglayout google_protobuf_ServiceDescriptorProto_msginit = {
   &google_protobuf_ServiceDescriptorProto_submsgs[0],
   &google_protobuf_ServiceDescriptorProto__fields[0],
-  UPB_SIZE(24, 48), 3, _UPB_MSGEXT_NONE, 3, 255,
+  0, UPB_SIZE(24, 48), 3, _UPB_MSGEXT_NONE, 3, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_MethodDescriptorProto_submsgs[1] = {
@@ -247,7 +247,7 @@
 const upb_msglayout google_protobuf_MethodDescriptorProto_msginit = {
   &google_protobuf_MethodDescriptorProto_submsgs[0],
   &google_protobuf_MethodDescriptorProto__fields[0],
-  UPB_SIZE(32, 64), 6, _UPB_MSGEXT_NONE, 6, 255,
+  0, UPB_SIZE(32, 64), 6, _UPB_MSGEXT_NONE, 6, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_FileOptions_submsgs[2] = {
@@ -282,7 +282,7 @@
 const upb_msglayout google_protobuf_FileOptions_msginit = {
   &google_protobuf_FileOptions_submsgs[0],
   &google_protobuf_FileOptions__fields[0],
-  UPB_SIZE(104, 192), 21, _UPB_MSGEXT_EXTENDABLE, 1, 255,
+  0, UPB_SIZE(104, 192), 21, _UPB_MSGEXT_EXTENDABLE, 1, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_MessageOptions_submsgs[1] = {
@@ -300,7 +300,7 @@
 const upb_msglayout google_protobuf_MessageOptions_msginit = {
   &google_protobuf_MessageOptions_submsgs[0],
   &google_protobuf_MessageOptions__fields[0],
-  UPB_SIZE(16, 16), 5, _UPB_MSGEXT_EXTENDABLE, 3, 255,
+  0, UPB_SIZE(16, 16), 5, _UPB_MSGEXT_EXTENDABLE, 3, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_FieldOptions_submsgs[3] = {
@@ -322,7 +322,7 @@
 const upb_msglayout google_protobuf_FieldOptions_msginit = {
   &google_protobuf_FieldOptions_submsgs[0],
   &google_protobuf_FieldOptions__fields[0],
-  UPB_SIZE(24, 24), 7, _UPB_MSGEXT_EXTENDABLE, 3, 255,
+  0, UPB_SIZE(24, 24), 7, _UPB_MSGEXT_EXTENDABLE, 3, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_OneofOptions_submsgs[1] = {
@@ -336,7 +336,7 @@
 const upb_msglayout google_protobuf_OneofOptions_msginit = {
   &google_protobuf_OneofOptions_submsgs[0],
   &google_protobuf_OneofOptions__fields[0],
-  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumOptions_submsgs[1] = {
@@ -352,7 +352,7 @@
 const upb_msglayout google_protobuf_EnumOptions_msginit = {
   &google_protobuf_EnumOptions_submsgs[0],
   &google_protobuf_EnumOptions__fields[0],
-  UPB_SIZE(8, 16), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  0, UPB_SIZE(8, 16), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumValueOptions_submsgs[1] = {
@@ -367,7 +367,7 @@
 const upb_msglayout google_protobuf_EnumValueOptions_msginit = {
   &google_protobuf_EnumValueOptions_submsgs[0],
   &google_protobuf_EnumValueOptions__fields[0],
-  UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 1, 255,
+  0, UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 1, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_ServiceOptions_submsgs[1] = {
@@ -382,7 +382,7 @@
 const upb_msglayout google_protobuf_ServiceOptions_msginit = {
   &google_protobuf_ServiceOptions_submsgs[0],
   &google_protobuf_ServiceOptions__fields[0],
-  UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  0, UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_MethodOptions_submsgs[2] = {
@@ -399,7 +399,7 @@
 const upb_msglayout google_protobuf_MethodOptions_msginit = {
   &google_protobuf_MethodOptions_submsgs[0],
   &google_protobuf_MethodOptions__fields[0],
-  UPB_SIZE(16, 24), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  0, UPB_SIZE(16, 24), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_UninterpretedOption_submsgs[1] = {
@@ -419,7 +419,7 @@
 const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
   &google_protobuf_UninterpretedOption_submsgs[0],
   &google_protobuf_UninterpretedOption__fields[0],
-  UPB_SIZE(64, 96), 7, _UPB_MSGEXT_NONE, 0, 255,
+  0, UPB_SIZE(64, 96), 7, _UPB_MSGEXT_NONE, 0, 255,
 };
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption_NamePart__fields[2] = {
@@ -430,7 +430,7 @@
 const upb_msglayout google_protobuf_UninterpretedOption_NamePart_msginit = {
   NULL,
   &google_protobuf_UninterpretedOption_NamePart__fields[0],
-  UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
+  6, UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_SourceCodeInfo_submsgs[1] = {
@@ -444,7 +444,7 @@
 const upb_msglayout google_protobuf_SourceCodeInfo_msginit = {
   &google_protobuf_SourceCodeInfo_submsgs[0],
   &google_protobuf_SourceCodeInfo__fields[0],
-  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
+  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
 };
 
 static const upb_msglayout_field google_protobuf_SourceCodeInfo_Location__fields[5] = {
@@ -458,7 +458,7 @@
 const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
   NULL,
   &google_protobuf_SourceCodeInfo_Location__fields[0],
-  UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 4, 255,
+  0, UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 4, 255,
 };
 
 static const upb_msglayout_sub google_protobuf_GeneratedCodeInfo_submsgs[1] = {
@@ -472,7 +472,7 @@
 const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit = {
   &google_protobuf_GeneratedCodeInfo_submsgs[0],
   &google_protobuf_GeneratedCodeInfo__fields[0],
-  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
+  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
 };
 
 static const upb_msglayout_field google_protobuf_GeneratedCodeInfo_Annotation__fields[4] = {
@@ -485,7 +485,7 @@
 const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit = {
   NULL,
   &google_protobuf_GeneratedCodeInfo_Annotation__fields[0],
-  UPB_SIZE(24, 48), 4, _UPB_MSGEXT_NONE, 4, 255,
+  0, UPB_SIZE(24, 48), 4, _UPB_MSGEXT_NONE, 4, 255,
 };
 
 static const upb_msglayout *messages_layout[27] = {
@@ -518,15 +518,21 @@
   &google_protobuf_GeneratedCodeInfo_Annotation_msginit,
 };
 
+const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit = {
+  NULL,
+  0x7fffeULL,
+  0,
+};
+
 const upb_enumlayout google_protobuf_FieldDescriptorProto_Label_enuminit = {
   NULL,
   0xeULL,
   0,
 };
 
-const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit = {
+const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit = {
   NULL,
-  0x7fffeULL,
+  0xeULL,
   0,
 };
 
@@ -542,12 +548,6 @@
   0,
 };
 
-const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit = {
-  NULL,
-  0xeULL,
-  0,
-};
-
 const upb_enumlayout google_protobuf_MethodOptions_IdempotencyLevel_enuminit = {
   NULL,
   0x7ULL,
@@ -555,11 +555,11 @@
 };
 
 static const upb_enumlayout *enums_layout[6] = {
-  &google_protobuf_FieldDescriptorProto_Label_enuminit,
   &google_protobuf_FieldDescriptorProto_Type_enuminit,
+  &google_protobuf_FieldDescriptorProto_Label_enuminit,
+  &google_protobuf_FileOptions_OptimizeMode_enuminit,
   &google_protobuf_FieldOptions_CType_enuminit,
   &google_protobuf_FieldOptions_JSType_enuminit,
-  &google_protobuf_FileOptions_OptimizeMode_enuminit,
   &google_protobuf_MethodOptions_IdempotencyLevel_enuminit,
 };
 
diff --git a/cmake/google/protobuf/descriptor.upb.h b/cmake/google/protobuf/descriptor.upb.h
index ad6fb7e..f3d7963 100644
--- a/cmake/google/protobuf/descriptor.upb.h
+++ b/cmake/google/protobuf/descriptor.upb.h
@@ -103,12 +103,6 @@
 extern const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit;
 
 typedef enum {
-  google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1,
-  google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2,
-  google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3
-} google_protobuf_FieldDescriptorProto_Label;
-
-typedef enum {
   google_protobuf_FieldDescriptorProto_TYPE_DOUBLE = 1,
   google_protobuf_FieldDescriptorProto_TYPE_FLOAT = 2,
   google_protobuf_FieldDescriptorProto_TYPE_INT64 = 3,
@@ -130,6 +124,18 @@
 } google_protobuf_FieldDescriptorProto_Type;
 
 typedef enum {
+  google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1,
+  google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2,
+  google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3
+} google_protobuf_FieldDescriptorProto_Label;
+
+typedef enum {
+  google_protobuf_FileOptions_SPEED = 1,
+  google_protobuf_FileOptions_CODE_SIZE = 2,
+  google_protobuf_FileOptions_LITE_RUNTIME = 3
+} google_protobuf_FileOptions_OptimizeMode;
+
+typedef enum {
   google_protobuf_FieldOptions_STRING = 0,
   google_protobuf_FieldOptions_CORD = 1,
   google_protobuf_FieldOptions_STRING_PIECE = 2
@@ -142,23 +148,17 @@
 } google_protobuf_FieldOptions_JSType;
 
 typedef enum {
-  google_protobuf_FileOptions_SPEED = 1,
-  google_protobuf_FileOptions_CODE_SIZE = 2,
-  google_protobuf_FileOptions_LITE_RUNTIME = 3
-} google_protobuf_FileOptions_OptimizeMode;
-
-typedef enum {
   google_protobuf_MethodOptions_IDEMPOTENCY_UNKNOWN = 0,
   google_protobuf_MethodOptions_NO_SIDE_EFFECTS = 1,
   google_protobuf_MethodOptions_IDEMPOTENT = 2
 } google_protobuf_MethodOptions_IdempotencyLevel;
 
 
-extern const upb_enumlayout google_protobuf_FieldDescriptorProto_Label_enuminit;
 extern const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit;
+extern const upb_enumlayout google_protobuf_FieldDescriptorProto_Label_enuminit;
+extern const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit;
 extern const upb_enumlayout google_protobuf_FieldOptions_CType_enuminit;
 extern const upb_enumlayout google_protobuf_FieldOptions_JSType_enuminit;
-extern const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit;
 extern const upb_enumlayout google_protobuf_MethodOptions_IdempotencyLevel_enuminit;
 
 /* google.protobuf.FileDescriptorSet */
@@ -170,7 +170,7 @@
                         upb_arena *arena) {
   google_protobuf_FileDescriptorSet *ret = google_protobuf_FileDescriptorSet_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_FileDescriptorSet_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_FileDescriptorSet_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_FileDescriptorSet *google_protobuf_FileDescriptorSet_parse_ex(const char *buf, size_t size,
@@ -178,7 +178,7 @@
                            upb_arena *arena) {
   google_protobuf_FileDescriptorSet *ret = google_protobuf_FileDescriptorSet_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_FileDescriptorSet_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_FileDescriptorSet_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -213,7 +213,7 @@
                         upb_arena *arena) {
   google_protobuf_FileDescriptorProto *ret = google_protobuf_FileDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_FileDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_FileDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_FileDescriptorProto *google_protobuf_FileDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -221,7 +221,7 @@
                            upb_arena *arena) {
   google_protobuf_FileDescriptorProto *ret = google_protobuf_FileDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_FileDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_FileDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -382,7 +382,7 @@
                         upb_arena *arena) {
   google_protobuf_DescriptorProto *ret = google_protobuf_DescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_DescriptorProto *google_protobuf_DescriptorProto_parse_ex(const char *buf, size_t size,
@@ -390,7 +390,7 @@
                            upb_arena *arena) {
   google_protobuf_DescriptorProto *ret = google_protobuf_DescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -547,7 +547,7 @@
                         upb_arena *arena) {
   google_protobuf_DescriptorProto_ExtensionRange *ret = google_protobuf_DescriptorProto_ExtensionRange_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ExtensionRange_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ExtensionRange_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_DescriptorProto_ExtensionRange *google_protobuf_DescriptorProto_ExtensionRange_parse_ex(const char *buf, size_t size,
@@ -555,7 +555,7 @@
                            upb_arena *arena) {
   google_protobuf_DescriptorProto_ExtensionRange *ret = google_protobuf_DescriptorProto_ExtensionRange_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ExtensionRange_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ExtensionRange_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -602,7 +602,7 @@
                         upb_arena *arena) {
   google_protobuf_DescriptorProto_ReservedRange *ret = google_protobuf_DescriptorProto_ReservedRange_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ReservedRange_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ReservedRange_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_DescriptorProto_ReservedRange *google_protobuf_DescriptorProto_ReservedRange_parse_ex(const char *buf, size_t size,
@@ -610,7 +610,7 @@
                            upb_arena *arena) {
   google_protobuf_DescriptorProto_ReservedRange *ret = google_protobuf_DescriptorProto_ReservedRange_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ReservedRange_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ReservedRange_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -642,7 +642,7 @@
                         upb_arena *arena) {
   google_protobuf_ExtensionRangeOptions *ret = google_protobuf_ExtensionRangeOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_ExtensionRangeOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_ExtensionRangeOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_ExtensionRangeOptions *google_protobuf_ExtensionRangeOptions_parse_ex(const char *buf, size_t size,
@@ -650,7 +650,7 @@
                            upb_arena *arena) {
   google_protobuf_ExtensionRangeOptions *ret = google_protobuf_ExtensionRangeOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_ExtensionRangeOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_ExtensionRangeOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -685,7 +685,7 @@
                         upb_arena *arena) {
   google_protobuf_FieldDescriptorProto *ret = google_protobuf_FieldDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_FieldDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_FieldDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_FieldDescriptorProto *google_protobuf_FieldDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -693,7 +693,7 @@
                            upb_arena *arena) {
   google_protobuf_FieldDescriptorProto *ret = google_protobuf_FieldDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_FieldDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_FieldDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -709,9 +709,9 @@
 UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_number(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 3); }
 UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_number(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 12), int32_t); }
 UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_label(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 4); }
-UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_label(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_label(const google_protobuf_FieldDescriptorProto *msg) { return google_protobuf_FieldDescriptorProto_has_label(msg) ? *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) : 1; }
 UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_type(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 5); }
-UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_type(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_type(const google_protobuf_FieldDescriptorProto *msg) { return google_protobuf_FieldDescriptorProto_has_type(msg) ? *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) : 1; }
 UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_type_name(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 6); }
 UPB_INLINE upb_strview google_protobuf_FieldDescriptorProto_type_name(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(40, 56), upb_strview); }
 UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_default_value(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 7); }
@@ -788,7 +788,7 @@
                         upb_arena *arena) {
   google_protobuf_OneofDescriptorProto *ret = google_protobuf_OneofDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_OneofDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_OneofDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_OneofDescriptorProto *google_protobuf_OneofDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -796,7 +796,7 @@
                            upb_arena *arena) {
   google_protobuf_OneofDescriptorProto *ret = google_protobuf_OneofDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_OneofDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_OneofDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -837,7 +837,7 @@
                         upb_arena *arena) {
   google_protobuf_EnumDescriptorProto *ret = google_protobuf_EnumDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_EnumDescriptorProto *google_protobuf_EnumDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -845,7 +845,7 @@
                            upb_arena *arena) {
   google_protobuf_EnumDescriptorProto *ret = google_protobuf_EnumDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -927,7 +927,7 @@
                         upb_arena *arena) {
   google_protobuf_EnumDescriptorProto_EnumReservedRange *ret = google_protobuf_EnumDescriptorProto_EnumReservedRange_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_EnumDescriptorProto_EnumReservedRange *google_protobuf_EnumDescriptorProto_EnumReservedRange_parse_ex(const char *buf, size_t size,
@@ -935,7 +935,7 @@
                            upb_arena *arena) {
   google_protobuf_EnumDescriptorProto_EnumReservedRange *ret = google_protobuf_EnumDescriptorProto_EnumReservedRange_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -967,7 +967,7 @@
                         upb_arena *arena) {
   google_protobuf_EnumValueDescriptorProto *ret = google_protobuf_EnumValueDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_EnumValueDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_EnumValueDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_EnumValueDescriptorProto *google_protobuf_EnumValueDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -975,7 +975,7 @@
                            upb_arena *arena) {
   google_protobuf_EnumValueDescriptorProto *ret = google_protobuf_EnumValueDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_EnumValueDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_EnumValueDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1022,7 +1022,7 @@
                         upb_arena *arena) {
   google_protobuf_ServiceDescriptorProto *ret = google_protobuf_ServiceDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_ServiceDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_ServiceDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_ServiceDescriptorProto *google_protobuf_ServiceDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -1030,7 +1030,7 @@
                            upb_arena *arena) {
   google_protobuf_ServiceDescriptorProto *ret = google_protobuf_ServiceDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_ServiceDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_ServiceDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1086,7 +1086,7 @@
                         upb_arena *arena) {
   google_protobuf_MethodDescriptorProto *ret = google_protobuf_MethodDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_MethodDescriptorProto_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_MethodDescriptorProto_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_MethodDescriptorProto *google_protobuf_MethodDescriptorProto_parse_ex(const char *buf, size_t size,
@@ -1094,7 +1094,7 @@
                            upb_arena *arena) {
   google_protobuf_MethodDescriptorProto *ret = google_protobuf_MethodDescriptorProto_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_MethodDescriptorProto_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_MethodDescriptorProto_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1159,7 +1159,7 @@
                         upb_arena *arena) {
   google_protobuf_FileOptions *ret = google_protobuf_FileOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_FileOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_FileOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_FileOptions *google_protobuf_FileOptions_parse_ex(const char *buf, size_t size,
@@ -1167,7 +1167,7 @@
                            upb_arena *arena) {
   google_protobuf_FileOptions *ret = google_protobuf_FileOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_FileOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_FileOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1181,7 +1181,7 @@
 UPB_INLINE bool google_protobuf_FileOptions_has_java_outer_classname(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 2); }
 UPB_INLINE upb_strview google_protobuf_FileOptions_java_outer_classname(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(28, 40), upb_strview); }
 UPB_INLINE bool google_protobuf_FileOptions_has_optimize_for(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 3); }
-UPB_INLINE int32_t google_protobuf_FileOptions_optimize_for(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE int32_t google_protobuf_FileOptions_optimize_for(const google_protobuf_FileOptions *msg) { return google_protobuf_FileOptions_has_optimize_for(msg) ? *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) : 1; }
 UPB_INLINE bool google_protobuf_FileOptions_has_java_multiple_files(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 4); }
 UPB_INLINE bool google_protobuf_FileOptions_java_multiple_files(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), bool); }
 UPB_INLINE bool google_protobuf_FileOptions_has_go_package(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 5); }
@@ -1199,7 +1199,7 @@
 UPB_INLINE bool google_protobuf_FileOptions_has_java_string_check_utf8(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 11); }
 UPB_INLINE bool google_protobuf_FileOptions_java_string_check_utf8(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(14, 14), bool); }
 UPB_INLINE bool google_protobuf_FileOptions_has_cc_enable_arenas(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 12); }
-UPB_INLINE bool google_protobuf_FileOptions_cc_enable_arenas(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(15, 15), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_cc_enable_arenas(const google_protobuf_FileOptions *msg) { return google_protobuf_FileOptions_has_cc_enable_arenas(msg) ? *UPB_PTR_AT(msg, UPB_SIZE(15, 15), bool) : true; }
 UPB_INLINE bool google_protobuf_FileOptions_has_objc_class_prefix(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 13); }
 UPB_INLINE upb_strview google_protobuf_FileOptions_objc_class_prefix(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(44, 72), upb_strview); }
 UPB_INLINE bool google_protobuf_FileOptions_has_csharp_namespace(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 14); }
@@ -1322,7 +1322,7 @@
                         upb_arena *arena) {
   google_protobuf_MessageOptions *ret = google_protobuf_MessageOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_MessageOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_MessageOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_MessageOptions *google_protobuf_MessageOptions_parse_ex(const char *buf, size_t size,
@@ -1330,7 +1330,7 @@
                            upb_arena *arena) {
   google_protobuf_MessageOptions *ret = google_protobuf_MessageOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_MessageOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_MessageOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1389,7 +1389,7 @@
                         upb_arena *arena) {
   google_protobuf_FieldOptions *ret = google_protobuf_FieldOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_FieldOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_FieldOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_FieldOptions *google_protobuf_FieldOptions_parse_ex(const char *buf, size_t size,
@@ -1397,7 +1397,7 @@
                            upb_arena *arena) {
   google_protobuf_FieldOptions *ret = google_protobuf_FieldOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_FieldOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_FieldOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1468,7 +1468,7 @@
                         upb_arena *arena) {
   google_protobuf_OneofOptions *ret = google_protobuf_OneofOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_OneofOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_OneofOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_OneofOptions *google_protobuf_OneofOptions_parse_ex(const char *buf, size_t size,
@@ -1476,7 +1476,7 @@
                            upb_arena *arena) {
   google_protobuf_OneofOptions *ret = google_protobuf_OneofOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_OneofOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_OneofOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1511,7 +1511,7 @@
                         upb_arena *arena) {
   google_protobuf_EnumOptions *ret = google_protobuf_EnumOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_EnumOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_EnumOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_EnumOptions *google_protobuf_EnumOptions_parse_ex(const char *buf, size_t size,
@@ -1519,7 +1519,7 @@
                            upb_arena *arena) {
   google_protobuf_EnumOptions *ret = google_protobuf_EnumOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_EnumOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_EnumOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1566,7 +1566,7 @@
                         upb_arena *arena) {
   google_protobuf_EnumValueOptions *ret = google_protobuf_EnumValueOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_EnumValueOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_EnumValueOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_EnumValueOptions *google_protobuf_EnumValueOptions_parse_ex(const char *buf, size_t size,
@@ -1574,7 +1574,7 @@
                            upb_arena *arena) {
   google_protobuf_EnumValueOptions *ret = google_protobuf_EnumValueOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_EnumValueOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_EnumValueOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1615,7 +1615,7 @@
                         upb_arena *arena) {
   google_protobuf_ServiceOptions *ret = google_protobuf_ServiceOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_ServiceOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_ServiceOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_ServiceOptions *google_protobuf_ServiceOptions_parse_ex(const char *buf, size_t size,
@@ -1623,7 +1623,7 @@
                            upb_arena *arena) {
   google_protobuf_ServiceOptions *ret = google_protobuf_ServiceOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_ServiceOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_ServiceOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1664,7 +1664,7 @@
                         upb_arena *arena) {
   google_protobuf_MethodOptions *ret = google_protobuf_MethodOptions_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_MethodOptions_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_MethodOptions_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_MethodOptions *google_protobuf_MethodOptions_parse_ex(const char *buf, size_t size,
@@ -1672,7 +1672,7 @@
                            upb_arena *arena) {
   google_protobuf_MethodOptions *ret = google_protobuf_MethodOptions_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_MethodOptions_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_MethodOptions_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1719,7 +1719,7 @@
                         upb_arena *arena) {
   google_protobuf_UninterpretedOption *ret = google_protobuf_UninterpretedOption_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_UninterpretedOption *google_protobuf_UninterpretedOption_parse_ex(const char *buf, size_t size,
@@ -1727,7 +1727,7 @@
                            upb_arena *arena) {
   google_protobuf_UninterpretedOption *ret = google_protobuf_UninterpretedOption_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1798,7 +1798,7 @@
                         upb_arena *arena) {
   google_protobuf_UninterpretedOption_NamePart *ret = google_protobuf_UninterpretedOption_NamePart_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_NamePart_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_NamePart_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_UninterpretedOption_NamePart *google_protobuf_UninterpretedOption_NamePart_parse_ex(const char *buf, size_t size,
@@ -1806,7 +1806,7 @@
                            upb_arena *arena) {
   google_protobuf_UninterpretedOption_NamePart *ret = google_protobuf_UninterpretedOption_NamePart_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_NamePart_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_NamePart_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1838,7 +1838,7 @@
                         upb_arena *arena) {
   google_protobuf_SourceCodeInfo *ret = google_protobuf_SourceCodeInfo_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_SourceCodeInfo *google_protobuf_SourceCodeInfo_parse_ex(const char *buf, size_t size,
@@ -1846,7 +1846,7 @@
                            upb_arena *arena) {
   google_protobuf_SourceCodeInfo *ret = google_protobuf_SourceCodeInfo_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1881,7 +1881,7 @@
                         upb_arena *arena) {
   google_protobuf_SourceCodeInfo_Location *ret = google_protobuf_SourceCodeInfo_Location_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_Location_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_Location_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_SourceCodeInfo_Location *google_protobuf_SourceCodeInfo_Location_parse_ex(const char *buf, size_t size,
@@ -1889,7 +1889,7 @@
                            upb_arena *arena) {
   google_protobuf_SourceCodeInfo_Location *ret = google_protobuf_SourceCodeInfo_Location_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_Location_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_Location_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1954,7 +1954,7 @@
                         upb_arena *arena) {
   google_protobuf_GeneratedCodeInfo *ret = google_protobuf_GeneratedCodeInfo_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_GeneratedCodeInfo *google_protobuf_GeneratedCodeInfo_parse_ex(const char *buf, size_t size,
@@ -1962,7 +1962,7 @@
                            upb_arena *arena) {
   google_protobuf_GeneratedCodeInfo *ret = google_protobuf_GeneratedCodeInfo_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
@@ -1997,7 +1997,7 @@
                         upb_arena *arena) {
   google_protobuf_GeneratedCodeInfo_Annotation *ret = google_protobuf_GeneratedCodeInfo_Annotation_new(arena);
   if (!ret) return NULL;
-  if (!upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena)) return NULL;
+  if (upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena)) return NULL;
   return ret;
 }
 UPB_INLINE google_protobuf_GeneratedCodeInfo_Annotation *google_protobuf_GeneratedCodeInfo_Annotation_parse_ex(const char *buf, size_t size,
@@ -2005,7 +2005,7 @@
                            upb_arena *arena) {
   google_protobuf_GeneratedCodeInfo_Annotation *ret = google_protobuf_GeneratedCodeInfo_Annotation_new(arena);
   if (!ret) return NULL;
-  if (!_upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_Annotation_msginit, extreg, options, arena)) {
+  if (_upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_Annotation_msginit, extreg, options, arena)) {
     return NULL;
   }
   return ret;
diff --git a/tests/conformance_upb.c b/tests/conformance_upb.c
index 12c7f1f..6330433 100644
--- a/tests/conformance_upb.c
+++ b/tests/conformance_upb.c
@@ -87,7 +87,8 @@
 bool parse_proto(upb_msg *msg, const upb_msgdef *m, const ctx* c) {
   upb_strview proto =
       conformance_ConformanceRequest_protobuf_payload(c->request);
-  if (upb_decode(proto.data, proto.size, msg, upb_msgdef_layout(m), c->arena)) {
+  if (upb_decode(proto.data, proto.size, msg, upb_msgdef_layout(m), c->arena) ==
+      UPB_DECODE_OK) {
     return true;
   } else {
     static const char msg[] = "Parse error";
diff --git a/tests/test_table.cc b/tests/test_table.cc
index 84ede2b..1c44280 100644
--- a/tests/test_table.cc
+++ b/tests/test_table.cc
@@ -155,7 +155,7 @@
   std::pair<bool, upb_value> Remove(const std::string& key) {
     std::pair<bool, upb_value> ret;
     ret.first =
-        upb_strtable_remove(&table_, key.c_str(), key.size(), &ret.second);
+        upb_strtable_remove2(&table_, key.c_str(), key.size(), &ret.second);
     return ret;
   }
 
diff --git a/upb/bindings/lua/msg.c b/upb/bindings/lua/msg.c
index 1b4d9ef..530d9b3 100644
--- a/upb/bindings/lua/msg.c
+++ b/upb/bindings/lua/msg.c
@@ -961,7 +961,7 @@
   buf = upb_arena_malloc(arena, len);
   memcpy(buf, pb, len);
 
-  ok = _upb_decode(buf, len, msg, layout, NULL, UPB_DECODE_ALIAS, arena);
+  ok = _upb_decode(buf, len, msg, layout, NULL, UPB_DECODE_ALIAS, arena) == 0;
 
   if (!ok) {
     lua_pushstring(L, "Error decoding protobuf.");
diff --git a/upb/decode.c b/upb/decode.c
index 20a08fc..65e3934 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -189,23 +189,22 @@
 static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
                               const upb_msglayout *layout);
 
-UPB_NORETURN static const char *decode_err(upb_decstate *d) {
-  UPB_LONGJMP(d->err, 1);
+UPB_NORETURN static void *decode_err(upb_decstate *d, upb_decodestatus status) {
+  UPB_LONGJMP(d->err, status);
 }
 
-const char *fastdecode_err(upb_decstate *d) {
-  longjmp(d->err, 1);
+const char *fastdecode_err(upb_decstate *d, int status) {
+  UPB_LONGJMP(d->err, status);
   return NULL;
 }
-
 static void decode_verifyutf8(upb_decstate *d, const char *buf, int len) {
-  if (!decode_verifyutf8_inl(buf, len)) decode_err(d);
+  if (!decode_verifyutf8_inl(buf, len)) decode_err(d, UPB_DECODE_BAD_UTF8);
 }
 
 static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) {
   bool need_realloc = arr->size - arr->len < elem;
   if (need_realloc && !_upb_array_realloc(arr, arr->len + elem, &d->arena)) {
-    decode_err(d);
+    decode_err(d, UPB_DECODE_OOM);
   }
   return need_realloc;
 }
@@ -241,15 +240,14 @@
     return ptr + 1;
   } else {
     decode_vret res = decode_longvarint64(ptr, byte);
-    if (!res.ptr) return decode_err(d);
+    if (!res.ptr) return decode_err(d, UPB_DECODE_MALFORMED);
     *val = res.val;
     return res.ptr;
   }
 }
 
 UPB_FORCEINLINE
-static const char *decode_tag(upb_decstate *d, const char *ptr,
-                                   uint32_t *val) {
+static const char *decode_tag(upb_decstate *d, const char *ptr, uint32_t *val) {
   uint64_t byte = (uint8_t)*ptr;
   if (UPB_LIKELY((byte & 0x80) == 0)) {
     *val = byte;
@@ -259,7 +257,9 @@
     decode_vret res = decode_longvarint64(ptr, byte);
     ptr = res.ptr;
     *val = res.val;
-    if (!ptr || *val > UINT32_MAX || ptr - start > 5) return decode_err(d);
+    if (!ptr || *val > UINT32_MAX || ptr - start > 5) {
+      return decode_err(d, UPB_DECODE_MALFORMED);
+    }
     return ptr;
   }
 }
@@ -298,20 +298,21 @@
 UPB_NOINLINE
 const char *decode_isdonefallback(upb_decstate *d, const char *ptr,
                                   int overrun) {
-  ptr = decode_isdonefallback_inl(d, ptr, overrun);
+  int status; 
+  ptr = decode_isdonefallback_inl(d, ptr, overrun, &status);
   if (ptr == NULL) {
-    return decode_err(d);
+    return decode_err(d, status);
   }
   return ptr;
 }
 
 static const char *decode_readstr(upb_decstate *d, const char *ptr, int size,
                                   upb_strview *str) {
-  if (d->alias) {
+  if (d->options & UPB_DECODE_ALIAS) {
     str->data = ptr;
   } else {
     char *data =  upb_arena_malloc(&d->arena, size);
-    if (!data) return decode_err(d);
+    if (!data) return decode_err(d, UPB_DECODE_OOM);
     memcpy(data, ptr, size);
     str->data = data;
   }
@@ -320,32 +321,39 @@
 }
 
 UPB_FORCEINLINE
-static const char *decode_tosubmsg(upb_decstate *d, const char *ptr,
-                                   upb_msg *submsg,
-                                   const upb_msglayout_sub *subs,
-                                   const upb_msglayout_field *field, int size) {
-  const upb_msglayout *subl = subs[field->submsg_index].submsg;
+static const char *decode_tosubmsg2(upb_decstate *d, const char *ptr,
+                                    upb_msg *submsg, const upb_msglayout *subl,
+                                    int size) {
   int saved_delta = decode_pushlimit(d, ptr, size);
-  if (--d->depth < 0) return decode_err(d);
+  if (--d->depth < 0) return decode_err(d, UPB_DECODE_MALFORMED);
   if (!decode_isdone(d, &ptr)) {
     ptr = decode_msg(d, ptr, submsg, subl);
   }
-  if (d->end_group != DECODE_NOGROUP) return decode_err(d);
+  if (d->end_group != DECODE_NOGROUP) return decode_err(d, UPB_DECODE_MALFORMED);
   decode_poplimit(d, ptr, saved_delta);
   d->depth++;
   return ptr;
 }
 
 UPB_FORCEINLINE
+static const char *decode_tosubmsg(upb_decstate *d, const char *ptr,
+                                   upb_msg *submsg,
+                                   const upb_msglayout_sub *subs,
+                                   const upb_msglayout_field *field, int size) {
+  return decode_tosubmsg2(d, ptr, submsg, subs[field->submsg_index].submsg,
+                          size);
+}
+
+UPB_FORCEINLINE
 static const char *decode_group(upb_decstate *d, const char *ptr,
                                 upb_msg *submsg, const upb_msglayout *subl,
                                 uint32_t number) {
-  if (--d->depth < 0) return decode_err(d);
+  if (--d->depth < 0) return decode_err(d, UPB_DECODE_MALFORMED);
   if (decode_isdone(d, &ptr)) {
-    return decode_err(d);
+    return decode_err(d, UPB_DECODE_MALFORMED);
   }
   ptr = decode_msg(d, ptr, submsg, subl);
-  if (d->end_group != number) return decode_err(d);
+  if (d->end_group != number) return decode_err(d, UPB_DECODE_MALFORMED);
   d->end_group = DECODE_NOGROUP;
   d->depth++;
   return ptr;
@@ -390,7 +398,7 @@
   end = encode_varint32(v, end);
 
   if (!_upb_msg_addunknown(msg, buf, end - buf, &d->arena)) {
-    decode_err(d);
+    decode_err(d, UPB_DECODE_OOM);
   }
 
   return false;
@@ -430,7 +438,8 @@
   int mask = (1 << lg2) - 1;
   size_t count = val->size >> lg2;
   if ((val->size & mask) != 0) {
-    return decode_err(d); /* Length isn't a round multiple of elem size. */
+    // Length isn't a round multiple of elem size.
+    return decode_err(d, UPB_DECODE_MALFORMED);
   }
   decode_reserve(d, arr, count);
   void *mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
@@ -504,7 +513,7 @@
   } else {
     size_t lg2 = desctype_to_elem_size_lg2[field->descriptortype];
     arr = _upb_array_new(&d->arena, 4, lg2);
-    if (!arr) return decode_err(d);
+    if (!arr) return decode_err(d, UPB_DECODE_OOM);
     *arrp = arr;
   }
 
@@ -542,6 +551,7 @@
     case OP_FIXPCK_LG2(3):
       return decode_fixed_packed(d, ptr, arr, val, field,
                                  op - OP_FIXPCK_LG2(0));
+        return decode_err(d, UPB_DECODE_MALFORMED); 
     case OP_VARPCK_LG2(0):
     case OP_VARPCK_LG2(2):
     case OP_VARPCK_LG2(3):
@@ -652,6 +662,19 @@
   }
 
   return ptr;
+} 
+
+UPB_FORCEINLINE
+static bool decode_checkrequired(upb_decstate *d, const upb_msg *msg,
+                                 const upb_msglayout *l) {
+  if (UPB_LIKELY(!l->required_mask)) return false;
+  // if (UPB_UNLIKELY(l->required_mask & 1)) {
+  //   return decode_checkrequired_slow(d, msg, l);
+  // }
+  uint64_t msg_head;
+  memcpy(&msg_head, msg, 8);
+  msg_head = _upb_be_swap64(msg_head);
+  return l->required_mask & ~msg_head;
 }
 
 UPB_FORCEINLINE
@@ -813,7 +836,7 @@
     default:
       break;
   }
-  return decode_err(d);
+  return decode_err(d, UPB_DECODE_MALFORMED);
 }
 
 UPB_FORCEINLINE
@@ -827,7 +850,7 @@
   if (UPB_UNLIKELY(mode & _UPB_MODE_IS_EXTENSION)) {
     const upb_msglayout_ext *ext_layout = (const upb_msglayout_ext*)field;
     upb_msg_ext *ext = _upb_msg_getorcreateext(msg, ext_layout, &d->arena);
-    if (UPB_UNLIKELY(!ext)) return decode_err(d);
+        if (UPB_UNLIKELY(!ext)) return decode_err(d, UPB_DECODE_OOM);
     msg = &ext->data;
     subs = &ext->ext->sub;
   }
@@ -847,21 +870,22 @@
 UPB_FORCEINLINE
 static const char *decode_unknown(upb_decstate *d, const char *ptr,
                                   upb_msg *msg, int field_number, int wire_type,
-                                  wireval val, const char **field_start) {
-  if (field_number == 0) return decode_err(d);
+                                  wireval val) {
+  if (field_number == 0) return decode_err(d, UPB_DECODE_MALFORMED);
 
   if (wire_type == UPB_WIRE_TYPE_DELIMITED) ptr += val.size;
   if (msg) {
     if (wire_type == UPB_WIRE_TYPE_START_GROUP) {
-      d->unknown = *field_start;
+      const char *preserved_unknown = d->unknown;
       d->unknown_msg = msg;
       ptr = decode_group(d, ptr, NULL, NULL, field_number);
       d->unknown_msg = NULL;
-      *field_start = d->unknown;
+      d->unknown = preserved_unknown;
+      // XXX: pointer could have flipped during decoding the group,
+      // ptr - d->unknown below will be borked.
     }
-    if (!_upb_msg_addunknown(msg, *field_start, ptr - *field_start,
-                             &d->arena)) {
-      return decode_err(d);
+    if (!_upb_msg_addunknown(msg, d->unknown, ptr - d->unknown, &d->arena)) {
+      return decode_err(d, UPB_DECODE_OOM);
     }
   } else if (wire_type == UPB_WIRE_TYPE_START_GROUP) {
     ptr = decode_group(d, ptr, NULL, NULL, field_number);
@@ -878,11 +902,11 @@
     const upb_msglayout_field *field;
     int field_number;
     int wire_type;
-    const char *field_start = ptr;
     wireval val;
     int op;
 
     UPB_ASSERT(ptr < d->limit_ptr);
+    d->unknown = ptr;
     ptr = decode_tag(d, ptr, &tag);
     field_number = tag >> 3;
     wire_type = tag & 7;
@@ -900,8 +924,7 @@
     } else {
       switch (op) {
         case OP_UNKNOWN:
-          ptr = decode_unknown(d, ptr, msg, field_number, wire_type, val,
-                               &field_start);
+          ptr = decode_unknown(d, ptr, msg, field_number, wire_type, val);
           break;
         case OP_MSGSET_ITEM:
           ptr = decode_msgset(d, ptr, msg, layout);
@@ -915,8 +938,14 @@
       }
     }
 
-    if (decode_isdone(d, &ptr)) return ptr;
-    if (decode_tryfastdispatch(d, &ptr, msg, layout)) return ptr;
+    if (decode_isdone(d, &ptr) ||
+        decode_tryfastdispatch(d, &ptr, msg, layout)) {
+      if (UPB_UNLIKELY(d->options & UPB_CHECK_REQUIRED) &&
+          decode_checkrequired(d, msg, layout)) {
+        d->missing_required = true;
+      }
+      return ptr;
+    }
   }
 }
 
@@ -928,34 +957,34 @@
   return decode_msg(d, ptr, msg, decode_totablep(table));
 }
 
-static bool decode_top(struct upb_decstate *d, const char *buf, void *msg,
-                       const upb_msglayout *l) {
+static upb_decodestatus decode_top(struct upb_decstate *d, const char *buf,
+                                   void *msg, const upb_msglayout *l) {
   if (!decode_tryfastdispatch(d, &buf, msg, l)) {
     decode_msg(d, buf, msg, l);
   }
-  return d->end_group == DECODE_NOGROUP;
+  if (d->end_group != DECODE_NOGROUP) return UPB_DECODE_MALFORMED;
+  if (d->missing_required) return UPB_DECODE_MISSING_REQUIRED;
+  return UPB_DECODE_OK;
 }
 
-bool _upb_decode(const char *buf, size_t size, void *msg,
-                 const upb_msglayout *l, const upb_extreg *extreg, int options,
-                 upb_arena *arena) {
-  bool ok;
+upb_decodestatus _upb_decode(const char *buf, size_t size, void *msg,
+                             const upb_msglayout *l, const upb_extreg *extreg,
+                             int options, upb_arena *arena) {
   upb_decstate state;
   unsigned depth = (unsigned)options >> 16;
 
   if (size == 0) {
-    return true;
+    return UPB_DECODE_OK;
   } else if (size <= 16) {
     memset(&state.patch, 0, 32);
     memcpy(&state.patch, buf, size);
     buf = state.patch;
     state.end = buf + size;
     state.limit = 0;
-    state.alias = false;
+    options &= ~UPB_DECODE_ALIAS;  // Can't alias patch buf.
   } else {
     state.end = buf + size - 16;
     state.limit = 16;
-    state.alias = options & UPB_DECODE_ALIAS;
   }
 
   state.extreg = extreg;
@@ -963,21 +992,22 @@
   state.unknown_msg = NULL;
   state.depth = depth ? depth : 64;
   state.end_group = DECODE_NOGROUP;
+  state.options = (uint16_t)options;
+  state.missing_required = false;
   state.arena.head = arena->head;
   state.arena.last_size = arena->last_size;
   state.arena.cleanup_metadata = arena->cleanup_metadata;
   state.arena.parent = arena;
 
-  if (UPB_UNLIKELY(UPB_SETJMP(state.err))) {
-    ok = false;
-  } else {
-    ok = decode_top(&state, buf, msg, l);
+  upb_decodestatus status = UPB_SETJMP(state.err);
+  if (UPB_LIKELY(!status)) {
+    status = decode_top(&state, buf, msg, l);
   }
 
   arena->head.ptr = state.arena.head.ptr;
   arena->head.end = state.arena.head.end;
   arena->cleanup_metadata = state.arena.cleanup_metadata;
-  return ok;
+  return status;
 }
 
 #undef OP_UNKNOWN
diff --git a/upb/decode.h b/upb/decode.h
index 2400891..98bf884 100644
--- a/upb/decode.h
+++ b/upb/decode.h
@@ -45,17 +45,46 @@
   /* If set, strings will alias the input buffer instead of copying into the
    * arena. */
   UPB_DECODE_ALIAS = 1,
+
+  /* If set, the parse will return failure if any message is missing any required
+   * fields when the message data ends.  The parse will still continue, and the
+   * failure will only be reported at the end.
+   *
+   * IMPORTANT CAVEATS:
+   *
+   * 1. This can throw a false positive failure if an incomplete message is seen
+   *    on the wire but is later completed when the sub-message occurs again.
+   *    For this reason, a second pass is required to verify a failure, to be
+   *    truly robust.
+   *
+   * 2. This can return a false success if you are decoding into a message that
+   *    already has some sub-message fields present.  If the sub-message does
+   *    not occur in the binary payload, we will never visit it and discover the
+   *    incomplete sub-message.  For this reason, this check is only useful for
+   *    implemting ParseFromString() semantics.  For MergeFromString(), a
+   *    post-parse validation step will always be necessary. */
+  UPB_CHECK_REQUIRED = 2,
 };
 
 #define UPB_DECODE_MAXDEPTH(depth) ((depth) << 16)
 
-bool _upb_decode(const char *buf, size_t size, upb_msg *msg,
-                 const upb_msglayout *l, const upb_extreg *extreg, int options,
-                 upb_arena *arena);
+typedef enum {
+  UPB_DECODE_OK = 0,
+  // UPB_CHECK_REQUIRED failed (see above), but the parse otherwise succeeded.
+  UPB_DECODE_MISSING_REQUIRED = 1,
+  UPB_DECODE_OOM = 2,               // Arena alloc failed.
+  UPB_DECODE_BAD_UTF8 = 3,          // String field had bad UTF-8.
+  UPB_DECODE_MAXDEPTH_EXCEEDED = 4,
+  UPB_DECODE_MALFORMED = 5,         // Binary data was malformed.
+} upb_decodestatus;
+
+upb_decodestatus _upb_decode(const char *buf, size_t size, upb_msg *msg,
+                             const upb_msglayout *l, const upb_extreg *extreg,
+                             int options, upb_arena *arena);
 
 UPB_INLINE
-bool upb_decode(const char *buf, size_t size, upb_msg *msg,
-                const upb_msglayout *l, upb_arena *arena) {
+upb_decodestatus upb_decode(const char *buf, size_t size, upb_msg *msg,
+                            const upb_msglayout *l, upb_arena *arena) {
   return _upb_decode(buf, size, msg, l, NULL, 0, arena);
 }
 
diff --git a/upb/decode_fast.c b/upb/decode_fast.c
index d38136a..5287bfd 100644
--- a/upb/decode_fast.c
+++ b/upb/decode_fast.c
@@ -68,9 +68,10 @@
 UPB_NOINLINE
 static const char *fastdecode_isdonefallback(UPB_PARSE_PARAMS) {
   int overrun = data;
-  ptr = decode_isdonefallback_inl(d, ptr, overrun);
+  int status;
+  ptr = decode_isdonefallback_inl(d, ptr, overrun, &status);
   if (ptr == NULL) {
-    return fastdecode_err(d);
+    return fastdecode_err(d, status);
   }
   data = fastdecode_loadtag(ptr);
   UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);
@@ -397,8 +398,7 @@
                                                                                \
   ptr += tagbytes;                                                             \
   ptr = fastdecode_varint64(ptr, &val);                                        \
-  if (ptr == NULL)                                                             \
-    return fastdecode_err(d);                                                  \
+  if (ptr == NULL) return fastdecode_err(d, UPB_DECODE_MALFORMED);             \
   val = fastdecode_munge(val, valbytes, zigzag);                               \
   memcpy(dst, &val, valbytes);                                                 \
                                                                                \
@@ -406,14 +406,14 @@
     fastdecode_nextret ret = fastdecode_nextrepeated(                          \
         d, dst, &ptr, &farr, data, tagbytes, valbytes);                        \
     switch (ret.next) {                                                        \
-    case FD_NEXT_SAMEFIELD:                                                    \
-      dst = ret.dst;                                                           \
-      goto again;                                                              \
-    case FD_NEXT_OTHERFIELD:                                                   \
-      data = ret.tag;                                                          \
-      UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);              \
-    case FD_NEXT_ATLIMIT:                                                      \
-      return ptr;                                                              \
+      case FD_NEXT_SAMEFIELD:                                                  \
+        dst = ret.dst;                                                         \
+        goto again;                                                            \
+      case FD_NEXT_OTHERFIELD:                                                 \
+        data = ret.tag;                                                        \
+        UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);            \
+      case FD_NEXT_ATLIMIT:                                                    \
+        return ptr;                                                            \
     }                                                                          \
   }                                                                            \
                                                                                \
@@ -462,7 +462,7 @@
   ptr = fastdecode_delimited(d, ptr, &fastdecode_topackedvarint, &ctx);        \
                                                                                \
   if (UPB_UNLIKELY(ptr == NULL)) {                                             \
-    return fastdecode_err(d);                                                  \
+    return fastdecode_err(d, UPB_DECODE_MALFORMED);                            \
   }                                                                            \
                                                                                \
   UPB_MUSTTAIL return fastdecode_dispatch(d, ptr, msg, table, hasbits, 0);
@@ -579,7 +579,7 @@
                                                                             \
   if (UPB_UNLIKELY(fastdecode_boundscheck(ptr, size, d->limit_ptr) ||       \
                    (size % valbytes) != 0)) {                               \
-    return fastdecode_err(d);                                               \
+    return fastdecode_err(d, UPB_DECODE_MALFORMED);                         \
   }                                                                         \
                                                                             \
   upb_array **arr_p = fastdecode_fieldmem(msg, data);                       \
@@ -590,7 +590,7 @@
   if (UPB_LIKELY(!arr)) {                                                   \
     *arr_p = arr = _upb_array_new(&d->arena, elems, elem_size_lg2);         \
     if (!arr) {                                                             \
-      return fastdecode_err(d);                                             \
+      return fastdecode_err(d, UPB_DECODE_MALFORMED);                       \
     }                                                                       \
   } else {                                                                  \
     _upb_array_resize(arr, elems, &d->arena);                               \
@@ -656,7 +656,7 @@
                                          uint64_t hasbits, uint64_t data) {
   upb_strview *dst = (upb_strview*)data;
   if (!decode_verifyutf8_inl(dst->data, dst->size)) {
-    return fastdecode_err(d);
+    return fastdecode_err(d, UPB_DECODE_BAD_UTF8);
   }
   UPB_MUSTTAIL return fastdecode_dispatch(UPB_PARSE_ARGS);
 }
@@ -670,16 +670,16 @@
                                                                                \
   if (UPB_UNLIKELY(fastdecode_boundscheck(ptr, size, d->limit_ptr))) {         \
     dst->size = 0;                                                             \
-    return fastdecode_err(d);                                                  \
+    return fastdecode_err(d, UPB_DECODE_MALFORMED);                            \
   }                                                                            \
                                                                                \
-  if (d->alias) {                                                              \
+  if (d->options & UPB_DECODE_ALIAS) {                                         \
     dst->data = ptr;                                                           \
     dst->size = size;                                                          \
   } else {                                                                     \
     char *data = upb_arena_malloc(&d->arena, size);                            \
     if (!data) {                                                               \
-      return fastdecode_err(d);                                                \
+      return fastdecode_err(d, UPB_DECODE_OOM);                                \
     }                                                                          \
     memcpy(data, ptr, size);                                                   \
     dst->data = data;                                                          \
@@ -732,7 +732,7 @@
   size_t common_has;                                                           \
   char *buf;                                                                   \
                                                                                \
-  UPB_ASSERT(!d->alias);                                                       \
+  UPB_ASSERT((d->options & UPB_DECODE_ALIAS) == 0);                            \
   UPB_ASSERT(fastdecode_checktag(data, tagbytes));                             \
                                                                                \
   dst = fastdecode_getfield(d, ptr, msg, &data, &hasbits, &farr,               \
@@ -752,22 +752,18 @@
   common_has = UPB_MIN(arena_has, (d->end - ptr) + 16);                        \
                                                                                \
   if (UPB_LIKELY(size <= 15 - tagbytes)) {                                     \
-    if (arena_has < 16)                                                        \
-      goto longstr;                                                            \
+    if (arena_has < 16) goto longstr;                                          \
     d->arena.head.ptr += 16;                                                   \
     memcpy(buf, ptr - tagbytes - 1, 16);                                       \
     dst->data = buf + tagbytes + 1;                                            \
   } else if (UPB_LIKELY(size <= 32)) {                                         \
-    if (UPB_UNLIKELY(common_has < 32))                                         \
-      goto longstr;                                                            \
+    if (UPB_UNLIKELY(common_has < 32)) goto longstr;                           \
     fastdecode_docopy(d, ptr, size, 32, buf, dst);                             \
   } else if (UPB_LIKELY(size <= 64)) {                                         \
-    if (UPB_UNLIKELY(common_has < 64))                                         \
-      goto longstr;                                                            \
+    if (UPB_UNLIKELY(common_has < 64)) goto longstr;                           \
     fastdecode_docopy(d, ptr, size, 64, buf, dst);                             \
   } else if (UPB_LIKELY(size < 128)) {                                         \
-    if (UPB_UNLIKELY(common_has < 128))                                        \
-      goto longstr;                                                            \
+    if (UPB_UNLIKELY(common_has < 128)) goto longstr;                          \
     fastdecode_docopy(d, ptr, size, 128, buf, dst);                            \
   } else {                                                                     \
     goto longstr;                                                              \
@@ -777,19 +773,19 @@
                                                                                \
   if (card == CARD_r) {                                                        \
     if (validate_utf8 && !decode_verifyutf8_inl(dst->data, dst->size)) {       \
-      return fastdecode_err(d);                                                \
+      return fastdecode_err(d, UPB_DECODE_BAD_UTF8);                           \
     }                                                                          \
     fastdecode_nextret ret = fastdecode_nextrepeated(                          \
         d, dst, &ptr, &farr, data, tagbytes, sizeof(upb_strview));             \
     switch (ret.next) {                                                        \
-    case FD_NEXT_SAMEFIELD:                                                    \
-      dst = ret.dst;                                                           \
-      goto again;                                                              \
-    case FD_NEXT_OTHERFIELD:                                                   \
-      data = ret.tag;                                                          \
-      UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);              \
-    case FD_NEXT_ATLIMIT:                                                      \
-      return ptr;                                                              \
+      case FD_NEXT_SAMEFIELD:                                                  \
+        dst = ret.dst;                                                         \
+        goto again;                                                            \
+      case FD_NEXT_OTHERFIELD:                                                 \
+        data = ret.tag;                                                        \
+        UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);            \
+      case FD_NEXT_ATLIMIT:                                                    \
+        return ptr;                                                            \
     }                                                                          \
   }                                                                            \
                                                                                \
@@ -820,7 +816,7 @@
     RETURN_GENERIC("string field tag mismatch\n");                             \
   }                                                                            \
                                                                                \
-  if (UPB_UNLIKELY(!d->alias)) {                                               \
+  if (UPB_UNLIKELY((d->options & UPB_DECODE_ALIAS) == 0)) {                    \
     UPB_MUSTTAIL return copyfunc(UPB_PARSE_ARGS);                              \
   }                                                                            \
                                                                                \
@@ -852,27 +848,27 @@
                                                                                \
   if (card == CARD_r) {                                                        \
     if (validate_utf8 && !decode_verifyutf8_inl(dst->data, dst->size)) {       \
-      return fastdecode_err(d);                                                \
+      return fastdecode_err(d, UPB_DECODE_BAD_UTF8);                           \
     }                                                                          \
     fastdecode_nextret ret = fastdecode_nextrepeated(                          \
         d, dst, &ptr, &farr, data, tagbytes, sizeof(upb_strview));             \
     switch (ret.next) {                                                        \
-    case FD_NEXT_SAMEFIELD:                                                    \
-      dst = ret.dst;                                                           \
-      if (UPB_UNLIKELY(!d->alias)) {                                           \
-        /* Buffer flipped and we can't alias any more. Bounce to */            \
-        /* copyfunc(), but via dispatch since we need to reload table */       \
-        /* data also. */                                                       \
-        fastdecode_commitarr(dst, &farr, sizeof(upb_strview));                 \
+      case FD_NEXT_SAMEFIELD:                                                  \
+        dst = ret.dst;                                                         \
+        if (UPB_UNLIKELY((d->options & UPB_DECODE_ALIAS) == 0)) {              \
+          /* Buffer flipped and we can't alias any more. Bounce to */          \
+          /* copyfunc(), but via dispatch since we need to reload table */     \
+          /* data also. */                                                     \
+          fastdecode_commitarr(dst, &farr, sizeof(upb_strview));               \
+          data = ret.tag;                                                      \
+          UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);          \
+        }                                                                      \
+        goto again;                                                            \
+      case FD_NEXT_OTHERFIELD:                                                 \
         data = ret.tag;                                                        \
         UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);            \
-      }                                                                        \
-      goto again;                                                              \
-    case FD_NEXT_OTHERFIELD:                                                   \
-      data = ret.tag;                                                          \
-      UPB_MUSTTAIL return fastdecode_tagdispatch(UPB_PARSE_ARGS);              \
-    case FD_NEXT_ATLIMIT:                                                      \
-      return ptr;                                                              \
+      case FD_NEXT_ATLIMIT:                                                    \
+        return ptr;                                                            \
     }                                                                          \
   }                                                                            \
                                                                                \
@@ -964,7 +960,9 @@
     RETURN_GENERIC("submessage field tag mismatch\n");                    \
   }                                                                       \
                                                                           \
-  if (--d->depth == 0) return fastdecode_err(d);                          \
+  if (--d->depth == 0) {                                                  \
+    return fastdecode_err(d, UPB_DECODE_MAXDEPTH_EXCEEDED);               \
+  }                                                                       \
                                                                           \
   upb_msg **dst;                                                          \
   uint32_t submsg_idx = (data >> 16) & 0xff;                              \
@@ -1000,7 +998,7 @@
   ptr = fastdecode_delimited(d, ptr, fastdecode_tosubmsg, &submsg);       \
                                                                           \
   if (UPB_UNLIKELY(ptr == NULL || d->end_group != DECODE_NOGROUP)) {      \
-    return fastdecode_err(d);                                             \
+    return fastdecode_err(d, UPB_DECODE_MALFORMED);                       \
   }                                                                       \
                                                                           \
   if (card == CARD_r) {                                                   \
diff --git a/upb/decode_internal.h b/upb/decode_internal.h
index 74003e4..2085ae4 100644
--- a/upb/decode_internal.h
+++ b/upb/decode_internal.h
@@ -35,9 +35,10 @@
 
 #include <setjmp.h>
 
+#include "third_party/utf8_range/utf8_range.h"
+#include "upb/decode.h"
 #include "upb/msg_internal.h"
 #include "upb/upb_internal.h"
-#include "third_party/utf8_range/utf8_range.h"
 
 /* Must be last. */
 #include "upb/port_def.inc"
@@ -51,9 +52,10 @@
   const char *unknown;     /* Start of unknown data. */
   const upb_extreg *extreg;  /* For looking up extensions during the parse. */
   int limit;               /* Submessage limit relative to end. */
-  int depth;
+  int depth;               /* Tracks recursion depth to bound stack usage. */
   uint32_t end_group;   /* field number of END_GROUP tag, else DECODE_NOGROUP */
-  bool alias;
+  uint16_t options;
+  bool missing_required;
   char patch[32];
   upb_arena arena;
   jmp_buf err;
@@ -66,7 +68,7 @@
  * of our optimizations. That is also why we must declare it in a separate file,
  * otherwise the compiler will see that it calls longjmp() and deduce that it is
  * noreturn. */
-const char *fastdecode_err(upb_decstate *d);
+const char *fastdecode_err(upb_decstate *d, int status);
 
 extern const uint8_t upb_utf8_offsets[];
 
@@ -106,13 +108,14 @@
 
 UPB_INLINE
 const char *decode_isdonefallback_inl(upb_decstate *d, const char *ptr,
-                                      int overrun) {
+                                      int overrun, int *status) {
   if (overrun < d->limit) {
     /* Need to copy remaining data into patch buffer. */
     UPB_ASSERT(overrun < 16);
     if (d->unknown_msg) {
       if (!_upb_msg_addunknown(d->unknown_msg, d->unknown, ptr - d->unknown,
                                &d->arena)) {
+        *status = UPB_DECODE_OOM;
         return NULL;
       }
       d->unknown = &d->patch[0] + overrun;
@@ -123,10 +126,11 @@
     d->end = &d->patch[16];
     d->limit -= 16;
     d->limit_ptr = d->end + d->limit;
-    d->alias = false;
+    d->options &= ~UPB_DECODE_ALIAS;
     UPB_ASSERT(ptr < d->limit_ptr);
     return ptr;
   } else {
+    *status = UPB_DECODE_MALFORMED;
     return NULL;
   }
 }
diff --git a/upb/def.c b/upb/def.c
index a2d533d..b9f5dd7 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -2200,7 +2200,7 @@
       if (ctx->symtab->allow_name_conflicts &&
           deftype(existing_v) == UPB_DEFTYPE_FIELD_JSONNAME) {
         // Field name takes precedence over json name.
-        upb_strtable_remove(&m->ntof, shortname, strlen(shortname), NULL);
+        upb_strtable_remove2(&m->ntof, shortname, strlen(shortname), NULL);
       } else {
         symtab_errf(ctx, "duplicate field name (%s)", shortname);
       }
diff --git a/upb/json_encode.c b/upb/json_encode.c
index 6ec9c84..3fd46b1 100644
--- a/upb/json_encode.c
+++ b/upb/json_encode.c
@@ -374,7 +374,7 @@
   upb_arena *arena = jsonenc_arena(e);
   upb_msg *any = upb_msg_new(any_m, arena);
 
-  if (!upb_decode(value.data, value.size, any, any_layout, arena)) {
+  if (upb_decode(value.data, value.size, any, any_layout, arena)) {
     jsonenc_err(e, "Error decoding message in Any");
   }
 
diff --git a/upb/msg.c b/upb/msg.c
index 54426ec..e96fd86 100644
--- a/upb/msg.c
+++ b/upb/msg.c
@@ -392,7 +392,7 @@
   for (end = e, e = start; e < end; e++) {
     const upb_msglayout_ext *ext = *e;
     extreg_key(buf, ext->extendee, ext->field.number);
-    upb_strtable_remove(&r->exts, buf, EXTREG_KEY_SIZE, NULL);
+    upb_strtable_remove2(&r->exts, buf, EXTREG_KEY_SIZE, NULL);
   }
   return false;
 }
diff --git a/upb/msg_internal.h b/upb/msg_internal.h
index 1fbb2c7..2b6b580 100644
--- a/upb/msg_internal.h
+++ b/upb/msg_internal.h
@@ -56,13 +56,6 @@
  * members are public so generated code can initialize them, but users MUST NOT
  * read or write any of its members. */
 
-/* These aren't real labels according to descriptor.proto, but in the table we
- * use these for map/packed fields instead of UPB_LABEL_REPEATED. */
-enum {
-  _UPB_LABEL_MAP = 4,
-  _UPB_LABEL_PACKED = 7  /* Low 3 bits are common with UPB_LABEL_REPEATED. */
-};
-
 typedef struct {
   uint32_t number;
   uint16_t offset;
@@ -136,6 +129,17 @@
   int value_count;
 } upb_enumlayout;
 
+UPB_INLINE bool _upb_enumlayout_checkval(const upb_enumlayout *e, int32_t val) {
+  uint32_t uval = (uint32_t)val;
+  if (uval < 64) return e->mask & (1 << uval);
+  // OPT: binary search long lists?
+  int n = e->value_count;
+  for (int i = 0; i < n; i++) {
+    if (e->values[i] == val) return true;
+  }
+  return false;
+}
+
 typedef union {
   const struct upb_msglayout *submsg;
   const upb_enumlayout *subenum;
@@ -165,6 +169,7 @@
 struct upb_msglayout {
   const upb_msglayout_sub *subs;
   const upb_msglayout_field *fields;
+  uint64_t required_mask;
   /* Must be aligned to sizeof(void*).  Doesn't include internal members like
    * unknown fields, extension dict, pointer to msglayout, etc. */
   uint16_t size;
@@ -237,6 +242,7 @@
 
 typedef struct {
   upb_msg_internaldata *internal;
+  /* Message data follows. */
 } upb_msg_internal;
 
 /* Maps upb_fieldtype_t -> memory size. */
@@ -279,19 +285,24 @@
 
 /* The internal representation of an extension is self-describing: it contains
  * enough information that we can serialize it to binary format without needing
- * to look it up in a registry. */
+ * to look it up in a upb_extreg.
+ *
+ * This representation allocates 16 bytes to data on 64-bit platforms.  This is
+ * rather wasteful for scalars (in the extreme case of bool, it wastes 15
+ * bytes). We accept this because we expect messages to be the most common
+ * extension type. */
 typedef struct {
   const upb_msglayout_ext *ext;
   union {
     upb_strview str;
     void *ptr;
-    double dbl;
     char scalar_data[8];
   } data;
 } upb_msg_ext;
 
-/* Adds the given extension data to the given message. The returned extension will
- * have its "ext" member initialized according to |ext|. */
+/* Adds the given extension data to the given message. |ext| is copied into the
+ * message instance. This logically replaces any previously-added extension with
+ * this number */
 upb_msg_ext *_upb_msg_getorcreateext(upb_msg *msg, const upb_msglayout_ext *ext,
                                      upb_arena *arena);
 
@@ -306,6 +317,8 @@
 
 void _upb_msg_clearext(upb_msg *msg, const upb_msglayout_ext *ext);
 
+void _upb_msg_clearext(upb_msg *msg, const upb_msglayout_ext *ext);
+
 /** Hasbit access *************************************************************/
 
 UPB_INLINE bool _upb_hasbit(const upb_msg *msg, size_t idx) {
@@ -634,13 +647,13 @@
   if (!_upb_map_tovalue(val, val_size, &tabval, a)) return false;
 
   /* TODO(haberman): add overwrite operation to minimize number of lookups. */
-  upb_strtable_remove(&map->table, strkey.data, strkey.size, NULL);
+  upb_strtable_remove2(&map->table, strkey.data, strkey.size, NULL);
   return upb_strtable_insert(&map->table, strkey.data, strkey.size, tabval, a);
 }
 
 UPB_INLINE bool _upb_map_delete(upb_map *map, const void *key, size_t key_size) {
   upb_strview k = _upb_map_tokey(key, key_size);
-  return upb_strtable_remove(&map->table, k.data, k.size, NULL);
+  return upb_strtable_remove2(&map->table, k.data, k.size, NULL);
 }
 
 UPB_INLINE void _upb_map_clear(upb_map *map) {
diff --git a/upb/table.c b/upb/table.c
index dabcff0..63fecf2 100644
--- a/upb/table.c
+++ b/upb/table.c
@@ -516,8 +516,8 @@
   return lookup(&t->t, strkey2(key, len), v, hash, &streql);
 }
 
-bool upb_strtable_remove(upb_strtable *t, const char *key, size_t len,
-                         upb_value *val) {
+bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
+                          upb_value *val) {
   uint32_t hash = table_hash(key, len);
   upb_tabkey tabkey;
   return rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql);
@@ -812,7 +812,7 @@
     while (++i < t->array_size) {
       upb_tabval ent = t->array[i];
       if (upb_arrhas(ent)) {
-        *key = i;
+        *key = i; 
         *val = _upb_value_val(ent.val);
         *iter = i;
         return true;
diff --git a/upb/table_internal.h b/upb/table_internal.h
index 11fa503..fb0003e 100644
--- a/upb/table_internal.h
+++ b/upb/table_internal.h
@@ -245,9 +245,14 @@
 /* Removes an item from the table.  Returns true if the remove was successful,
  * and stores the removed item in *val if non-NULL. */
 bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val);
-bool upb_strtable_remove(upb_strtable *t, const char *key, size_t len,
+bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
                           upb_value *val);
 
+UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,
+                                    upb_value *v) {
+  return upb_strtable_remove2(t, key, strlen(key), v);
+}
+
 /* Updates an existing entry in an inttable.  If the entry does not exist,
  * returns false and does nothing.  Unlike insert/remove, this does not
  * invalidate iterators. */
diff --git a/upbc/message_layout.cc b/upbc/message_layout.cc
index 1c5cd0b..1c71804 100644
--- a/upbc/message_layout.cc
+++ b/upbc/message_layout.cc
@@ -166,18 +166,29 @@
             });
 
   // Place/count hasbits.
-  int hasbit_count = 0;
+  hasbit_count_ = 0;
+  required_mask_ = 0;
   for (auto field : FieldHotnessOrder(descriptor)) {
     if (HasHasbit(field)) {
       // We don't use hasbit 0, so that 0 can indicate "no presence" in the
       // table. This wastes one hasbit, but we don't worry about it for now.
-      hasbit_indexes_[field] = ++hasbit_count;
+      int index = ++hasbit_count_;
+      hasbit_indexes_[field] = index;
+      if (field->is_required()) {
+        if (index > 63) {
+          std::cerr << "upb does not support messages with more than 64 "
+                       "required fields: "
+                    << field->full_name() << "\n";
+          exit(1);
+        }
+        required_mask_ |= 1 << index;
+      }
     }
   }
 
   // Place hasbits at the beginning.
-  int64_t hasbit_bytes = DivRoundUp(hasbit_count, 8);
-  Place(SizeAndAlign{{hasbit_bytes, hasbit_bytes}, {1, 1}});
+  hasbit_bytes_ = DivRoundUp(hasbit_count_, 8);
+  Place(SizeAndAlign{{hasbit_bytes_, hasbit_bytes_}, {1, 1}});
 
   // Place non-oneof fields.
   for (auto field : field_order) {
diff --git a/upbc/message_layout.h b/upbc/message_layout.h
index e4e5d74..9d4c1e2 100644
--- a/upbc/message_layout.h
+++ b/upbc/message_layout.h
@@ -85,6 +85,10 @@
 
   Size message_size() const { return size_; }
 
+  int hasbit_count() const { return hasbit_count_; }
+  int hasbit_bytes() const { return hasbit_bytes_; }
+  uint64_t required_mask() const { return required_mask_; }
+
   static bool HasHasbit(const google::protobuf::FieldDescriptor* field);
   static SizeAndAlign SizeOfUnwrapped(
       const google::protobuf::FieldDescriptor* field);
@@ -126,6 +130,9 @@
       oneof_case_offsets_;
   Size maxalign_;
   Size size_;
+  int hasbit_count_;
+  int hasbit_bytes_;
+  uint64_t required_mask_;
 };
 
 // Returns fields in order of "hotness", eg. how frequently they appear in
@@ -140,7 +147,8 @@
   std::sort(fields.begin(), fields.end(),
             [](const google::protobuf::FieldDescriptor* a,
                const google::protobuf::FieldDescriptor* b) {
-              return a->number() < b->number();
+              return std::make_pair(!a->is_required(), a->number()) <
+                     std::make_pair(!b->is_required(), b->number());
             });
   return fields;
 }
diff --git a/upbc/protoc-gen-upb.cc b/upbc/protoc-gen-upb.cc
index 0b77713..7a685e0 100644
--- a/upbc/protoc-gen-upb.cc
+++ b/upbc/protoc-gen-upb.cc
@@ -83,12 +83,6 @@
   }
 }
 
-template <class T>
-void SortDefs(std::vector<T>* defs) {
-  std::sort(defs->begin(), defs->end(),
-            [](T a, T b) { return a->full_name() < b->full_name(); });
-}
-
 std::vector<const protobuf::EnumDescriptor*> SortedEnums(
     const protobuf::FileDescriptor* file) {
   std::vector<const protobuf::EnumDescriptor*> enums;
@@ -98,7 +92,6 @@
   for (int i = 0; i < file->message_type_count(); i++) {
     AddEnums(file->message_type(i), &enums);
   }
-  SortDefs(&enums);
   return enums;
 }
 
@@ -294,6 +287,35 @@
   }
 }
 
+bool HasNonZeroDefault(const protobuf::FieldDescriptor* field) {
+  switch (field->cpp_type()) {
+    case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
+      return false;
+    case protobuf::FieldDescriptor::CPPTYPE_STRING:
+      return !field->default_value_string().empty();
+    case protobuf::FieldDescriptor::CPPTYPE_INT32:
+      return field->default_value_int32() != 0;
+    case protobuf::FieldDescriptor::CPPTYPE_INT64:
+      return field->default_value_int64() != 0;
+    case protobuf::FieldDescriptor::CPPTYPE_UINT32:
+      return field->default_value_uint32() != 0;
+    case protobuf::FieldDescriptor::CPPTYPE_UINT64:
+      return field->default_value_uint64() != 0;
+    case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
+      return field->default_value_float() != 0;
+    case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
+      return field->default_value_double() != 0;
+    case protobuf::FieldDescriptor::CPPTYPE_BOOL:
+      return field->default_value_bool() != false;
+    case protobuf::FieldDescriptor::CPPTYPE_ENUM:
+      // Use a number instead of a symbolic name so that we don't require
+      // this enum's header to be included.
+      return field->default_value_enum()->number() != 0;
+  }
+  ABSL_ASSERT(false);
+  return "XXX";
+}
+
 std::string FieldDefault(const protobuf::FieldDescriptor* field) {
   switch (field->cpp_type()) {
     case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
@@ -396,7 +418,7 @@
         "                        upb_arena *arena) {\n"
         "  $0 *ret = $0_new(arena);\n"
         "  if (!ret) return NULL;\n"
-        "  if (!upb_decode(buf, size, ret, &$1, arena)) return NULL;\n"
+        "  if (upb_decode(buf, size, ret, &$1, arena)) return NULL;\n"
         "  return ret;\n"
         "}\n"
         "UPB_INLINE $0 *$0_parse_ex(const char *buf, size_t size,\n"
@@ -404,7 +426,7 @@
         "                           upb_arena *arena) {\n"
         "  $0 *ret = $0_new(arena);\n"
         "  if (!ret) return NULL;\n"
-        "  if (!_upb_decode(buf, size, ret, &$1, extreg, options, arena)) {\n"
+        "  if (_upb_decode(buf, size, ret, &$1, extreg, options, arena)) {\n"
         "    return NULL;\n"
         "  }\n"
         "  return ret;\n"
@@ -512,11 +534,19 @@
           GetSizeInit(layout.GetOneofCaseOffset(field->real_containing_oneof())),
           field->number(), FieldDefault(field));
     } else {
-      output(
-          "UPB_INLINE $0 $1_$2(const $1 *msg) { "
-          "return *UPB_PTR_AT(msg, $3, $0); }\n",
-          CTypeConst(field), msg_name, field->name(),
-          GetSizeInit(layout.GetFieldOffset(field)));
+      if (HasNonZeroDefault(field)) {
+        output(
+            "UPB_INLINE $0 $1_$2(const $1 *msg) { "
+            "return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4; }\n",
+            CTypeConst(field), msg_name, field->name(),
+            GetSizeInit(layout.GetFieldOffset(field)), FieldDefault(field));
+      } else {
+        output(
+            "UPB_INLINE $0 $1_$2(const $1 *msg) { "
+            "return *UPB_PTR_AT(msg, $3, $0); }\n",
+            CTypeConst(field), msg_name, field->name(),
+            GetSizeInit(layout.GetFieldOffset(field)));
+      }
     }
   }
 
@@ -1144,6 +1174,7 @@
   std::string msg_name = ToCIdent(message->full_name());
   std::string fields_array_ref = "NULL";
   std::string submsgs_array_ref = "NULL";
+  std::string subenums_array_ref = "NULL";
   uint8_t dense_below = 0;
   const int dense_below_max = std::numeric_limits<decltype(dense_below)>::max();
   MessageLayout layout(message);
@@ -1221,7 +1252,8 @@
   output("const upb_msglayout $0 = {\n", MessageInit(message));
   output("  $0,\n", submsgs_array_ref);
   output("  $0,\n", fields_array_ref);
-  output("  $0, $1, $2, $3, $4,\n",
+  output("  $0, $1, $2, $3, $4, $5,\n",
+         layout.required_mask(),
          GetSizeInit(layout.message_size()),
          field_number_order.size(),
          msgext,
@@ -1294,19 +1326,6 @@
   return this_file_enums.size();
 }
 
-void WriteExtension(const protobuf::FieldDescriptor* ext, Output& output) {
-  output("const upb_msglayout_ext $0 = {\n  ", ExtensionLayout(ext));
-  WriteField(ext, "0", "0", 0, output);
-  output(",\n");
-  output("  &$0,\n", MessageInit(ext->containing_type()));
-  if (ext->message_type()) {
-    output("  {.submsg = &$0},\n", MessageInit(ext->message_type()));
-  } else {
-    output("  {.submsg = NULL},\n");
-  }
-  output("\n};\n");
-}
-
 int WriteMessages(const protobuf::FileDescriptor* file, Output& output,
                    bool fasttable_enabled) {
   std::vector<const protobuf::Descriptor*> file_messages =
@@ -1328,6 +1347,21 @@
   return file_messages.size();
 }
 
+void WriteExtension(const protobuf::FieldDescriptor* ext, Output& output) {
+  output("const upb_msglayout_ext $0 = {\n  ", ExtensionLayout(ext));
+  WriteField(ext, "0", "0", 0, output);
+  output(",\n");
+  output("    &$0,\n", MessageInit(ext->containing_type()));
+  if (ext->message_type()) {
+    output("    {.submsg = &$0},\n", MessageInit(ext->message_type()));
+  } else if (ext->enum_type() && ext->enum_type()->file()->syntax() == protobuf::FileDescriptor::SYNTAX_PROTO2) {
+    output("    {.subenum = &$0},\n", EnumInit(ext->enum_type()));
+  } else {
+    output("    {.submsg = NULL},\n");
+  }
+  output("\n};\n");
+}
+
 int WriteExtensions(const protobuf::FileDescriptor* file, Output& output) {
   auto exts = SortedExtensions(file);
   absl::flat_hash_set<const protobuf::Descriptor*> forward_decls;