我正在对虚拟机进行编程,并且我结合了受限指针来迭代脚本的指令流:
union Pointer {
uint8_t *restrict UInt8Ptr;
uint16_t *restrict UInt16Ptr;
uint32_t *restrict UInt32Ptr;
uint64_t *restrict UInt64Ptr;
int8_t *restrict Int8Ptr;
int16_t *restrict Int16Ptr;
int32_t *restrict Int32Ptr;
int64_t *restrict Int64Ptr;
float *restrict FloatPtr;
double *restrict DoublePtr;
const char *restrict CStrPtr;
void *restrict Ptr;
};
对于CALL操作码,我(间接)保存了指令指针的值,如果我了解“ restrict”关键字的用法,则会导致不确定的行为。
(--regs[regStk].SelfPtr)->Ptr = ip.Ptr; /* push rip */
*--regs[regStk].SelfPtr = regs[regBase]; /* push rbp */
regs[regBase] = regs[regStk]; /* mov rbp, rsp */
我还应该说,在RET操作码中,指令指针的值已恢复。
regs[regStk] = regs[regBase]; /* mov rsp, rbp */
regs[regBase] = *regs[regStk].SelfPtr++; /* pop rbp */
ip.Ptr = (*regs[regStk].SelfPtr++).Ptr; /* pop rip */
我做了很多测试,甚至使用了不同的编译器(GCC和clang v3.5和clang v6.0),这似乎并没有产生不确定的行为,为什么?
编辑更新:
将变量都声明为局部块作用域:
int32_t VM_Exec(struct VM *const restrict vm)
{
if( !vm or !vm->CurrScript.Ptr ) {
return ErrInstrBounds;
}
union Value *const restrict regs = vm->Regs; // <--
union Pointer pc = (union Pointer){.UInt8Ptr = regs[regInstr].UCharPtr}; // <--
答案 0 :(得分:2)
Restrict关键字。仅限gcc -O2和-O3。
在您的示例中,我看不到任何会导致问题的内容,因为我们不知道如何声明这些数组以及如何使用它们。
这里有个示例-我违反了与编译器的合同。
unsigned p = 100;
void foo1(void)
{
p++;
}
void foo(unsigned *restrict x)
{
printf("p = %u\n", *x);
foo1();
printf("p = %u\n", *x);
}
int main()
{
foo(&p);
}
结果为(-O3)
100
100
用-O1编译
100
101
另一个例子:-O3
unsigned p = 100;
void foo1(void)
{
p++;
}
void foo(unsigned *restrict x)
{
unsigned *restrict p1;
p1 = x;
printf("p = %u\n", *x);
foo1();
printf("p = %u\n", *x);
*p1++;
printf("p = %u\n", *x);
}
int main()
{
foo(&p);
}
和结果:
p = 100
p = 100
p = 101