diff --git a/BUILD b/BUILD
index d74b66120a..59c61f9df2 100644
--- a/BUILD
+++ b/BUILD
@@ -41,6 +41,7 @@ CPPOPTS = [
 COPTS = CPPOPTS + [
     # copybara:strip_for_google3_begin
     #"-pedantic",
+    #"-Wno-gnu-flexible-array-initializer",
     #"-Werror=pedantic",
     "-std=gnu11",
     "-Wstrict-prototypes",
diff --git a/generated_for_cmake/google/protobuf/descriptor.upb.c b/generated_for_cmake/google/protobuf/descriptor.upb.c
index 9a412110df..7c880f9be7 100644
--- a/generated_for_cmake/google/protobuf/descriptor.upb.c
+++ b/generated_for_cmake/google/protobuf/descriptor.upb.c
@@ -62,15 +62,15 @@ const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
     {&fastdecode_generic, UPB_SIZE(0, 0)},
     {&upb_pss_1bt, UPB_SIZE(1125899906973706, 2251799813816330)},
     {&upb_pss_1bt, UPB_SIZE(3377699720790034, 6755399441317906)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
+    {&upb_prs_1bt, UPB_SIZE(10133099161583642, 20266198323167258)},
     {&upb_prm_1bt_max128b, UPB_SIZE(11258999068426274, 22517998136852514)},
     {&upb_prm_1bt_max128b, UPB_SIZE(12384898975334442, 24769797950603306)},
     {&upb_prm_1bt_max64b, UPB_SIZE(13510798882373682, 27021597764485170)},
     {&upb_prm_1bt_max128b, UPB_SIZE(14636698789085242, 29273397578039354)},
     {&upb_psm_1bt_max256b, UPB_SIZE(7881385247440962, 15762684595339330)},
     {&upb_psm_1bt_max64b, UPB_SIZE(9007289449381962, 18014488704122954)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
+    {&upb_prv4_1bt, UPB_SIZE(15762598695796816, 31525197391593552)},
+    {&upb_prv4_1bt, UPB_SIZE(16888498602639448, 33776997205278808)},
     {&upb_pss_1bt, UPB_SIZE(5629499534737506, 11258999068950626)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
@@ -116,7 +116,7 @@ const upb_msglayout google_protobuf_DescriptorProto_msginit = {
     {&upb_psm_1bt_max64b, UPB_SIZE(3377777030266938, 6755476750794810)},
     {&upb_prm_1bt_max64b, UPB_SIZE(10133099161976898, 20266198323560514)},
     {&upb_prm_1bt_max64b, UPB_SIZE(11258999068557386, 22517998136983626)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
+    {&upb_prs_1bt, UPB_SIZE(12384898975268946, 24769797950537810)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
@@ -284,7 +284,7 @@ const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
     {&upb_prm_1bt_max64b, UPB_SIZE(4503599627501586, 9007199254872082)},
     {&upb_psm_1bt_max64b, UPB_SIZE(3377777030004762, 6755476750532634)},
     {&upb_prm_1bt_max64b, UPB_SIZE(5629499534213154, 11258999068426274)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
+    {&upb_prs_1bt, UPB_SIZE(6755399441055786, 13510798882111530)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
   },
@@ -635,7 +635,7 @@ const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
     {&upb_pss_1bt, UPB_SIZE(9007199255789594, 9007199255789594)},
     {&upb_psv8_1bt, UPB_SIZE(2251799813816352, 2251799813816352)},
     {&upb_psv8_1bt, UPB_SIZE(4503599627632680, 4503599627632680)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
+    {&upb_psf8_1bt, UPB_SIZE(6755399441580081, 6755399441580081)},
     {&upb_pss_1bt, UPB_SIZE(11258999070523450, 13510798884208698)},
     {&upb_pss_1bt, UPB_SIZE(13510798886305858, 18014398513676354)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
@@ -702,7 +702,7 @@ const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
     {&upb_pss_1bt, UPB_SIZE(1125899906973722, 2251799813816346)},
     {&upb_pss_1bt, UPB_SIZE(3377699720790050, 6755399441317922)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
-    {&fastdecode_generic, UPB_SIZE(0, 0)},
+    {&upb_prs_1bt, UPB_SIZE(7881299347898418, 15762598695796786)},
     {&fastdecode_generic, UPB_SIZE(0, 0)},
   },
 };
diff --git a/upb/decode.c b/upb/decode.c
index eb40f67ca9..5c32c26fe2 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -627,8 +627,8 @@ const char *fastdecode_generic(upb_decstate *d, const char *ptr, upb_msg *msg,
 UPB_NOINLINE
 static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
                               const upb_msglayout *layout) {
-  if (msg) {
-    ptr = fastdecode_dispatch(d, ptr, msg, decode_totable(layout), 0);
+  if (msg && layout->table_mask != (unsigned char)-1) {
+      ptr = fastdecode_dispatch(d, ptr, msg, decode_totable(layout), 0);
   } else {
     while (ptr < d->limit) {
       decode_parseret ret = decode_field(d, ptr, msg, layout);
diff --git a/upb/decode.int.h b/upb/decode.int.h
index bcaf0c073b..981a2b8cbe 100644
--- a/upb/decode.int.h
+++ b/upb/decode.int.h
@@ -10,6 +10,7 @@
 
 #include "upb/msg.h"
 #include "upb/upb.int.h"
+#include "upb/decode_fast.h"
 
 /* Must be last. */
 #include "upb/port_def.inc"
@@ -24,9 +25,6 @@ typedef struct upb_decstate {
   jmp_buf err;
 } upb_decstate;
 
-const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
-                                intptr_t table, uint64_t hasbits);
-
 /* Error function that will abort decoding with longjmp(). We can't declare this
  * UPB_NORETURN, even though it is appropriate, because if we do then compilers
  * will "helpfully" refuse to tailcall to it
@@ -46,6 +44,41 @@ UPB_INLINE const upb_msglayout *decode_totablep(intptr_t table) {
   return (void*)(table >> 8);
 }
 
+UPB_FORCEINLINE static
+const char *fastdecode_tagdispatch(upb_decstate *d, const char *ptr,
+                                    upb_msg *msg, intptr_t table,
+                                    uint64_t hasbits, uint32_t tag) {
+  const upb_msglayout *table_p = decode_totablep(table);
+  uint8_t mask = table;
+  uint64_t data;
+  size_t idx = tag & mask;
+  __builtin_assume((idx & 7) == 0);
+  idx >>= 3;
+  data = table_p->fasttable[idx].field_data ^ tag;
+  return table_p->fasttable[idx].field_parser(d, ptr, msg, table, hasbits, data);
+}
+
+UPB_FORCEINLINE static
+uint32_t fastdecode_loadtag(const char* ptr) {
+  uint16_t tag;
+  memcpy(&tag, ptr, 2);
+  return tag;
+}
+
+UPB_FORCEINLINE static
+const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
+                                intptr_t table, uint64_t hasbits) {
+  if (UPB_UNLIKELY(ptr >= d->fastlimit)) {
+    if (UPB_LIKELY(ptr == d->limit)) {
+      *(uint32_t*)msg |= hasbits >> 16;  /* Sync hasbits. */
+      return ptr;
+    }
+    uint64_t data = 0;
+    return fastdecode_generic(d, ptr, msg, table, hasbits, data);
+  }
+  return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, fastdecode_loadtag(ptr));
+}
+
 #include "upb/port_undef.inc"
 
 #endif  /* UPB_DECODE_INT_H_ */
diff --git a/upb/decode_fast.c b/upb/decode_fast.c
index 773e2e78fd..9ba1c76d06 100644
--- a/upb/decode_fast.c
+++ b/upb/decode_fast.c
@@ -47,50 +47,6 @@ upb_msg *decode_newmsg_ceil(upb_decstate *d, size_t size, int msg_ceil_bytes) {
   return msg_data + sizeof(upb_msg_internal);
 }
 
-UPB_FORCEINLINE
-const char *fastdecode_tagdispatch(upb_decstate *d, const char *ptr,
-                                   upb_msg *msg, intptr_t table,
-                                   uint64_t hasbits, uint32_t tag) {
-  const upb_msglayout *table_p = decode_totablep(table);
-  uint8_t mask = table;
-
-  // Get N bits of field number, based on the message table size.
-  size_t idx = tag & mask;
-  __builtin_assume((idx & 7) == 0);
-  idx >>= 3;
-  uint64_t data = table_p->fasttable[idx].field_data ^ tag;
-
-  // Jump to the specialized field parser function.
-  return table_p->fasttable[idx].field_parser(UPB_PARSE_ARGS);
-}
-
-UPB_FORCEINLINE
-static uint32_t fastdecode_loadtag(const char *ptr) {
-  uint16_t tag;
-  memcpy(&tag, ptr, 2);
-  return tag;
-}
-
-UPB_FORCEINLINE
-const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
-                                intptr_t table, uint64_t hasbits) {
-  if (UPB_UNLIKELY(ptr >= d->fastlimit)) {
-    if (UPB_LIKELY(ptr == d->limit)) {
-      // Parse is finished.
-      *(uint32_t*)msg |= hasbits >> 16;  // Sync hasbits.
-      return ptr;
-    }
-    // We are within 16 bytes of end-of-buffer, so we can't use fast parsing
-    // functions anymore (they will read up to 16b without bounds checks).
-    uint64_t data = 0;
-    RETURN_GENERIC("dispatch hit end\n");
-  }
-
-  // Read two bytes of tag data (for a one-byte tag, the high byte is junk).
-  uint16_t tag = fastdecode_loadtag(ptr);
-  return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
-}
-
 UPB_FORCEINLINE
 static bool fastdecode_checktag(uint64_t data, int tagbytes) {
   if (tagbytes == 1) {
@@ -145,14 +101,6 @@ static void *fastdecode_getfield_ofs(upb_decstate *d, const char *ptr,
   }
 }
 
-UPB_FORCEINLINE
-static void *fastdecode_getfield(upb_decstate *d, const char *ptr, upb_msg *msg,
-                                 uint64_t *data, uint64_t *hasbits,
-                                 int valbytes, upb_card card) {
-  return fastdecode_getfield_ofs(d, ptr, msg, data, hasbits, NULL, NULL,
-                                 valbytes, card, false);
-}
-
 /* varint fields **************************************************************/
 
 UPB_FORCEINLINE
@@ -179,8 +127,33 @@ static const char *fastdecode_varint(UPB_PARSE_PARAMS, int tagbytes,
   if (UPB_UNLIKELY(!fastdecode_checktag(data, tagbytes))) {
     RETURN_GENERIC("varint field tag mismatch\n");
   }
-  dst = fastdecode_getfield(d, ptr, msg, &data, &hasbits, valbytes,
-                            card);
+  
+  upb_array* arr;
+  void* end;
+  dst = fastdecode_getfield_ofs(d, ptr, msg, &data, &hasbits, &arr, &end,
+                            valbytes, card, false);
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(!dst)) {
+      RETURN_GENERIC("need array resize\n");
+    }
+  }
+  
+again:
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(dst == end)) {
+      size_t old_size = arr->size;
+      size_t old_bytes = old_size * valbytes;
+      size_t new_size = old_size * 2;
+      size_t new_bytes = new_size * valbytes;
+      char *old_ptr = _upb_array_ptr(arr);
+      char *new_ptr = upb_arena_realloc(&d->arena, old_ptr, old_bytes, new_bytes);
+      arr->size = new_size;
+      arr->data = _upb_array_tagptr(new_ptr, 3);
+      dst = (void*)(new_ptr + (old_size * valbytes));
+      end = (void*)(new_ptr + (new_size * valbytes));
+    }
+  }
+  
   ptr += tagbytes + 1;
   val = (uint8_t)ptr[-1];
   if (UPB_UNLIKELY(val & 0x80)) {
@@ -193,12 +166,32 @@ static const char *fastdecode_varint(UPB_PARSE_PARAMS, int tagbytes,
     }
     ptr++;
     uint64_t byte = (uint8_t)ptr[-1];
-    if (byte > 1) return fastdecode_err(d);
+    if (byte > 1) {
+      return fastdecode_err(d);
+    }
     val += (byte - 1) << 63;
   }
 done:
   val = fastdecode_munge(val, valbytes, zigzag);
   memcpy(dst, &val, valbytes);
+  
+  if (card == CARD_r) {
+    dst = (char*)dst + valbytes;
+    if (UPB_LIKELY(ptr < d->fastlimit)) {
+      uint32_t tag = fastdecode_loadtag(ptr);
+      if (tagbytes == 1) {
+        if ((uint8_t)tag == (uint8_t)data) goto again;
+      } else {
+        if ((uint16_t)tag == (uint16_t)data) goto again;
+      }
+      arr->len = (size_t)((char*)dst - (char*)_upb_array_ptr(arr)) / valbytes;
+      return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
+    } else {
+      arr->len = (size_t)((char*)dst - (char*)_upb_array_ptr(arr)) / valbytes;
+      RETURN_GENERIC("repeated generic");
+    }
+  }
+  
   return fastdecode_dispatch(d, ptr, msg, table, hasbits);
 }
 
@@ -228,14 +221,94 @@ done:
 
 TAGBYTES(s)
 TAGBYTES(o)
-/* TAGBYTES(r) */
+TAGBYTES(r)
 
 #undef z_ZZ
 #undef b_ZZ
 #undef v_ZZ
-#undef o_ONEOF
-#undef s_ONEOF
-#undef r_ONEOF
+#undef F
+#undef TYPES
+#undef TAGBYTES
+
+/* fixed fields ***************************************************************/
+
+UPB_FORCEINLINE
+static const char *fastdecode_fixed(UPB_PARSE_PARAMS, int tagbytes,
+                                    int valbytes, upb_card card) {
+  void *dst;
+  if (UPB_UNLIKELY(!fastdecode_checktag(data, tagbytes))) {
+    RETURN_GENERIC("varint field tag mismatch\n");
+  }
+  upb_array* arr;
+  void* end;
+  dst = fastdecode_getfield_ofs(d, ptr, msg, &data, &hasbits, &arr, &end,
+                            valbytes, card, false);
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(!dst)) {
+      RETURN_GENERIC("need array resize\n");
+    }
+  }
+  
+again:
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(dst == end)) {
+      size_t old_size = arr->size;
+      size_t old_bytes = old_size * valbytes;
+      size_t new_size = old_size * 2;
+      size_t new_bytes = new_size * valbytes;
+      char *old_ptr = _upb_array_ptr(arr);
+      char *new_ptr = upb_arena_realloc(&d->arena, old_ptr, old_bytes, new_bytes);
+      arr->size = new_size;
+      arr->data = _upb_array_tagptr(new_ptr, 3);
+      dst = (void*)(new_ptr + (old_size * valbytes));
+      end = (void*)(new_ptr + (new_size * valbytes));
+    }
+  }
+  
+  ptr += tagbytes;
+  memcpy(dst, ptr, valbytes);
+  ptr += valbytes;
+  
+  if (card == CARD_r) {
+    dst = (char*)dst + valbytes;
+    if (UPB_LIKELY(ptr < d->fastlimit)) {
+      uint32_t tag = fastdecode_loadtag(ptr);
+      if (tagbytes == 1) {
+        if ((uint8_t)tag == (uint8_t)data) goto again;
+      } else {
+        if ((uint16_t)tag == (uint16_t)data) goto again;
+      }
+      arr->len = (size_t)((char*)dst - (char*)_upb_array_ptr(arr)) / valbytes;
+      return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
+    } else {
+      arr->len = (size_t)((char*)dst - (char*)_upb_array_ptr(arr)) / valbytes;
+      RETURN_GENERIC("repeated generic");
+    }
+  }
+  
+  return fastdecode_dispatch(d, ptr, msg, table, hasbits);
+}
+
+/* Generate all varint functions.
+ * {s,o,r} x {b1,v4,z4,v8,z8} x {1bt,2bt} */
+
+#define F(card, type, valbytes, tagbytes)                                      \
+  const char *upb_p##card##type##valbytes##_##tagbytes##bt(UPB_PARSE_PARAMS) { \
+    return fastdecode_fixed(UPB_PARSE_ARGS, tagbytes, valbytes, CARD_##card);            \
+  }
+
+#define TYPES(card, tagbytes) \
+  F(card, f, 4, tagbytes)     \
+  F(card, f, 8, tagbytes)
+
+#define TAGBYTES(card) \
+  TYPES(card, 1)       \
+  TYPES(card, 2)
+
+TAGBYTES(s)
+TAGBYTES(o)
+TAGBYTES(r)
+
 #undef F
 #undef TYPES
 #undef TAGBYTES
@@ -261,35 +334,92 @@ static const char *fastdecode_string(UPB_PARSE_PARAMS, int tagbytes,
     RETURN_GENERIC("string field tag mismatch\n");
   }
 
-  dst = fastdecode_getfield(d, ptr, msg, &data, &hasbits,
-                            sizeof(upb_strview), card);
+  upb_array* arr;
+  void* end;
+  dst = fastdecode_getfield_ofs(d, ptr, msg, &data, &hasbits, &arr, &end,
+                            sizeof(upb_strview), card, false);
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(!dst)) {
+      RETURN_GENERIC("need array resize\n");
+    }
+  }
+  
+again:
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(dst == end)) {
+      size_t old_size = arr->size;
+      size_t old_bytes = old_size * sizeof(upb_strview);
+      size_t new_size = old_size * 2;
+      size_t new_bytes = new_size * sizeof(upb_strview);
+      char *old_ptr = _upb_array_ptr(arr);
+      char *new_ptr = upb_arena_realloc(&d->arena, old_ptr, old_bytes, new_bytes);
+      arr->size = new_size;
+      arr->data = _upb_array_tagptr(new_ptr, 3);
+      dst = (void*)(new_ptr + (old_size * sizeof(upb_strview*)));
+      end = (void*)(new_ptr + (new_size * sizeof(upb_strview*)));
+    }
+  }
+  
   len = (int8_t)ptr[tagbytes];
   str = ptr + tagbytes + 1;
   dst->data = str;
   dst->size = len;
