Move macro instruction related info into machine_insn_info.h

Without separate machine_insn_info namespace

Bug: 399130034

Test: m berberis_all

Change-Id: I68a13fa53d8ff91f352f1a307eb605cd3369f87b
diff --git a/backend/include/berberis/backend/x86_64/machine_insn_intrinsics.h b/backend/include/berberis/backend/x86_64/machine_insn_intrinsics.h
index e5a48ef..b2cfe0d 100644
--- a/backend/include/berberis/backend/x86_64/machine_insn_intrinsics.h
+++ b/backend/include/berberis/backend/x86_64/machine_insn_intrinsics.h
@@ -58,33 +58,34 @@
 struct ConstructorArg;
 
 // Immediates expand into their class type.
-template <typename T>
-struct ConstructorArg<ArgTraits<T>, std::enable_if_t<ArgTraits<T>::Class::kIsImmediate, void>> {
-  using type = std::tuple<typename ArgTraits<T>::Class::Type>;
+template <typename T, typename O>
+struct ConstructorArg<ArgTraits<T, O>,
+                      std::enable_if_t<ArgTraits<T, O>::Class::kIsImmediate, void>> {
+  using type = std::tuple<typename ArgTraits<T, O>::Class::Type>;
 };
 
 // Mem ops expand into base register and disp.
-template <typename T>
-struct ConstructorArg<ArgTraits<T>,
-                      std::enable_if_t<!ArgTraits<T>::Class::kIsImmediate &&
-                                           ArgTraits<T>::RegisterClass::kAsRegister == 'm',
+template <typename T, typename O>
+struct ConstructorArg<ArgTraits<T, O>,
+                      std::enable_if_t<!ArgTraits<T, O>::Class::kIsImmediate &&
+                                           ArgTraits<T, O>::RegisterClass::kAsRegister == 'm',
                                        void>> {
-  static_assert(ArgTraits<T>::kUsage == intrinsics::bindings::kDefEarlyClobber);
+  static_assert(ArgTraits<T, O>::kUsage == intrinsics::bindings::kDefEarlyClobber);
   // Need to emit base register AND disp.
   using type = std::tuple<MachineReg, int32_t>;
 };
 
 // Everything else expands into a MachineReg.
-template <typename T>
-struct ConstructorArg<ArgTraits<T>,
-                      std::enable_if_t<!ArgTraits<T>::Class::kIsImmediate &&
-                                           ArgTraits<T>::RegisterClass::kAsRegister != 'm',
+template <typename T, typename O>
+struct ConstructorArg<ArgTraits<T, O>,
+                      std::enable_if_t<!ArgTraits<T, O>::Class::kIsImmediate &&
+                                           ArgTraits<T, O>::RegisterClass::kAsRegister != 'm',
                                        void>> {
   using type = std::tuple<MachineReg>;
 };
 
 template <typename T>
-using constructor_one_arg_t = typename ConstructorArg<ArgTraits<T>>::type;
+using constructor_one_arg_t = typename ConstructorArg<T>::type;
 
 // Use this alias to generate constructor Args from bindings.
 template <typename... T>
@@ -102,7 +103,7 @@
 using is_mem_t = is_mem_impl<T>;
 
 template <typename... Bindings>
-constexpr size_t mem_count_v = std::tuple_size_v<filter_t<is_mem_t, ArgTraits<Bindings>...>>;
+constexpr size_t mem_count_v = std::tuple_size_v<filter_t<is_mem_t, Bindings...>>;
 
 template <size_t N, typename... Bindings>
 constexpr bool has_n_mem_v = mem_count_v<Bindings...> > (N - 1);
@@ -136,7 +137,7 @@
   template <typename>
   struct GenMachineInsnInfoT;
   // We want to filter out any bindings that are not used for Register args.
-  using RegBindings = filter_t<has_reg_class_t, ArgTraits<Bindings>...>;
+  using RegBindings = filter_t<has_reg_class_t, Bindings...>;
 
  public:
   // This static simplifies constructing this MachineInsn in intrinsic implementations.
@@ -160,8 +161,8 @@
     std::string s(kMnemo);
     // Code below assumes that we have at most two memory operands.
     static_assert(([]<typename Binding> {
-                    if constexpr (!ArgTraits<Binding>::Class::kIsImmediate) {
-                      return ArgTraits<Binding>::Class::kAsRegister == 'm';
+                    if constexpr (!Binding::Class::kIsImmediate) {
+                      return Binding::Class::kAsRegister == 'm';
                     }
                     return false;
                   }.template operator()<Bindings>() +
@@ -173,7 +174,7 @@
           if (arg_idx > 0) {
             s += ", ";
           }
-          if constexpr (ArgTraits<Binding>::Class::kIsImmediate) {
+          if constexpr (Binding::Class::kIsImmediate) {
             // Without static_cast here compilation fails with extemely criptic error message:
             // error: implicit instantiation of undefined template 'berberis::InOutArg<0, 0, …>'
             //   …
@@ -184,7 +185,7 @@
             // Same below.
             s += GetImmOperandDebugString(static_cast<const MachineInsnX86_64*>(this));
             arg_idx++;
-          } else if constexpr (ArgTraits<Binding>::Class::kAsRegister == 'm') {
+          } else if constexpr (Binding::Class::kAsRegister == 'm') {
             if (disp_idx == 0) {
               s += GetBaseDispMemOperandDebugString(static_cast<const MachineInsnX86_64*>(this),
                                                     reg_idx);
@@ -196,7 +197,7 @@
                   disp2());
             }
             arg_idx++, reg_idx++, disp_idx++;
-          } else if constexpr (ArgTraits<Binding>::RegisterClass::kIsImplicitReg) {
+          } else if constexpr (Binding::RegisterClass::kIsImplicitReg) {
             s += GetImplicitRegOperandDebugString(static_cast<const MachineInsnX86_64*>(this),
                                                   reg_idx);
             arg_idx++, reg_idx++;
@@ -216,8 +217,8 @@
   void Emit(CodeEmitter* as) const override {
     // Code below assumes that we have at most two memory operands.
     static_assert(([]<typename Binding> {
-                    if constexpr (!ArgTraits<Binding>::Class::kIsImmediate) {
-                      return ArgTraits<Binding>::Class::kAsRegister == 'm';
+                    if constexpr (!Binding::Class::kIsImmediate) {
+                      return Binding::Class::kAsRegister == 'm';
                     }
                     return false;
                   }.template operator()<Bindings>() +
@@ -227,17 +228,16 @@
         kMacroInstruction,
         std::tuple_cat(
             std::tuple<CodeEmitter&>{*as}, [&reg_idx, &disp_idx, this]<typename Binding> {
-              if constexpr (ArgTraits<Binding>::Class::kIsImmediate) {
+              if constexpr (Binding::Class::kIsImmediate) {
                 return std::tuple{
                     static_cast<constructor_one_arg_t<Binding>>(MachineInsnX86_64::imm())};
-              } else if constexpr (ArgTraits<Binding>::RegisterClass::kAsRegister == 'x') {
+              } else if constexpr (Binding::RegisterClass::kAsRegister == 'x') {
                 return std::tuple{GetXReg(this->RegAt(reg_idx++))};
-              } else if constexpr (ArgTraits<Binding>::RegisterClass::kAsRegister == 'r' ||
-                                   ArgTraits<Binding>::RegisterClass::kAsRegister == 'q') {
+              } else if constexpr (Binding::RegisterClass::kAsRegister == 'r' ||
+                                   Binding::RegisterClass::kAsRegister == 'q') {
                 return std::tuple{GetGReg(this->RegAt(reg_idx++))};
-              } else if constexpr (ArgTraits<Binding>::RegisterClass::kAsRegister == 'm' &&
-                                   ArgTraits<Binding>::kUsage ==
-                                       intrinsics::bindings::kDefEarlyClobber) {
+              } else if constexpr (Binding::RegisterClass::kAsRegister == 'm' &&
+                                   Binding::kUsage == intrinsics::bindings::kDefEarlyClobber) {
                 disp_idx++;
                 if (disp_idx == 1) {
                   return std::tuple{Assembler::Operand{.base = GetGReg(this->RegAt(reg_idx++)),
@@ -246,7 +246,7 @@
                   return std::tuple{Assembler::Operand{.base = GetGReg(this->RegAt(reg_idx++)),
                                                        .disp = static_cast<int32_t>(disp2())}};
                 }
-              } else if constexpr (ArgTraits<Binding>::RegisterClass::kIsImplicitReg) {
+              } else if constexpr (Binding::RegisterClass::kIsImplicitReg) {
                 return std::tuple{};
               } else {
                 static_assert(kDependentTypeFalse<Binding>);
@@ -264,7 +264,7 @@
             typename... BindingsRest,
             typename T,
             typename... Args>
-  auto ProcessArgs(T arg, Args... args) -> std::enable_if_t<ArgTraits<B>::Class::kIsImmediate> {
+  auto ProcessArgs(T arg, Args... args) -> std::enable_if_t<B::Class::kIsImmediate> {
     this->set_imm(arg);
     ProcessArgs<reg_idx, disp_idx, BindingsRest...>(args...);
   }
@@ -275,8 +275,7 @@
             typename... BindingsRest,
             typename T,
             typename... Args>
-  auto ProcessArgs(T arg, Args... args)
-      -> std::enable_if_t<ArgTraits<B>::RegisterClass::kAsRegister != 'm'> {
+  auto ProcessArgs(T arg, Args... args) -> std::enable_if_t<B::RegisterClass::kAsRegister != 'm'> {
     static_assert(std::is_same_v<MachineReg, T>);
     this->SetRegAt(reg_idx, arg);
     ProcessArgs<reg_idx + 1, disp_idx, BindingsRest...>(args...);
@@ -289,10 +288,11 @@
             typename T1,
             typename T2,
             typename... Args>
-  auto ProcessArgs(T1 base, T2 disp, Args... args)
-      -> std::enable_if_t<ArgTraits<B>::RegisterClass::kAsRegister == 'm'> {
+  auto ProcessArgs(T1 base,
+                   T2 disp,
+                   Args... args) -> std::enable_if_t<B::RegisterClass::kAsRegister == 'm'> {
     // Only tmp memory args are supported.
-    static_assert(ArgTraits<B>::arg_info.arg_type == ArgInfo::TMP_ARG);
+    static_assert(B::arg_info.arg_type == ArgInfo::TMP_ARG);
     this->SetRegAt(reg_idx, base);
     if constexpr (disp_idx == 0) {
       this->set_disp(disp);
diff --git a/backend/x86_64/machine_insn_intrinsics_tests.cc b/backend/x86_64/machine_insn_intrinsics_tests.cc
index 958968f..afafb9d 100644
--- a/backend/x86_64/machine_insn_intrinsics_tests.cc
+++ b/backend/x86_64/machine_insn_intrinsics_tests.cc
@@ -17,46 +17,73 @@
 #include "gtest/gtest.h"
 
 #include "berberis/backend/x86_64/machine_insn_intrinsics.h"
-#include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h"
 #include "berberis/intrinsics/intrinsics_args.h"
+#include "berberis/intrinsics/intrinsics_bindings.h"
 
 namespace berberis {
 
 namespace {
 
 // TEST(MachineInsnIntrinsicsTest, HasNMem)
-static_assert(x86_64::has_n_mem_v<
-              1,
-              TmpArg<intrinsics::bindings::Mem32, intrinsics::bindings::kDefEarlyClobber>>);
+static_assert(
+    x86_64::has_n_mem_v<
+        1,
+        ArgTraits<TmpArg,
+                  intrinsics::bindings::OperandInfo<intrinsics::bindings::Mem32,
+                                                    intrinsics::bindings::kDefEarlyClobber>>>);
 static_assert(!x86_64::has_n_mem_v<1>);
-static_assert(!x86_64::has_n_mem_v<
-              1,
-              TmpArg<intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDefEarlyClobber>>);
-static_assert(x86_64::has_n_mem_v<2,
-                                  TmpArg<intrinsics::bindings::Mem32, intrinsics::bindings::kUse>,
-                                  TmpArg<intrinsics::bindings::Mem32, intrinsics::bindings::kDef>>);
-static_assert(!x86_64::has_n_mem_v<
-              2,
-              TmpArg<intrinsics::bindings::Mem32, intrinsics::bindings::kDefEarlyClobber>>);
+static_assert(
+    !x86_64::has_n_mem_v<
+        1,
+        ArgTraits<TmpArg,
+                  intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                    intrinsics::bindings::kDefEarlyClobber>>>);
+static_assert(
+    x86_64::has_n_mem_v<2,
+                        ArgTraits<TmpArg,
+                                  intrinsics::bindings::OperandInfo<intrinsics::bindings::Mem32,
+                                                                    intrinsics::bindings::kUse>>,
+                        ArgTraits<TmpArg,
+                                  intrinsics::bindings::OperandInfo<intrinsics::bindings::Mem32,
+                                                                    intrinsics::bindings::kDef>>>);
+static_assert(
+    !x86_64::has_n_mem_v<
+        2,
+        ArgTraits<TmpArg,
+                  intrinsics::bindings::OperandInfo<intrinsics::bindings::Mem32,
+                                                    intrinsics::bindings::kDefEarlyClobber>>>);
 
 // TEST(MachineInsnIntrinsicsTest, ConstructorArgs)
 static_assert(
-    std::is_same_v<x86_64::constructor_args_t<
-                       TmpArg<intrinsics::bindings::Mem64, intrinsics::bindings::kDefEarlyClobber>>,
+    std::is_same_v<x86_64::constructor_args_t<ArgTraits<
+                       TmpArg,
+                       intrinsics::bindings::OperandInfo<intrinsics::bindings::Mem64,
+                                                         intrinsics::bindings::kDefEarlyClobber>>>,
                    std::tuple<MachineReg, int32_t>>);
 static_assert(
-    std::is_same_v<x86_64::constructor_args_t<TmpArg<intrinsics::bindings::GeneralReg64,
-                                                     intrinsics::bindings::kDefEarlyClobber>>,
+    std::is_same_v<x86_64::constructor_args_t<ArgTraits<
+                       TmpArg,
+                       intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg64,
+                                                         intrinsics::bindings::kDefEarlyClobber>>>,
                    std::tuple<MachineReg>>);
-static_assert(std::is_same_v<x86_64::constructor_args_t<
-                                 InArg<0, intrinsics::bindings::Imm32, intrinsics::bindings::kUse>>,
-                             std::tuple<int32_t>>);
+static_assert(
+    std::is_same_v<x86_64::constructor_args_t<
+                       ArgTraits<InArg<0>,
+                                 intrinsics::bindings::OperandInfo<intrinsics::bindings::Imm32,
+                                                                   intrinsics::bindings::kUse>>>,
+                   std::tuple<int32_t>>);
 static_assert(
     std::is_same_v<
         x86_64::constructor_args_t<
-            InArg<0, intrinsics::bindings::Imm16, intrinsics::bindings::kUse>,
-            TmpArg<intrinsics::bindings::Mem64, intrinsics::bindings::kDefEarlyClobber>,
-            TmpArg<intrinsics::bindings::GeneralReg64, intrinsics::bindings::kDefEarlyClobber>>,
+            ArgTraits<InArg<0>,
+                      intrinsics::bindings::OperandInfo<intrinsics::bindings::Imm16,
+                                                        intrinsics::bindings::kUse>>,
+            ArgTraits<TmpArg,
+                      intrinsics::bindings::OperandInfo<intrinsics::bindings::Mem64,
+                                                        intrinsics::bindings::kDefEarlyClobber>>,
+            ArgTraits<TmpArg,
+                      intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg64,
+                                                        intrinsics::bindings::kDefEarlyClobber>>>,
         std::tuple<int16_t, MachineReg, int32_t, MachineReg>>);
 
 }  // namespace
diff --git a/heavy_optimizer/riscv64/inline_intrinsic.h b/heavy_optimizer/riscv64/inline_intrinsic.h
index c293500..92f173b 100644
--- a/heavy_optimizer/riscv64/inline_intrinsic.h
+++ b/heavy_optimizer/riscv64/inline_intrinsic.h
@@ -32,9 +32,9 @@
 #include "berberis/base/checks.h"
 #include "berberis/base/config.h"
 #include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h"
 #include "berberis/intrinsics/intrinsics.h"
 #include "berberis/intrinsics/intrinsics_args.h"
+#include "berberis/intrinsics/intrinsics_bindings.h"
 #include "berberis/intrinsics/intrinsics_process_bindings.h"
 #include "berberis/intrinsics/macro_assembler.h"
 #include "berberis/runtime_primitives/platform.h"
@@ -363,22 +363,23 @@
     return true;
   }
 
-  template <typename ArgBinding, typename IntrinsicBindingInfo>
-  auto /*MakeTuplefromBindingsClient*/ operator()(ArgTraits<ArgBinding>, IntrinsicBindingInfo) {
-    static constexpr const auto& arg_info = ArgTraits<ArgBinding>::arg_info;
+  template <typename ArgBinding, typename OperandInfo, typename IntrinsicBindingInfo>
+  auto /*MakeTuplefromBindingsClient*/ operator()(ArgTraits<ArgBinding, OperandInfo>,
+                                                  IntrinsicBindingInfo) {
+    static constexpr const auto& arg_info = ArgTraits<ArgBinding, OperandInfo>::arg_info;
     if constexpr (arg_info.arg_type == ArgInfo::IMM_ARG) {
       auto imm = std::get<arg_info.from>(input_args_);
       return std::tuple{imm};
     } else {
-      return ProcessArgInput<ArgBinding, IntrinsicBindingInfo>();
+      return ProcessArgInput<ArgBinding, OperandInfo, IntrinsicBindingInfo>();
     }
   }
 
-  template <typename ArgBinding, typename IntrinsicBindingInfo>
+  template <typename ArgBinding, typename OperandInfo, typename IntrinsicBindingInfo>
   auto ProcessArgInput() {
-    static constexpr const auto& arg_info = ArgTraits<ArgBinding>::arg_info;
-    using RegisterClass = typename ArgTraits<ArgBinding>::RegisterClass;
-    static constexpr auto kUsage = ArgTraits<ArgBinding>::kUsage;
+    static constexpr const auto& arg_info = ArgTraits<ArgBinding, OperandInfo>::arg_info;
+    using RegisterClass = typename ArgTraits<ArgBinding, OperandInfo>::RegisterClass;
+    static constexpr auto kUsage = ArgTraits<ArgBinding, OperandInfo>::kUsage;
     static constexpr auto kNumOut =
         std::tuple_size_v<typename IntrinsicBindingInfo::OutputArguments>;
 
@@ -400,7 +401,7 @@
       static_assert(!RegisterClass::kIsImplicitReg);
       if constexpr (RegisterClass::kAsRegister == 'x') {
         if constexpr (kNumOut > 1) {
-          static_assert(kDependentTypeFalse<ArgTraits<ArgBinding>>);
+          static_assert(kDependentTypeFalse<ArgTraits<ArgBinding, OperandInfo>>);
         } else {
           CHECK(xmm_result_reg_.IsInvalidReg());
           xmm_result_reg_ = AllocVReg();
@@ -421,7 +422,7 @@
       static_assert(kUsage == intrinsics::bindings::kUseDef);
       static_assert(RegisterClass::kIsImplicitReg);
       if constexpr (kNumOut > 1) {
-        static_assert(kDependentTypeFalse<ArgTraits<ArgBinding>>);
+        static_assert(kDependentTypeFalse<ArgTraits<ArgBinding, OperandInfo>>);
       } else {
         CHECK(implicit_result_reg_.IsInvalidReg());
         implicit_result_reg_ = AllocVReg();
@@ -440,7 +441,7 @@
       }
     } else if constexpr (arg_info.arg_type == ArgInfo::OUT_TMP_ARG) {
       if constexpr (kNumOut > 1) {
-        static_assert(kDependentTypeFalse<ArgTraits<ArgBinding>>);
+        static_assert(kDependentTypeFalse<ArgTraits<ArgBinding, OperandInfo>>);
       } else {
         CHECK(implicit_result_reg_.IsInvalidReg());
         implicit_result_reg_ = AllocVReg();
@@ -499,9 +500,9 @@
     using type = T;
   };
 
-  template <typename IntrinsicBindingInfo, typename... ArgBinding>
-  void ProcessBindingsResults(type_wrapper<std::tuple<ArgBinding...>>) {
-    (ProcessBindingResult<ArgBinding, IntrinsicBindingInfo>(), ...);
+  template <typename IntrinsicBindingInfo, typename... ArgBinding, typename... OperandInfo>
+  void ProcessBindingsResults(type_wrapper<std::tuple<ArgTraits<ArgBinding, OperandInfo>...>>) {
+    (ProcessBindingResult<ArgBinding, OperandInfo, IntrinsicBindingInfo>(), ...);
     if constexpr (std::tuple_size_v<typename IntrinsicBindingInfo::OutputArguments> == 0) {
       // No return value. Do nothing.
     } else if constexpr (std::tuple_size_v<typename IntrinsicBindingInfo::OutputArguments> == 1) {
@@ -529,13 +530,13 @@
     }
   }
 
-  template <typename ArgBinding, typename IntrinsicBindingInfo>
+  template <typename ArgBinding, typename OperandInfo, typename IntrinsicBindingInfo>
   void ProcessBindingResult() {
-    if constexpr (ArgTraits<ArgBinding>::Class::kIsImmediate) {
+    if constexpr (ArgTraits<ArgBinding, OperandInfo>::Class::kIsImmediate) {
       return;
     } else {
-      using RegisterClass = typename ArgTraits<ArgBinding>::RegisterClass;
-      static constexpr const auto& arg_info = ArgTraits<ArgBinding>::arg_info;
+      using RegisterClass = typename ArgTraits<ArgBinding, OperandInfo>::RegisterClass;
+      static constexpr const auto& arg_info = ArgTraits<ArgBinding, OperandInfo>::arg_info;
       if constexpr (RegisterClass::kAsRegister == 'm' || RegisterClass::kAsRegister == 0) {
         return;
       } else if constexpr ((arg_info.arg_type == ArgInfo::IN_OUT_ARG ||
diff --git a/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/intrinsics_bindings.h b/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/machine_insn_info.h
similarity index 82%
rename from intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/intrinsics_bindings.h
rename to intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/machine_insn_info.h
index d6e9fc7..06f2358 100644
--- a/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/intrinsics_bindings.h
+++ b/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/machine_insn_info.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef BERBERIS_INTRINSICS_COMMON_TO_RISCV_INTRINSICS_BINDINGS_H_
-#define BERBERIS_INTRINSICS_COMMON_TO_RISCV_INTRINSICS_BINDINGS_H_
+#ifndef BERBERIS_INTRINSICS_COMMON_TO_RISCV_MACHINE_INSN_INFO_H_
+#define BERBERIS_INTRINSICS_COMMON_TO_RISCV_MACHINE_INSN_INFO_H_
 
 #include <cstdint>
 
 #include "berberis/assembler/riscv.h"
-#include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/common/intrinsics_bindings.h"
-#include "berberis/intrinsics/intrinsics_args.h"
-#include "berberis/intrinsics/type_traits.h"
+#include "berberis/intrinsics/common/machine_insn_info.h"
 
 namespace berberis::intrinsics::bindings {
 
@@ -97,4 +94,4 @@
 
 }  // namespace berberis::intrinsics::bindings
 
-#endif  // BERBERIS_INTRINSICS_COMMON_TO_RISCV_INTRINSICS_BINDINGS_H_
+#endif  // BERBERIS_INTRINSICS_COMMON_TO_RISCV_MACHINE_INSN_INFO_H_
diff --git a/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/text_assembler_riscv.h b/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/text_assembler_riscv.h
index 3fc0ae2..b87b031 100644
--- a/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/text_assembler_riscv.h
+++ b/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/text_assembler_riscv.h
@@ -27,7 +27,8 @@
 #include "berberis/base/checks.h"
 #include "berberis/base/config.h"
 #include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/all_to_riscv64/intrinsics_bindings.h"
+#include "berberis/intrinsics/all_to_riscv64/machine_insn_info.h"
+#include "berberis/intrinsics/common/intrinsics_bindings.h"
 
 namespace berberis {
 
diff --git a/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/verifier_assembler_riscv.h b/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/verifier_assembler_riscv.h
index 9b5248c..fa78b0a 100644
--- a/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/verifier_assembler_riscv.h
+++ b/intrinsics/all_to_riscv64/include/berberis/intrinsics/all_to_riscv64/verifier_assembler_riscv.h
@@ -26,7 +26,7 @@
 #include "berberis/base/checks.h"
 #include "berberis/base/config.h"
 #include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/all_to_riscv64/intrinsics_bindings.h"
+#include "berberis/intrinsics/all_to_riscv64/machine_insn_info.h"
 #include "berberis/intrinsics/common/intrinsics_bindings.h"
 
 namespace berberis {
diff --git a/intrinsics/all_to_riscv64/include/berberis/intrinsics/intrinsics_bindings.h b/intrinsics/all_to_riscv64/include/berberis/intrinsics/intrinsics_bindings.h
new file mode 100644
index 0000000..9d67f35
--- /dev/null
+++ b/intrinsics/all_to_riscv64/include/berberis/intrinsics/intrinsics_bindings.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_INTRINSICS_INTRINSICS_BINDINGS_H_
+#define BERBERIS_INTRINSICS_INTRINSICS_BINDINGS_H_
+
+#include "berberis/intrinsics/common/intrinsics_bindings.h"
+#include "berberis/intrinsics/machine_insn_info.h"
+
+#endif  // BERBERIS_INTRINSICS_INTRINSICS_BINDINGS_H_
diff --git a/intrinsics/all_to_riscv64/include/berberis/intrinsics/machine_insn_info.h b/intrinsics/all_to_riscv64/include/berberis/intrinsics/machine_insn_info.h
new file mode 100644
index 0000000..09cd47c
--- /dev/null
+++ b/intrinsics/all_to_riscv64/include/berberis/intrinsics/machine_insn_info.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_INTRINSICS_MACHINE_INSN_INFO_H_
+#define BERBERIS_INTRINSICS_MACHINE_INSN_INFO_H_
+
+#include "berberis/intrinsics/all_to_riscv64/machine_insn_info.h"
+#include "berberis/intrinsics/common/machine_insn_info.h"
+
+#endif  // BERBERIS_INTRINSICS_MACHINE_INSN_INFO_H_
diff --git a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/machine_insn_info.h
similarity index 94%
rename from intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h
rename to intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/machine_insn_info.h
index 51cd64e..62eeb04 100644
--- a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h
+++ b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/machine_insn_info.h
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef BERBERIS_INTRINSICS_ALL_TO_X86_32_OR_x86_64_INTRINSICS_BINDINGS_H_
-#define BERBERIS_INTRINSICS_ALL_TO_X86_32_OR_x86_64_INTRINSICS_BINDINGS_H_
+#ifndef BERBERIS_INTRINSICS_ALL_TO_X86_32_OR_x86_64_MACHINE_INSN_INFO_H_
+#define BERBERIS_INTRINSICS_ALL_TO_X86_32_OR_x86_64_MACHINE_INSN_INFO_H_
 
 #include <xmmintrin.h>
 
 #include <cstdint>
 
-#include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/common/intrinsics_bindings.h"
-#include "berberis/intrinsics/intrinsics_args.h"
-#include "berberis/intrinsics/type_traits.h"
+#include "berberis/intrinsics/common/machine_insn_info.h"
 
 namespace berberis::intrinsics::bindings {
 
@@ -344,4 +341,4 @@
 
 }  // namespace berberis::intrinsics::bindings
 
-#endif  // BERBERIS_INTRINSICS_ALL_TO_X86_32_OR_x86_64_INTRINSICS_BINDINGS_H_
+#endif  // BERBERIS_INTRINSICS_ALL_TO_X86_32_OR_x86_64_MACHINE_INSN_INFO_H_
diff --git a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/text_assembler_x86_32_and_x86_64.h b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/text_assembler_x86_32_and_x86_64.h
index 15ea057..193e429 100644
--- a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/text_assembler_x86_32_and_x86_64.h
+++ b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/text_assembler_x86_32_and_x86_64.h
@@ -26,7 +26,7 @@
 #include "berberis/base/checks.h"
 #include "berberis/base/config.h"
 #include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h"
+#include "berberis/intrinsics/common/intrinsics_bindings.h"
 
 namespace berberis {
 
diff --git a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_x86_32_and_x86_64.h b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_x86_32_and_x86_64.h
index 35600bd..5a3ab3c 100644
--- a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_x86_32_and_x86_64.h
+++ b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_x86_32_and_x86_64.h
@@ -26,7 +26,7 @@
 #include "berberis/base/checks.h"
 #include "berberis/base/config.h"
 #include "berberis/base/dependent_false.h"
-#include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h"
+#include "berberis/intrinsics/all_to_x86_32_or_x86_64/machine_insn_info.h"
 #include "berberis/intrinsics/common/intrinsics_bindings.h"
 
 namespace berberis {
diff --git a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/intrinsics_bindings.h b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/intrinsics_bindings.h
index fe32c39..1f13a3f 100644
--- a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/intrinsics_bindings.h
+++ b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/intrinsics_bindings.h
@@ -17,6 +17,7 @@
 #ifndef BERBERIS_INTRINSICS_INTRINSICS_BINDINGS_H_
 #define BERBERIS_INTRINSICS_INTRINSICS_BINDINGS_H_
 
-#include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h"
+#include "berberis/intrinsics/common/intrinsics_bindings.h"
+#include "berberis/intrinsics/machine_insn_info.h"
 
 #endif  // BERBERIS_INTRINSICS_INTRINSICS_BINDINGS_H_
diff --git a/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/machine_insn_info.h b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/machine_insn_info.h
new file mode 100644
index 0000000..41c7b2a
--- /dev/null
+++ b/intrinsics/all_to_x86_32_or_x86_64/include/berberis/intrinsics/machine_insn_info.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_INTRINSICS_MACHINE_INSN_INFO_H_
+#define BERBERIS_INTRINSICS_MACHINE_INSN_INFO_H_
+
+#include "berberis/intrinsics/all_to_x86_32_or_x86_64/machine_insn_info.h"
+#include "berberis/intrinsics/common/machine_insn_info.h"
+
+#endif  // BERBERIS_INTRINSICS_MACHINE_INSN_INFO_H_
diff --git a/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_test.cc b/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_test.cc
index cfb80b5..49038f6 100644
--- a/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_test.cc
+++ b/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_test.cc
@@ -16,9 +16,10 @@
 
 #include "gtest/gtest.h"
 
-#include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_bindings.h"
 #include "berberis/intrinsics/all_to_x86_32_or_x86_64/intrinsics_float.h"
+#include "berberis/intrinsics/all_to_x86_32_or_x86_64/machine_insn_info.h"
 #include "berberis/intrinsics/all_to_x86_32_or_x86_64/verifier_assembler_x86_32_and_x86_64.h"
+#include "berberis/intrinsics/common/intrinsics_bindings.h"
 
 namespace berberis {
 
@@ -194,8 +195,12 @@
       false,
       std::tuple<SIMD128Register, SIMD128Register>,
       std::tuple<SIMD128Register>,
-      std::tuple<InOutArg<0, 0, intrinsics::bindings::XmmReg, intrinsics::bindings::kDef>,
-                 InArg<1, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>>>;
+      std::tuple<ArgTraits<InOutArg<0, 0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<1>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kUse>>>>;
 
   VerifyIntrinsic<IntrinsicBindingInfo>();
 }
@@ -211,8 +216,12 @@
       false,
       std::tuple<SIMD128Register, SIMD128Register>,
       std::tuple<SIMD128Register>,
-      std::tuple<InOutArg<0, 0, intrinsics::bindings::XmmReg, intrinsics::bindings::kDef>,
-                 InArg<1, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>>>;
+      std::tuple<ArgTraits<InOutArg<0, 0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<1>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kUse>>>>;
 
   ASSERT_DEATH(VerifyIntrinsic<IntrinsicBindingInfo>(), "error: expect_sse3 != need_sse3");
 }
@@ -229,9 +238,15 @@
       std::tuple<uint32_t, uint32_t>,
       std::tuple<uint32_t>,
       std::tuple<
-          OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDefEarlyClobber>,
-          InOutArg<1, 1, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUseDef>,
-          InArg<2, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>>>;
+          ArgTraits<OutArg<0>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                      intrinsics::bindings::kDefEarlyClobber>>,
+          ArgTraits<InOutArg<1, 1>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                      intrinsics::bindings::kUseDef>>,
+          ArgTraits<InArg<2>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                      intrinsics::bindings::kUse>>>>;
 
   ASSERT_DEATH(VerifyIntrinsic<IntrinsicBindingInfo>(), "error: expect_flags != defines_flags");
 }
@@ -247,10 +262,19 @@
       false,
       std::tuple<SIMD128Register, SIMD128Register>,
       std::tuple<SIMD128Register>,
-      std::tuple<OutArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kDefEarlyClobber>,
-                 InArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>,
-                 InArg<1, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>,
-                 TmpArg<intrinsics::bindings::FLAGS, intrinsics::bindings::kDef>>>;
+      std::tuple<
+          ArgTraits<OutArg<0>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                      intrinsics::bindings::kDefEarlyClobber>>,
+          ArgTraits<InArg<0>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                      intrinsics::bindings::kUse>>,
+          ArgTraits<InArg<1>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                      intrinsics::bindings::kUse>>,
+          ArgTraits<TmpArg,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::FLAGS,
+                                                      intrinsics::bindings::kDef>>>>;
 
   ASSERT_DEATH(VerifyIntrinsic<IntrinsicBindingInfo>(), "error: expect_flags != defines_flags");
 }
@@ -267,10 +291,18 @@
       std::tuple<uint32_t, uint32_t>,
       std::tuple<uint32_t>,
       std::tuple<
-          OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDefEarlyClobber>,
-          InOutArg<1, 1, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUseDef>,
-          InArg<2, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>,
-          TmpArg<intrinsics::bindings::FLAGS, intrinsics::bindings::kDef>>>;
+          ArgTraits<OutArg<0>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                      intrinsics::bindings::kDefEarlyClobber>>,
+          ArgTraits<InOutArg<1, 1>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                      intrinsics::bindings::kUseDef>>,
+          ArgTraits<InArg<2>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                      intrinsics::bindings::kUse>>,
+          ArgTraits<TmpArg,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::FLAGS,
+                                                      intrinsics::bindings::kDef>>>>;
 
   VerifyIntrinsic<IntrinsicBindingInfo>();
 }
@@ -286,10 +318,18 @@
       false,
       std::tuple<uint32_t, uint32_t>,
       std::tuple<uint32_t>,
-      std::tuple<OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDef>,
-                 InOutArg<1, 1, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUseDef>,
-                 InArg<2, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>,
-                 TmpArg<intrinsics::bindings::FLAGS, intrinsics::bindings::kDef>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InOutArg<1, 1>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kUseDef>>,
+                 ArgTraits<InArg<2>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kUse>>,
+                 ArgTraits<TmpArg,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::FLAGS,
+                                                             intrinsics::bindings::kDef>>>>;
 
   ASSERT_DEATH(
       VerifyIntrinsic<IntrinsicBindingInfo>(),
@@ -307,9 +347,16 @@
       false,
       std::tuple<SIMD128Register, SIMD128Register>,
       std::tuple<SIMD128Register>,
-      std::tuple<OutArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kDefEarlyClobber>,
-                 InArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>,
-                 InArg<1, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>>>;
+      std::tuple<
+          ArgTraits<OutArg<0>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                      intrinsics::bindings::kDefEarlyClobber>>,
+          ArgTraits<InArg<0>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                      intrinsics::bindings::kUse>>,
+          ArgTraits<InArg<1>,
+                    intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                      intrinsics::bindings::kUse>>>>;
 
   VerifyIntrinsic<IntrinsicBindingInfo>();
 }
@@ -325,9 +372,15 @@
       false,
       std::tuple<SIMD128Register, SIMD128Register>,
       std::tuple<SIMD128Register>,
-      std::tuple<OutArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kDef>,
-                 InArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>,
-                 InArg<1, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kUse>>,
+                 ArgTraits<InArg<1>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kUse>>>>;
 
   ASSERT_DEATH(VerifyIntrinsic<IntrinsicBindingInfo>(),
                "error: intrinsic used a 'use' xmm register after writing to a 'def' xmm register");
@@ -344,8 +397,12 @@
       false,
       std::tuple<uint32_t>,
       std::tuple<uint32_t>,
-      std::tuple<OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDef>,
-                 InArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kUse>>>>;
 
   VerifyIntrinsic<IntrinsicBindingInfo>();
 }
@@ -361,9 +418,15 @@
       false,
       std::tuple<uint32_t>,
       std::tuple<uint32_t>,
-      std::tuple<OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDef>,
-                 InArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>,
-                 TmpArg<intrinsics::bindings::FLAGS, intrinsics::bindings::kDef>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kUse>>,
+                 ArgTraits<TmpArg,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::FLAGS,
+                                                             intrinsics::bindings::kDef>>>>;
 
   ASSERT_DEATH(
       VerifyIntrinsic<IntrinsicBindingInfo>(),
@@ -381,9 +444,15 @@
       false,
       std::tuple<uint32_t>,
       std::tuple<uint32_t>,
-      std::tuple<OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDef>,
-                 InArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>,
-                 TmpArg<intrinsics::bindings::FLAGS, intrinsics::bindings::kDef>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kUse>>,
+                 ArgTraits<TmpArg,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::FLAGS,
+                                                             intrinsics::bindings::kDef>>>>;
 
   VerifyIntrinsic<IntrinsicBindingInfo>();
 }
@@ -399,9 +468,15 @@
       false,
       std::tuple<uint32_t>,
       std::tuple<uint32_t>,
-      std::tuple<OutArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kDef>,
-                 InArg<0, intrinsics::bindings::GeneralReg32, intrinsics::bindings::kUse>,
-                 TmpArg<intrinsics::bindings::FLAGS, intrinsics::bindings::kDef>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::GeneralReg32,
+                                                             intrinsics::bindings::kUse>>,
+                 ArgTraits<TmpArg,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::FLAGS,
+                                                             intrinsics::bindings::kDef>>>>;
 
   ASSERT_DEATH(
       VerifyIntrinsic<IntrinsicBindingInfo>(),
@@ -419,8 +494,12 @@
       false,
       std::tuple<SIMD128Register>,
       std::tuple<SIMD128Register>,
-      std::tuple<OutArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kDef>,
-                 InArg<0, intrinsics::bindings::XmmReg, intrinsics::bindings::kUse>>>;
+      std::tuple<ArgTraits<OutArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kDef>>,
+                 ArgTraits<InArg<0>,
+                           intrinsics::bindings::OperandInfo<intrinsics::bindings::XmmReg,
+                                                             intrinsics::bindings::kUse>>>>;
 
   ASSERT_DEATH(VerifyIntrinsic<IntrinsicBindingInfo>(),
                "error: intrinsic used a 'use' xmm register after writing to a 'def' xmm register");
diff --git a/intrinsics/gen_intrinsics.py b/intrinsics/gen_intrinsics.py
index b7ad0fe..d3d95ca 100755
--- a/intrinsics/gen_intrinsics.py
+++ b/intrinsics/gen_intrinsics.py
@@ -886,12 +886,13 @@
   if info_prefix is None:
     class_info = ''
   else:
-    class_info = ', %s::%s' % (info_prefix, arg['class'])
+    class_info = '%s::%s' % (info_prefix, arg['class'])
   if arg['class'] == 'Imm8':
     if  info_prefix is None:
       return 'ImmArg<%d, int8_t>' % (arg['ir_arg'])
     else:
-      return 'ImmArg<%d, int8_t%s>' % (arg['ir_arg'], class_info)
+      return 'ImmArg<%d>, intrinsics::bindings::OperandInfo<%s, %s::kUse>' % (
+         arg['ir_arg'], class_info, info_prefix)
   if info_prefix is None:
     using_info = ''
   else:
@@ -901,34 +902,37 @@
         'use': 'kUse',
         'use_def': 'kUseDef'
     }[arg['usage']])
+  if info_prefix is None:
+    operand_info = ''
+  else:
+    operand_info = ', intrinsics::bindings::OperandInfo<%s%s>' % (
+        class_info, using_info)
   if arg['usage'] == 'use':
     if need_tmp:
-      return 'InTmpArg<%d%s%s>' % (arg['ir_arg'], class_info, using_info)
-    return 'InArg<%d%s%s>' % (arg['ir_arg'], class_info, using_info)
+      return 'InTmpArg<%d>%s' % (arg['ir_arg'], operand_info)
+    return 'InArg<%d>%s' % (arg['ir_arg'], operand_info)
   if arg['usage'] in ('def', 'def_early_clobber'):
     assert 'ir_arg' not in arg
     if 'ir_res' in arg:
       if need_tmp:
-        return 'OutTmpArg<%d%s%s>' % (arg['ir_res'], class_info, using_info)
-      return 'OutArg<%d%s%s>' % (arg['ir_res'], class_info, using_info)
-    if info_prefix is None:
-      return 'TmpArg'
-    else:
-      return 'TmpArg<%s%s>' % (class_info[2:], using_info)
+        return 'OutTmpArg<%d>%s' % (arg['ir_res'], operand_info)
+      return 'OutArg<%d>%s' % (arg['ir_res'], operand_info)
+    return 'TmpArg%s' % operand_info
   if arg['usage'] == 'use_def':
     if 'ir_res' in arg:
       if need_tmp:
-        return 'InOutTmpArg<%s, %s%s%s>' % (arg['ir_arg'], arg['ir_res'],
-                                                class_info, using_info)
-      return 'InOutArg<%s, %s%s%s>' % (arg['ir_arg'], arg['ir_res'],
-                                           class_info, using_info)
-    return 'InTmpArg<%s%s%s>' % (arg['ir_arg'], class_info, using_info)
+        return 'InOutTmpArg<%s, %s>%s' % (
+          arg['ir_arg'], arg['ir_res'], operand_info)
+      return 'InOutArg<%s, %s>%s' % (
+          arg['ir_arg'], arg['ir_res'], operand_info)
+    return 'InTmpArg<%s>%s' % (
+        arg['ir_arg'], operand_info)
   assert False, 'unknown operand usage %s' % (arg['usage'])
 
 
 def _get_reg_operands_info(args, info_prefix=None):
   return 'std::tuple<%s>' % ', '.join(
-    _get_reg_operand_info(arg, info_prefix)
+    'ArgTraits<%s>' % _get_reg_operand_info(arg, info_prefix)
     for arg in args)
 
 
diff --git a/intrinsics/include/berberis/intrinsics/common/intrinsics_bindings.h b/intrinsics/include/berberis/intrinsics/common/intrinsics_bindings.h
index 55f0a69..bb67b8c 100644
--- a/intrinsics/include/berberis/intrinsics/common/intrinsics_bindings.h
+++ b/intrinsics/include/berberis/intrinsics/common/intrinsics_bindings.h
@@ -20,6 +20,7 @@
 #include <cstdint>
 
 #include "berberis/base/dependent_false.h"
+#include "berberis/intrinsics/common/machine_insn_info.h"
 #include "berberis/intrinsics/intrinsics_args.h"
 #include "berberis/intrinsics/type_traits.h"
 
@@ -27,49 +28,6 @@
 
 namespace intrinsics::bindings {
 
-class FLAGS {
- public:
-  static constexpr bool kIsImmediate = false;
-  static constexpr bool kIsImplicitReg = true;
-  static constexpr char kAsRegister = 0;
-  template <typename MachineInsnArch>
-  static constexpr auto kRegClass = MachineInsnArch::kFLAGS;
-};
-
-class Mem8 {
- public:
-  using Type = uint8_t;
-  static constexpr bool kIsImmediate = false;
-  static constexpr char kAsRegister = 'm';
-};
-
-class Mem16 {
- public:
-  using Type = uint16_t;
-  static constexpr bool kIsImmediate = false;
-  static constexpr char kAsRegister = 'm';
-};
-
-class Mem32 {
- public:
-  using Type = uint32_t;
-  static constexpr bool kIsImmediate = false;
-  static constexpr char kAsRegister = 'm';
-};
-
-class Mem64 {
- public:
-  using Type = uint64_t;
-  static constexpr bool kIsImmediate = false;
-  static constexpr char kAsRegister = 'm';
-};
-
-enum RegBindingKind { kDef = 2, kDefEarlyClobber = 3, kUse = 5, kUseDef = 7 };
-
-// Tag classes. They are never instantioned, only used as tags to pass information about
-// bindings.
-class NoCPUIDRestriction;  // All CPUs have at least “no CPUID restriction” mode.
-
 // Tag classes. They are never instantioned, only used as tags to pass information about
 // bindings.
 class NoNansOperation;
@@ -121,15 +79,15 @@
       TypeTraits<OutputArgumentsTypes>::kName...};
   template <typename Callback, typename... Args>
   constexpr static void ProcessBindings(Callback&& callback, Args&&... args) {
-    (callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...), ...);
+    (callback(BindingsTypes(), std::forward<Args>(args)...), ...);
   }
   template <typename Callback, typename... Args>
   constexpr static bool VerifyBindings(Callback&& callback, Args&&... args) {
-    return (callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...) && ...);
+    return (callback(BindingsTypes(), std::forward<Args>(args)...) && ...);
   }
   template <typename Callback, typename... Args>
   constexpr static auto MakeTuplefromBindings(Callback&& callback, Args&&... args) {
-    return std::tuple_cat(callback(ArgTraits<BindingsTypes>(), std::forward<Args>(args)...)...);
+    return std::tuple_cat(callback(BindingsTypes(), std::forward<Args>(args)...)...);
   }
   using InputArguments = std::tuple<InputArgumentsTypes...>;
   using OutputArguments = std::tuple<OutputArgumentsTypes...>;
diff --git a/intrinsics/include/berberis/intrinsics/common/machine_insn_info.h b/intrinsics/include/berberis/intrinsics/common/machine_insn_info.h
new file mode 100644
index 0000000..c62fb64
--- /dev/null
+++ b/intrinsics/include/berberis/intrinsics/common/machine_insn_info.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_INTRINSICS_COMMON_MACHINE_INSN_INFO_H_
+#define BERBERIS_INTRINSICS_COMMON_MACHINE_INSN_INFO_H_
+
+#include <cstdint>
+
+namespace berberis::intrinsics::bindings {
+
+class FLAGS {
+ public:
+  static constexpr bool kIsImmediate = false;
+  static constexpr bool kIsImplicitReg = true;
+  static constexpr char kAsRegister = 0;
+  template <typename MachineInsnArch>
+  static constexpr auto kRegClass = MachineInsnArch::kFLAGS;
+};
+
+class Mem8 {
+ public:
+  using Type = uint8_t;
+  static constexpr bool kIsImmediate = false;
+  static constexpr char kAsRegister = 'm';
+};
+
+class Mem16 {
+ public:
+  using Type = uint16_t;
+  static constexpr bool kIsImmediate = false;
+  static constexpr char kAsRegister = 'm';
+};
+
+class Mem32 {
+ public:
+  using Type = uint32_t;
+  static constexpr bool kIsImmediate = false;
+  static constexpr char kAsRegister = 'm';
+};
+
+class Mem64 {
+ public:
+  using Type = uint64_t;
+  static constexpr bool kIsImmediate = false;
+  static constexpr char kAsRegister = 'm';
+};
+
+// Note: value of RegBindingKind and MachineRegKind have to be the same since we convert one to
+// another with a static_cast in berberis/backend/x86_64/machine_insn_intrinsics.h. We don't care
+// about these values in intrinsics module, but for optimizations it's important to have LSB set
+// when an instruction uses the value (which is true for kDefEarlyClobber: in that case the
+// instruction sets the value and then uses it), the next bit is set when register is output and MSB
+// bit is set when register is input. We have static_assert in the aforemetioned header that ensures
+// that an attempt to change these two enums and make them different would lead to a compile-time
+// error.
+enum RegBindingKind { kDef = 2, kDefEarlyClobber = 3, kUse = 5, kUseDef = 7 };
+
+template <typename RegisterClassTemplateName, RegBindingKind kUsageTemplateName>
+class OperandInfo {
+ public:
+  using RegisterClass = RegisterClassTemplateName;
+  static constexpr RegBindingKind kUsage = kUsageTemplateName;
+  static_assert(!RegisterClass::kIsImmediate || kUsage == kUse);
+};
+
+// Tag classes. They are never instantioned, only used as tags to pass information about
+// bindings.
+class NoCPUIDRestriction;  // All CPUs have at least “no CPUID restriction” mode.
+
+}  // namespace berberis::intrinsics::bindings
+
+#endif  // BERBERIS_INTRINSICS_COMMON_MACHINE_INSN_INFO_H_
diff --git a/intrinsics/include/berberis/intrinsics/intrinsics_args.h b/intrinsics/include/berberis/intrinsics/intrinsics_args.h
index 758556b..76f46c1 100644
--- a/intrinsics/include/berberis/intrinsics/intrinsics_args.h
+++ b/intrinsics/include/berberis/intrinsics/intrinsics_args.h
@@ -21,6 +21,7 @@
 #include <cstdio>
 
 #include "berberis/base/checks.h"
+#include "berberis/intrinsics/common/machine_insn_info.h"
 
 namespace berberis {
 
@@ -118,35 +119,34 @@
   const int to = 0;
 };
 
-template <int N, typename RegisterClass, auto kUsage>
+template <int N>
 class InArg;
 
-template <int N, typename RegisterClass, auto kUsage>
+template <int N>
 class OutArg;
 
-template <int N, typename RegisterClass, auto kUsage>
+template <int N>
 class OutTmpArg;
 
-template <int N, int M, typename RegisterClass, auto kUsage>
+template <int N, int M>
 class InOutArg;
 
-template <int N, int M, typename RegisterClass, auto kUsage>
+template <int N, int M>
 class InOutTmpArg;
 
-template <int N, typename RegisterClass, auto kUsage>
+template <int N>
 class InTmpArg;
 
-template <int N, typename ImmType, typename ImmediateClass>
+template <int N, typename ImmType = void>
 class ImmArg;
 
-template <typename RegisterClass, auto kUsage>
 class TmpArg;
 
-template <typename ArgInfo>
+template <typename ArgInfo, typename OperandInfo>
 class ArgTraits;
 
 template <int N, typename RegisterClassType, auto kUsageParam>
-class ArgTraits<InArg<N, RegisterClassType, kUsageParam>> {
+class ArgTraits<InArg<N>, intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -155,7 +155,7 @@
 };
 
 template <int N, typename RegisterClassType, auto kUsageParam>
-class ArgTraits<OutArg<N, RegisterClassType, kUsageParam>> {
+class ArgTraits<OutArg<N>, intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -164,7 +164,7 @@
 };
 
 template <int N, typename RegisterClassType, auto kUsageParam>
-class ArgTraits<OutTmpArg<N, RegisterClassType, kUsageParam>> {
+class ArgTraits<OutTmpArg<N>, intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -173,7 +173,7 @@
 };
 
 template <int N, int M, typename RegisterClassType, auto kUsageParam>
-class ArgTraits<InOutArg<N, M, RegisterClassType, kUsageParam>> {
+class ArgTraits<InOutArg<N, M>, intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -182,7 +182,8 @@
 };
 
 template <int N, int M, typename RegisterClassType, auto kUsageParam>
-class ArgTraits<InOutTmpArg<N, M, RegisterClassType, kUsageParam>> {
+class ArgTraits<InOutTmpArg<N, M>,
+                intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -191,7 +192,7 @@
 };
 
 template <int N, typename RegisterClassType, auto kUsageParam>
-class ArgTraits<InTmpArg<N, RegisterClassType, kUsageParam>> {
+class ArgTraits<InTmpArg<N>, intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -199,8 +200,9 @@
   static constexpr ArgInfo arg_info{.arg_type = ArgInfo::IN_TMP_ARG, .from = N};
 };
 
-template <int N, typename ImmType, typename ImmediateClassType>
-class ArgTraits<ImmArg<N, ImmType, ImmediateClassType>> {
+template <int N, typename ImmediateClassType>
+class ArgTraits<ImmArg<N>,
+                intrinsics::bindings::OperandInfo<ImmediateClassType, intrinsics::bindings::kUse>> {
  public:
   using Class = ImmediateClassType;
   using ImmediateClass = ImmediateClassType;
@@ -208,7 +210,7 @@
 };
 
 template <typename RegisterClassType, auto kUsageParam>
-class ArgTraits<TmpArg<RegisterClassType, kUsageParam>> {
+class ArgTraits<TmpArg, intrinsics::bindings::OperandInfo<RegisterClassType, kUsageParam>> {
  public:
   using Class = RegisterClassType;
   using RegisterClass = RegisterClassType;
@@ -274,9 +276,9 @@
   return true;
 }
 
-template <typename MachineInsn, typename... Args>
+template <typename MachineInsn, typename... Args, typename... Operands>
 constexpr bool IsCompatible() {
-  const ArgInfo arguments[] = {ArgTraits<Args>::arg_info...};
+  const ArgInfo arguments[] = {ArgTraits<Args, Operands>::arg_info...};
   // Note: we couldn't pass arguments as an array into IsCompatible by reference
   // because this would cause compilation error in case where we have no arguments.
   //
diff --git a/lite_translator/riscv64_to_x86_64/inline_intrinsic.h b/lite_translator/riscv64_to_x86_64/inline_intrinsic.h
index deccaa8..e0c6df2 100644
--- a/lite_translator/riscv64_to_x86_64/inline_intrinsic.h
+++ b/lite_translator/riscv64_to_x86_64/inline_intrinsic.h
@@ -319,29 +319,33 @@
     return {true};
   }
 
-  template <typename ArgBinding, typename IntrinsicBindingInfo>
-  auto /*MakeTuplefromBindingsClient*/ operator()(ArgTraits<ArgBinding>, IntrinsicBindingInfo) {
-    static constexpr const auto& arg_info = ArgTraits<ArgBinding>::arg_info;
+  template <typename ArgBinding, typename OperandInfo, typename IntrinsicBindingInfo>
+  auto /*MakeTuplefromBindingsClient*/ operator()(ArgTraits<ArgBinding, OperandInfo>,
+                                                  IntrinsicBindingInfo) {
+    static constexpr const auto& arg_info = ArgTraits<ArgBinding, OperandInfo>::arg_info;
     if constexpr (arg_info.arg_type == ArgInfo::IMM_ARG) {
-      return ProcessArgInput<ArgBinding, IntrinsicBindingInfo>(reg_alloc_);
+      return ProcessArgInput<ArgBinding, OperandInfo, IntrinsicBindingInfo>(reg_alloc_);
     } else {
-      using RegisterClass = typename ArgTraits<ArgBinding>::RegisterClass;
+      using RegisterClass = typename ArgTraits<ArgBinding, OperandInfo>::RegisterClass;
       if constexpr (RegisterClass::kAsRegister == 'x') {
-        return ProcessArgInput<ArgBinding, IntrinsicBindingInfo>(simd_reg_alloc_);
+        return ProcessArgInput<ArgBinding, OperandInfo, IntrinsicBindingInfo>(simd_reg_alloc_);
       } else {
-        return ProcessArgInput<ArgBinding, IntrinsicBindingInfo>(reg_alloc_);
+        return ProcessArgInput<ArgBinding, OperandInfo, IntrinsicBindingInfo>(reg_alloc_);
       }
     }
   }
 
-  template <typename ArgBinding, typename IntrinsicBindingInfo, typename RegAllocForArg>
+  template <typename ArgBinding,
+            typename OperandInfo,
+            typename IntrinsicBindingInfo,
+            typename RegAllocForArg>
   auto ProcessArgInput(RegAllocForArg&& reg_alloc) {
-    static constexpr const auto& arg_info = ArgTraits<ArgBinding>::arg_info;
+    static constexpr const auto& arg_info = ArgTraits<ArgBinding, OperandInfo>::arg_info;
     if constexpr (arg_info.arg_type == ArgInfo::IMM_ARG) {
       return std::tuple{std::get<arg_info.from>(input_args_)};
     } else {
-      using RegisterClass = typename ArgTraits<ArgBinding>::RegisterClass;
-      static constexpr auto kUsage = ArgTraits<ArgBinding>::kUsage;
+      using RegisterClass = typename ArgTraits<ArgBinding, OperandInfo>::RegisterClass;
+      static constexpr auto kUsage = ArgTraits<ArgBinding, OperandInfo>::kUsage;
       if constexpr (arg_info.arg_type == ArgInfo::IN_ARG) {
         using Type =
             std::tuple_element_t<arg_info.from, typename IntrinsicBindingInfo::InputArguments>;