Search code examples
c++assemblyvisual-c++inline-assembly

Assembly and template classes


I'm working on small project and trying to get some hardcoded values to inline assembly. For doing that I'm using templates. I have created a code snip to show what I'm seeing

      #include <iostream>

      template <size_t T>
      struct MyClass
      {
          size_t myValue = T;

          void doSomething()
          {
              size_t value = T;
              __asm
              {
                  mov eax, [T]
                  mov [value], eax
              }
              std::cout << value << std::endl;
          }
      };

   int main()
   {
       auto o = new MyClass<999>();

       o->doSomething();
       return 0;
   }

It turns out that for the assembly code it is trying to use the data segment instead "pasting the number there directly"

        ; 25   : {

            push    ebp
            mov ebp, esp
            push    ecx

        ; 26   :     auto o = new MyClass<999>();

            push    4
            call    ??2@YAPAXI@Z                ; operator new
            add esp, 4

        ; 14   :         size_t value = T;

            mov DWORD PTR _value$2[ebp], 999        ; 000003e7H

        ; 26   :     auto o = new MyClass<999>();

            mov DWORD PTR [eax], 0
            mov DWORD PTR [eax], 999            ; 000003e7H

        ; 15   :         __asm
        ; 16   :         {
        ; 17   :             mov eax, [T]

            mov eax, DWORD PTR ds:0

        ; 18   :             mov [value], eax

            mov DWORD PTR _value$2[ebp], eax

        ; 19   :         }
        ; 20   :         std::cout << value << std::endl;

I'm using Visual Studio 2015. Is there any other way to achieve this.


Solution

  • Ahh, what a lovely and twisted question!

    I tried a constexpr variable initialized with T. The result was the same - value loaded from memory. Macros can be used to pass literals to inline assembly, but they don't mix well with templates.

    Initializing an enum inside the class using T should in theory work (https://msdn.microsoft.com/en-us/library/ydwz5zc6.aspx mentions enums can be used in inline assembly), but using that in the inline assembly crashes the visual studio 2015 compiler :-).

    What seems to work is a function template that declares an enum using the template parameter, and then using that enum in the inline assembly. If you must have it in a templated class, you can instantiate the template function inside the class like this:

        #include <iostream>
    
        template <size_t T> void dosomething() {
          enum { LOCALENUM = T };
          size_t value = 0;
          __asm
          {
            mov eax, LOCALENUM
            mov[value], eax
          }
          std::cout << value << std::endl;
        }
    
    
        template <size_t T>
        struct MyClass
        {
          size_t myValue = T;
          void doSomething()
          {
            ::dosomething<T>();
          }
        };
    
        int main()
        {
          //dosomething<999>();
          auto o = new MyClass<999>();
          o->doSomething();
          return 0;
        }
    

    This results in the following assembly code:

         auto o = new MyClass<999>();
         001B1015  mov         dword ptr [eax],0  
         001B101B  mov         dword ptr [eax],3E7h  
          o->doSomething();
         001B1021  mov         eax,3E7h    <--- Victory!
         001B1026  mov         dword ptr [ebp-4],eax  
         001B1029  mov         ecx,dword ptr            [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (01B2048h)]  
         001B102F  push        offset std::endl<char,std::char_traits<char> > (01B1050h)  
         001B1034  push        dword ptr [ebp-4]  
         001B1037  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (01B2044h)]