+  ptr = str + len;
   if (UPB_UNLIKELY(fastdecode_boundscheck(str, len, d->limit))) {
     dst->size = 0;
-    RETURN_GENERIC("string field len >1 byte\n");
+    goto generic;
+  }
+  if (card == CARD_r) {
+    dst++;
+    if (UPB_LIKELY(ptr < d->fastlimit)) {
+      uint32_t tag = fastdecode_loadtag(ptr);
+      if (tagbytes == 1) {
+        if ((uint8_t)tag == (uint8_t)data) goto again;
+      } else {
+        if ((uint16_t)tag == (uint16_t)data) goto again;
+      }
+      arr->len = dst - (upb_strview*)_upb_array_ptr(arr);
+      return fastdecode_tagdispatch(d, ptr, msg, table, hasbits, tag);
+    } else {
+      goto generic;
+    }
+  }
+  if (card == CARD_r) {
+    arr->len = dst - (upb_strview*)_upb_array_ptr(arr);
+  }
+  return fastdecode_dispatch(d, ptr, msg, table, hasbits);
+  
+generic:
+  if (card == CARD_r) {
+    arr->len = dst - (upb_strview*)_upb_array_ptr(arr);
   }
-  return fastdecode_dispatch(d, str + len, msg, table, hasbits);
+  RETURN_GENERIC("repeated generic");
 }
 
 const char *upb_pss_1bt(UPB_PARSE_PARAMS) {
   return fastdecode_string(UPB_PARSE_ARGS, 1, CARD_s);
 }
 
