[VMVX] Added an implementation of fpowi in vm for vmvx backend (#24608)

We have found some models, which failed to compile to vmvx, due to a
missing conversion for math.fpowi.

This PR includes an implementation for this op, as well as an addition
of VM_ComplexArithmeticOp class, since fpowi has a float and int input.
This case was not covered by the VM_BinaryArithmeticOp class. However,
since we only found fpowi in models, which utilize a i64 const for the
exponent, the op was implemented in such a way that it only accepts i64
as an exponent. Due to this, it was not necessary to implement a
ComplexArithmeticOpConversion in
[MathToVM/Patterns.cpp](https://github.com/iree-org/iree/blob/main/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/Patterns.cpp).

I also expanded the tests in arithmetic_ops.mlir, to include a check for
vm.fpowi.f32.

Signed-off-by: Hakan Eyilmez
[eyilmez@roofline.ai](mailto:eyilmez@roofline.ai)

---------

Signed-off-by: Hakan Eyilmez <eyilmez@roofline.ai>
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/Patterns.cpp b/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/Patterns.cpp
index 48b8b3a..78ff73d 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/Patterns.cpp
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/Patterns.cpp
@@ -122,6 +122,11 @@
                                   IREE::VM::Log2F64Op>,
       BinaryArithmeticOpConversion<math::PowFOp, IREE::VM::PowF32Op,
                                    IREE::VM::PowF64Op>,
+      // Using BinaryArithmeticOp for fpowi op, since the int is
+      // coded to only be 64 bits, as this has been the only usecase
+      // so far. So a check of lhs (the float value) is sufficient.
+      BinaryArithmeticOpConversion<math::FPowIOp, IREE::VM::FPowI32Op,
+                                   IREE::VM::FPowI64Op>,
       UnaryArithmeticOpConversion<math::RsqrtOp, IREE::VM::RsqrtF32Op,
                                   IREE::VM::RsqrtF64Op>,
       UnaryArithmeticOpConversion<math::SqrtOp, IREE::VM::SqrtF32Op,
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/test/arithmetic_ops.mlir b/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/test/arithmetic_ops.mlir
index 209debe..97ec9bc 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/test/arithmetic_ops.mlir
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/MathToVM/test/arithmetic_ops.mlir
@@ -93,3 +93,14 @@
 
   return %20 : f32
 }
+
+// -----
+
+// CHECK-LABEL: @arithmetic_f32_i64
+func.func @arithmetic_f32_i64(%arg0: f32, %arg1: i64) -> f32 {
+
+  // CHECK: vm.fpowi.f32
+  %0 = math.fpowi %arg0, %arg1 : f32, i64
+
+  return %0 : f32
+}
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
index 740e317..13a1849 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
@@ -5120,6 +5120,7 @@
   ADD_GENERIC_PATTERN(IREE::VM::OrI32Op, "vm_or_i32");
   ADD_GENERIC_PATTERN(IREE::VM::OrI64Op, "vm_or_i64");
   ADD_GENERIC_PATTERN(IREE::VM::PowF32Op, "vm_pow_f32");
+  ADD_GENERIC_PATTERN(IREE::VM::FPowI32Op, "vm_fpowi_f32");
   ADD_GENERIC_PATTERN(IREE::VM::RemF32Op, "vm_rem_f32");
   ADD_GENERIC_PATTERN(IREE::VM::RemI32SOp, "vm_rem_i32s");
   ADD_GENERIC_PATTERN(IREE::VM::RemI32UOp, "vm_rem_i32u");
diff --git a/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF32.td b/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF32.td
index 0127acc..2ce466d 100644
--- a/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF32.td
+++ b/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF32.td
@@ -71,6 +71,7 @@
 def VM_OPC_SqrtF32               : VM_OPC<0x27, "SqrtF32">;
 def VM_OPC_TanhF32               : VM_OPC<0x28, "TanhF32">;
 def VM_OPC_ErfF32                : VM_OPC<0x29, "ErfF32">;
+def VM_OPC_FPowI32               : VM_OPC<0x3E, "FPowI32">;
 
 def VM_OPC_CmpEQF32O             : VM_OPC<0x2A, "CmpEQF32O">;
 def VM_OPC_CmpEQF32U             : VM_OPC<0x2B, "CmpEQF32U">;
@@ -148,6 +149,7 @@
     VM_OPC_SqrtF32,
     VM_OPC_TanhF32,
     VM_OPC_ErfF32,
+    VM_OPC_FPowI32,
 
     VM_OPC_CmpEQF32O,
     VM_OPC_CmpEQF32U,
diff --git a/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF64.td b/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF64.td
index c1230bd..ab6a1e0 100644
--- a/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF64.td
+++ b/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesF64.td
@@ -73,6 +73,7 @@
 def VM_OPC_SqrtF64               : VM_OPC<0x2D, "SqrtF64">;
 def VM_OPC_TanhF64               : VM_OPC<0x2E, "TanhF64">;
 def VM_OPC_ErfF64                : VM_OPC<0x2F, "ErfF64">;
+def VM_OPC_FPowI64               : VM_OPC<0x40, "FPowI64">;
 
 def VM_OPC_CmpEQF64O             : VM_OPC<0x30, "CmpEQF64O">;
 def VM_OPC_CmpEQF64U             : VM_OPC<0x31, "CmpEQF64U">;
@@ -153,6 +154,7 @@
     VM_OPC_SqrtF64,
     VM_OPC_TanhF64,
     VM_OPC_ErfF64,
+    VM_OPC_FPowI64,
 
     VM_OPC_CmpEQF64O,
     VM_OPC_CmpEQF64U,
diff --git a/compiler/src/iree/compiler/Dialect/VM/IR/VMOps.td b/compiler/src/iree/compiler/Dialect/VM/IR/VMOps.td
index 1082aea..09b1fab 100644
--- a/compiler/src/iree/compiler/Dialect/VM/IR/VMOps.td
+++ b/compiler/src/iree/compiler/Dialect/VM/IR/VMOps.td
@@ -2442,6 +2442,32 @@
   ];
 }
 
