llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.29k stars 11.68k forks source link

clang does not support union or struct types for "register" variables #109777

Open DavidSpickett opened 1 week ago

DavidSpickett commented 1 week ago

Originally reported by https://discourse.llvm.org/t/frontend-how-to-use-register-union-or-struct-in-c/81392.

https://godbolt.org/z/bz8nM1E9x

typedef union Test {
    int i;
} Test;

register Test t asm ("x21");

typedef struct Test2 {
    int i;
} Test2;

register Test t2 asm ("x22");

register int t3 asm ("w23");
register int *t4 asm ("x24");

Clang does not allow the first 2, the union and struct, despite their sizeof being correct for the register chosen.

<source>:5:22: error: size of register 'x21' does not match variable size
    5 | register Test t asm ("x21");
      |                      ^
<source>:5:1: error: bad type for named register variable
    5 | register Test t asm ("x21");
      | ^
<source>:11:1: error: bad type for named register variable
   11 | register Test t2 asm ("w22");
      | ^

GCC allows all of them.

It appears that types have to be explicitly added, as was done for pointers https://github.com/llvm/llvm-project/commit/2e31e4e47b09ca5e889e7470e5ae372348661692.

fioraking commented 1 week ago

Just imagine, I offen need read a union. Like this,

typedef union Test {
    int key1;
    int key2;
} Test;

register volatile  Test t asm("x21");

If I each read it from memory, this is very slow. I put it to register, it will be faster. May gcc do that because it it so faster when it exist "volatile".

llvmbot commented 1 week ago

@llvm/issue-subscribers-clang-frontend

Author: David Spickett (DavidSpickett)

Originally reported by https://discourse.llvm.org/t/frontend-how-to-use-register-union-or-struct-in-c/81392. https://godbolt.org/z/bz8nM1E9x ``` typedef union Test { int i; } Test; register Test t asm ("x21"); typedef struct Test2 { int i; } Test2; register Test t2 asm ("x22"); register int t3 asm ("w23"); register int *t4 asm ("x24"); ``` Clang does not allow the first 2, the union and struct, despite their sizeof being correct for the register chosen. ``` <source>:5:22: error: size of register 'x21' does not match variable size 5 | register Test t asm ("x21"); | ^ <source>:5:1: error: bad type for named register variable 5 | register Test t asm ("x21"); | ^ <source>:11:1: error: bad type for named register variable 11 | register Test t2 asm ("w22"); | ^ ``` GCC allows all of them. It appears that types have to be explicitly added, as was done for pointers https://github.com/llvm/llvm-project/commit/2e31e4e47b09ca5e889e7470e5ae372348661692.
DavidSpickett commented 1 week ago

Example struct accepted by GCC:

typedef struct Test {
    int a;
    int b;
} Test;

register Test t asm("x20");

void fn() {
    t.a = 1;
    t.b = 3.14;
}

https://godbolt.org/z/co6YYYzn5

This errors if you add more members because it can't fit in the register:

<source>:7:15: error: data type of 't' isn't suitable for a register
    7 | register Test t asm("x20");
      |               ^

So I guess that GCC's criteria is "sizeof(T) <= sizeof(register)".

fioraking commented 3 days ago

I wanna implement it, but I not familiar with it(Frontend part). Did someone give me any suggestions?

Sirraide commented 3 days ago

I wanna implement it, but I not familiar with it(Frontend part). Did someone give me any suggestions?

Well, I haven’t thought too much about this, but looking at the code, in Sema, changing the condition of this if stmt here should work: https://github.com/llvm/llvm-project/blob/fb6feb86a7dc324dcb2516397ba3fc5fe05ea584/clang/lib/Sema/SemaDecl.cpp#L7951-L7953

Looks like whether the type fits in the register is already validated a few lines above, so just changing this to something like e.g. !R->isTrivialType(Context) should do the trick (we definitely don’t want to allow putting e.g. a class that has a copy constructor in a register!).

However, I’m not sure if codegen is set up to deal with this (e.g. if some struct x is in a global register variable, does x.y work properly?).