The fields are defined __IO, if you grep for the headers you'll find __IO is defined as a short-hand for volatile (+ possibly something else).
This is highly important; usually you want the IO port operations to happen exactly as you write them, not to be combined. This is significant with many peripherals: for example, a status register readout may clear flags, and you want to do that exactly once. Or reading a data register pops one from the fifo. You want to do that exactly as many times as you have written it.
It appears your particular use case would be fine with more optimized behavior. You still can't redefine this particular IO port without the volatile qualifier, because then the compiler might optimize too much, i.e., not just combine the two operations, but also perform the operation at a different place altogether (so that it has been performed when the function returns!)
The solution is indeed to type-pun via union, giving all possible access patterns you would like to use, yet still keep the volatile qualifier. If you don't want to modify the headers, you can also just type cast.
Often you need to write out read-modify-write explicitly, which isn't a bad thing:
volatile type_t volatile_thing;
type_t tmp = volatile_thing;
tmp |= 0x123;
tmp += 42;
tmp *= 3;
volatile_thing = tmp;
this code reads and writes volatile_thing once, and only once, yet you can do multiple operations on it and let compiler optimise those, because the tmp variable isn't volatile.