+class VM_MixedBinaryArithmeticOp<Type ltype, Type rtype, string mnemonic, VM_OPC opcode,
+                            list<Trait> traits = []> :
+    VM_TrivialOp<mnemonic, !listconcat(traits, [
+      DeclareOpInterfaceMethods<VM_SerializableOpInterface>,
+      AllTypesMatch<["lhs", "result"]>,
+    ])> {
+  let arguments = (ins
+    ltype:$lhs,
+    rtype:$rhs
+  );
+  let results = (outs
+    ltype:$result
+  );
+
+  let assemblyFormat = [{
+    operands attr-dict `:` type($result)
+  }];
+
+  let encoding = [
+    VM_EncOpcode<opcode>,
+    VM_EncOperand<"lhs", 0>,
+    VM_EncOperand<"rhs", 1>,
+    VM_EncResult<"result">,
+  ];
+}
+
 // Arithmetic ops defined for all inputs which do not exhibit UB.
 class VM_TotalUnaryArithmeticOp<Type type, string mnemonic, VM_OPC opcode,
                                 list<Trait> traits = []> :
@@ -2458,6 +2484,11 @@
     VM_TernaryArithmeticOp<type, mnemonic, opcode,
                            !listconcat(traits, [AlwaysSpeculatable])> {}
 
+class VM_TotalMixedBinaryArithmeticOp<Type ltype, Type rtype, string mnemonic, VM_OPC opcode,
+                                 list<Trait> traits = []> :
+    VM_MixedBinaryArithmeticOp<ltype, rtype, mnemonic, opcode,
+                          !listconcat(traits, [AlwaysSpeculatable])> {}
+
 } // OpGroupArithmeticHelperOps
 
 //===----------------------------------------------------------------------===//
@@ -2974,6 +3005,16 @@
   let summary = [{Floating point raised to the power of operation.}];
 }
 
