Generalizing some integer-specific tablegen goo to support other types.
diff --git a/iree/compiler/Dialect/VM/IR/VMBase.td b/iree/compiler/Dialect/VM/IR/VMBase.td
index bbf9a10..e75a063 100644
--- a/iree/compiler/Dialect/VM/IR/VMBase.td
+++ b/iree/compiler/Dialect/VM/IR/VMBase.td
@@ -393,12 +393,12 @@
     "e.encodeType(" # expr # ")">;
 class VM_EncTypeOf<string name> : VM_EncEncodeExpr<
     "e.encodeType(" # name # "())">;
-class VM_EncIntAttr<string name, int thisBitwidth> : VM_EncEncodeExpr<
-    "e.encodeIntAttr(getOperation()->getAttrOfType<IntegerAttr>(\"" # name # "\"))"> {
+class VM_EncPrimitiveAttr<string name, int thisBitwidth> : VM_EncEncodeExpr<
+    "e.encodePrimitiveAttr(getOperation()->getAttrOfType<Attribute>(\"" # name # "\"))"> {
   int bitwidth = thisBitwidth;
 }
-class VM_EncIntArrayAttr<string name, int thisBitwidth> : VM_EncEncodeExpr<
-    "e.encodeIntArrayAttr(getOperation()->getAttrOfType<DenseIntElementsAttr>(\"" # name # "\"))"> {
+class VM_EncPrimitiveArrayAttr<string name, int thisBitwidth> : VM_EncEncodeExpr<
+    "e.encodePrimitiveArrayAttr(getOperation()->getAttrOfType<DenseElementsAttr>(\"" # name # "\"))"> {
   int bitwidth = thisBitwidth;
 }
 class VM_EncStrAttr<string name> : VM_EncEncodeExpr<
@@ -634,7 +634,7 @@
   FloatOfWidths<[16, 32, 64]>,
 ]>;
 
-class VM_ConstIntValueAttr<I type> : Attr<
+class VM_ConstantIntegerValueAttr<I type> : Attr<
     Or<[
       SignlessIntegerAttrBase<type,
                               type.bitwidth # "-bit integer value">.predicate,
diff --git a/iree/compiler/Dialect/VM/IR/VMFuncEncoder.h b/iree/compiler/Dialect/VM/IR/VMFuncEncoder.h
index 57c0998..1b511bf 100644
--- a/iree/compiler/Dialect/VM/IR/VMFuncEncoder.h
+++ b/iree/compiler/Dialect/VM/IR/VMFuncEncoder.h
@@ -54,11 +54,12 @@
   virtual LogicalResult encodeType(Value value) = 0;
   virtual LogicalResult encodeType(Type type) = 0;
 
-  // Encodes an integer attribute as a fixed byte length based on bitwidth.
-  virtual LogicalResult encodeIntAttr(IntegerAttr value) = 0;
+  // Encodes an integer or floating-point primitive attribute as a fixed byte
+  // length based on bitwidth.
+  virtual LogicalResult encodePrimitiveAttr(Attribute value) = 0;
 
-  // Encodes a variable-length integer array attribute.
-  virtual LogicalResult encodeIntArrayAttr(DenseIntElementsAttr value) = 0;
+  // Encodes a variable-length integer or floating-point array attribute.
+  virtual LogicalResult encodePrimitiveArrayAttr(DenseElementsAttr value) = 0;
 
   // Encodes a string attribute as a B-string.
   virtual LogicalResult encodeStrAttr(StringAttr value) = 0;
diff --git a/iree/compiler/Dialect/VM/IR/VMOps.td b/iree/compiler/Dialect/VM/IR/VMOps.td
index f95fe08..0107f92 100644
--- a/iree/compiler/Dialect/VM/IR/VMOps.td
+++ b/iree/compiler/Dialect/VM/IR/VMOps.td
@@ -320,7 +320,8 @@
   let verifier = [{ return verifyGlobalOp(*this); }];
 }
 
