Is there any implicit understanding in the community that byte types will inevitably be added to LLVM?
Among the people who are familiar with such things, yes. An RFC on the topic will be posted in the near future.
HN user
Blog: http://nikic.github.com/ Email: nikic@php.net
Is there any implicit understanding in the community that byte types will inevitably be added to LLVM?
Among the people who are familiar with such things, yes. An RFC on the topic will be posted in the near future.
That was ambiguously phrased. The point I was trying to make here is that we don't have the situation that is very common for open-source projects, where a project might nominally have a 100 contributors, but in reality it's one person doing 95% of the changes.
LLVM of course has plenty of contributors that only ever landed one change, but the thing that matters for project health is that that the group of "top contributors" is fairly large.
(And yes, this does differ by subproject, e.g. lld is an example of a subproject where one contributor is more active than everyone else combined.)
Yes, the Orc C API follows different rules from the rest of the C API (https://github.com/llvm/llvm-project/blob/501416a755d1b85ca1...).
This particular case isn't really due to pattern matching -- it's a result of a generic optimization that evaluates the exit value of an add recurrence using binomial coefficients (even if the recurrence is non-affine). This means it will work even if the contents of the loop get more exotic (e.g. if you perform the sum over x * x * x * x * x instead of x).
This depends a lot on what you're doing with LLVM. If you are just using LLVM as a code generation backend for your language frontend, you generally do not need an LLVM fork.
For example, while Rust does have an LLVM fork, it just exists for tighter control over backports, not because there are any modifications to LLVM.
LLVM is a trap.
Is it? I think Rust is a great showcase for why it isn't. Of course it depends somewhat on your compiler implementation approach, but actual codegen-to-LLVM tends to only be a tiny part of the compiler, and it is not particularly hard to replace it with codegen-to-something-else if you so desire. Which is why there is now codegen_cranelift, codegen_gcc, etc.
The main "vendor lock-in" LLVM has is if you are exposing the tens of thousands of vendor SIMD intrinsics, but I think that's inherent to the problem space.
Of course, whether you're going to find another codegen backend (or are willing to write one yourself) that provides similar capabilities to LLVM is another question...
You bootstrap extra fast, you get all sorts of optimization passes and platforms for free, but you lose out on the ability to tune the final optimization passes and performance of the linking stages.
You can tune the pass pipeline when using LLVM. If your language is C/C++ "like", the default pipeline is good enough that many such users of LLVM don't bother, but languages that differ more substantially will usually use fully custom pipelines.
I think we'll see cranelift take off in Rust quite soon, though I also think it wouldn't be the juggernaut of a language if they hadn't stuck with LLVM those early years.
I'd expect that most (compiled) languages do well to start with an LLVM backend. Having a tightly integrated custom backend can certainly be worthwhile (and Go is a great success story in that space), but it's usually not the defining the feature of the language, and there is a great opportunity cost to implementing one.
That's a great point. I initially thought we could assume no exposure for loads with non-pointer-compatible TBAA, but you are right that this is not correct if the memory has been laundered through memcpy.
That type punning through memory does not expose or synthesize memory. There are some possible variations on this, but the most straightforward is that pointer to integer transmutes just return the address (without exposure) and integer to pointer transmutes return a pointer with nullary provenance.
At least at a skim, what this specifies for exposure/synthesis for reads/writes of the object representation is concerning. One of the consequences is that dead integer loads cannot be eliminated, as they may have an exposure side effect. I guess C might be able to get away with it due to the interaction with strict aliasing rules. Still quite surprised that they are going against consensus here (and reduces the likelihood that these semantics will get adopted by implementers).
Interestingly, Windows on ARM hasn't made it up to Tier 1 yet.
An RFC for that has been submitted recently: https://github.com/rust-lang/rfcs/pull/3817
One peculiar thing about the benchmark results is that disabling individual UB seems to fairly consistently reduce performance without LTO, but improve it with LTO. I could see how the UB may be less useful with LTO, but it's not obvious to me why reducing UB would actually help LTO. As far as I can tell, the paper does not attempt to explain this effect.
Another interesting thing is that there is clearly synergy between different UB. For the LTO results, disabling each individual UB seems to be either neutral or an improvement, but if you disable all of them at once, then you get a significant regression.
They do a bit more than that. One of the options (-disable-object-based-analysis under AA2) disables the assumption that distinct identified objects do not alias, which is disabling pointer provenance in at least one key place.
So I think this option very roughly approximates a kind of "no provenance, but with address non-determinism" model, which still permits optimizations like SROA on non-escaping objects.
Fun fact: GCC decided to adopt Clang's (old) behavior at the same time Clang decided to adopt GCC's (old) behavior.
So now you have this matrix of behaviors: * Old GCC: Initializes whole union. * New GCC: Initializes first member only. * Old Clang: Initializes first member only. * New Clang: Initializes whole union.
Where possible, undefined behavior at the LLVM IR level is always optional, controlled by various flags, attributes and metadata. Things like signed integer overflow, or even the forward progress guarantee are controlled that way, and admit a wide range of possible language semantics.
Provenance (and to some degree the entire memory model) is the big exception to this. It's a very core part of the IR semantics, and an optimizing compiler without provenance would have to use entirely different methodology to justify many optimizations. I don't believe that anyone has yet been able to demonstrate the feasibility of highly optimizing compiler without provenance.
NULL == NULL was already defined -- but NULL <= NULL wasn't :)
Huh, this is interesting. Normally the reason to become a CNA is to reduce the amount of bogus CVEs that are issued for your project due to security researchers trying to pad their portfolio.
Linux seems to have taken the reverse approach, by just filing their own bogus CVEs instead. One for every bug fix going into the kernel, rendering the CVE system useless.
LLVM actually also supports instruction-level granularity for fast-math (using essentially the same mechanism as things like unchecked_add), but Clang doesn't expose that level of control.
The background here is that "ctpop < 2" or "ctpop == 1" (depending on zero behavior) is LLVM's canonical representation for a "power of two" check. It is used on the premise that the backend will expand it back into a cheap bitwise check and not use an actual ctpop operation. However, due to complex interactions in the backend, this does not actually happen in this case (https://github.com/llvm/llvm-project/issues/94829).
It's true that MSVC doesn't have an equivalent of -fno-strict-aliasing, but that's because it just doesn't apply optimizations that assume strict aliasing in the first place. Admittedly the picture around signed overflow is more complicated, but it's essentially the same story.
Of course it's fine if the compiler doesn't implement this as an option, but rather as the default behavior -- as long as this is actually a documented guarantee, rather than "we just haven't implemented those optimizations yet, we might start exploiting this UB at any point in the future". I'm not familiar with what guarantees MSVC documents in this area.
By that standard the Linux kernel isn't written in C.
I think that in a very real sense, it isn't. The kernel is written in standard C plus a few hundred GCC extensions, of which additional compiler options are but a small part. It took very extensive effort to make the kernel compile with clang (which already was mostly gcc-compatible, but nowhere near enough for the kernel's level of extension use).
Even though Postgres compiles when -fwrapv and -fno-strict-aliasing are removed, and still passes all tests.
I think there is a lot of difference between "requires signed integer overflow to be well-defined because we explicitly rely on it" and "disable optimization based on signed integer overflow as a hardening measure, in case we got this wrong somewhere".
The implication of what you're saying seems to be that all of these open source projects each independently decided to "go there own way".
You kind of make it sound like use of these options is common. To the best of my knowledge, this is not the case, and these projects are rare exceptions, not the rule. So, yes, they decided to go their own way (which is perfectly fine.)
People can (and do) point at the C spec for fault of this and it is true that if the C spec was more strict then these compilers would not have the free pass to do these crazy miscompilations. However there is nothing stopping these compilers from just not doing that, there is nothing stopping them from just defining their own sane behavior for what the C spec defines as undefined behavior. It is no longer the case where we have a dozen or so C compilers that people need to target with their programs, the spec is not the bottom line anymore.
Some compilers (like gcc and clang) do give you the option to make most undefined behaviors well-defined, using flags like -fwrapv, -fno-delete-null-pointer-checks, -fno-strict-aliasing, etc.
The key thing to understand is that if you use these flags, you opt-in to a non-standard C dialect. Going forward, you are no longer writing C code. Your code is now non-portable. Your code can no longer be compiled by a compiler that does not support these special C dialects.
Using these C dialects is a perfectly sensible thing to do, as long as you understand that this is what you are doing.
The main performance-critical undefined behavior in C is provenance. The rest can be removed without major performance impact. (Which is not to say they can't give you 10% on specific workloads, just they aren't what is taking you from -O0 to -O3.)
A related student poster from EuroLLVM 2023: https://llvm.org/devmtg/2023-05/slides/Posters/05-Popescu-Pe... It tests the performance impact of some of the secondary undefined behaviors, and the result is basically what you'd expect. They do have impact, but if you average over all benchmarks the improvement is, at best, in the low single digits.
What's with RISC-V and these peculiar extension names?
You cannot do business and be anonymous. The tax man would eat you alive. At least outside of USA. You also need a license as sole proprietor or be incorporated as a company in order to make repetitive sales and not one-offs. So again, no anonymity whatsoever.
There is no anonymity between the creator and Patreon, or between Patreon and patrons. The anonymity is between the creator and the patrons.
I don't see any reason why this would be illegal, especially as Patreon does this in countless jurisdictions.
"by works" payment model is illegal. You can receive donations or have subscriptions or pay per view but you cannot "maybe deliver", "deliver maybe one work, maybe ten". Again, illegal as fuck.
This is a model that Patreon already supports (again in countless jurisdictions), so unless their legal counsel is completely incompetent it can hardly be "illegal as fuck".
Why does the flag use different representation depending on whether the value is negative?
From this kind of response, it's always so hard to tell whether it's easy for you because you have the relevant experience, or you just think it's easy because you don't.
A (very non-exhaustive) list of interesting questions about IR design are:
* What's your model for delayed UB and UB materialization?
* What's your provenance model?
* What's your strategy for information retention and handling of flow-sensitive facts?
* What inherent canonicality is there?
* What's your approach to target or domain specific extensions?
* What is your strategy for formal verification of refinements?
Questions like "What instructions does your IR support?" are fairly uninteresting, and are not what IR design is (mostly) about.
It's worth noting that LLVM's own IR design doesn't have a very good answer to some of those questions either, in part because making changes to an IR that is as widely used as LLVM IR is hard (been there, done that). It's easier to design a new IR than to change an existing one -- however, unless you just want to reinvent past mistakes, it is certainly helpful to have deep familiarity with an existing IR design and its problems.
JITs are generally one of the most challenging places to use LLVM, exactly because of its bad compile-time characteristics. There are some successful uses of LLVM based JIT compilers (e.g. Azul's Falcon JIT), but this is definitely a use case where you can't just use the standard optimization pipeline. You'll generally use a custom pipeline and likely only use LLVM for the second stage JIT compiler.
That said, I don't think your statement that LLVM optimizations only benefit you if you generate bad input IR is correct. It just sounds like they are not useful for your specific problem domain.
You can see the impact on Rust here: https://perf.rust-lang.org/compare.html?start=2d0a7def330ed9...
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.
FWIW, if you're on a distro with a default GCC toolchain, it's pretty typical to use clang together with libstdc++. E.g. if you use clang on Fedora, you're using libstdc++ by default. You have to explicitly pass -stdlib=libc++ to use libc++ instead. So I wouldn't consider this a particularly unusual build configuration.
Of course, it's different on distros with a default Clang toolchain, like macOS, where building with anything but libc++ would be pretty unusual.
I'm personally not convinced and I wonder why other 10 projects didn't suffer from the same problem?
This has a simple answer: The C++ projects in the test set are kimwitu++, Bullet, tramp3d-v4 and 7zip. kimwitu++ is built in c++14 mode and Bullet in gnu++98 mode, so they obviously aren't affected. tramp3d-v4 does show some impact, but it's a "single 2MB source file" style program, so STL headers don't dominate compile-time. 7zip is the only program that both uses C++ 17 and doesn't have huge source files, so it's the only one showing the major impact this has.
I would love to see the claim supported and checked by recompiling 7zip with -ftime-trace and then post-processed through ninjatracing to get the nice and detailed flamegraph depicting where exactly the build-time is spent. It's surprising that this hasn't been done already.
This was a single paragraph in a large blog post, why the heck would I be including a detailed analysis of how I reached that conclusion? On one out of dozens of compile-time regressions I investigated? Something I do all the time, and am likely an expert on?
If you really need to know, this claim was based on comparing callgrind profiles between -std=c++14 and -std=c++17 compilations.
Also, I think that the project sample distribution is not convincing. 9 out of 11 are rather very very small and roughly the same sized projects. Remaining two are not very very small but are still very small. So experiments are by definition made biased by not making better care of dataset distribution.
I would certainly love a larger testing corpus than CTMark currently provides.
Regarding restaurants, I think you generally get pretty good mileage out of Tabelog. A key problem for short-term visits is that highly-rated restaurants (>= 3.7) tend to fall into two categories: Either the restaurant is cheap and has a long queue outside it (typically things like ramen, but also cheap+good sushi) or it is expensive and requires a reservation. You generally can't just walk into this kind of restaurant unless you get lucky and it just happens to not be fully booked.
Placing a reservation requires a Japanese phone number (which you can get easily for the duration of travel) and most restaurants only accept reservations by phone. Some can be reserved on Tabelog directly, but this also requires a Japanese phone number, and some restaurants will call you back to confirm the reservation, so I'm not sure I would recommend this if you don't speak at least some Japanese. Even if you do reservations by phone, you should expect that most restaurants are already fully booked out for the next week. The best chance to get a reservation are restaurants that are a bit too expensive for their rating.
Ideally you would do reservations before you travel, but I'm not sure what the best way to do that would be in practice.