With modern compilers and -O2, is volatile still required for hardware registers, or are memory barriers enough?
Still required: volatile stops the compiler from caching or eliding the accesses. Barriers order accesses but do not force them to happen. You often need both on multi-core parts.