+def VM_FPowI32Op :
+    VM_TotalMixedBinaryArithmeticOp<F32, I64, "fpowi.f32", VM_OPC_FPowI32, [VM_ExtF32]> {
+  let summary = [{Floating point raised to the integer power of operation.}];
+}
+
+def VM_FPowI64Op :
+    VM_TotalMixedBinaryArithmeticOp<F64, I64, "fpowi.f64", VM_OPC_FPowI64, [VM_ExtF64]> {
+  let summary = [{Floating point raised to the integer power of operation.}];
+}
+
 def VM_RsqrtF32Op :
     VM_TotalUnaryArithmeticOp<F32, "rsqrt.f32", VM_OPC_RsqrtF32, [VM_ExtF32]> {
   let summary = [{Reciprocal of sqrt (1 / sqrt of the specified value).}];
diff --git a/runtime/src/iree/base/internal/math.h b/runtime/src/iree/base/internal/math.h
index ef07cce..7afffed 100644
--- a/runtime/src/iree/base/internal/math.h
+++ b/runtime/src/iree/base/internal/math.h
@@ -641,4 +641,61 @@
   return biased < 0 ? 0 : biased > 0xFF ? 0xFF : biased;
 }
 
+//==============================================================================
+// FpowI support.
+//==============================================================================
+
+// The implementation of fpowi was copied from
+// https://github.com/llvm/llvm-project/blob/c7c340b41e4fe424f0f5f68be2f2812ac6d17b41/flang-rt/lib/runtime/complex-powi.cpp#L21
+// However, since <limits> is not included the min and max of the exponent
+// were coded in hexadecimal format
+#define IREE_MATH_FPOWI(TYPE, base, exp)                  \
+  if ((exp) == 0) {                                       \
+    return (TYPE)1.0;                                     \
+  }                                                       \
+  bool invertResult = (exp) < 0;                          \
+  bool isMin = ((exp) == 0x8000000000000000); /* -2^63 */ \
+  if (isMin) {                                            \
+    (exp) = 0x7FFFFFFFFFFFFFFF; /* 2^63 - 1 */            \
+  }                                                       \
+  if ((exp) < 0) {                                        \
+    (exp) = (exp) * -1;                                   \
+  }                                                       \
+  TYPE origBase = (base);                                 \
+  while (((exp) & 1) == 0) {                              \
+    (base) *= (base);                                     \
+    (exp) >>= 1;                                          \
+  }                                                       \
+  TYPE acc = (base);                                      \
+  while ((exp) > 1) {                                     \
+    (exp) >>= 1;                                          \
+    (base) *= (base);                                     \
+    if (((exp) & 1) == 1) {                               \
+      acc *= (base);                                      \
+    }                                                     \
+  }                                                       \
+  if (isMin) {                                            \
+    acc *= origBase;                                      \
+  }                                                       \
+  if (invertResult) {                                     \
+    acc = (TYPE)1.0 / acc;                                \
+  }                                                       \
+  return acc;
+
+static inline float iree_math_float_powi_i64(float base, int64_t exp) {
+#if defined(IREE_COMPILER_GCC_COMPAT)
+  return __builtin_powif(base, exp);
+#else
+  IREE_MATH_FPOWI(float, base, exp)
+#endif
+}
+
+static inline double iree_math_double_powi_i64(double base, int64_t exp) {
+#if defined(IREE_COMPILER_GCC_COMPAT)
+  return __builtin_powi(base, exp);
+#else
+  IREE_MATH_FPOWI(double, base, exp)
+#endif
+}
+
 #endif  // IREE_BASE_INTERNAL_MATH_H_
diff --git a/runtime/src/iree/base/internal/math_test.cc b/runtime/src/iree/base/internal/math_test.cc
index 222555d..34118dd 100644
--- a/runtime/src/iree/base/internal/math_test.cc
+++ b/runtime/src/iree/base/internal/math_test.cc
@@ -1080,4 +1080,26 @@
   EXPECT_TRUE(isnan(iree_math_f8e8m0fnu_to_f32(0xFF)));
 }
 
