In SAM, "Group" represents a group of registers 128 bytes long and everything below is just unions.
I don't suppose the exact sizes matter much, as long as you stay within what can be done with a simple offset.
"PORT" would be a fixed location in memory space. So, what the code actually does is setting 2 bits at the fixed memory location.
Setting two bits at fixed locations .. yep .. that's what I compiled.
There's no pointer loading (which takes whopping 50% in Motorola, and 49% in Intel which you decided to compile as position-independent code).
I compiled them the way they came. None of the other ISAs have problems using PC-relative addressing.
You need to get the address of the hardware registers *somehow*. Now, it's true that you'd probably get slightly smaller code using the address of "PORT" as a #define instead of as a global variable, but that's the same for all ISAs and doesn't favour one over another.
Moreover, when someone builds an MCU with RISC-V, they will probably provide some way of setting bits without reading registers, as Atmel did here:
PORT->Group[0].DIRSET.reg = 1<<12; // no need for "|="
The register is called DIRSET because writing to it only sets the bits (and the bits which are written "0" remain unchanged), and there's an opposite register called DIRCLR which clears the bits, and also DIRTGL which xors.
I took the C code exactly as given by westfw, which also matches the ARM assembly language he gave in loading, ORing, and storing.
Incidentally, RISC-V *does* have a way to change bits without bringing the data to the CPU and back, but it seemed unfair to use it. I'm concentrating here on compiled C code.
AMOOR.W res,addr,val
This sends a message with the address, value, and operation out over the TileLink bus. If all the channels of the bus go as far as the peripheral, then the peripheral itself will do the OR operation locally and report back the new value. If at some point on the way to the peripheral the bus narrows to just a simple read/write bus then the controller at that point will do the read/modify/write and report the result back to the CPU.
The compiler may be clever enough to keep one of the registers permanently pointing to the IO registers area, so the whole thing boils down to this:
6685 lui a3,0x1
0ce7a423 sw a3,200(a5) ; replace "200" with correct offset from a5
Sure, of course. But that value has to *get* into a5 somehow, and I showed that.
If I'd chosen to put the code into a function that took PORT as an argument then *all* of the ISAs would show shorter code.