Issue |
128778
|
Summary |
[AArch64] `!struct.x && !struct.y` produces worse code than `(struct.x == 0) && (struct.y == 0)`
|
Labels |
backend:AArch64,
llvm:instcombine
|
Assignees |
|
Reporter |
Kmeakin
|
`!it.x && !it.y` produces worse code than `(it.x == 0) && (it.y == 0)` if `it` is a struct type with `bool` members `x` and `y`.
Oddly using a `uint8_t` for `x` and `y` does not suffer from this problem.
# Real-world motivation
[This struct from mimalloc](https://github.com/microsoft/mimalloc/blob/a857a04803175cb51e1016339bda5b7993530729/include/mimalloc/types.h#L255)
```c++
// The `in_full` and `has_aligned` page flags are put in a union to efficiently
// test if both are false (`full_aligned == 0`) in the `mi_free` routine.
typedef union mi_page_flags_s {
uint8_t full_aligned;
struct {
uint8_t in_full : 1;
uint8_t has_aligned : 1;
} x;
} mi_page_flags_t;
```
# C++ code
https://godbolt.org/z/1387M789s
```c++
#include <cstdint>
struct S1 {
bool x;
bool y;
};
struct S2 {
bool x : 1;
bool y : 1;
};
struct S3 {
uint8_t x : 1;
uint8_t y : 1;
};
struct S4 {
uint8_t x ;
uint8_t y ;
};
extern "C" {
auto src1(S1 it) -> bool { return !it.x && !it.y; }
auto tgt1(S1 it) -> bool { return (it.x == 0) && (it.y == 0); }
auto src2(S2 it) -> bool { return !it.x && !it.y; }
auto tgt2(S2 it) -> bool { return (it.x == 0) && (it.y == 0); }
auto src3(S3 it) -> bool { return !it.x && !it.y; }
auto tgt3(S3 it) -> bool { return (it.x == 0) && (it.y == 0); }
auto src4(S4 it) -> bool { return !it.x && !it.y; }
auto tgt4(S4 it) -> bool { return (it.x == 0) && (it.y == 0); }
}
```
# AArch64 assembly
```asm
src1:
tst x0, #0x100
eor w9, w0, #0x1
cset w8, eq
and w0, w8, w9
ret
tgt1:
mov w8, #257
tst x0, x8
cset w0, eq
ret
src2:
tst x0, #0x2
eor w9, w0, #0x1
cset w8, eq
and w0, w8, w9
ret
tgt2:
tst x0, #0x3
cset w0, eq
ret
src3:
tst x0, #0x3
cset w0, eq
ret
tgt3:
tst x0, #0x3
cset w0, eq
ret
src4:
tst x0, #0xffff
cset w0, eq
ret
tgt4:
tst x0, #0xffff
cset w0, eq
ret
```
# Alive proof
https://alive2.llvm.org/ce/z/JRwXu7
```llvm
----------------------------------------
define i1 @src1(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = trunc i64 %#0 to i1
%#3 = and i64 %#0, 256
%#4 = icmp eq i64 %#3, 0
%#5 = xor i1 %#2, 1
%#6 = and i1 %#4, %#5
ret i1 %#6
}
=>
define i1 @tgt1(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = and i64 %#0, 257
%#3 = icmp eq i64 %#2, 0
ret i1 %#3
}
Transformation seems to be correct!
----------------------------------------
define i1 @src2(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = trunc i64 %#0 to i1
%#3 = and i64 %#0, 2
%#4 = icmp eq i64 %#3, 0
%#5 = xor i1 %#2, 1
%#6 = and i1 %#4, %#5
ret i1 %#6
}
=>
define i1 @tgt2(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = and i64 %#0, 3
%#3 = icmp eq i64 %#2, 0
ret i1 %#3
}
Transformation seems to be correct!
----------------------------------------
define i1 @src3(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = and i64 %#0, 3
%#3 = icmp eq i64 %#2, 0
ret i1 %#3
}
=>
define i1 @tgt3(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = and i64 %#0, 3
%#3 = icmp eq i64 %#2, 0
ret i1 %#3
}
Transformation seems to be correct!
----------------------------------------
define i1 @src4(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = and i64 %#0, 65535
%#3 = icmp eq i64 %#2, 0
ret i1 %#3
}
=>
define i1 @tgt4(i64 %#0) nofree willreturn memory(none) {
#1:
%#2 = and i64 %#0, 65535
%#3 = icmp eq i64 %#2, 0
ret i1 %#3
}
Transformation seems to be correct!
Summary:
4 correct transformations
0 incorrect transformations
0 failed-to-prove transformations
0 Alive2 errors
```
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs