3 ms·
zinc's "ioregs" macro system takes care of that sort of optimization quite nicely these days, actually. For each peripheral register, one of the things it gene
by mokus 11y ago
zinc's "ioregs" macro system takes care of that sort of optimization quite nicely these days, actually. For each peripheral register, one of the things it generates is an "update" structure that the optimizer can eliminate, which allows you to chain updates to multiple fields in a single write. For example, in my stm32f4-discovery "blinky" example it does the bsrr writes to flip 2 LEDs in a single write (I've inspected the generated asm to verify this).
EDIT: nevermind, I realize I misunderstood; you're referring to the optimizer being able to see that both reg addresses have the same high 16 bits. I thought you were referring to the actual interactions with the register contents. Yea, that makes sense.
EDIT 2: for what it's worth, i did a quick test just now and found that moving the addresses into the source actually made the generated code significantly worse. I'm just tinkering around on a lunch break and I need to get back to real work, so I can't investigate too deeply, but the 2 biggest factors I see are that (1) it simply doesn't perform the optimization you describe, and (2) it makes things even worse because rather than addressing relative to the peripheral base address it loads a complete new constant address for every operation.
- mindslight 11y agoI just remembered, hardcoded addresses are actually even more important for utilizing "bit banding" for atomic writes to peripheral registers. Not that you couldn't find a way to do this with addresses in the linker script, but it's more straightforward to handle efficiently with compiler optimization. (And if one isn't atomically writing to control registers when interrupts are enabled, you are most likely building on a foundation of nondeterministic bugs. Rust's linear types kind of inform you of this, but to do anything useful the temptation is to unsafely share across threads) re edit 2: Really? I'd obviously looked at my generated code and saw the expected improvement, so we must be doing something different.
- mokus 11y agoRe edit 2: I'm pretty new at Rust so it's entirely possible I did something in an unusual way that caused the compiler to emit worse code than it would otherwise. My quick hack to test was to declare the registers as &mut, and assign them by casting a u32 literal to *mut, dereference and take &mut of that. It was just the first thing that actually compiled ;). I did also try directly transmuting the u32 to &mut and got the same output. It's also possible something has changed in rustc since you were playing with it.
- mindslight 11y agoIt's very possible rustc has changed, but I hope not because the generated code was looking pretty good. My basic register write is volatile_store(($base as * mut u8).offset(idx as isize) as * mut u32, v); $base is just a hex literal address. There is an extra cast because I defined 'idx' in terms of bytes. I might not have been concerned about the absolute smallest code when I accessed $base+4 then $base+8 (use of a small offset vs loading a second constant), but I definitely observed eliminating redundant loading of the high half between different registers. I was compiling with "-O -Z no-landing-pads -C lto".