-const char *upb_pos_1bt(UPB_PARSE_PARAMS) {
-  return fastdecode_string(UPB_PARSE_ARGS, 1, CARD_o);
-}
-
 const char *upb_pss_2bt(UPB_PARSE_PARAMS) {
   return fastdecode_string(UPB_PARSE_ARGS, 2, CARD_s);
 }
 
+const char *upb_pos_1bt(UPB_PARSE_PARAMS) {
+  return fastdecode_string(UPB_PARSE_ARGS, 1, CARD_o);
+}
+
 const char *upb_pos_2bt(UPB_PARSE_PARAMS) {
   return fastdecode_string(UPB_PARSE_ARGS, 2, CARD_o);
 }
 
+const char *upb_prs_1bt(UPB_PARSE_PARAMS) {
+  return fastdecode_string(UPB_PARSE_ARGS, 1, CARD_r);
+}
+
+const char *upb_prs_2bt(UPB_PARSE_PARAMS) {
+  return fastdecode_string(UPB_PARSE_ARGS, 2, CARD_r);
+}
+
 /* message fields *************************************************************/
 
 UPB_NOINLINE
@@ -350,6 +480,12 @@ static const char *fastdecode_submsg(UPB_PARSE_PARAMS, int tagbytes,
     hasbits = 0;
   }
 
