J'ai un code qui ressemble plus ou moins à ceci:
#include <bitset>
enum Flags { A = 1, B = 2, C = 3, D = 5,
E = 8, F = 13, G = 21, H,
I, J, K, L, M, N, O };
void apply_known_mask(std::bitset<64> &bits) {
const Flags important_bits[] = { B, D, E, H, K, M, L, O };
std::remove_reference<decltype(bits)>::type mask{};
for (const auto& bit : important_bits) {
mask.set(bit);
}
bits &= mask;
}
Clang> = 3.6 fait la chose intelligente et compile cela en une seule and
instruction (qui est ensuite intégrée partout ailleurs):
apply_known_mask(std::bitset<64ul>&): # @apply_known_mask(std::bitset<64ul>&)
and qword ptr [rdi], 775946532
ret
Mais chaque version de GCC que j'ai essayée compile cela dans un énorme désordre qui inclut la gestion des erreurs qui devraient être statiquement DCE. Dans un autre code, il placera même l' important_bits
équivalent sous forme de données en ligne avec le code!
.LC0:
.string "bitset::set"
.LC1:
.string "%s: __position (which is %zu) >= _Nb (which is %zu)"
apply_known_mask(std::bitset<64ul>&):
sub rsp, 40
xor esi, esi
mov ecx, 2
movabs rax, 21474836482
mov QWORD PTR [rsp], rax
mov r8d, 1
movabs rax, 94489280520
mov QWORD PTR [rsp+8], rax
movabs rax, 115964117017
mov QWORD PTR [rsp+16], rax
movabs rax, 124554051610
mov QWORD PTR [rsp+24], rax
mov rax, rsp
jmp .L2
.L3:
mov edx, DWORD PTR [rax]
mov rcx, rdx
cmp edx, 63
ja .L7
.L2:
mov rdx, r8
add rax, 4
sal rdx, cl
lea rcx, [rsp+32]
or rsi, rdx
cmp rax, rcx
jne .L3
and QWORD PTR [rdi], rsi
add rsp, 40
ret
.L7:
mov ecx, 64
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:.LC1
xor eax, eax
call std::__throw_out_of_range_fmt(char const*, ...)
Comment écrire ce code pour que les deux compilateurs puissent faire la bonne chose? A défaut, comment écrire ceci pour qu'il reste clair, rapide et maintenable?
(1ULL << B) | ... | (1ULL << O)
(1ULL << Constant)
| par ligne, et alignez les noms de constantes sur les différentes lignes, ce serait plus facile pour les yeux.
int
résultat de l'opération de bit PEUT ÊTRE int
OU peut long long
dépendre de la valeur et enum
n'est formellement pas un équivalent à une int
constante. clang appelle à "comme si", gcc reste pédant
B | D | E | ... | O
?