+//==============================================================================
+// FpowI support
+//==============================================================================
+
+TEST(FPowITests, iree_math_float_powi_i64) {
+  EXPECT_FLOAT_EQ(1.0f, iree_math_float_powi_i64(12.0f, 0));
+  EXPECT_FLOAT_EQ(1.0f, iree_math_float_powi_i64(1.0f, 4));
+  EXPECT_FLOAT_EQ(27.0f, iree_math_float_powi_i64(3.0f, 3));
+  EXPECT_FLOAT_EQ(0.25f, iree_math_float_powi_i64(2.0f, -2));
+  // No testcase for if iree_math_float_powi_i64 correctly behaves for
+  // exp = -2 ^ 63, since the resulting value is too large.
+}
+
+TEST(FPowITests, iree_math_double_powi_i64) {
+  EXPECT_DOUBLE_EQ(1.0, iree_math_double_powi_i64(12.0, 0));
+  EXPECT_DOUBLE_EQ(1.0, iree_math_double_powi_i64(1.0, 4));
+  EXPECT_DOUBLE_EQ(27.0, iree_math_double_powi_i64(3.0, 3));
+  EXPECT_DOUBLE_EQ(0.25, iree_math_double_powi_i64(2.0, -2));
+  // No testcase for if iree_math_double_powi_i64 correctly behaves for
+  // exp = -2 ^ 63, since the resulting value is too large.
+}
+
 }  // namespace
diff --git a/runtime/src/iree/vm/bytecode/disassembler.c b/runtime/src/iree/vm/bytecode/disassembler.c
index 811211e..29ce3d7 100644
--- a/runtime/src/iree/vm/bytecode/disassembler.c
+++ b/runtime/src/iree/vm/bytecode/disassembler.c
@@ -409,6 +409,22 @@
     break;                                                             \
   }
 
+#define IREE_VM_ISA_EMIT_OP_EXT_F32_MIXED_BINARY_F32(op_name, op_mnemonic) \
+  IREE_VM_ISA_EMIT_OP(EXT_F32, op_name) {                                  \
+    IREE_VM_ISA_DECODE_OPERAND_F32(lhs_reg);                               \
+    IREE_VM_ISA_DECODE_OPERAND_I64(rhs_reg);                               \
+    IREE_VM_ISA_DECODE_RESULT_F32(result_reg);                             \
+    IREE_VM_ISA_EMIT_F32_REG_NAME(result_reg);                             \
+    IREE_RETURN_IF_ERROR(                                                  \
+        iree_string_builder_append_format(b, " = %s ", op_mnemonic));      \
+    IREE_VM_ISA_EMIT_F32_REG_NAME(lhs_reg);                                \
+    IREE_VM_ISA_EMIT_OPTIONAL_VALUE_F32(regs->i32[lhs_reg]);               \
+    IREE_RETURN_IF_ERROR(iree_string_builder_append_cstring(b, ", "));     \
+    IREE_VM_ISA_EMIT_I64_REG_NAME(rhs_reg);                                \
+    IREE_VM_ISA_EMIT_OPTIONAL_VALUE_I64(regs->i32[rhs_reg]);               \
+    break;                                                                 \
+  }
+
 #define IREE_VM_ISA_EMIT_OP_EXT_F32_TERNARY_F32(op_name, op_mnemonic)  \
   IREE_VM_ISA_EMIT_OP(EXT_F32, op_name) {                              \
     IREE_VM_ISA_DECODE_OPERAND_F32(a_reg);                             \
@@ -457,6 +473,22 @@
     break;                                                             \
   }
 
