Menu ▾ ▴

#1035 Reuse loaded structure members across writes to disjoint members

open
nobody
None
5
3 days ago
3 days ago
No

Please allow common-subexpression elimination to retain an indirect load when an intervening store provably cannot modify the loaded object. Currently, writing one structure member prevents reuse of a previously loaded member.

For example:

struct S
{
  int value;
  int other;
};

int f(struct S *s)
{
  int old = s->value;
  s->other = 7;
  return old + s->value;
}

Compiled for Z80 with SDCC 4.6.4 r16999:

sdcc -mz80 --std-c99 --max-allocs-per-node 100 -S example.c

Before: 21 bytes. The generated function reloads s->value after the store:

_f::
        ld      e, l
        ld      d, h
        ld      c, (hl)
        inc     hl
        ld      b, (hl)
        dec     hl
        inc     de
        inc     de
        ld      a, #0x07
        ld      (de), a
        inc     de
        xor     a, a
        ld      (de), a
        ld      a, (hl)
        inc     hl
        ld      h, (hl)
        ld      l, a
        add     hl, bc
        ex      de, hl
        ret

After implementing the proposal improvement to CSE: 18 bytes. The original value is reused:

_f::
        ld      e, l
        ld      d, h
        ld      c, (hl)
        inc     hl
        ld      b, (hl)
        ld      l, c
        ld      h, b
        inc     de
        inc     de
        ld      a, #0x07
        ld      (de), a
        inc     de
        xor     a, a
        ld      (de), a
        add     hl, bc
        ex      de, hl
        ret

Changing the member, local variable and function return type gives these differences in code size when compiling for Z80 with the same options:

Value type Before After
char 15 bytes 10 bytes
int 21 bytes 18 bytes
long 89 bytes 65 bytes
float 112 bytes 75 bytes

The only requirement to achieve these improvements is to recognise constant, non-overlapping byte ranges derived from the same base pointer. Unknown or overlapping accesses remain conservative.

A secondary benefit is better _Optional inference. For example:

struct P
{
  char padding;
  _Optional int *p;
  int count;
};

int g(struct P *s)
{
  if (s->p)
    {
      s->count = 7;
      return *s->p; // unwanted diagnostic message
    }
  return 0;
}

The store s->count = 7 cannot change the value read by s->p, but r16999 emits warning 355 at the pointer dereference:

<source>:13: warning 355: pointer to _Optional could not be proven to be non-null at dereference

See SDCC 4.6.0 on Compiler Explorer: https://godbolt.org/z/Ps1WaTW65

The proposed optimisation preserves the guarded load and removes that warning. First-member cases additionally need the separate #4148 fix. In CBUtilLib, the same improvement removes a warning in StringBuff.c and reduces its Z80 set_len function from 119 to 109 bytes. See https://github.com/chrisbazley/CBUtilLib/blob/main/StringBuff.c#L131

Discussion


Log in to post a comment.