Ah. Good question.
The "pointer method" is slightly better (neglecting, for the moment, that there isn't a REG_ definition for the IOBUS (there could be.))
All of that fancy pointer de-referencing and indexing in PORT_IOBUS->Group[1].OUTSET.reg happens at compile time. So if you have:
void trystruct() {
PORT_IOBUS->Group[1].OUTSET.reg = (1 << 21);
}
void tryREG() {
REG_PORT_OUTSET1 = (1 << 21);
}
They will compile to essentially EXACTLY the same code:
void trystruct() {
PORT->Group[0].OUTSET.reg = (1 << 21);
20fc: 2280 movs r2, #128 ;construct constant 1<<21
20fe: 4b02 ldr r3, [pc, #8] ; get address of PORT
2100: 0392 lsls r2, r2, #14 ; more constant construction (7+14 = 21)
2102: 619a str r2, [r3, #24] ; store to PORT.OUTSET.reg offset
2104: 4770 bx lr ;return
2106: 46c0 nop ; (padding)
2108: 41004400 .word 0x41004400 ;address of PORT0
void tryREG() {
REG_PORT_OUTSET0 = (1 << 21);
210c: 2280 movs r2, #128 ; 0x80
210e: 4b02 ldr r3, [pc, #8] ; address of (individual) register
2110: 0392 lsls r2, r2, #14
2112: 601a str r2, [r3, #0]
2114: 4770 bx lr
2116: 46c0 nop
2118: 41004418 .word 0x41004418 ; address of PORT_OUTSET0
However, if you expand the source code to generate a pulse:
void trystruct() {
PORT->Group[0].OUTSET.reg = (1 << 21);
PORT->Group[0].OUTCLR.reg = (1 << 21);
}
void tryREG() {
REG_PORT_OUTSET0 = (1 << 21);
REG_PORT_OUTCLR0 = (1 << 21);
}
The pointer-based version "knows" that OUTSET and OUTCLR are reachable via the same base PORTGroup pointer, while the register-based code thinks they are independent and needs to load them separately:
void trystruct() {
PORT->Group[0].OUTSET.reg = (1 << 21);
20fc: 2280 movs r2, #128 ; constant
20fe: 4b02 ldr r3, [pc, #8] ;base address of portgroup 0
2100: 0392 lsls r2, r2, #14 ; constant
2102: 619a str r2, [r3, #24] ; store to portgroup.outset
PORT->Group[0].OUTCLR.reg = (1 << 21);
2104: 615a str r2, [r3, #20] ; store to portgroup.outclr
2106: 4770 bx lr ; return
; Look! No padding! (lucky)
2108: 41004400 .word 0x41004400 ; base address of portgroup0
void tryREG() {
REG_PORT_OUTSET0 = (1 << 21);
210c: 2380 movs r3, #128 ; constant
210e: 4a03 ldr r2, [pc, #12] ; get address of OUTSET0
2110: 039b lsls r3, r3, #14 ; constant
2112: 6013 str r3, [r2, #0] ; store to OUTSET0
REG_PORT_OUTCLR0 = (1 << 21);
2114: 4a02 ldr r2, [pc, #8] ; get address of OUTCLR0
2116: 6013 str r3, [r2, #0] ; store to OUTCLR0
2118: 4770 bx lr ; return
211a: 46c0 nop ; padding
211c: 41004418 .word 0x41004418 ; OUTSET0
2120: 41004414 .word 0x41004414 ; OUTCLR0
Somewhat subject to the whims of the compiler, alas. It was doing weird things when I used IOBUS in trystruct, since it was able to "construct" the port address rather than using a literal value. And I guess it doesn't know that IOBUS is faster than normal memory (?), so it likes to break up consecutive store operations. Sigh.