+#define IREE_VM_ISA_EMIT_OP_EXT_F64_MIXED_BINARY_F64(op_name, op_mnemonic) \
+  IREE_VM_ISA_EMIT_OP(EXT_F64, op_name) {                                  \
+    IREE_VM_ISA_DECODE_OPERAND_F64(lhs_reg);                               \
+    IREE_VM_ISA_DECODE_OPERAND_I64(rhs_reg);                               \
+    IREE_VM_ISA_DECODE_RESULT_F64(result_reg);                             \
+    IREE_VM_ISA_EMIT_F64_REG_NAME(result_reg);                             \
+    IREE_RETURN_IF_ERROR(                                                  \
+        iree_string_builder_append_format(b, " = %s ", op_mnemonic));      \
+    IREE_VM_ISA_EMIT_F64_REG_NAME(lhs_reg);                                \
+    IREE_VM_ISA_EMIT_OPTIONAL_VALUE_F64(regs->i32[lhs_reg]);               \
+    IREE_RETURN_IF_ERROR(iree_string_builder_append_cstring(b, ", "));     \
+    IREE_VM_ISA_EMIT_I64_REG_NAME(rhs_reg);                                \
+    IREE_VM_ISA_EMIT_OPTIONAL_VALUE_I64(regs->i32[rhs_reg]);               \
+    break;                                                                 \
+  }
+
 #define IREE_VM_ISA_EMIT_OP_EXT_F64_TERNARY_F64(op_name, op_mnemonic)  \
   IREE_VM_ISA_EMIT_OP(EXT_F64, op_name) {                              \
     IREE_VM_ISA_DECODE_OPERAND_F64(a_reg);                             \
@@ -2144,6 +2176,7 @@
     IREE_VM_ISA_EMIT_OP_EXT_F32_UNARY_F32(Log1pF32, "vm.log1p.f32");
     IREE_VM_ISA_EMIT_OP_EXT_F32_UNARY_F32(Log2F32, "vm.log2.f32");
     IREE_VM_ISA_EMIT_OP_EXT_F32_BINARY_F32(PowF32, "vm.pow.f32");
+    IREE_VM_ISA_EMIT_OP_EXT_F32_MIXED_BINARY_F32(FPowI32, "vm.fpowi.f32");
     IREE_VM_ISA_EMIT_OP_EXT_F32_UNARY_F32(RsqrtF32, "vm.rsqrt.f32");
     IREE_VM_ISA_EMIT_OP_EXT_F32_UNARY_F32(SqrtF32, "vm.sqrt.f32");
     IREE_VM_ISA_EMIT_OP_EXT_F32_UNARY_F32(TanhF32, "vm.tanh.f32");
@@ -2533,6 +2566,7 @@
     IREE_VM_ISA_EMIT_OP_EXT_F64_UNARY_F64(Log1pF64, "vm.log1p.f64");
     IREE_VM_ISA_EMIT_OP_EXT_F64_UNARY_F64(Log2F64, "vm.log2.f64");
     IREE_VM_ISA_EMIT_OP_EXT_F64_BINARY_F64(PowF64, "vm.pow.f64");
+    IREE_VM_ISA_EMIT_OP_EXT_F64_MIXED_BINARY_F64(FPowI64, "vm.fpowi.f64");
     IREE_VM_ISA_EMIT_OP_EXT_F64_UNARY_F64(RsqrtF64, "vm.rsqrt.f64");
     IREE_VM_ISA_EMIT_OP_EXT_F64_UNARY_F64(SqrtF64, "vm.sqrt.f64");
     IREE_VM_ISA_EMIT_OP_EXT_F64_UNARY_F64(TanhF64, "vm.tanh.f64");
diff --git a/runtime/src/iree/vm/bytecode/dispatch.c b/runtime/src/iree/vm/bytecode/dispatch.c
index b2e50da..4d0a53c 100644
--- a/runtime/src/iree/vm/bytecode/dispatch.c
+++ b/runtime/src/iree/vm/bytecode/dispatch.c
@@ -2541,6 +2541,7 @@
       IREE_VM_ISA_DISPATCH_OP_EXT_F32_UNARY_F32(Log1pF32, vm_log1p_f32);
       IREE_VM_ISA_DISPATCH_OP_EXT_F32_UNARY_F32(Log2F32, vm_log2_f32);
       IREE_VM_ISA_DISPATCH_OP_EXT_F32_BINARY_F32(PowF32, vm_pow_f32);
+      IREE_VM_ISA_DISPATCH_OP_EXT_F32_MIXED_BINARY_F32(FPowI32, vm_fpowi_f32);
       IREE_VM_ISA_DISPATCH_OP_EXT_F32_UNARY_F32(RsqrtF32, vm_rsqrt_f32);
       IREE_VM_ISA_DISPATCH_OP_EXT_F32_UNARY_F32(SqrtF32, vm_sqrt_f32);
       IREE_VM_ISA_DISPATCH_OP_EXT_F32_UNARY_F32(TanhF32, vm_tanh_f32);
@@ -2832,6 +2833,7 @@
       IREE_VM_ISA_DISPATCH_OP_EXT_F64_UNARY_F64(Log1pF64, vm_log1p_f64);
       IREE_VM_ISA_DISPATCH_OP_EXT_F64_UNARY_F64(Log2F64, vm_log2_f64);
       IREE_VM_ISA_DISPATCH_OP_EXT_F64_BINARY_F64(PowF64, vm_pow_f64);
+      IREE_VM_ISA_DISPATCH_OP_EXT_F64_MIXED_BINARY_F64(FPowI64, vm_fpowi_f64);
       IREE_VM_ISA_DISPATCH_OP_EXT_F64_UNARY_F64(RsqrtF64, vm_rsqrt_f64);
       IREE_VM_ISA_DISPATCH_OP_EXT_F64_UNARY_F64(SqrtF64, vm_sqrt_f64);
       IREE_VM_ISA_DISPATCH_OP_EXT_F64_UNARY_F64(TanhF64, vm_tanh_f64);
diff --git a/runtime/src/iree/vm/bytecode/dispatch_util.h b/runtime/src/iree/vm/bytecode/dispatch_util.h
index a10acfb..7936b66 100644
--- a/runtime/src/iree/vm/bytecode/dispatch_util.h
+++ b/runtime/src/iree/vm/bytecode/dispatch_util.h
@@ -327,6 +327,14 @@
     *result = op_func(lhs, rhs);                                     \
   });
 