-def VM_GlobalI32Op : VM_GlobalOp<"global.i32", VM_ConstIntValueAttr<I32>> {
+def VM_GlobalI32Op : VM_GlobalOp<"global.i32",
+                                 VM_ConstantIntegerValueAttr<I32>> {
   let summary = [{32-bit integer global declaration}];
   let description = [{
     Defines a global value that is treated as a scalar literal at runtime.
@@ -330,7 +331,8 @@
   let hasCanonicalizer = 1;
 }
 
-def VM_GlobalI64Op : VM_GlobalOp<"global.i64", VM_ConstIntValueAttr<I64>,
+def VM_GlobalI64Op : VM_GlobalOp<"global.i64",
+                                 VM_ConstantIntegerValueAttr<I64>,
                                  [VM_ExtI64]> {
   let summary = [{64-bit integer global declaration}];
   let description = [{
@@ -633,23 +635,21 @@
   }];
 }
 
-class VM_ConstIntegerOp<I type, string mnemonic, VM_OPC opcode, string ctype,
-                        list<OpTrait> traits = []> :
+class VM_ConstantPrimitiveOp<Type type, int width, string mnemonic,
+                             VM_OPC opcode,
+                             string ctype, list<OpTrait> traits = []> :
     VM_ConstOp<mnemonic, ctype, traits> {
   let description = [{
     Defines a constant value that is treated as a scalar literal at runtime.
   }];
 
-  let arguments = (ins
-    VM_ConstIntValueAttr<type>:$value
-  );
   let results = (outs
     type:$result
   );
 
   let encoding = [
     VM_EncOpcode<opcode>,
-    VM_EncIntAttr<"value", type.bitwidth>,
+    VM_EncPrimitiveAttr<"value", width>,
     VM_EncResult<"result">,
   ];
 
@@ -658,25 +658,32 @@
 }
 
 def VM_ConstI32Op :
-    VM_ConstIntegerOp<I32, "const.i32", VM_OPC_ConstI32, "int32_t"> {
+    VM_ConstantPrimitiveOp<I32, 32, "const.i32", VM_OPC_ConstI32,
+                           "int32_t"> {
   let summary = [{32-bit integer constant operation}];
+  let arguments = (ins
+    VM_ConstantIntegerValueAttr<I32>:$value
+  );
   let hasFolder = 1;
   let hasCanonicalizer = 1;
 }
 
 def VM_ConstI64Op :
-    VM_ConstIntegerOp<I64, "const.i64", VM_OPC_ConstI64, "int64_t",
-                      [VM_ExtI64]> {
+    VM_ConstantPrimitiveOp<I64, 64, "const.i64", VM_OPC_ConstI64,
+                           "int64_t", [VM_ExtI64]> {
   let summary = [{64-bit integer constant operation}];
+  let arguments = (ins
+    VM_ConstantIntegerValueAttr<I64>:$value
+  );
   let hasFolder = 1;
   let hasCanonicalizer = 1;
 }
 
-class VM_ConstIntegerZeroOp<I type, string mnemonic, VM_OPC opcode,
-                            string ctype, list<OpTrait> traits = []> :
+class VM_ConstantPrimitiveZeroOp<Type type, string mnemonic, VM_OPC opcode,
+                                 string ctype, list<OpTrait> traits = []> :
     VM_ConstOp<mnemonic, ctype, traits> {
   let description = [{
-    Defines a constant zero integer.
+    Defines a constant zero primitive.
   }];
 
   let results = (outs
@@ -697,15 +704,15 @@
 }
 
 def VM_ConstI32ZeroOp :
-    VM_ConstIntegerZeroOp<I32, "const.i32.zero", VM_OPC_ConstI32Zero,
-                          "int32_t"> {
+    VM_ConstantPrimitiveZeroOp<I32, "const.i32.zero", VM_OPC_ConstI32Zero,
+                               "int32_t"> {
   let summary = [{32-bit integer constant zero operation}];
   let hasFolder = 1;
 }
 
 def VM_ConstI64ZeroOp :
-    VM_ConstIntegerZeroOp<I64, "const.i64.zero", VM_OPC_ConstI64Zero,
-                          "int64_t", [VM_ExtI64]> {
+    VM_ConstantPrimitiveZeroOp<I64, "const.i64.zero", VM_OPC_ConstI64Zero,
+                               "int64_t", [VM_ExtI64]> {
   let summary = [{64-bit integer constant zero operation}];
   let hasFolder = 1;
 }
@@ -1171,8 +1178,8 @@
   let hasFolder = 1;
 }
 
-class VM_SwitchPrimitiveOp<I type, string mnemonic, VM_OPC opcode,
-                           list<OpTrait> traits = []> :
+class VM_SwitchIntegerOp<I type, string mnemonic, VM_OPC opcode,
+                         list<OpTrait> traits = []> :
     VM_PureOp<mnemonic, !listconcat(traits, [
       DeclareOpInterfaceMethods<VM_SerializableOpInterface>,
       AllTypesMatch<["default_value", "result"]>,
@@ -1204,19 +1211,58 @@
   let encoding = [
     VM_EncOpcode<opcode>,
     VM_EncOperand<"index", 0>,
-    VM_EncIntAttr<"default_value", type.bitwidth>,
+    VM_EncPrimitiveAttr<"default_value", type.bitwidth>,
     VM_EncVariadicOperands<"values">,
     VM_EncResult<"result">,
   ];
 }
 
-def VM_SwitchI32Op : VM_SwitchPrimitiveOp<I32, "switch.i32", VM_OPC_SwitchI32> {
+class VM_SwitchFloatOp<F type, string mnemonic, VM_OPC opcode,
+                       list<OpTrait> traits = []> :
+    VM_PureOp<mnemonic, !listconcat(traits, [
+      DeclareOpInterfaceMethods<VM_SerializableOpInterface>,
+      AllTypesMatch<["default_value", "result"]>,
+    ])> {
+  let description = [{
+    Returns the value with the given `index` in `values` or `default_value` if
+    the index is out of bounds.
+
+    ```mlir
+    // Switch %index to cases of %c100/%c200/%c300 if index==0, ==1, ==2.
+    // If %index is out of range (<0 or >2) then default to %c5.
+    %0 = vm.switch.f32 %index[%c100, %c200, %c300] else %c5 : f32
+    ```
+  }];
+
+  let arguments = (ins
+    I32:$index,
+    type:$default_value,
+    Variadic<type>:$values
+  );
+  let results = (outs
+    type:$result
+  );
+
+  let assemblyFormat = [{
+    $index `[` $values `]` `else` $default_value attr-dict `:` type($result)
+  }];
+
+  let encoding = [
+    VM_EncOpcode<opcode>,
+    VM_EncOperand<"index", 0>,
+    VM_EncPrimitiveAttr<"default_value", type.bitwidth>,
+    VM_EncVariadicOperands<"values">,
+    VM_EncResult<"result">,
+  ];
+}
+
+def VM_SwitchI32Op : VM_SwitchIntegerOp<I32, "switch.i32", VM_OPC_SwitchI32> {
   let summary = [{integer switch operation}];
   let hasFolder = 1;
 }
 
-def VM_SwitchI64Op : VM_SwitchPrimitiveOp<I64, "switch.i64", VM_OPC_SwitchI64,
-                                          [VM_ExtI64]> {
+def VM_SwitchI64Op : VM_SwitchIntegerOp<I64, "switch.i64", VM_OPC_SwitchI64,
+                                        [VM_ExtI64]> {
   let summary = [{integer switch operation}];
   let hasFolder = 1;
 }
@@ -1264,7 +1310,7 @@
 }
 
 //===----------------------------------------------------------------------===//
-// Native integer arithmetic
+// Arithmetic helpers
 //===----------------------------------------------------------------------===//
 
 class VM_UnaryArithmeticOp<Type type, string mnemonic, VM_OPC opcode,
@@ -1313,6 +1359,36 @@
   ];
 }
 
+class VM_TernaryArithmeticOp<Type type, string mnemonic, VM_OPC opcode,
+                             list<OpTrait> traits = []> :
+    VM_PureOp<mnemonic, !listconcat(traits, [
+      DeclareOpInterfaceMethods<VM_SerializableOpInterface>,
+      AllTypesMatch<["a", "b", "c", "result"]>,
+    ])> {
+  let arguments = (ins
+    type:$a,
+    type:$b,
+    type:$c
+  );
+  let results = (outs
+    type:$result
+  );
+
+  let assemblyFormat = "operands attr-dict `:` type($result)";
+
+  let encoding = [
+    VM_EncOpcode<opcode>,
+    VM_EncOperand<"a", 0>,
+    VM_EncOperand<"b", 1>,
+    VM_EncOperand<"c", 2>,
+    VM_EncResult<"result">,
+  ];
+}
+
+//===----------------------------------------------------------------------===//
+// Integer arithmetic
+//===----------------------------------------------------------------------===//
+
 def VM_AddI32Op :
     VM_BinaryArithmeticOp<I32, "add.i32", VM_OPC_AddI32, [Commutative]> {
   let summary = [{integer add operation}];
@@ -1401,6 +1477,10 @@
   let hasFolder = 1;
 }
 
+//===----------------------------------------------------------------------===//
+// Integer bit manipulation
+//===----------------------------------------------------------------------===//
+
 def VM_NotI32Op :
     VM_UnaryArithmeticOp<I32, "not.i32", VM_OPC_NotI32> {
   let summary = [{integer binary not operation}];
@@ -1408,13 +1488,15 @@
 }
 
 def VM_NotI64Op :
-    VM_UnaryArithmeticOp<I64, "not.i64", VM_OPC_NotI64, [VM_ExtI64]> {
+    VM_UnaryArithmeticOp<I64, "not.i64", VM_OPC_NotI64,
+                          [VM_ExtI64]> {
   let summary = [{integer binary not operation}];
   let hasFolder = 1;
 }
 
 def VM_AndI32Op :
-    VM_BinaryArithmeticOp<I32, "and.i32", VM_OPC_AndI32, [Commutative]> {
+    VM_BinaryArithmeticOp<I32, "and.i32", VM_OPC_AndI32,
+                          [Commutative]> {
   let summary = [{integer binary and operation}];
   let hasFolder = 1;
 }
@@ -1427,7 +1509,8 @@
 }
 
 def VM_OrI32Op :
-    VM_BinaryArithmeticOp<I32, "or.i32", VM_OPC_OrI32, [Commutative]> {
+    VM_BinaryArithmeticOp<I32, "or.i32", VM_OPC_OrI32,
+                          [Commutative]> {
   let summary = [{integer binary or operation}];
   let hasFolder = 1;
 }
@@ -1440,7 +1523,8 @@
 }
 
 def VM_XorI32Op :
-    VM_BinaryArithmeticOp<I32, "xor.i32", VM_OPC_XorI32, [Commutative]> {
+    VM_BinaryArithmeticOp<I32, "xor.i32", VM_OPC_XorI32,
+                          [Commutative]> {
   let summary = [{integer binary exclusive-or operation}];
   let hasFolder = 1;
 }
@@ -1453,7 +1537,7 @@
 }
 
 //===----------------------------------------------------------------------===//
-// Native bitwise shifts and rotates
+// Bitwise shifts and rotates
 //===----------------------------------------------------------------------===//
 
 class VM_ShiftArithmeticOp<I type, string mnemonic, VM_OPC opcode,
@@ -1479,7 +1563,7 @@
   let encoding = [
     VM_EncOpcode<opcode>,
     VM_EncOperand<"operand", 0>,
-    VM_EncIntAttr<"amount", 8>,
+    VM_EncPrimitiveAttr<"amount", 8>,
     VM_EncResult<"result">,
   ];
 }
@@ -1570,13 +1654,15 @@
 }
 
 def VM_TruncI64I8Op :
-    VM_ConversionPseudoOp<I64, I32, "trunc.i64.i8", [VM_ExtI64]> {
+    VM_ConversionPseudoOp<I64, I32, "trunc.i64.i8",
+                          [VM_ExtI64]> {
   let summary = [{integer truncate to 8 bits}];
   let hasFolder = 1;
 }
 
 def VM_TruncI64I16Op :
-    VM_ConversionPseudoOp<I64, I32, "trunc.i64.i16", [VM_ExtI64]> {
+    VM_ConversionPseudoOp<I64, I32, "trunc.i64.i16",
+                          [VM_ExtI64]> {
   let summary = [{integer truncate to 16 bits}];
   let hasFolder = 1;
 }
@@ -1613,37 +1699,43 @@
 }
 
 def VM_ExtI8I64SOp :
-    VM_ConversionPseudoOp<I32, I64, "ext.i8.i64.s", [VM_ExtI64]> {
+    VM_ConversionPseudoOp<I32, I64, "ext.i8.i64.s",
+                          [VM_ExtI64]> {
   let summary = [{integer sign extend 8 bits to 64 bits}];
   let hasFolder = 1;
 }
 
 def VM_ExtI8I64UOp :
-    VM_ConversionPseudoOp<I32, I64, "ext.i8.i64.u", [VM_ExtI64]> {
+    VM_ConversionPseudoOp<I32, I64, "ext.i8.i64.u",
+                          [VM_ExtI64]> {
   let summary = [{integer zero extend 8 bits to 64 bits}];
   let hasFolder = 1;
 }
 
 def VM_ExtI16I64SOp :
-    VM_ConversionPseudoOp<I32, I64, "ext.i16.i64.s", [VM_ExtI64]> {
+    VM_ConversionPseudoOp<I32, I64, "ext.i16.i64.s",
+                          [VM_ExtI64]> {
   let summary = [{integer sign extend 16 bits to 64 bits}];
   let hasFolder = 1;
 }
 
 def VM_ExtI16I64UOp :
-    VM_ConversionPseudoOp<I32, I64, "ext.i16.i64.u", [VM_ExtI64]> {
+    VM_ConversionPseudoOp<I32, I64, "ext.i16.i64.u",
+                          [VM_ExtI64]> {
   let summary = [{integer zero extend 16 bits to 64 bits}];
   let hasFolder = 1;
 }
 
 def VM_ExtI32I64SOp :
-    VM_ConversionOp<I32, I64, "ext.i32.i64.s", VM_OPC_ExtI32I64S, [VM_ExtI64]> {
+    VM_ConversionOp<I32, I64, "ext.i32.i64.s", VM_OPC_ExtI32I64S,
+                    [VM_ExtI64]> {
   let summary = [{integer sign extend 32 bits to 64 bits}];
   let hasFolder = 1;
 }
 
 def VM_ExtI32I64UOp :
-    VM_ConversionOp<I32, I64, "ext.i32.i64.u", VM_OPC_ExtI32I64U, [VM_ExtI64]> {
+    VM_ConversionOp<I32, I64, "ext.i32.i64.u", VM_OPC_ExtI32I64U,
+                    [VM_ExtI64]> {
   let summary = [{integer zero extend 32 bits to 64 bits}];
   let hasFolder = 1;
 }
@@ -1733,7 +1825,8 @@
 }
 
 def VM_CmpEQI32Op :
-    VM_BinaryComparisonOp<I32, "cmp.eq.i32", VM_OPC_CmpEQI32, [Commutative]> {
+    VM_BinaryComparisonOp<I32, "cmp.eq.i32", VM_OPC_CmpEQI32,
+                          [Commutative]> {
   let summary = [{integer equality comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1748,7 +1841,8 @@
 }
 
 def VM_CmpNEI32Op :
-    VM_BinaryComparisonOp<I32, "cmp.ne.i32", VM_OPC_CmpNEI32, [Commutative]> {
+    VM_BinaryComparisonOp<I32, "cmp.ne.i32", VM_OPC_CmpNEI32,
+                          [Commutative]> {
   let summary = [{integer inequality comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1770,7 +1864,8 @@
 }
 
 def VM_CmpLTI64SOp :
-    VM_BinaryComparisonOp<I64, "cmp.lt.i64.s", VM_OPC_CmpLTI64S, [VM_ExtI64]> {
+    VM_BinaryComparisonOp<I64, "cmp.lt.i64.s", VM_OPC_CmpLTI64S,
+                          [VM_ExtI64]> {
   let summary = [{signed integer less-than comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1784,7 +1879,8 @@
 }
 
 def VM_CmpLTI64UOp :
-    VM_BinaryComparisonOp<I64, "cmp.lt.i64.u", VM_OPC_CmpLTI64U, [VM_ExtI64]> {
+    VM_BinaryComparisonOp<I64, "cmp.lt.i64.u", VM_OPC_CmpLTI64U,
+                          [VM_ExtI64]> {
   let summary = [{unsigned integer less-than comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1798,7 +1894,8 @@
 }
 
 def VM_CmpLTEI64SOp :
-    VM_BinaryComparisonPseudoOp<I64, "cmp.lte.i64.s", [VM_ExtI64]> {
+    VM_BinaryComparisonPseudoOp<I64, "cmp.lte.i64.s",
+                                [VM_ExtI64]> {
   let summary = [{signed integer less-than-or-equal comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1812,7 +1909,8 @@
 }
 
 def VM_CmpLTEI64UOp :
-    VM_BinaryComparisonPseudoOp<I64, "cmp.lte.i64.u", [VM_ExtI64]> {
+    VM_BinaryComparisonPseudoOp<I64, "cmp.lte.i64.u",
+                                [VM_ExtI64]> {
   let summary = [{unsigned integer less-than-or-equal comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1826,7 +1924,8 @@
 }
 
 def VM_CmpGTI64SOp :
-    VM_BinaryComparisonPseudoOp<I64, "cmp.gt.i64.s", [VM_ExtI64]> {
+    VM_BinaryComparisonPseudoOp<I64, "cmp.gt.i64.s",
+                                [VM_ExtI64]> {
   let summary = [{signed integer greater-than comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1840,7 +1939,8 @@
 }
 
 def VM_CmpGTI64UOp :
-    VM_BinaryComparisonPseudoOp<I64, "cmp.gt.i64.u", [VM_ExtI64]> {
+    VM_BinaryComparisonPseudoOp<I64, "cmp.gt.i64.u",
+                                [VM_ExtI64]> {
   let summary = [{unsigned integer greater-than comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1854,7 +1954,8 @@
 }
 
 def VM_CmpGTEI64SOp :
-    VM_BinaryComparisonPseudoOp<I64, "cmp.gte.i64.s", [VM_ExtI64]> {
+    VM_BinaryComparisonPseudoOp<I64, "cmp.gte.i64.s",
+                                [VM_ExtI64]> {
   let summary = [{signed integer greater-than-or-equal comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1868,7 +1969,8 @@
 }
 
 def VM_CmpGTEI64UOp :
-    VM_BinaryComparisonPseudoOp<I64, "cmp.gte.i64.u", [VM_ExtI64]> {
+    VM_BinaryComparisonPseudoOp<I64, "cmp.gte.i64.u",
+                                [VM_ExtI64]> {
   let summary = [{unsigned integer greater-than-or-equal comparison operation}];
   let hasCanonicalizer = 1;
   let hasFolder = 1;
@@ -1884,7 +1986,8 @@
 }
 
 def VM_CmpNZI64Op :
-    VM_UnaryComparisonOp<I64, "cmp.nz.i64", VM_OPC_CmpNZI64, [VM_ExtI64]> {
+    VM_UnaryComparisonOp<I64, "cmp.nz.i64", VM_OPC_CmpNZI64,
+                         [VM_ExtI64]> {
   let summary = [{integer non-zero comparison operation}];
   let description = [{
     Compares the given integer operand for a non-zero value.
@@ -2206,7 +2309,7 @@
   let encoding = [
     VM_EncOpcode<VM_OPC_CallVariadic>,
     VM_EncFuncAttr<"callee">,
-    VM_EncIntArrayAttr<"segment_sizes", 16>,
+    VM_EncPrimitiveArrayAttr<"segment_sizes", 16>,
     VM_EncVariadicOperands<"operands">,
     VM_EncVariadicResults<"results">,
   ];
diff --git a/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp b/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp
index 349c56c..643a1da 100644
--- a/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp
+++ b/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp
@@ -108,32 +108,38 @@
     return writeUint32(typeOrdinal);
   }
 
-  LogicalResult encodeIntAttr(IntegerAttr value) override {
-    auto attr = value.cast<IntegerAttr>();
+  LogicalResult encodePrimitiveAttr(Attribute attr) override {
     unsigned int bitWidth = attr.getType().getIntOrFloatBitWidth();
-    uint64_t limitedValue = attr.getValue().extractBitsAsZExtValue(bitWidth, 0);
-    switch (bitWidth) {
-      case 8:
-        return writeUint8(static_cast<uint8_t>(limitedValue));
-      case 16:
-        return writeUint16(static_cast<uint16_t>(limitedValue));
-      case 32:
-        return writeUint32(static_cast<uint32_t>(limitedValue));
-      case 64:
-        return writeUint64(static_cast<uint64_t>(limitedValue));
-      default:
-        return currentOp_->emitOpError()
-               << "attribute of bitwidth " << bitWidth << " not supported";
+    if (auto integerAttr = attr.dyn_cast<IntegerAttr>()) {
+      uint64_t limitedValue =
+          integerAttr.getValue().extractBitsAsZExtValue(bitWidth, 0);
+      switch (bitWidth) {
+        case 8:
+          return writeUint8(static_cast<uint8_t>(limitedValue));
+        case 16:
+          return writeUint16(static_cast<uint16_t>(limitedValue));
+        case 32:
+          return writeUint32(static_cast<uint32_t>(limitedValue));
+        case 64:
+          return writeUint64(static_cast<uint64_t>(limitedValue));
+        default:
+          return currentOp_->emitOpError()
+                 << "attribute of bitwidth " << bitWidth << " not supported";
+      }
+    } else {
+      return currentOp_->emitOpError()
+             << "attribute type not supported for primitive serialization: "
+             << attr;
     }
   }
 
-  LogicalResult encodeIntArrayAttr(DenseIntElementsAttr value) override {
+  LogicalResult encodePrimitiveArrayAttr(DenseElementsAttr value) override {
     if (value.getNumElements() > UINT16_MAX ||
         failed(writeUint16(value.getNumElements()))) {
       return currentOp_->emitOpError() << "integer array size out of bounds";
     }
     for (auto el : value.getAttributeValues()) {
-      if (failed(encodeIntAttr(el.cast<IntegerAttr>()))) {
+      if (failed(encodePrimitiveAttr(el))) {
         return currentOp_->emitOpError() << "failed to encode element " << el;
       }
     }