为何仅首次读取CNTVCT_EL0寄存器能得到有效值?
问题描述
在Xcode 15.4(运行于macOS 14.5的2020款M1 Mac mini)中创建终端应用,编写如下代码读取ARM64的系统计数器寄存器CNTVCT_EL0:
#include <stdint.h> #include <stdio.h> #include <string.h> uint64_t getfreq(void) { uint64_t value=0; asm("isb; mrs %0, CNTVCT_EL0" : "=r"(value)); return value; } int main() { do { printf("CNTVCT_EL0 = 0x%016llX\n", getfreq()); } while(1); }
运行后仅首次输出有效,后续输出均为0x0000000000000001,无循环时结果相同。询问原因及解决办法。
解决办法
修改getfreq函数中的汇编语句,添加volatile关键字:
uint64_t getfreq(void) { uint64_t value=0; asm volatile("isb; mrs %0, CNTVCT_EL0" : "=r"(value)); return value; }
原因解释
Xcode使用的Clang编译器会对未标记volatile的汇编语句进行优化:编译器默认认为这类汇编代码没有副作用,多次调用时会尝试复用之前的计算结果,甚至在某些优化级别下会错误地将结果替换为固定常量。
添加volatile关键字后,编译器会明确知晓该汇编语句存在副作用(每次执行都会读取硬件寄存器的新值),因此会确保每次调用getfreq时都执行完整的汇编指令,不会进行不合理的优化。
若开启了-O2及以上的编译优化级别,这种优化行为会更明显,而volatile可以直接规避该问题。
内容的提问来源于stack exchange,提问作者AnArrayOfFunctions
相关产品推荐
相关产品推荐

