Informing clang that inline assembly reads a parti

2019-02-18 00:44发布

问题:

GCC (all versions I can conveniently test) can be told that an inline assembly statement reads a particular region of memory (expressed as a pointer p and a size n) with this admittedly awkward construct:

asm ("..." : : "m" (*(struct { char x[n]; } *)p));

However, this does not work in clang (3.[45]), you get a hard error:

 error: fields must have a constant size: 'variable
      length array in structure' extension will never be supported
 asm ("..." : : "m" (*(struct {char x[n];} *)p));
                                    ^

Is there (ideally) a different construct which will produce the same effect in both compilers, or (failing that) a different construct which will produce the same effect in clang, only?

Note that in the case I care about, I insert no actual assembly instructions; the point of the construct is to direct the compiler not to delete an apparently-dead memset. Thus, the "different construct" could perfectly well not involve inline assembly at all. However, please suggest constructs which read arbitrary memory, or generate additional code, only if there is no alternative. Also, DO NOT suggest memset_s, explicit_bzero, or similar; this is an attempt to implement a fallback for those functions without having to hack the compiler.

Full-scale demo program follows --

#include <string.h>

extern void foo(const char *a, const char *b, const char *c, char *d);

void bar(const char *x, char *y, size_t n)
{
  char w[16];
  char v[n];
  memset(w, 0x11, n);
  memset(v, 0x22, n);

  foo(w, v, x, y);

  memset(w, 0, 16);
  memset(v, 0, n);
  asm ("" : : "m" (*(struct {char _[n];} *)v));
}

-- as compiled by gcc 5.0 at -O2 -S, x86-64, CFI goo elided --

bar:
        pushq   %rbp
        leaq    15(%rdx), %rax
        movq    %rsp, %rbp
        pushq   %r14
        andq    $-16, %rax
        movq    %rsi, %r14
        pushq   %r13
        movl    $17, %esi
        movq    %rdi, %r13
        pushq   %r12
        leaq    -48(%rbp), %rdi
        pushq   %rbx
        movq    %rdx, %rbx
        subq    $16, %rsp
        subq    %rax, %rsp
        call    memset
        movq    %rbx, %rdx
        movq    %rsp, %rdi
        movl    $34, %esi
        call    memset
        movq    %r14, %rcx
        movq    %r13, %rdx
        movq    %rsp, %rsi
        leaq    -48(%rbp), %rdi
        call    foo
        movq    %rbx, %rdx
        xorl    %esi, %esi
        movq    %rsp, %rdi
        call    memset
        leaq    -32(%rbp), %rsp
        popq    %rbx
        popq    %r12
        popq    %r13
        popq    %r14
        popq    %rbp
        ret

-- the goal is to get the same number of block memory fills out of clang. Two is wrong, but four is also wrong.