+  if (card == CARD_r) {
+    if (UPB_UNLIKELY(!submsg)) {
+      RETURN_GENERIC("need array resize\n");
+    }
+  }
+  
   const char *saved_limit = d->limit;
   const char *saved_fastlimit = d->fastlimit;
 
@@ -368,7 +504,7 @@ again:
       end = (void*)(new_ptr + (new_size * sizeof(upb_msg*)));
     }
   }
-
+  
   upb_msg* child = *submsg;
 
   if (card == CARD_r || UPB_LIKELY(!child)) {
@@ -413,7 +549,6 @@ again:
   d->depth++;
 
   return fastdecode_dispatch(d, ptr, msg, table, hasbits);
-
 repeated_generic:
   arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
   d->limit = saved_limit;
diff --git a/upb/decode_fast.h b/upb/decode_fast.h
index 441b6aa340..335e83745b 100644
--- a/upb/decode_fast.h
+++ b/upb/decode_fast.h
@@ -29,20 +29,24 @@ const char *fastdecode_generic(struct upb_decstate *d, const char *ptr,
   F(card, v, 4, tagbytes)     \
   F(card, v, 8, tagbytes)     \
   F(card, z, 4, tagbytes)     \
-  F(card, z, 8, tagbytes)
+  F(card, z, 8, tagbytes)     \
+  F(card, f, 4, tagbytes)     \
+  F(card, f, 8, tagbytes)
 
 #define TAGBYTES(card) \
   TYPES(card, 1)       \
   TYPES(card, 2)
 
 TAGBYTES(s)
-TAGBYTES(o)
-/* TAGBYTES(r) */
+// TAGBYTES(o)
+TAGBYTES(r)
 
 const char *upb_pss_1bt(UPB_PARSE_PARAMS);
 const char *upb_pss_2bt(UPB_PARSE_PARAMS);
 const char *upb_pos_1bt(UPB_PARSE_PARAMS);
 const char *upb_pos_2bt(UPB_PARSE_PARAMS);
+const char *upb_prs_1bt(UPB_PARSE_PARAMS);
+const char *upb_prs_2bt(UPB_PARSE_PARAMS);
 
 #undef F
 #undef TYPES
diff --git a/upb/def.c b/upb/def.c
index c392781331..f1c4e3f0d2 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "google/protobuf/descriptor.upb.h"
+#include "upb/decode.int.h"
 
 #include "upb/port_def.inc"
 
diff --git a/upbc/generator.cc b/upbc/generator.cc
index 5dbf0206a6..b374c4aef9 100644
--- a/upbc/generator.cc
+++ b/upbc/generator.cc
@@ -777,18 +777,27 @@ void TryFillTableEntry(const protobuf::Descriptor* message,
       type = "m";
       wire_type = 2;
       break;
+    case protobuf::FieldDescriptor::TYPE_FIXED32:
+    case protobuf::FieldDescriptor::TYPE_SFIXED32:
+    case protobuf::FieldDescriptor::TYPE_FLOAT:
+      type = "f4";
+      wire_type = 5;
+      break;
+    case protobuf::FieldDescriptor::TYPE_FIXED64:
+    case protobuf::FieldDescriptor::TYPE_SFIXED64:
+    case protobuf::FieldDescriptor::TYPE_DOUBLE:
+      type = "f8";
+      wire_type = 1;
+      break;      
     default:
       return;  // Not supported yet.
   }
 
   switch (field->label()) {
     case protobuf::FieldDescriptor::LABEL_REPEATED:
-      if (field->type() == protobuf::FieldDescriptor::TYPE_MESSAGE) {
-        cardinality = "r";
-        break;
-      } else {
-        return;  // Not supported yet.
-      }
+      if (field->is_packed()) return;  // Packed fields are not supported
+      cardinality = "r";
+      break;
     case protobuf::FieldDescriptor::LABEL_OPTIONAL:
     case protobuf::FieldDescriptor::LABEL_REQUIRED:
       if (field->real_containing_oneof()) {