Adding iree_bitfield_parse utility.
diff --git a/runtime/src/iree/base/bitfield.c b/runtime/src/iree/base/bitfield.c
index 428220c..c395940 100644
--- a/runtime/src/iree/base/bitfield.c
+++ b/runtime/src/iree/base/bitfield.c
@@ -9,6 +9,61 @@
 #include <stdlib.h>
 #include <string.h>
 
+static bool iree_bitfield_lookup_mapping(
+    iree_string_view_t value, iree_host_size_t mapping_count,
+    const iree_bitfield_string_mapping_t* mappings, uint32_t* out_bits) {
+  *out_bits = 0;
+  for (iree_host_size_t mapping_index = 0; mapping_index < mapping_count;
+       ++mapping_index) {
+    const iree_bitfield_string_mapping_t mapping = mappings[mapping_index];
+    if (iree_string_view_equal_case(mapping.string, value)) {
+      *out_bits = mapping.bits;
+      return true;
+    }
+  }
+  return false;
+}
+
+static inline bool iree_isdigit(char c) { return (unsigned)c - '0' < 10; }
+
+IREE_API_EXPORT iree_status_t iree_bitfield_parse(
+    iree_string_view_t value, iree_host_size_t mapping_count,
+    const iree_bitfield_string_mapping_t* mappings, uint32_t* out_value) {
+  uint32_t bits_value = 0;
+  while (!iree_string_view_is_empty(value)) {
+    // Slice off the next part (or the tail).
+    iree_string_view_t part = iree_string_view_empty();
+    iree_string_view_split(value, '|', &part, &value);
+    part = iree_string_view_trim(part);
+    if (iree_string_view_is_empty(part)) continue;
+
+    // Scan the mapping table and match case-insensitive.
+    uint32_t mapping_bits = 0;
+    if (iree_bitfield_lookup_mapping(part, mapping_count, mappings,
+                                     &mapping_bits)) {
+      bits_value |= mapping_bits;
+      continue;
+    }
+
+    // If it starts with a number we try to parse it like one.
+    if (iree_isdigit(part.data[0])) {
+      uint32_t int_bits = 0;
+      if (iree_string_view_atoi_uint32(part, &int_bits)) {
+        bits_value |= int_bits;
+        continue;
+      }
+    }
+
+    // Unknown bitfield value.
+    return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+                            "unrecognized bitfield member '%.*s'",
+                            (int)part.size, part.data);
+  }
+
+  *out_value = bits_value;
+  return iree_ok_status();
+}
+
 IREE_API_EXPORT iree_status_t
 iree_bitfield_format(uint32_t value, iree_host_size_t mapping_count,
                      const iree_bitfield_string_mapping_t* mappings,
diff --git a/runtime/src/iree/base/bitfield.h b/runtime/src/iree/base/bitfield.h
index 4329d53..6f7660f 100644
--- a/runtime/src/iree/base/bitfield.h
+++ b/runtime/src/iree/base/bitfield.h
@@ -30,7 +30,30 @@
   iree_string_view_t string;
 } iree_bitfield_string_mapping_t;
 
-// Appends the formatted contents of the given bitfield value.
+// Parses the bitfield |value| from a string.
+// The provided |mappings| table is used for string lookup. Unknown values
+// result in a failure.
+//
+// Usage:
+//  // Static mapping table:
+//  static const iree_bitfield_string_mapping_t my_bitfield_mappings[] = {
+//    {MY_BITFIELD_ALL, IREE_SVL("ALL")},  // combined flags first
+//    {MY_BITFIELD_A,   IREE_SVL("A")},
+//    {MY_BITFIELD_B,   IREE_SVL("B")},
+//    {MY_BITFIELD_C,   IREE_SVL("C")},
+//  };
+//
+//  // Produces the bits MY_BITFIELD_A|MY_BITFIELD_B:
+//  uint32_t value_ab = 0;
+//  IREE_RETURN_IF_ERROR(iree_bitfield_parse(
+//      IREE_SV("A|B"),
+//      IREE_ARRAYSIZE(my_bitfield_mappings), my_bitfield_mappings,
+//      &value_ab));
+IREE_API_EXPORT iree_status_t iree_bitfield_parse(
+    iree_string_view_t value, iree_host_size_t mapping_count,
+    const iree_bitfield_string_mapping_t* mappings, uint32_t* out_value);
+
+// Appends the formatted contents of the given bitfield |value|.
 // Processes values in the order of the mapping table provided and will only
 // use each bit once. Use this to prioritize combined flags over split ones.
 //
