Author Topic: How does microcontroller keep track of heap?  (Read 21895 times)

0 Members and 2 Guests are viewing this topic.

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: How does microcontroller keep track of heap?
« Reply #100 on: July 11, 2025, 08:37:59 am »
Quote
Most of the compiler support routines used by GCC are present in libgcc, but there are a few exceptions. GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.

https://gcc.gnu.org/onlinedocs/gcc/Standards.html

Hmmm. "Contrary to the standards covering memcpy GCC expects the case of an exact overlap of source and destination to work and not invoke undefined behavior"

By which they mean dst == src ?  That should work naturally in simple implementations, but if it happens often then I guess you can just add "beq dst,src,return" to optimise it, along of course with "beqz n,return".

I wonder whether gcc requires you to return the original value of dst, and the standard memcpy does. That generally adds one instruction :-)

Assuming the concern is size rather than speed (you don't want to drag in an 800 byte implementation of memcpy from libc), if only takes a couple of dozen instructions to implement all four routines.

I'm usually more annoyed at gcc NOT calling memcpy but using a completely unrolled copy of individual bytes when I told it to use memcpy.

https://godbolt.org/z/cbc8sW4M9
 

Online hans

  • Super Contributor
  • ***
  • Posts: 1998
  • Country: 00
Re: How does microcontroller keep track of heap?
« Reply #101 on: July 11, 2025, 08:39:07 am »
And it's up to you how you use it. If C ain't your thing, then don't use it. I rarely use C other than embedded software, and most often that's safety critical software. And that means I do a LOT of C, and it's a very warm, fuzzy place for us, absent of all the ridiculous pointer problems and other nonsense that seems to plague everyone else. Good standards and good patterns.

Ohhhh, just use Rust. Or SparkADA or whatever. Great. Where's all the tooling for unit testing and other things that actual safety critical software is kind of expected to have?

But you think C#...or Rust....or anything else makes anything better? You just have different bugs to deal with, so you have to put different standards in place to deal with them.

Side tangent.. anecdotally my time spent debugging my own code went down drastically as soon I started bumping it up to C++.

RAII is quite a powerful tool, also for unit testing. Unlike some I really like C++'s stricter type system. It forces better patterns, even though I can still type reinterpret_cast to get the same pointer casting shenanigans in C. Its usually indication of a code smell that needs attention. Sometimes a small amount of templating can fix the problem.

C# also allows "unsafe" code, and iirc Rust has similar facilities in place with their borrow checker, but I think its good these protections are in place.

You don't need them to get the job done though. Especially when you need to rely more on standards like MISRA, static analyzers, etc., then I acknowledge there is a huge gap. C++ could even be a major complication not worth going through.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1833
  • Country: de
Re: How does microcontroller keep track of heap?
« Reply #102 on: July 11, 2025, 09:36:10 am »
Assuming the concern is size rather than speed (you don't want to drag in an 800 byte implementation of memcpy from libc), if only takes a couple of dozen instructions to implement all four routines.

I'm usually more annoyed at gcc NOT calling memcpy but using a completely unrolled copy of individual bytes when I told it to use memcpy.

https://godbolt.org/z/cbc8sW4M9

If code size matters, GCC apparently emits a tail call to memcpy when -Os is used.

The unrolled inline code has, of course, no call overhead, and is branch-free, avoiding branch misprediction issues.

Another drawback of calling an external memcpy function is that it is not possible to optimize for a constant length at compile time. Instead, conditional branches are required inside memcpy to distinguish between different cases of the implementations. The same applies to the alignment of the source and destination. In many cases, the alignment is known at compile time by the caller of memcpy. However, if memcpy wants to use different code for different alignments, it can only decide dynamically at runtime. Again, any conditinoal branch can be mispredicted, penalizing performance.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: How does microcontroller keep track of heap?
« Reply #103 on: July 11, 2025, 09:44:56 am »
Yes and no.

I'm arguing that the tool has become so complex that very few people can understand it.

That was demonstrably the case in the 1990s, when even the standards committee was having severe problems.

Part of that was the desire for C to be all an Allen/torx/screw/nail, part the requirement to not break existing code. Those problems haven't changed, and people haven't got cleverer, so ...

But it's not the 90s. Casting rules are set in stone. Standards like MISRA are pretty well accepted and give good advice. The entire point of C is that it's a very good abstraction of a very simple general purpose processor that pretty much everything can implement. It's better thought of as a high level processor abstraction than a programming language.

And it's up to you how you use it. If C ain't your thing, then don't use it. I rarely use C other than embedded software, and most often that's safety critical software. And that means I do a LOT of C, and it's a very warm, fuzzy place for us, absent of all the ridiculous pointer problems and other nonsense that seems to plague everyone else. Good standards and good patterns.

Ohhhh, just use Rust. Or SparkADA or whatever. Great. Where's all the tooling for unit testing and other things that actual safety critical software is kind of expected to have?

But you think C#...or Rust....or anything else makes anything better? You just have different bugs to deal with, so you have to put different standards in place to deal with them.

Look, I'm not going to pretend that we've done, or do, a good job of training software engineers. I think our training is pitiful, and frankly it was pitiful as an undergrad in the late 80's and early 90's. I finally learned something when I had to do something real in grad school, and honestly I only really learned anything useful when I got my first job and real engineers showed me the way. So I get what you're saying.

But it's just a complicated thing if you're not playing in some web sandbox and if you're doing anything that's remotely close to hardware. It's just the nature of the beast.

MISRA is not, of course, C. It is a neutered subset of C. C zealots claim the non-neutered power of C is compelling, in which case MISRA cannot be an argument. For the avoidance of doubt, I like the objectives and feel of MISRA.

Ada and Spark developers will claim that if it compiles it is likely to work. While I'm sure that's an exaggeration, I suspect there is a grain of truth. Potentially useful, but hardly compelling.

Tooling is an important issue, but there are languages with much better tooling support than C can ever have - due to fundamental aspects of C.

I'm sure const casting is nailed down. I used it as an example of the dilemmas faced by the standards committees.

I doubt all of the issues raised in MacLarens object diatribe have been nailed down; I expect it will be a case for most developers of "operational semantics", i.e. suck it and see.

Please explain why Boehm felt it necessary to write his threads paper. Clearly it was aimed at experts, and probably committees.

Please explain why, if the standards committee is so expert, they didn't feel it necessary to have a memory model for a quarter of a century after it was obvious to other language developers with a wider experience.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: How does microcontroller keep track of heap?
« Reply #104 on: July 11, 2025, 10:23:31 am »
Assuming the concern is size rather than speed (you don't want to drag in an 800 byte implementation of memcpy from libc), if only takes a couple of dozen instructions to implement all four routines.

I'm usually more annoyed at gcc NOT calling memcpy but using a completely unrolled copy of individual bytes when I told it to use memcpy.

https://godbolt.org/z/cbc8sW4M9

If code size matters, GCC apparently emits a tail call to memcpy when -Os is used.

The unrolled inline code has, of course, no call overhead, and is branch-free, avoiding branch misprediction issues.

Another drawback of calling an external memcpy function is that it is not possible to optimize for a constant length at compile time.

???

Maybe you didn't follow the godbolt link, above, but it shows calling libc memcpy with a constant length and gcc replaces the call to memcpy with inline straight line unrolled code.

This will happen even if you're using your own memcpy at link time, because gcc has no way of knowing you're going to do that. So, for example, you can't do logging "Memcpy called with src=... dst=... n=..."
 
The following users thanked this post: SiliconWizard

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 5745
  • Country: Earth
Re: How does microcontroller keep track of heap?
« Reply #105 on: July 11, 2025, 08:17:06 pm »
C# also allows "unsafe" code, and iirc Rust has similar facilities in place with their borrow checker, but I think its good these protections are in place.

yes, the safety checks in C# (and similar mechanisms in Rust) are very helpful for catching out-of-bounds errors early and easy. Certainly, you can use something like .net nanoframework for MCU, however such languages rely heavily on heap allocation, which makes them less suitable for MCU where memory size is extremely limited. On MCU, static or stack allocation is typically preferred to avoid issues like fragmentation and unpredictable memory usage.
« Last Edit: July 11, 2025, 08:25:57 pm by radiolistener »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17828
  • Country: fr
Re: How does microcontroller keep track of heap?
« Reply #106 on: July 11, 2025, 08:58:05 pm »
Assuming the concern is size rather than speed (you don't want to drag in an 800 byte implementation of memcpy from libc), if only takes a couple of dozen instructions to implement all four routines.

I'm usually more annoyed at gcc NOT calling memcpy but using a completely unrolled copy of individual bytes when I told it to use memcpy.

https://godbolt.org/z/cbc8sW4M9

If code size matters, GCC apparently emits a tail call to memcpy when -Os is used.

The unrolled inline code has, of course, no call overhead, and is branch-free, avoiding branch misprediction issues.

Another drawback of calling an external memcpy function is that it is not possible to optimize for a constant length at compile time.

???

Maybe you didn't follow the godbolt link, above, but it shows calling libc memcpy with a constant length and gcc replaces the call to memcpy with inline straight line unrolled code.

This will happen even if you're using your own memcpy at link time, because gcc has no way of knowing you're going to do that. So, for example, you can't do logging "Memcpy called with src=... dst=... n=..."

Note that if using the '-ffreestanding' compilation flag, it will emit a call to memcpy at any optimization level. The linking pass will (or at least should) fail as it won't link with the standard lib, so memcpy will be undefined.

You can't directly use the equivalent builtin function in freestanding mode as far as I've tested (but I may have missed something) even when it could be inlined code as with your example - probably out if simplicity and consistency.
The compiler assumes that there is no access to the std library in freestanding mode, so, no memcpy for you. Not even when it could be inlined.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1833
  • Country: de
Re: How does microcontroller keep track of heap?
« Reply #107 on: July 11, 2025, 09:35:51 pm »
Assuming the concern is size rather than speed (you don't want to drag in an 800 byte implementation of memcpy from libc), if only takes a couple of dozen instructions to implement all four routines.

I'm usually more annoyed at gcc NOT calling memcpy but using a completely unrolled copy of individual bytes when I told it to use memcpy.

https://godbolt.org/z/cbc8sW4M9

If code size matters, GCC apparently emits a tail call to memcpy when -Os is used.

The unrolled inline code has, of course, no call overhead, and is branch-free, avoiding branch misprediction issues.

Another drawback of calling an external memcpy function is that it is not possible to optimize for a constant length at compile time.

???

Maybe you didn't follow the godbolt link, above, but it shows calling libc memcpy with a constant length and gcc replaces the call to memcpy with inline straight line unrolled code.

This will happen even if you're using your own memcpy at link time, because gcc has no way of knowing you're going to do that. So, for example, you can't do logging "Memcpy called with src=... dst=... n=..."

I think you misunderstand me. Had I not followed the Godbolt link, I would not have responded with: 'The unrolled inline code has, of course, no call overhead and is branch-free, thus avoiding branch misprediction issues.'

OTOH, I just wanted to point out some drawbacks of emitting a call to memcpy instead of emitting inline code. The former happens, for example, when you replace -O with -Os in your Godbolt example, or if you add -fno-builtin-memcpy. What I wanted to say is, if a memcpy implementaion wants to distinguish different cases (e.g. byte-wise copy versus word-wise copy), it can only make this decision at runtime, while inline code can make some of these decisions at compile time, omitting the comparison instructions for constant conditions and omitting the dead code for the unreachable branches.

I also don't recall saying that the compiler would generate different code if I provided my own memcpy at link time. Of course it cannot know that in advance.



Note that if using the '-ffreestanding' compilation flag, it will emit a call to memcpy at any optimization level.

As bruceholt pointed out earlier, it's actually -fno-builtin, which prevents memcpy() from being replaced by __builtin_memcpy(). Whether __builtin_memcpy emits inline code or a call to memcpy depends on the optimization level1). Since -ffreestanding also implies -fno-builtin, the same applies to -ffreestanding.

1) Edit: I think it's not only a matter of optimization level. __builtin_memcpy may generally fall back to emitting a call to memcpy in some cases, for example if the inline code would become too complex or too big.

Quote
You can't directly use the equivalent builtin function in freestanding mode

Not sure what you mean exactly. Your program can call __builtin_memcpy() in freestanding mode, and GCC emits either inline code or a call to memcpy, depending on optimization level. See https://godbolt.org/z/na18z77r3
« Last Edit: July 12, 2025, 06:38:03 am by gf »
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 8913
  • Country: 00
Re: How does microcontroller keep track of heap?
« Reply #108 on: July 11, 2025, 10:18:40 pm »
Quote
On MCU, static or stack allocation is typically preferred to avoid issues like fragmentation and unpredictable memory usage.

I would have thought stack allocation would be worse than dynamic allocation, since there is no way to return a fail due to lack of memory.
 

Offline John Coloccia

  • Super Contributor
  • ***
  • Posts: 1622
  • Country: us
Re: How does microcontroller keep track of heap?
« Reply #109 on: July 11, 2025, 11:53:59 pm »
Quote
On MCU, static or stack allocation is typically preferred to avoid issues like fragmentation and unpredictable memory usage.

I would have thought stack allocation would be worse than dynamic allocation, since there is no way to return a fail due to lack of memory.

The stack doesn't fragment. Also, you can determine max stack required as long as you can determine your max recursion depth, assuming you're recursing for some reason.

The idea is that if you understand your problem well, and define your limits, it's basically impossible to run out of memory at runtime. Part of that strategy is statically defined memory, and an upper bound on stack usage.

And there are clever ways of estimating your max stack usage.
« Last Edit: July 12, 2025, 12:05:48 am by John Coloccia »
 
The following users thanked this post: PlainName, Siwastaja


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->