rust-lang / rustc_codegen_gcc

libgccjit AOT codegen for rustc
Apache License 2.0
920 stars 60 forks source link

Finish implementing the conversion from register class to gcc constraint code #60

Closed antoyo closed 7 months ago

antoyo commented 3 years ago

Here, here and here.

The GCC constraints doc will be helpful to do this.

ghost commented 3 years ago

I'll try to tackle this one.

Btw, I think you were right that support for explicit registers like in("r11") will require creating "GCC register variables" which is not yet supported by gccjit yet. I guess that'll be one more libgccjit patch. Anyway, I don't think this kind of syntax is used often; syscall is the only example I can come up with.

For now, I'm going to emit an error. The code as it's now is definitely incorrect.

bjorn3 commented 3 years ago

Explicit registers are used by compiler-builtins. Almost every input and output uses an explicit register in https://github.com/rust-lang/compiler-builtins/blob/826d9e9d41dca79c9893118fdbb600fc9a837764/src/mem/x86_64.rs

Tock uses them when switching to userspace: https://github.com/tock/tock/blob/bd4808c787434f6d493f643ea7f9abe8339048e0/arch/cortex-m0/src/lib.rs#L204-L260

The x86_64 crate uses them for ports (https://github.com/rust-osdev/x86_64/blob/a9cbf1471d38b18dcfe22bf399082a445341c3e5/src/instructions/port.rs#L14) and model specific registers (https://github.com/rust-osdev/x86_64/blob/a9cbf1471d38b18dcfe22bf399082a445341c3e5/src/registers/model_specific.rs#L134-L139)

ghost commented 3 years ago

@bjorn3 I agree with all that you said, let me clarify. (I'm using x86-64 as example; everything I say applies to other architectures as well)

Here's the code part I was referring to:

https://github.com/antoyo/rustc_codegen_gcc/blob/6f50986667debbfc67776304a8ee23fe0158613f/src/asm.rs#L235-L250

It translates Rust constraints to GCC constraints, for example in("eax") => a. GCC supports a set of predefined constraints (look for "X86") for specific registers, like a for eax/rax, S for esi/rsi and so on. As you can see from your own examples, these constraints cover 9/10 uses of registers, and this is what I meant when I said that registers like r11 are not used often by programmers.

Unfortunately, when you really do need to use registers like r11, as demonstrated by your Tock example (I think we can add low-level OS/embed code to my list of exceptions), GCC ExtendedAsm does not support them directly. There's a nasty hack widely used and respectable workaround called "register variables", and libgccjit does not support them yet. I do intend to make a patch for it, but I would like to get the base cases working first.

I'd like to stress that current code translating r11 to r11 is plain incorrect and I suspect it will trigger a cryptic "incorrect constraint" error in libgccjit. Oh yes, it does, see for yourself. I think our own error, explaining what's going on and why it's not yet supported will be better.

P.S: This problem does not exist in LLVM because llvm supports "register name in curly braces" syntax for non-covered registers. Shame on you, GCC.

P.P.S Also, while I'm 99% sure that "register variables" is the only proper way to support it - GCC doc explicitly cites it as the way to go, I'll probably ask on GCC mailing list to be sure).

ghost commented 3 years ago

This got me thinking: since it might be a while until I get familiar enough with gccjit codebase (I'm a complete noob to compilers, I build the road as I travel, you've been warned), we could resort to a hack of our own in the meantime. We could get it working by turning these "unknown registers" to the rm constraint and then adjusting the template with code that moves the value from this temporary register/memory to the a desired register.

For example, this Rust code:

let x: u64 = 2;
asm!("add r11, r11", inout("r11") x);

will correspond to this GNU C code:

uint64_t x = 2;
asm("
     mov r11, %0 ; inserted by the backend
     add r11, r11 ; the original template
     mov %0, r11 ; inserted by the backend"

     : "+&rm" (x) // libgccjit will find a spare register (or memory location) for us
     :
     : "r11" // add the original r11 to the clobbers list
);

This code is obviously ineffective, it will clobber more registers than it should, it will diminish some optimizations around this asm block, but this code is both correct and working (in my noob's opinion). And of course this is just a temporary measure until we get libgccjit to support GCC register variables and rewrite it properly.

@antoyo what do you think?

antoyo commented 3 years ago

I can probably quickly make a patch to support register variables. I'm on vacation, but I'll be back on Monday, so I could start working on it then.

That way, it should be simpler and correct.

In the meantime, you can start working on what does not require them. If at some point you're waiting for me to add the support of register variables, please ping me and I'll see what I can do.

antoyo commented 7 months ago

Finished in https://github.com/antoyo/rustc_codegen_gcc/pull/8.