GCC のインラインアセンブラのプログラムの部分は文字列リテラルであるため、プリプロセッサを使ってトークンを埋め込むのは難しい話ではありません。
#define ITEMTYPE "byte" ... asm(R"( .intel_syntax noprefix movzx eax, )" ITEMTYPE R"( ptr [rax] )")
一方、C++ テンプレートプログラミングをする場合、こういった置き換えはプリプロセッサによらない方法でやりたいところですが、やり方が分からなかったので UtVideo ではプリプロセッサでやっています(呼び出し側、実装側)。
で、昨日久しぶりに調べていたら、とりあえず x86/x86-64 でできるダーティーハックを見つけました。
$ cat aaa.cc static constexpr char byte_[0] asm("byte") = {}; void foo() { asm(R"( .intel_syntax noprefix movzx eax, %p[aaa] ptr [rax] )":: [aaa]"i"(byte_)); } $ gcc -S -o - -std=c++11 -masm=intel aaa.cc .file "aaa.cc" .intel_syntax noprefix .text .section .rodata .type byte, @object .size byte, 0 byte: .text .globl _Z3foov .type _Z3foov, @function _Z3foov: .LFB0: .cfi_startproc endbr64 push rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 mov rbp, rsp .cfi_def_cfa_register 6 #APP # 5 "aaa.cc" 1 .intel_syntax noprefix movzx rax, byte ptr [rax] # 0 "" 2 #NO_APP nop pop rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size _Z3foov, .-_Z3foov .ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0" .section .note.GNU-stack,"",@progbits .section .note.gnu.property,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4:
byte_
という名前の変数の後ろについている asm
はアセンブラレベルでのシンボル名を指定する記法で、これが埋め込みたい文字列になります(C での変数名は何でもいい)。この変数(の先頭アドレスの値)を "i"
constraint で input operand として渡し、生のシンボル名を出力するための p
modifier を付けて参照させると、オペランドとして指定した値に対応するシンボル名がそこに埋め込まれます。
これを利用すると以下のようなテンプレートプログラミングができます。
$ cat bbb.cc #include <stdint.h> static constexpr char byte_[0] asm("byte") = {}; static constexpr char word_[0] asm("word") = {}; template<typename T> void bar() { static_assert(sizeof(T) == 1 || sizeof(T) == 2); asm (R"( .intel_syntax noprefix movzx rax, %p[aaa] ptr [rax] )"::[aaa]"i"((sizeof(T) == 1) ? byte_ : word_)); } template void bar<uint8_t>(); template void bar<uint16_t>(); $ gcc -c -o bbb.o -std=c++11 -masm=intel bbb.cc $ objdump -d -Mintel bbb.o bbb.o: file format elf64-x86-64 Disassembly of section .text._Z3barIhEvv: 0000000000000000 <_Z3barIhEvv>: 0: f3 0f 1e fa endbr64 4: 55 push rbp 5: 48 89 e5 mov rbp,rsp 8: 48 0f b6 00 movzx rax,BYTE PTR [rax] c: 90 nop d: 5d pop rbp e: c3 ret Disassembly of section .text._Z3barItEvv: 0000000000000000 <_Z3barItEvv>: 0: f3 0f 1e fa endbr64 4: 55 push rbp 5: 48 89 e5 mov rbp,rsp 8: 48 0f b7 00 movzx rax,WORD PTR [rax] c: 90 nop d: 5d pop rbp e: c3 ret
シンボルを埋め込むための constexpr 変数そのものは何でもいいので、サイズがゼロの配列にします。こうするとシンボルはオブジェクトファイルに出力されますがデータは何も出力されないので、最終的なプログラムが大きくなることはありません。
あと、constexpr 変数が static とはいえグローバルスコープを持つのが嫌だからといってローカル変数にした場合、asm
で指定した名前にプレフィックスやサフィックスが付いてしまうのでダメです。GCC と Clang でここの挙動がちょっと違いますが、なんか付いちゃってダメなのは同じです。
p
modifier はどうやら x86/x86-64 にしかないため、他のアーキテクチャで同じことはたぶんできません。汎用性のある利用法があるのなら他のアーキテクチャにもあって良さそうなのですがそういうことはなく、ではどういう利用法が想定されてるんですかね…?
参考文献: Is it possible to template the asm opcode in c++? – Stack Overflow
no comment untill now