loongarch64 inconsistencies between hardware and qemu due too lenient decoding of floating point comparisons Description of problem: (Related to #2971 ) Lenient instruction decoding of floating point comparisons leads to instructions that do not exist on hardware (Loongson 3A5000). The decoding of floating point compare instruction allows for instructions with illegal fcmp modes. The following are affected (`target/loongarch/insns.decode`): ``` vfcmp_cond_s 0000 11000101 ..... ..... ..... ..... @vvv_fcond vfcmp_cond_d 0000 11000110 ..... ..... ..... ..... @vvv_fcond xvfcmp_cond_s 0000 11001001 ..... ..... ..... ..... @vvv_fcond xvfcmp_cond_d 0000 11001010 ..... ..... ..... ..... @vvv_fcond fcmp_cond_s 0000 11000001 ..... ..... ..... 00 ... @cff_fcond fcmp_cond_d 0000 11000010 ..... ..... ..... 00 ... @cff_fcond ``` The `get_fcmp_flags` function in `target/loongarch/tcg/insn_trans/trans_fcmp.c.inc` allows decoding all possible 4-bit condition values even though some are invalid on hardware (Loongson 3A5000):
qemu-loongarch64-static ``` --- fcond = 0b0 () --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1 () --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10 (LT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11 (LT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b100 (EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b101 (EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b110 (LT | EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b111 (LT | EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1000 (UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1001 (UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1010 (LT | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1011 (LT | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1100 (EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1101 (EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1110 (LT | EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1111 (LT | EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10000 (LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10001 (LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10010 (LT | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10011 (LT | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10100 (EQ | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10101 (EQ | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10110 (LT | EQ | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10111 (LT | EQ | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11000 (UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11001 (UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11010 (LT | UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11011 (LT | UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11100 (EQ | UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11101 (EQ | UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11110 (LT | EQ | UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11111 (LT | EQ | UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True ```
Loongson 3A5000 ``` --- fcond = 0b0 () --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1 () --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10 (LT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11 (LT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b100 (EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b101 (EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b110 (LT | EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b111 (LT | EQ) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1000 (UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1001 (UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1010 (LT | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1011 (LT | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1100 (EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1101 (EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1110 (LT | EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b1111 (LT | EQ | UN) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10000 (LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10001 (LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10010 (LT | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b10011 (LT | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b10100 (EQ | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10101 (EQ | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b10110 (LT | EQ | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b10111 (LT | EQ | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b11000 (UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11001 (UN | LT | GT) --- vfcmp_cond_s True vfcmp_cond_d True xvfcmp_cond_s True xvfcmp_cond_d True fcmp_cond_s True fcmp_cond_d True --- fcond = 0b11010 (LT | UN | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b11011 (LT | UN | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b11100 (EQ | UN | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b11101 (EQ | UN | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b11110 (LT | EQ | UN | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False --- fcond = 0b11111 (LT | EQ | UN | LT | GT) --- vfcmp_cond_s False vfcmp_cond_d False xvfcmp_cond_s False xvfcmp_cond_d False fcmp_cond_s False fcmp_cond_d False ```
Steps to reproduce: 1. compile the `test_instr_valid` test program for loongarch64 (see additional information). 2. run the `check_defined.py` python script to print out information about defined instructions. 3. All tested fcmp instructions are defined when run using qemu, but some are invalid (SIGILL) on actual hardware. Additional information: I will post a patch for this issue to the mailing list soon. `test_instr_valid` source code: ```C #include #include #include #include #include #include #include #define PAGE_SIZE 0x4000 #define ret 0x4c000020 typedef void (*instr_fun)(void); static __attribute__((aligned(PAGE_SIZE))) uint32_t code[PAGE_SIZE / sizeof(uint32_t)]; void handler(int signo, siginfo_t *info, void *context) { printf("False"); exit(0); } int main(int argc, char** argv) { struct sigaction act = { 0 }; act.sa_flags = SA_SIGINFO | SA_ONSTACK; act.sa_sigaction = &handler; sigaction(SIGILL, &act, NULL); uint32_t instr = (uint32_t)strtoull(argv[1], NULL, 0); code[0] = instr; code[1] = ret; mprotect(code, sizeof(code), PROT_READ | PROT_EXEC); instr_fun f = (instr_fun)(void*)code; f(); printf("True"); exit(0); } ``` `check_defined.py`: ```py import subprocess CMD = ["qemu-loongarch64-static", "./test_instr_valid"] # remove "qemu-loongarch64-static" when running without emulation def is_defined(instr): try: return eval(subprocess.check_output(CMD + [hex(instr)]).decode()) except: return False bases = [ ("vfcmp_cond_s ", 0b00001100010100000000000000000000), # vfcmp_cond_s 0000 11000101 ..... ..... ..... ..... @vvv_fcond ("vfcmp_cond_d ", 0b00001100011000000000000000000000), # vfcmp_cond_d 0000 11000110 ..... ..... ..... ..... @vvv_fcond ("xvfcmp_cond_s", 0b00001100100100000000000000000000), # xvfcmp_cond_s 0000 11001001 ..... ..... ..... ..... @vvv_fcond ("xvfcmp_cond_d", 0b00001100101000000000000000000000), # xvfcmp_cond_d 0000 11001010 ..... ..... ..... ..... @vvv_fcond ] bases_2 = [ ("fcmp_cond_s ", 0b00001100000100000000000000000000), # fcmp_cond_s 0000 11000001 ..... ..... ..... 00 ... @cff_fcond ("fcmp_cond_d ", 0b00001100001000000000000000000000), # fcmp_cond_d 0000 11000010 ..... ..... ..... 00 ... @cff_fcond ] def format_fcond(val): x = ["LT", "EQ", "UN", "LT | GT"] return " | ".join(x[i] for i in range(4) if (val >> 1) & (1 << i)) fcond_shift = 15 # use r1, r2, r3 for vector instructions reg_mask = 0b000110001000001 # use c0, r1, r2 for normal instructions reg_mask_2 = 0b000100000100000 for fcond in range(1 << 5): print(f" --- fcond = {bin(fcond)} ({format_fcond(fcond)}) --- ") for name, mask in bases: print(name, is_defined(mask | reg_mask | (fcond << fcond_shift))) for name, mask in bases_2: print(name, is_defined(mask | reg_mask_2 | (fcond << fcond_shift))) print("") ```