I see that after 50 years, C has finally gained an official null pointer value and type! "nullptr" and "nullptr_t".
That wouldn't be so interesting, but it highlights the complexity of specifying a language and implementing a specification. For what sounds at first hearing a lot like "#define nullptr 0" the Clang release notes identify 7 syntactic cases where the C standard and the C++ standard disagree about whether "nullptr" should be acceptable in a ___location. Clang takes each side about equally and it sounds like they are waiting for the standards groups to fix the standards.
Nothing changes. memseting a struct to zero has always been and still is implementation defined behavior, as there are no guarantees on the bit representation of a null pointer. [1]
The C11 standard says that a null pointer constant is defined as follows: "An integer constant expression with the value 0, or such an expression cast to type void , is called a null pointer constant."
The new definition is: "An integer constant expression with the value 0, such an expression cast to type void , or the constant nullptr are all a null pointer constant."
The reason for adding nullptr, is because NULL is roughly defined as a macro that expands to "0" or "(void*)0". This causes trouble with the addition of type detection trough _Generic and now typeof.
memseting a struct to zero has always been and still is implementation defined behavior, as there are no guarantees on the bit representation of a null pointer.
The number of bits in a char has also been implementation defined, yet most if not all code either doesn't care, or assumes it to be 8. This is another one of those "in theory, theory and practice are the same; in practice, they're not" things.
N.B. nonzero bitpattern nullptrs are common in C++ for pointer-to-member s, which are typically implemented as an offset from the start of the structure - with an all-0 bitpattern being a valid non-null pointer to the first member of said structure.
struct S { int i; };
int S::*a = &S::i; // probably 0 under the hood
int S::*b = nullptr; // probably -1 under the hood
int S::*c = 0; // probably -1 under the hood (no typo!)
Consider using brace initializers as an ergonomic alternative:
struct S d = { 0 };
struct S e = {}; /* as of C23 or C++ */
That's exactly the point of "implementation defined". The code is fine on all those compilers/architectures where the expectation is valid. It just isn't portable to those exotic/hypothetical architectures where it doesn't, which apparently doesn't bother you in your practice. Nothing wrong with that.
The null address isn't necessarily 0. When a literal 0 is assigned to a pointer it can be magically changed to a platform's true null. Memset() doesn't have this behavior. This isn't relevant on any modern systems but it is an affordance of the C standard. That is the true significance of nullptr. It obsoletes the magic 0 literals.
Sure but on what modern platform isn't null equal to 0? Do modern C standards need to support hardware that no longer exists and will likely never exist?
Just like with undefined behaviour, IMHO the C standards are deliberately underspecified to allow the language to remain applicable to just about any feasible computer.
Notably, function specialization optimization has been finally enabled by default, although it still seems to only be capable of constant propagating variables of register size [1].
The heuristics are, understandably so, somewhat conservative [2], and I presume there are some projects that could benefit from enabling `-mllvm -funcspec-for-literal-constant`, raising `-mllvm funcspec-max-clones=3` ad lowering `-mllvm -funcspec-min-function-size=100`.
I don't think it is likely to help C++, as templates and `auto` arguments (in C++20) were already specializing.
These probably cover most of the cases you'd encounter there.
I'm less familiar with Rust, but I'd have assumed trait implementations specialize there as well.
> I'm less familiar with Rust, but I'd have assumed trait implementations specialize there as well.
Yeah - as I understand it, traits + generics in rust monomorphize basically everything.
If anything, for rust I want the compiler to learn to do the opposite of this optimization. I'd like the compiler to be able to emit code which uses dynamic dispatch instead of monomorphization when optimizing for code size, or in cold functions. Monomorphization makes the rust compiler slower and rust binaries larger. Outside of hot loops, its often not worth it.
You can change this manually by rewriting rust functions to take dyn function pointers instead of <F: Fn> generic arguments. But I'd prefer the compiler to make that choice for me automatically based on PGO or from -Oz.
Primarily companies at this point, with some ongoing input from individual contributors or universities / research organisations. I'm at Igalia (an open source software consultancy), so contributions from me and my colleagues are paid for by companies within the ecosystem. Vendors like SiFive and Rivos have particularly active contributors on the RISC-V backend, but there's many more.
From organisations, I understand LowRISC to be involved https://github.com/lowrisc. I'd also presume Espressif, as they have a growing number of risc32 chips.
I was previously at lowRISC but yes, they're still involved. My ex-colleague Luís Marques authored the patch I mentioned in the article to optimise global address accesses in the medany code model for instance.
That's fairly interesting about lld speedup. What I gather is it's faster if you build lld itself with mimalloc, and the reason they chose mimalloc was largely because it was easy or at least possible to integrate mimalloc with the project on supported platforms, and jemalloc or tcmalloc was harder. Which makes me wonder: will distro packages build it with mimalloc? Do they have to do something, or is on by default?
It appears that simply preloading either mimalloc or tcmalloc has a significant speedup for clang-16 and -15 for that matter. I wish these Linux systems came with better defaults!
With mimalloc, a medium-sized C++ test repeatedly takes exactly 43.7s to build and link on this system, while with the default allocator it's all over the place from 46s to 49s. I would loosely characterize that as a free 10%.
I think this is the first time in quite a few releases where we end up with an overall regression. However, the situation is not quite as bad as it looks.
In particular, the large regression on the right is due to enabling C++17 by default. The close to two times slowdown in 7zip O0 builds comes down to STL headers becoming 2-3 times as large in C++17.
While this is sad, and I had to edit out some choice words on the C++ standardization committee (I hear that this gets even worse in C++20), at least this does not affect non-clang compilers (e.g. Rust) and C code.
I've been working both with Clang and MSVC nearly daily for the last 10 years. My impression during that whole time is that MSVC (on Windows) is universally slower than Clang (on Linux or macOS, Clang on Windows is also not exactly a performance wonder though).
Clang might have become slower over time, but it still has a long way to go until it gets down to MSVC's average performance.
Shouldn't people expect compile times to be slower at -O3? Presumably the improvements are better optimizations, many of which trade compile time for execution time. Now if it's getting slower at -O0, that might be a concern.
There are dynamic interpreted languages that use LLVM as a way to compile fast paths (the details are more complicated). For such languages how fast the heavily-optimized code is compiled matters. Julia is a great example.
In this particular case though, no it does not seem it matters, as the slowdown is related to C++ headers in the newer standard lib, not to the low-level compilation.
Is Julia using -O3? I would find that surprising as it is not recommended. -O3 could be a good starting point, but with maturity a frontend should be evaluating individual passes.
"One of the most common mistakes made by new language frontend projects is to use the existing -O2 or -O3 pass pipelines as is. These pass pipelines make a good starting point for an optimizing compiler for any language, but they have been carefully tuned for C and C++, not your target language. You will almost certainly need to use a custom pass order to achieve optimal performance."
The tl;dr is that check builds are faster (i.e. optimization quality has improved) while debug/opt builds are mixed, but slightly slower when averaged over everything.
It can't be used to build against recent versions of musl or static glibc because they require thread-local variables, meaning it pretty much can only be used with dynamic glibc.
AFAIK the original Clang 15 broke a lot of software, including pretty much all "configure" scripts generated by Autoconf due to some default options[0] that treated some of the code used for various feature checks invalid and the end result was checks failing even though they should have passed - often with barely any notification to the user (the user would see a "no" instead of a "yes" in some check). This was reverted in 15.0.1 but that was only to give people time to update until Clang 16 was released, the Clang developers wanted to get ahead with the change regardless and expected upstream developers to fix their code (this includes not only configure scripts but also projects using old style code - and AFAIK it wasn't only Autoconf that made such tests, though the focus was on it).
The problem however is that thousands of projects that still haven't updated. Gentoo developers are trying to patch the packages but as can be seen in [1] that tracks all the relevant bugs, not even half of them are fixed. Also those are fixes in Gentoo - AFAIK while the Gentoo devs are trying to upstream their patches, not everything is patchable. Note that i mention Gentoo since they were the ones that showed up in the discussions i've seen, but the same is case with all distributions (and projects like Homebrew, etc) - it is just that most likely Gentoo users were affected the most as they build everything from source and can use Clang for that.
But there seems to be two additional issues with Autoconf specifically (remember that this isn't just about Autoconf, it is just that due to how the existing Autoconf checks were written, configure scripts generated by it are the most likely to encounter the issue):
The first is that while there have been fixes for the failing checks, there hasn't yet been a new release of Autoconf that uses these checks - when i run a freshly regenerated configure scripts on my PC with the latest stable release as provided by my distribution i still get the failing checks if i force Clang 15 to use the options that were previously disabled. This means that any configure scripts generated right now will fail when those become enabled again.
The second is that some widely used macros in Autoconf[2], those to find if a library exists and to find which library exposes a symbol (e.g. if libm is needed to be linked to access the functions in math.h or they are part of the standard library - different OSes still need different options here) do it by writing and attempting to link a small C program that defines the function as (IIRC) "char the_function_name();" with code that calls that function (the function is not actually called, the check only tries to compile and link that program) and if that fails it assumes the library does not exist / does not support the function. Problem being is that the above fails with Clang 15.0.0 because of the "()" part (should have been "(void)" but when the check was written many years ago it was meant to work with pre-C89 compilers that didn't support the void keyword). This is fixed in Autoconf now but as mentioned above, no release yet so any currently made brand new configure script will have this check failing. But the more important issue is that this check seems to be a ticking bomb - at least as far as Clang is concerned - since it can trigger various errors in the future and judging from the responses by one of the Clang developers[2] (i actually recommend reading the entire thread) they seem to be of the opinion that treating this as an error in the future is something they might do as it is technically UB. As of right now there hasn't been any fix for this and from the discussions from that thread, the Autoconf developers do not seem to be able to figure out a generic solution (the only solution they found is to include prototypes for all known libc functions but this wont work if you, e.g., want to figure out if the system has curses or ncurses - as an example NetBSD needs -lcurses not -lncurses, at least last time i checked). So even if a new Autoconf release is made now, the configure scripts might still break in the future if Clang developers decide to make this an error (AFAIK even without a release some distros have patched Autoconf to fix the errors introduced and reverted in Clang 15.0.0 but even those do not have a fix for the above).
Also all these are about freshly generated configure scripts, in practice there are projects out there where their configure scripts haven't been regenerated for a long time (often because there hasn't been a new release for a long time). Though many of them can be brought up to date with "autoreconf -fi", assuming of course the fixes i mentioned above are available.
(FWIW from what i can tell this isn't a problem with GCC and the devs contemplated adding a special check for Autoconf generated code to skip the errors if they decide in the future to make these errors, but of course this only affects Autoconf - while i focus on it, Autoconf isn't the only affected program - and regardless the entire point of Autoconf is to allow whatever compiler and tools there are around instead of requiring specific ones from the user)
Considering the above i wonder if Clang 16 decided to proceed with the changes regardless. The release notes do mention some changes that might affect configure scripts[4] but i'm not sure if the notes there are just some heads up for people making configure scripts to check for potential breakage in the future or honeyed words for "technically not every configure script will break, so hey, we warned you". This is a bit amusing since if you check in the discussions i linked to, a concern others had was that the release notes in the version of Clang 15 barely made a mention of the potential for breakage.
(as a side note, all the above focus on Autoconf might sound like me ragging on Autoconf/Autotools, but this wouldn't be farther from the truth - having built a lot of software from source code, including on minimal non-Desktop non-Linux Unix-like environments like NetBSD, i absolutely love that Autotools-based releases do not require anything that isn't already there in a Unix-like environment to work and even recently decided to use it for some of my own projects because of that, which is how i found all the above. From a practical perspective it isn't that hard to work around the issues even with existing configure scripts - AFAIK some of Gentoo patches do exactly that - so despite all the above it is still my favorite build system to build stuff with as a user... as a developer, well, i haven't found it to be any worse than other build systems)
The autotools deserve their share of criticism here. They should be solving the portability problems of the present day, but they are still stuck in the past solving the portability problems of the 1990s.
Still catering for pre-C89 compilers in 2003 was questionable. Doing it 20 years later in 2023 is beyond questionable. It's not a portability problem any one of us has had to care about in decades. And it's now a source of problems because it's breaking with current tools. That defect is squarely upon them.
Portability problems are of their time, and old workarounds can be dropped once they are no longer required. Many of the Autotools checks and workarounds are for platforms which were retired decades ago. The whole lot could be deleted. When you look at the cost:benefit of them, they are utterly niche and are largely untested by anyone. When you actually look at which tools are portable in practice, the autotools are usually worse than the alternatives like CMake, because they ceased to keep up-to-date with contemporary systems.
As the other poster mentioned, the fact that the generated code is embedded in thousands of projects, all of which need independently updating is another aspect of its design which should also have been retired once the tools reached maturity. There is zero need for this, just require them to be installed like any other build dependency.
I think your criticism is a bit outdated itself :-P, Autotools hasn't been supporting pre-C89 compilers for a long time now. C89 is the minimum requirement, the reason that the pre-C89 code was there was most likely because pretty much all compilers so far accepted K&R style and thus it still worked, so nobody bothered to update it. This code has now been replaced, the bigger issue with that is that there hasn't been a new release with the fixes (some distros do have the patches applied though - but sadly not mine, though i guess i can just download them myself).
Also as i replied to the other comment, you can still regenerate the configure script if you have Autotools installed, however the benefit of Autotools is that you don't have to have Autotools installed to build a project (you can always generate those scripts on another computer).
You're missing the point. "Nobody bothered to update it" is key. It's not being proactively maintained, and as a result it's looking to the past, and ignoring the present. And it's not just about this one specific defect, that's just the example of the moment. And being fixed but not released is just the icing on the cake. These tools are no longer being actively and properly maintained.
I'm really asking about the bigger picture about the whole philosophy of what problems the Autotools are trying to solve, and why. Yes, the Autotools are ostensibly for "portability", but if you dig a bit deeper you start to appreciate firstly how outdated it is in terms of the specific portability problems it tackles, with many current portability problems being completely unaddressed, and how superficial a lot of its solutions are. At one time, the Autotools were the showcase for how to solve portability problems, but this is no longer the case.
Regarding regeneration of the scripts. Of course you can. But embedding is superficially helpful but ignores the bigger-picture issue of having outdated junk in thousands of projects' release artefacts. No other build system actively recommends embedding themselves in source releases. And we all use them without trouble. If anything, embedding generated files also exacerbated the (historically) poor compatibility story of Autoconf and Automake, back when it was being actively developed. Embedding hid this to some extent, though developers often had to have several versions of each installed. The only reason this stopped being such a problem is because the maintenance of both effectively ceased. (Yes, I know they are still nominally maintained, but they are in end-of-life maintenance at this point.)
> You're missing the point. "Nobody bothered to update it" is key. It's not being proactively maintained, and as a result it's looking to the past, and ignoring the present.
Nobody bothered to updated not because of maintenance but because it wasn't broken. The moment it was pointed out that this is an issue the developers fixed it - this was already fixed in the repository last year.
> I'm really asking about the bigger picture about the whole philosophy of what problems the Autotools are trying to solve, and why.
Autotools, or at least Autoconf, are trying to solve the problem of figuring out at build time if something is available while minimizing the dependencies the user (builder) has to provide to what is assumed to be there.
> Yes, the Autotools are ostensibly for "portability", but if you dig a bit deeper you start to appreciate firstly how outdated it is in terms of the specific portability problems it tackles
Sorry but this "outdated" aspect is not specific, ask different people and they'll come up with different answers of what is outdated. The only thing that matters is if it works and so far it seems to work.
I'm not going to defend how Autotools/Autoconf work or are implemented, because they are a mess, but i do defend both the idea of minimizing dependencies for the users and the test-based approach they are using.
> embedding is superficially helpful but ignores the bigger-picture issue of having outdated junk in thousands of projects' release artefacts.
As i wrote, the question is a matter of if they work or not. If they work, then that is perfectly fine, they do what they are supposed to. If they do not, they can be regenerated using Autotools, which brings it back to almost the same position as other build systems that need themselves available to work.
And i write "almost" because Autotools are still better here: chances are they'll still work and if you need to regenerate the scripts you can do it in another machine that has the dependencies already available.
> No other build system actively recommends embedding themselves in source releases. [..] And we all use them without trouble.
Sorry but this comes in complete contrast with my experience actually building software with Autotools, especially on platforms that aren't your standard desktop Linux setup and do not have everything and the kitchen sink there. Projects using autotools just work there and provide the best UX of all other systems.
Hell, i mentioned i recently decided to use Autotools for some of my own projects - i didn't because they were nice or easy to use (though they weren't particularly hard either). If anything as a developer i find something like premake much easier and convenient but i also care about the user's experience and Autotools not only do much better there but also give a bigger bang (features) for buck (effort from my side and dependencies for the user).
Also Autotools do not embed themselves in the source releases, they generate script files to generate the makefiles. Autotools have dependencies for themselves that are not required to build the software releases (unless you want to regenerate the scripts).
I would call this a design flaw with autotools. It would be ideal if you only had to update autotools to work around this. But it seems like autotools brittle configure scripts are spread out between thousands of projects instead of a single place. The amount of brittle code should be minimized and easily updatable.
Not needing to have Autotools installed is certainly a feature and not a design flaw, it is the entire purpose of the project and as i wrote near the end it is the reason i personally prefer using it compared to other projects.
Also note that you can still regenerate configure scripts if you have Autotools installed, so it isn't like what you describe cannot be done with how Autotools are right now. According to a comment in the discussions i linked to, Debian already regenerates the configure scripts as part of their building process.
The difference is that unlike other tools, you don't need to do that - for example i can easily download the source of some program with an outdated configure script on my main Linux PC, regenerate the script with my PC's version of Autotools and then copy the resulting package to another machine running -say- NetBSD that doesn't have Autotools (or any of its dependencies) installed and be able to build it there.
I've seen the bugs in Gentoo, so I am aware that this is annoying (and some of them I'm responsible to fix), but I think it's a good thing this is finally being cleaned up.
The issue here is that functions are called without a function prototype, aka the proper headers included. The compiler basically guesses what the function prototype is. This is certainly error-prone, and the fact that autoconf relies on tons of test code pieces that aren't really correct C code is if anything worrying.
It's also probably a good thing that this shows up first in clang, as most linux distros are built with gcc, so you have a testbed to fix all that stuff. gcc should eventually catch up and introduce the same default.
> [...] treated some of the code used for various feature checks invalid and the end result was checks failing even though they should have passed - often with barely any notification to the user (the user would see a "no" instead of a "yes" in some check)
This is a perfect illustration of conceptual bankruptcy of the autoconf approach. Using compile/link tests and basing a decision on whether they succeeded or failed without distinguishing the reasons for failure will inevitably lead to silent false negatives.
> This is a perfect illustration of conceptual bankruptcy of the autoconf approach. Using compile/link tests and basing a decision on whether they succeeded or failed without distinguishing the reasons for failure will inevitably lead to silent false negatives.
The tests are actually fine as an idea, it is much better to perform tests to see if something is there instead of trying to guess based on other means, like OS name or whatever.
The problem isn't the idea of testing for the features you need (like checking if a header file or function is there), it is that the code generated for some of the tests was pre-C89. This was accepted by pretty much all C compilers from the 80s to today, until Clang 16 decided not to accept it anymore.
Technically they could have taken a different approach like checking the libraries themselves directly (for the case of libraries), but that'd require knowledge about the library formats and availability and i'm almost certain that the current approach gives the largest portability while requiring the least knowledge about that system.
Even now it is Clang 16 specifically that has the issue and it was already been fixed upstream last year.
Apple turned this on several years ago when targeting Apple silicon, which might’ve influenced the decision. Obviously, software targeting macOS adapted; it was substantial effort on the part of packagers and open source projects. I wonder if they didn’t feel that pain and saw that things mostly ended up working in the end and used it to provide support for pushing the change through.
That wouldn't be so interesting, but it highlights the complexity of specifying a language and implementing a specification. For what sounds at first hearing a lot like "#define nullptr 0" the Clang release notes identify 7 syntactic cases where the C standard and the C++ standard disagree about whether "nullptr" should be acceptable in a ___location. Clang takes each side about equally and it sounds like they are waiting for the standards groups to fix the standards.