From 737371dca6a6a3c18e2f185f8ce893639dd03247 Mon Sep 17 00:00:00 2001 From: Per <Per Lindgren> Date: Mon, 8 Jan 2018 16:28:06 +0100 Subject: [PATCH] bare0 --- README.md | 130 +++++++++++++++++++--------------------------- examples/bare0.rs | 2 +- 2 files changed, 53 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 9d12913..d1bb158 100644 --- a/README.md +++ b/README.md @@ -322,7 +322,7 @@ unsafe extern "C" fn reset_handler() -> ! { ``` -Here we see that the `reset_handler` intitates static heap variables (by 0 or value accordingly) before calling `main` (and puts the processor in sleep mode in case `main` returns). The user `main` has the symbol `bare0::main`, which is unknown to the `cortex-m-rt` library (defining the generic `reset_handler`). Hence the, `reset_handler` calls a compiler generated (trampoline) `main`, that in turn calls `bare0::main` (the user `main`). +Here we see that the `reset_handler` intitates static heap variables (by 0 or value accordingly) before calling `main` (and puts the processor in sleep mode in case `main` returns). The user `main` has the symbol `bare0::main`, which is unknown to the `cortex-m-rt` library (defining the generic `reset_handler`). Hence the, `reset_handler` calls a compiler generated start item `main`, that in turn trampolines to `bare0::main` (the user `main`). Finally, we can have a look at the user `main`. @@ -348,92 +348,66 @@ fn main() { x += 1; 80004a2: 9802 ldr r0, [sp, #8] 80004a4: 1c41 adds r1, r0, #1 - 80004a6: 2201 movs r2, #1 - 80004a8: 4281 cmp r1, r0 - 80004aa: bf28 it cs - 80004ac: 2200 movcs r2, #0 - 80004ae: 2a00 cmp r2, #0 - 80004b0: 9101 str r1, [sp, #4] - 80004b2: d146 bne.n 8000542 <bare0::main+0xb4> - 80004b4: e7ff b.n 80004b6 <bare0::main+0x28> - 80004b6: 9801 ldr r0, [sp, #4] - 80004b8: 9002 str r0, [sp, #8] + ... + ```` + +We see that the compiler has genereted quite some code for the small `main` function. + +We can compile the `bare0` application in -- release mode instead. + +``` shell +> xargo build --release --example bare0 +> arm-none-eabi-objdump -S -C target/thumbv7em-none-eabihf/release/examples/bare0 > objdump_release +``` + +The resulting user main now looks like this. +``` rust +8000476 <_ZN5bare04main17h1039292c3948856dE.llvm.453292E5>: + loop { + x += 1; unsafe { X += 1; - 80004ba: f240 0104 movw r1, #4 - 80004be: f2c2 0100 movt r1, #8192 ; 0x2000 - 80004c2: 6809 ldr r1, [r1, #0] - 80004c4: 1c4a adds r2, r1, #1 - 80004c6: 2301 movs r3, #1 - 80004c8: 428a cmp r2, r1 - 80004ca: bf28 it cs - 80004cc: 2300 movcs r3, #0 - 80004ce: 2b00 cmp r3, #0 - 80004d0: 9200 str r2, [sp, #0] - 80004d2: d13d bne.n 8000550 <bare0::main+0xc2> - 80004d4: e7ff b.n 80004d6 <bare0::main+0x48> - 80004d6: f240 0004 movw r0, #4 - 80004da: f2c2 0000 movt r0, #8192 ; 0x2000 - 80004de: 9900 ldr r1, [sp, #0] - 80004e0: 6001 str r1, [r0, #0] Y = X; - 80004e2: 6802 ldr r2, [r0, #0] - 80004e4: f240 0300 movw r3, #0 - 80004e8: f2c2 0300 movt r3, #8192 ; 0x2000 - 80004ec: 601a str r2, [r3, #0] assert!(x == X && X == Y); - 80004ee: 9a02 ldr r2, [sp, #8] - 80004f0: 6800 ldr r0, [r0, #0] - 80004f2: 4282 cmp r2, r0 - 80004f4: d008 beq.n 8000508 <bare0::main+0x7a> - 80004f6: e003 b.n 8000500 <bare0::main+0x72> - 80004f8: 2001 movs r0, #1 - 80004fa: f807 0c01 strb.w r0, [r7, #-1] - 80004fe: e010 b.n 8000522 <bare0::main+0x94> - 8000500: 2000 movs r0, #0 - 8000502: f807 0c01 strb.w r0, [r7, #-1] - 8000506: e00c b.n 8000522 <bare0::main+0x94> - 8000508: f240 0004 movw r0, #4 - 800050c: f2c2 0000 movt r0, #8192 ; 0x2000 - 8000510: 6800 ldr r0, [r0, #0] - 8000512: f240 0100 movw r1, #0 - 8000516: f2c2 0100 movt r1, #8192 ; 0x2000 - 800051a: 6809 ldr r1, [r1, #0] - 800051c: 4288 cmp r0, r1 - 800051e: d0eb beq.n 80004f8 <bare0::main+0x6a> - 8000520: e7ee b.n 8000500 <bare0::main+0x72> - 8000522: f817 0c01 ldrb.w r0, [r7, #-1] - 8000526: 43c0 mvns r0, r0 - 8000528: 07c0 lsls r0, r0, #31 - 800052a: 2800 cmp r0, #0 - 800052c: d008 beq.n 8000540 <bare0::main+0xb2> - 800052e: e7ff b.n 8000530 <bare0::main+0xa2> - 8000530: f640 10dc movw r0, #2524 ; 0x9dc - 8000534: f6c0 0000 movt r0, #2048 ; 0x800 - 8000538: 6800 ldr r0, [r0, #0] - 800053a: f000 f9c1 bl 80008c0 <core::panicking::panic> - 800053e: defe udf #254 ; 0xfe + 8000476: e7fe b.n 8000476 <_ZN5bare04main17h1039292c3948856dE.llvm.453292E5> +``` + +The compiler has optimized away ALL your code!!! (`8000476: e7fe b.n 8000476` implements an infinite loop). Why you might ask, well it figures out that your the program has no *observable* effect (it will not output or change anything). + +We can make a small change to the program (violating the assertion, by commenting out `X += 1;`. After compilation the oject dump of user main looks like this: + +``` rust +08000476 <_ZN5bare04main17h1039292c3948856dE.llvm.E032C40E>: + +static mut X: u32 = X_INIT; +static mut Y: u32 = 0; + +#[inline(never)] +fn main() { + 8000476: b580 push {r7, lr} + 8000478: 466f mov r7, sp loop { - 8000540: e7af b.n 80004a2 <bare0::main+0x14> x += 1; - 8000542: f640 106c movw r0, #2412 ; 0x96c - 8000546: f6c0 0000 movt r0, #2048 ; 0x800 - 800054a: f000 f9b9 bl 80008c0 <core::panicking::panic> - 800054e: defe udf #254 ; 0xfe - X += 1; - 8000550: f640 1084 movw r0, #2436 ; 0x984 - 8000554: f6c0 0000 movt r0, #2048 ; 0x800 - 8000558: f000 f9b2 bl 80008c0 <core::panicking::panic> - 800055c: defe udf #254 ; 0xfe - ```` + unsafe { + //X += 1; + Y = X; + assert!(x == X && X == Y); + 800047a: f240 5034 movw r0, #1332 ; 0x534 + 800047e: f6c0 0000 movt r0, #2048 ; 0x800 + 8000482: f000 f802 bl 800048a <core::panicking::panic> + 8000486: defe udf #254 ; 0xfe +``` -We see that the compiler has genereted quite some code for the small `main` function. +So the Rust compiler is able to figure out that the assertion will be violated and merely calls the `panic` routine. So be aware, the Rust compiler is extremely aggressive in optimizing your code. On a side note the semantics of integer additions is slightly different between `dev` (normal/non-optimized) and `--release` (optimized) builds. In `dev` build the arithmics are checked and overflows result in a `panic`, in `--release`, arithmetics are unchecked (for performance reasons), and oveflows wrap (under two's complement semantics). To avoid ambiguity, you may use methods defined in the std/core library: + +- `wrapping_add`, `wrapping_sub`,returns the straight two’s complement result, +- `saturating_add`, `saturating_sub`, returns the largest/smallest value (as appropriate) of the type when overflow occurs, +- `overflowing_add`, `overflowing_sub`, returns the two’s complement result along with a boolean indicating if overflow occured, and +- `checked_add`, `checked_sub`, returns an `Option` that’s `None` when overflow occurs, and `Some(v)` elsewise. + +Those methods never `panic`, but code might be verbose, e.g., expressing `x - y + z` under wrapping arithmetics equates to `x.wrapping_sub(y).wrapping_add(z)`. To this end you may choose to use the [Wrapping](https://doc.rust-lang.org/std/num/struct.Wrapping.html) type. -We can compile the `bare0` application in -- release mode instead. -``` shell -> xargo build --release --example bare0 -``` # License diff --git a/examples/bare0.rs b/examples/bare0.rs index fdb45a2..d099aba 100644 --- a/examples/bare0.rs +++ b/examples/bare0.rs @@ -21,7 +21,7 @@ fn main() { loop { x += 1; unsafe { - X += 1; + //X += 1; Y = X; assert!(x == X && X == Y); } -- GitLab