+#define IREE_VM_ISA_DISPATCH_OP_EXT_F32_MIXED_BINARY_F32(op_name, op_func) \
+  IREE_VM_ISA_DISPATCH_OP(EXT_F32, op_name, {                              \
+    IREE_VM_ISA_DISPATCH_DECODE_OPERAND_F32(lhs);                          \
+    IREE_VM_ISA_DISPATCH_DECODE_OPERAND_I64(rhs);                          \
+    IREE_VM_ISA_DISPATCH_DECODE_RESULT_F32(result);                        \
+    *result = op_func(lhs, rhs);                                           \
+  });
+
 #define IREE_VM_ISA_DISPATCH_OP_EXT_F32_TERNARY_F32(op_name, op_func) \
   IREE_VM_ISA_DISPATCH_OP(EXT_F32, op_name, {                         \
     IREE_VM_ISA_DISPATCH_DECODE_OPERAND_F32(a);                       \
@@ -351,6 +359,14 @@
     *result = op_func(lhs, rhs);                                     \
   });
 
+#define IREE_VM_ISA_DISPATCH_OP_EXT_F64_MIXED_BINARY_F64(op_name, op_func) \
+  IREE_VM_ISA_DISPATCH_OP(EXT_F64, op_name, {                              \
+    IREE_VM_ISA_DISPATCH_DECODE_OPERAND_F64(lhs);                          \
+    IREE_VM_ISA_DISPATCH_DECODE_OPERAND_I64(rhs);                          \
+    IREE_VM_ISA_DISPATCH_DECODE_RESULT_F64(result);                        \
+    *result = op_func(lhs, rhs);                                           \
+  });
+
 #define IREE_VM_ISA_DISPATCH_OP_EXT_F64_TERNARY_F64(op_name, op_func) \
   IREE_VM_ISA_DISPATCH_OP(EXT_F64, op_name, {                         \
     IREE_VM_ISA_DISPATCH_DECODE_OPERAND_F64(a);                       \
diff --git a/runtime/src/iree/vm/bytecode/isa/isa.json b/runtime/src/iree/vm/bytecode/isa/isa.json
index 207ae63..cb56d10 100644
--- a/runtime/src/iree/vm/bytecode/isa/isa.json
+++ b/runtime/src/iree/vm/bytecode/isa/isa.json
@@ -3305,6 +3305,13 @@
       "encoding": "i64_to_f32_conversion"
     },
     {
+      "opcode_set": "ext_f32",
+      "opcode": 62,
+      "symbol": "FPowI32",
+      "mnemonic": "fpowi.f32",
+      "encoding": "f32_binary"
+    },
+    {
       "opcode_set": "ext_f64",
       "opcode": 0,
       "symbol": "GlobalLoadF64",
@@ -3751,6 +3758,13 @@
       "symbol": "RoundF64Even",
       "mnemonic": "round.f64.even",
       "encoding": "f64_unary"
+    },
+    {
+      "opcode_set": "ext_f64",
+      "opcode": 64,
+      "symbol": "FPowI64",
+      "mnemonic": "fpowi.f64",
+      "encoding": "f64_binary"
     }
   ]
 }
