Skip to content

[X86] Poor codegen when accessing a single bit of very large integers #164225

@RKSimon

Description

@RKSimon

https://godbolt.org/z/WG3fK6vaz

bool get512(_BitInt(512) x, unsigned i) {
    _BitInt(512) k = 1;
    return (x & (k << i)) != 0;
}

results in a huge amount of expanded shift and bit logic, even though we know that we could access a single legal word directly (assumes little endian):

bool get512m(_BitInt(512) x, unsigned i) {
    i %= 512; // not really necessary, but basic bounds check
    const uint64_t *p = (const uint64_t *)&x;
    uint64_t r = p[i / 64];
    return (r & (1 << (i/64))) != 0;
}

Cases where we're altering single bits could be simplified similarly:

void clear512(_BitInt(512) &x, unsigned i) {
    _BitInt(512) k = 1;
    x &= ~(k << i);
}
void flip512(_BitInt(512) &x, unsigned i) {
    _BitInt(512) k = 1;
    x ^= (k << i);
}
void set512(_BitInt(512) &x, unsigned i) {
    _BitInt(512) k = 1;
    x |= (k << i);
}
void init512(_BitInt(512) &x, bool v, unsigned i) {
    _BitInt(512) k = 1;
    _BitInt(512) m = (unsigned)v & 1;
    x &= ~(k << i);
    x |= (m << i);
}

vs

void clear512m(_BitInt(512) &x, unsigned i) {
    i %= 512;
    uint64_t *p = (uint64_t *)&x;
    uint64_t *r = p + (i / 64);
    *r &= ~(1 << (i % 64));
}
void flip512m(_BitInt(512) &x, unsigned i) {
    i %= 512;
    uint64_t *p = (uint64_t *)&x;
    uint64_t *r = p + (i / 64);
    *r ^= ~(1 << (i % 64));
}
void set512m(_BitInt(512) &x, unsigned i) {
    i %= 512;
    uint64_t *p = (uint64_t *)&x;
    uint64_t *r = p + (i / 64);
    *r |= (1 << (i % 64));
}
void init512m(_BitInt(512) &x, bool v, unsigned i) {
    i %= 512;
    uint64_t *p = (uint64_t *)&x;
    uint64_t *r = p + (i / 64);
    *r &= ~(1 << (i % 64));
    *r |= (((unsigned)v & 1) << (i % 64));
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions