C++ 在C+中取uint64 t的上半部分的说明/内在要求+;?

C++ 在C+中取uint64 t的上半部分的说明/内在要求+;?,c++,c,bit-manipulation,intrinsics,instructions,C++,C,Bit Manipulation,Intrinsics,Instructions,想象一下下面的代码: y接收64位整数的较高32位部分。我的问题是,是否存在任何内在函数或任何CPU指令,它们在单次操作中不进行移动和移位 至少CLang(链接在上面的Try it online中)为此创建了两条指令mov rax,rdi和shr rax,32,因此CLang要么不进行此类优化,要么不存在此类特殊指令 如果存在像movhi dst_reg,src_reg这样的假想单指令,那就太好了。如果有更好的方法来提取任意uint64_t的位字段,编译器就会使用它。(至少在理论上是这样;编译

想象一下下面的代码:


y
接收64位整数的较高32位部分。我的问题是,是否存在任何内在函数或任何CPU指令,它们在单次操作中不进行移动和移位

至少CLang(链接在上面的Try it online中)为此创建了两条指令
mov rax,rdi
shr rax,32
,因此CLang要么不进行此类优化,要么不存在此类特殊指令


如果存在像
movhi dst_reg,src_reg

这样的假想单指令,那就太好了。如果有更好的方法来提取任意uint64_t的位字段,编译器就会使用它。(至少在理论上是这样;编译器确实错过了优化,他们的选择有时倾向于延迟,即使延迟成本更高。)

对于无法用纯C高效表达的内容,您只需要使用内部函数,编译器已经可以很容易地理解这些内容。(或者如果您的编译器很笨,无法发现明显的问题。)

您可以想象输入值来自两个32位值相乘的情况,那么在某些CPU上,编译器可能值得使用加宽
mul r32
在两个单独的32位寄存器中生成结果,而不是
imul r64,r64
+
shr reg,32
,如果可以轻松使用EAX/EDX。但是除了
gcc-mtune=silvermont
或其他调优选项之外,您不能让编译器这样做


shr reg,32
具有1个周期延迟,并且可以在大多数现代x86微体系结构()上的多个执行端口上运行。人们唯一希望的是,它可以将结果放在不同的寄存器中,而不会覆盖输入


大多数现代非x86 ISA都是类似RISC的3操作数指令,因此移位指令可以进行复制和移位,而不像x86移位,在x86移位中,编译器除了需要
shr
之外,还需要
mov
,如果以后还需要原始64位值,或者(对于小函数)需要不同寄存器中的返回值

有些ISA有位字段提取指令。PowerPC甚至有一个有趣的旋转和掩码指令(
rlwinm
)(掩码是由immediates指定的位范围),它是一个不同于正常移位的指令。编译器将根据需要使用它-不需要内部代码


x86,用于复制和旋转,而不是卡在同一寄存器内移位。在独立版本中,返回
uint32_t
的函数可以/应该使用该函数,而不是mov+shr,因为调用方已经必须忽略RAX中的高垃圾,因此该函数不内联。(x86-64 System V和Windows x64都将返回值定义为仅与arg的C类型匹配的寄存器宽度;例如,返回
uint32_t
意味着RAX的高32位不是返回值的一部分,并且可以保存任何内容。通常它们为零,因为写入32位寄存器会隐式地将零扩展到64位,但有些是零g如
return bar()
其中bar返回uint64\t可以不触及RAX而不必截断它;事实上,优化的tailcall是可能的。)

对于
rorx
,没有内在属性;编译器应该知道何时使用它。(但是gcc/clang
-O3-march=haswell
忽略了这个优化。)


如果编译器在循环中执行此操作,则它可以将
shrx reg,reg,reg
的寄存器中的
32
作为拷贝和移位。或者更愚蠢的是,它可以将
pext
0xfffffffull一起使用,为什么不使用移位?为什么你认为一些内在的会比转移好?Shift做的正是你想要的,那么为什么要有任何特殊指令呢?如果有一些秘密指令以常数32优化右移,我希望编译器知道并应用它。“任何不做Shift而执行此操作的CPU指令?”-->C不指定CPUinstructions@Arty使用这样的联合可能根本不涉及内存,因为编译器已经存在。这可能是过早的优化,我不认为任何体系结构都有这样的说明。一些16位机器有交换指令来交换2个字节,在某些情况下,可以使用这些字节来实现此建议进行读取。您能告诉我是否有许多(特别是旧的)CPU支持此BMI2集吗?大约世界上有多少百分比的CPU具有BMI2?同样对我来说,奇怪的是,
rorx
没有内在的BMI2。我认为所有假想指令都有内在函数,只是为了方便,如果我不想依赖编译器猜测,而是显式地使用特定指令的话。@Arty:BMI2在Haswell中是Intel的新指令,在AMD中是类似的一代。云服务器将拥有它,但有很多家庭CPU没有。一些编译器在一般情况下为rotates()提供了内部函数,但是RORX和ROR之间的唯一区别在于没有设置标志,并且有一个单独的目标。登记分配和管理标志完全是编译器,所以强制它在ROR上使用RORX是没有意义的。“大多数现代非x86 ISAS都是像3操作数指令一样的RISC”——“那么,现在大多数处理器都是每年数十亿的嵌入式处理器吗?”CH UnStReSimoNoCIC:不是。我认为ISA可以用于高性能计算,比如AArch64和PowerPC64。如果像大多数嵌入式应用一样使用32位ISA,则
uint64\t
的两部分已经分开。或者对于ARM的16位半uint32_t,即使在thumb模式下,
lsr dst,src,16
也可以作为单个thumb指令进行编码。即使是没有桶移位器的低端嵌入式设备也是另一回事,但通常是这样
uint64_t x = 0x81C6E3292A71F955ULL;
uint32_t y = (uint32_t) (x >> 32);