diff --git a/runtime/src/iree/vm/bytecode/verifier.c b/runtime/src/iree/vm/bytecode/verifier.c
index 45b6118..496ad88 100644
--- a/runtime/src/iree/vm/bytecode/verifier.c
+++ b/runtime/src/iree/vm/bytecode/verifier.c
@@ -744,6 +744,13 @@
     IREE_VM_ISA_DECODE_RESULT_F32(result);                \
   });
 
+#define IREE_VM_ISA_VERIFY_OP_EXT_F32_MIXED_BINARY_F32(op_name) \
+  IREE_VM_ISA_VERIFY_OP(EXT_F32, op_name, {                     \
+    IREE_VM_ISA_DECODE_OPERAND_F32(lhs);                        \
+    IREE_VM_ISA_DECODE_OPERAND_I64(rhs);                        \
+    IREE_VM_ISA_DECODE_RESULT_F32(result);                      \
+  });
+
 #define IREE_VM_ISA_VERIFY_OP_EXT_F32_TERNARY_F32(op_name) \
   IREE_VM_ISA_VERIFY_OP(EXT_F32, op_name, {                \
     IREE_VM_ISA_DECODE_OPERAND_F32(a);                     \
@@ -765,6 +772,13 @@
     IREE_VM_ISA_DECODE_RESULT_F64(result);                \
   });
 