diff --git a/runtime/src/iree/base/bitfield_test.cc b/runtime/src/iree/base/bitfield_test.cc
index 95109ed..2989ce2 100644
--- a/runtime/src/iree/base/bitfield_test.cc
+++ b/runtime/src/iree/base/bitfield_test.cc
@@ -13,6 +13,9 @@
 namespace iree {
 namespace {
 
+using iree::testing::status::IsOkAndHolds;
+using iree::testing::status::StatusIs;
+
 enum my_bitfield_e {
   MY_BITFIELD_NONE = 0,
   MY_BITFIELD_A = 1 << 0,
@@ -22,6 +25,62 @@
 typedef uint32_t my_bitfield_t;
 
 template <size_t mapping_count>
+StatusOr<uint32_t> ParseBitfieldValue(
+    const char* value,
+    const iree_bitfield_string_mapping_t (&mappings)[mapping_count]) {
+  uint32_t bits_value = 0;
+  IREE_RETURN_IF_ERROR(iree_bitfield_parse(
+      iree_make_cstring_view(value), mapping_count, mappings, &bits_value));
+  return bits_value;
+}
+
+// Tests general parser usage.
+TEST(BitfieldTest, ParseBitfieldValue) {
+  static const iree_bitfield_string_mapping_t mappings[] = {
+      {MY_BITFIELD_A, IREE_SV("A")},
+      {MY_BITFIELD_B, IREE_SV("B")},
+  };
+  EXPECT_THAT(ParseBitfieldValue("", mappings), IsOkAndHolds(MY_BITFIELD_NONE));
+  EXPECT_THAT(ParseBitfieldValue("A", mappings), IsOkAndHolds(MY_BITFIELD_A));
+  EXPECT_THAT(ParseBitfieldValue("A|B", mappings),
+              IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+  EXPECT_THAT(ParseBitfieldValue("a|b", mappings),
+              IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+  EXPECT_THAT(ParseBitfieldValue("|a||B|", mappings),
+              IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+}
+
+// Tests that empty mapping tables behave ok.
+TEST(BitfieldTest, ParseBitfieldValueEmpty) {
+  static const iree_bitfield_string_mapping_t mappings[1] = {
+      {0, IREE_SV("UNUSED")},  // unused; required for C++ compat
+  };
+  // Empty strings always mean 0, no mapping fields needed.
+  EXPECT_THAT(ParseBitfieldValue("", mappings), IsOkAndHolds(0));
+  // If any named values are provided, though, we fail.
+  EXPECT_THAT(ParseBitfieldValue("foo", mappings),
+              StatusIs(StatusCode::kInvalidArgument));
+  // Manually-specified values are ok, though.
+  EXPECT_THAT(ParseBitfieldValue("2h|1h", mappings), IsOkAndHolds(0x2u | 0x1u));
+}
+
+// Tests that values not found in the mappings are still parsed.
+TEST(BitfieldTest, ParseBitfieldValueUnhandledValues) {
+  static const iree_bitfield_string_mapping_t mappings[] = {
+      {MY_BITFIELD_A, IREE_SV("A")},
+      {MY_BITFIELD_B, IREE_SV("B")},
+  };
+  EXPECT_THAT(ParseBitfieldValue("A|2", mappings),
+              IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+  EXPECT_THAT(ParseBitfieldValue("A|2h", mappings),
+              IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+  EXPECT_THAT(ParseBitfieldValue("A|0x2", mappings),
+              IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+  EXPECT_THAT(ParseBitfieldValue("A|a08", mappings),
+              StatusIs(StatusCode::kInvalidArgument));
+}
+
+template <size_t mapping_count>
 std::string FormatBitfieldValue(
     uint32_t value,
     const iree_bitfield_string_mapping_t (&mappings)[mapping_count]) {
@@ -30,7 +89,7 @@
   return std::string(sv.data, sv.size);
 }
 
-// Tests general usage.
+// Tests general formatting usage.
 TEST(BitfieldTest, FormatBitfieldValue) {
   static const iree_bitfield_string_mapping_t mappings[] = {
       {MY_BITFIELD_A, IREE_SV("A")},
@@ -45,7 +104,7 @@
 // Tests that empty mapping tables are fine.
 TEST(BitfieldTest, FormatBitfieldValueEmpty) {
   static const iree_bitfield_string_mapping_t mappings[1] = {
-      {0, IREE_SV("UNUSED")},
+      {0, IREE_SV("UNUSED")},  // unused; required for C++ compat
   };
   iree_bitfield_string_temp_t temp;
   auto sv = iree_bitfield_format_inline(MY_BITFIELD_NONE, 0, mappings, &temp);
diff --git a/runtime/src/iree/base/string_view.c b/runtime/src/iree/base/string_view.c
index 8b6153e..477a473 100644
--- a/runtime/src/iree/base/string_view.c
+++ b/runtime/src/iree/base/string_view.c
@@ -18,6 +18,16 @@
   return a < b ? a : b;
 }
 
+// Here to ensure that we don't pull in locale-specific code:
+static bool iree_isupper(char c) { return (unsigned)c - 'A' < 26; }
+static bool iree_islower(char c) { return (unsigned)c - 'a' < 26; }
+static inline char iree_toupper(char c) {
+  return iree_islower(c) ? (c & 0x5F) : c;
+}
+static inline char iree_tolower(char c) {
+  return iree_isupper(c) ? (c | 32) : c;
+}
+
 IREE_API_EXPORT bool iree_string_view_equal(iree_string_view_t lhs,
                                             iree_string_view_t rhs) {
   if (lhs.size != rhs.size) return false;
@@ -27,6 +37,15 @@
   return true;
 }
 
+IREE_API_EXPORT bool iree_string_view_equal_case(iree_string_view_t lhs,
+                                                 iree_string_view_t rhs) {
+  if (lhs.size != rhs.size) return false;
+  for (iree_host_size_t i = 0; i < lhs.size; ++i) {
+    if (iree_tolower(lhs.data[i]) != iree_tolower(rhs.data[i])) return false;
+  }
+  return true;
+}
+
 IREE_API_EXPORT int iree_string_view_compare(iree_string_view_t lhs,
                                              iree_string_view_t rhs) {
   iree_host_size_t min_size = iree_min_host_size(lhs.size, rhs.size);
diff --git a/runtime/src/iree/base/string_view.h b/runtime/src/iree/base/string_view.h
index 1026772..65adcde 100644
--- a/runtime/src/iree/base/string_view.h
+++ b/runtime/src/iree/base/string_view.h
@@ -99,6 +99,10 @@
 // Returns true if the two strings are equal (compare == 0).
 IREE_API_EXPORT bool iree_string_view_equal(iree_string_view_t lhs,
                                             iree_string_view_t rhs);
+// Returns true if the two strings are equal (compare == 0) ignoring case.
+// Equivalent to strcasecmp.
+IREE_API_EXPORT bool iree_string_view_equal_case(iree_string_view_t lhs,
+                                                 iree_string_view_t rhs);
 
 // Like std::string::compare but with iree_string_view_t values.
 IREE_API_EXPORT int iree_string_view_compare(iree_string_view_t lhs,
diff --git a/runtime/src/iree/base/string_view_test.cc b/runtime/src/iree/base/string_view_test.cc
index 27fe076..3589cb3 100644
--- a/runtime/src/iree/base/string_view_test.cc
+++ b/runtime/src/iree/base/string_view_test.cc
@@ -35,6 +35,25 @@
   EXPECT_FALSE(equal("b", "ab"));
   EXPECT_TRUE(equal("abc", "abc"));
   EXPECT_FALSE(equal("abc", "aBc"));
+  EXPECT_TRUE(equal("a_c", "a_c"));
+}
+
+TEST(StringViewTest, EqualCase) {
+  auto equal_case = [](const char* lhs, const char* rhs) -> bool {
+    return iree_string_view_equal_case(iree_make_cstring_view(lhs),
+                                       iree_make_cstring_view(rhs));
+  };
+  EXPECT_TRUE(equal_case("", ""));
+  EXPECT_FALSE(equal_case("a", ""));
+  EXPECT_FALSE(equal_case("", "a"));
+  EXPECT_TRUE(equal_case("a", "a"));
+  EXPECT_TRUE(equal_case("A", "a"));
+  EXPECT_TRUE(equal_case("a", "A"));
+  EXPECT_FALSE(equal_case("a", "ab"));
+  EXPECT_FALSE(equal_case("b", "ab"));
+  EXPECT_TRUE(equal_case("abc", "abc"));
+  EXPECT_TRUE(equal_case("abc", "aBc"));
+  EXPECT_TRUE(equal_case("a_c", "a_C"));
 }
 
 TEST(StringViewTest, FindChar) {