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("")
```