+#define IREE_VM_ISA_VERIFY_OP_EXT_F64_MIXED_BINARY_F64(op_name) \
+  IREE_VM_ISA_VERIFY_OP(EXT_F64, op_name, {                     \
+    IREE_VM_ISA_DECODE_OPERAND_F64(lhs);                        \
+    IREE_VM_ISA_DECODE_OPERAND_I64(rhs);                        \
+    IREE_VM_ISA_DECODE_RESULT_F64(result);                      \
+  });
+
 #define IREE_VM_ISA_VERIFY_OP_EXT_F64_TERNARY_F64(op_name) \
   IREE_VM_ISA_VERIFY_OP(EXT_F64, op_name, {                \
     IREE_VM_ISA_DECODE_OPERAND_F64(a);                     \
@@ -1849,6 +1863,7 @@
     IREE_VM_ISA_VERIFY_OP_EXT_F32_UNARY_F32(Log1pF32);
     IREE_VM_ISA_VERIFY_OP_EXT_F32_UNARY_F32(Log2F32);
     IREE_VM_ISA_VERIFY_OP_EXT_F32_BINARY_F32(PowF32);
+    IREE_VM_ISA_VERIFY_OP_EXT_F32_MIXED_BINARY_F32(FPowI32);
     IREE_VM_ISA_VERIFY_OP_EXT_F32_UNARY_F32(RsqrtF32);
     IREE_VM_ISA_VERIFY_OP_EXT_F32_UNARY_F32(SqrtF32);
     IREE_VM_ISA_VERIFY_OP_EXT_F32_UNARY_F32(TanhF32);
@@ -2059,6 +2074,7 @@
     IREE_VM_ISA_VERIFY_OP_EXT_F64_UNARY_F64(Log1pF64);
     IREE_VM_ISA_VERIFY_OP_EXT_F64_UNARY_F64(Log2F64);
     IREE_VM_ISA_VERIFY_OP_EXT_F64_BINARY_F64(PowF64);
+    IREE_VM_ISA_VERIFY_OP_EXT_F64_MIXED_BINARY_F64(FPowI64);
     IREE_VM_ISA_VERIFY_OP_EXT_F64_UNARY_F64(RsqrtF64);
     IREE_VM_ISA_VERIFY_OP_EXT_F64_UNARY_F64(SqrtF64);
     IREE_VM_ISA_VERIFY_OP_EXT_F64_UNARY_F64(TanhF64);
diff --git a/runtime/src/iree/vm/ops.h b/runtime/src/iree/vm/ops.h
index f9c5510..12bc753 100644
--- a/runtime/src/iree/vm/ops.h
+++ b/runtime/src/iree/vm/ops.h
@@ -587,6 +587,9 @@
 static inline float vm_log1p_f32(float operand) { return log1pf(operand); }
 static inline float vm_log2_f32(float operand) { return log2f(operand); }
 static inline float vm_pow_f32(float b, float e) { return powf(b, e); }
+static inline float vm_fpowi_f32(float base, int64_t exp) {
+  return iree_math_float_powi_i64(base, exp);
+}
 static inline float vm_rsqrt_f32(float operand) {
   return 1.0f / sqrtf(operand);
 }
@@ -780,6 +783,9 @@
 static inline double vm_log1p_f64(double operand) { return log1p(operand); }
 static inline double vm_log2_f64(double operand) { return log2(operand); }
 static inline double vm_pow_f64(double b, double e) { return pow(b, e); }
+static inline double vm_fpowi_f64(double base, int64_t exp) {
+  return iree_math_double_powi_i64(base, exp);
+}
 static inline double vm_rsqrt_f64(double operand) {
   return 1.0 / sqrt(operand);
 }
diff --git a/runtime/src/iree/vm/test/arithmetic_ops_f32.vmasm b/runtime/src/iree/vm/test/arithmetic_ops_f32.vmasm
index 8ec3542..4900332 100644
--- a/runtime/src/iree/vm/test/arithmetic_ops_f32.vmasm
+++ b/runtime/src/iree/vm/test/arithmetic_ops_f32.vmasm
@@ -398,6 +398,22 @@
   vm.fail %i0, "pow(3.0,2.0)=9.0"
 }
 
+vm.export @test_fpowi_f32
+vm.func @test_fpowi_f32() -> () {
+^bb0:
+  %i0 = vm.const.i32 8  // 0x00000008
+  %i1 = vm.const.f32 8
+  %i2:3 = vm.const.i64 3
+  %i4 = vm.const.f32 2
+  %i5 = vm.fpowi.f32 %i4, %i2
+  %i4 = vm.cmp.ne.f32.o %i5, %i1
+  vm.cond_br %i4, ^bb2(), ^bb1()
+^bb1:
+  vm.return
+^bb2:
+  vm.fail %i0, "fpowi(2.0,3)=8.0"
+}
+
 vm.export @test_rsqrt_f32
 vm.func @test_rsqrt_f32() -> () {
 ^bb0:
diff --git a/runtime/src/iree/vm/test/arithmetic_ops_f64.vmasm b/runtime/src/iree/vm/test/arithmetic_ops_f64.vmasm
index ce1a11c..a1e5d70 100644
--- a/runtime/src/iree/vm/test/arithmetic_ops_f64.vmasm
+++ b/runtime/src/iree/vm/test/arithmetic_ops_f64.vmasm
@@ -398,6 +398,22 @@
   vm.fail %i0, "pow(3.0,2.0)=9.0"
 }
 
+vm.export @test_fpowi_f64
+vm.func @test_fpowi_f64() -> () {
+^bb0:
+  %i0 = vm.const.i32 8  // 0x00000008
+  %i2:3 = vm.const.f64 8
+  %i4:5 = vm.const.i64 3
+  %i6:7 = vm.const.f64 2
+  %i4:5 = vm.fpowi.f64 %i6:7, %i4:5
+  %i1 = vm.cmp.ne.f64.o %i4:5, %i2:3
+  vm.cond_br %i1, ^bb2(), ^bb1()
+^bb1:
+  vm.return
+^bb2:
+  vm.fail %i0, "fpowi(2.0,3.0)=8.0"
+}
+
 vm.export @test_rsqrt_f64
 vm.func @test_rsqrt_f64() -> () {
 ^bb0: