如果用两个变量或一个变量和一个数字文字进行加法,为什么从 C++ 调用的汇编函数会在标志中给出不同的结果?

Why assembly function called from C++ give different result in flags if addition made with two variables or one variable and one numeric literal?

如果有人可以,请帮助我!

我写了一个 3 行汇编代码来取回 RFLAGS 的值:

PUBLIC x64rflags
.code
; Caller C++ function prototype: unsigned long long int x64rflags();
x64rflags PROC     
    pushfq          ; RFLAGS into stack
    pop rax         ; RAX = RFLAGS
    ret    
x64rflags ENDP
End

我在 C++ 库中使用它来检查 C++ 程序的标志:

//The RFLAGS query function. Result in unsigned long long int value.
extern "C"
{
    unsigned long long int x64rflags();
}

//The FLAGS bits decimal values
enum
{
    FLAGS_CF = 1, //Carry
    FLAGS_PF = 4, //Parity
    FLAGS_AF = 16, //Adjust (Auxiliary carry)
    FLAGS_ZF = 64, //Zero
    FLAGS_SF = 128, //Sign
    FLAGS_TF = 256, //Trap
    FLAGS_IF = 512, //Interrupt enable
    FLAGS_DF = 1024, //Direction
    FLAGS_OF = 2048, //Overflow
    FLAGS_IOPL_LOW = 4096, //I/O privilege level low bit
    FLAGS_IOPL_HIGH = 8192, //I/O privilege level high bit
    FLAGS_NT = 16384, //Nested task
    FLAGS_RF = 65536, //Resume
    FLAGS_VM = 131072, //Virtual 8086 mode
    FLAGS_AC = 262144, //Alignment Check
    FLAGS_VIF = 524288, //Virtual interrupt
    FLAGS_VIP = 1048576, //Virtual interrupt Pending
    FLAGS_ID = 2097152, //CPUID available
};

//The compare function of RFLAGS and inpected values
bool flagschk(unsigned long long int flags, unsigned long long int flagsToChk)
{
    if (flagsToChk == (flagsToChk & flags))
    {
        return true;
    }
    else
    {
        return false;
    }
}

这是我的 C++ 测试程序:

#define _UNICODE
#define UNICODE

#include <iostream>
#include <windows.h>
#include <string>
#include <x64rflags.h>

using namespace std;
typedef unsigned long long int ulli;

int main()
{
    SetConsoleOutputCP(1250); //ANSI Central European; Central European (Windows)

    ulli ullia, ullib, ullir, actrflags;
    std::string stro1, stro2;

    ullia = 0xffffffffffffffff;
    ullib = 1;
    ullir = ullia + ullib; //CF
    actrflags = x64rflags(); //CALL THE RFLAGS RETURNER ASM FUNCTION
    printf("1 Result: %llu\n", ullir);
    stro1 = "1 The value of actrflags: ";
    stro2 = std::to_string(unsigned long long int(actrflags));
    stro1.append(stro2);
    stro1.append("\n");
    printf(stro1.c_str());


    if(flagschk(actrflags, FLAGS_CF))
    {
        printf("1 condparam = TRUE\n\n");
    }
    else
    {
        printf("1 condparam = FALSE\n\n");
    }
//*/
    ullia = 0xffffffffffffffff;
    ullir = ullia + 1; //CF
    actrflags = x64rflags(); //CALL THE RFLAGS RETURNER ASM FUNCTION
    printf("2 Result: %llu\n", ullir);
    stro1 = "2 The value of actrflags: ";
    stro2 = std::to_string(unsigned long long int(actrflags));
    stro1.append(stro2);
    stro1.append("\n");
    printf(stro1.c_str());


    if(flagschk(actrflags, FLAGS_CF))
    {
        printf("2 condparam = TRUE\n\n");
    }
    else
    {
        printf("2 condparam = FALSE\n\n");
    }

    return 0;
}

我的问题是它在两个探针上给我不同的结果。 第一个使用变量按预期工作:提高进位并回馈 "TRUE"

第二个使用一个变量和一个数字字面量不提高进位。

附加信息: ml64 带选项 /c 编译的汇编代码 用 Code::Blocks 编写的 C++ 代码,使用 MSVC 2019 编译。 Intel x64Arch (core i7) 上的程序 运行。 这些程序(理论上)使用默认的 (x64 ABI) 调用约定。 如果我不在 rflags 查询之前的添加表达式中使用“=”,则在这两种情况下都不起作用。 (我想 MSVC 将加法处理为 "NOP" 并独立于加法返回最后一个进位...)

谁能解释一下这个异常的原因?

关于编译器如何根据 asm 选择实现 C + 运算符的任何期望都是没有根据的。一些调整选项可以使它更喜欢 lea 而不是不设置标志的 add 。 (对于 GCC,-mtune=atom 实际上就是这种情况)。

启用优化后,它可能会以不同的顺序执行,而不是在非内联函数调用之前。

或者对于 +1 具体来说,MSVC 可能选择了 inc which doesn't modify CF. That looks like the case here, given that your FLAGS result differs only in the low bit. (CF is the lowest bit)。对于那一点,您正在读取 inc 之前恰好留在 CF 中的任何值。其他位由 inc.

写入

您可以使用调试器单步检查编译器生成的 asm,或者将其放在 https://godbolt.org/


而且也没有理由假设在执行到达您的函数调用时 FLAGS 仍然从 + 的 asm 中设置。即使是内联 asm 也不能可靠地做到这一点。 如果您想从 add 读取 FLAGS,add 需要在您的 asm 中,而不是编译器生成的。

优化器不会将先前 C 语句的 FLAG 结果视为函数调用的输入,或者根本不是可观察到的副作用(编译器生成的代码除外)有意读取 FLAGS 结果,例如 jnz 在添加后实现 if(foo))