[HN Gopher] A Review of the Odin Programming Language ___________________________________________________________________ A Review of the Odin Programming Language Author : gingerBill Score : 93 points Date : 2022-09-11 13:34 UTC (9 hours ago) (HTM) web link (graphitemaster.github.io) (TXT) w3m dump (graphitemaster.github.io) | michannne wrote: | > This means valgrind cannot really detect memory issues as well | since Odin has it's own internal allocators. helgrind cannot work | because Odin doesn't use pthread primitives. ltrace cannot work | because that just provides wrappers over every libc function, etc | | Unfortunately, I've gone through too many bugs that had to be | fixed through valgrind debugging that I'm unwilling to part with | it | gingerBill wrote: | The Odin team are working to put valgrind, helgrind, callgrind, | memcheck, etc in to the Odin core library to provide better | debugging tools! Along with many other debugging tools. | | https://github.com/odin-lang/Odin/tree/master/core/sys/valgr... | verdagon wrote: | One thing I like about Odin is that you can use any allocator | with any existing function, without wiring that function to | specifically do that. It just comes for free. In a way, it | completely decouples the code from the allocator choice. It's | especially nice because we can use any allocator with any third | party code. | | I think this kind of decoupling is a big step in the right | direction. The more concerns we can decouple from each other, the | simpler and more flexible our codebases become and the less time | we have to spend infectiously refactoring. | michaelwww wrote: | I was wondering why the author would think me, the reader, might | get very angry about something he wrote, but then I saw this from | the creator of Zig: | | _I see a lot of toxic Rust vs Zig discourse on Twitter right | now..._ | | https://twitter.com/andy_kelley/status/1568679389113757698 | | There seem to be a lot of heated passions about C replacement | languages right now. The Zig v Rust issue seems to boil down to | the Rust community putting a lot of effort into making memory | safety a priority for everyone in the industry and don't think | new languages should backslide into C's "unsaftey" | bigbillheck wrote: | It wasn't helped by Ziglang's VP of Community Loris Cro using | the term 'full-time safety coomer'. | wchar_t wrote: | He apologized: | https://twitter.com/croloris/status/1568704125826748416 | turminal wrote: | > the Rust community putting a lot of effort into making memory | safety a priority for everyone in the industry | | It would help if they did that in a manner that resembled | harrassment a bit less. I realize most of the community means | well but by now it should really be clear that they picked the | wrong way. | staticassertion wrote: | Never seen this. | adamdusty wrote: | Rust community harassment? Must have missed when rust | developers harassed the maintainer of actix so bad he quit | and temporarily deleted the project repo. | | https://news.ycombinator.com/item?id=22073908 | staticassertion wrote: | Nope, didn't miss that, have been a part of rust since | 2014. I don't consider any of that harassment at all, let | alone harassment with regards to other languages. I'm not | going to reshash the discussion as this happened years | ago, geofft summarizes things pretty well in that thread, | but this has all been discussed to death anyways. And, | again, that's still not an example of Rust users | "harassing" anyone for using other languages. | lifthrasiir wrote: | An unfortunate incident, but was that repeated? To my | knowledge the community has significantly calmed down | after that incident, exactly because it was an | unprecedented drama and many agreed that the drama is | bad. | verdagon wrote: | I suspect a lot of the toxic discourse originally comes from | folks not understanding each other's use cases and priorities. | | For some, memory safety is a means to an end: delivering value. | In this perspective, one must weigh the benefits of improving | memory safety against the complexity costs of proving memory | safety. In some situations, the improvement is too small and | the added complexity is too much. Some apps and some embedded | situations come to mind. Languages like Odin and Zig can be | stellar in these situations. | | For others, memory safety is a responsibility, and upholding it | is a basic requirement of modern software no matter what costs | we need to pay for it, and if the world would just accept that, | then we as a society could move past the days of rampant | vulnerabilities and odd memory bugs. | | Both sides are equally compelling, to me at least. What I hope | people can learn is that it really depends on the situation. | There is a place and time for both approaches. Once we can | accept that, I think the toxicity will dissipate. | ArrayBoundCheck wrote: | 1. The 2 most toxic community are the C/C++ community (users | heavily overlap and one is a superset so I'll consider them | the same) and rust. The rust community is more obsessed about | undefined behavior than the C++ community and rust doesn't | even have UB | | 2. Rust is a piece of crap language. We had memory safety in | java and C#. Rust doesn't have reasonable compile times, | doesn't have a reasonable standard library (I'm not trusting | cargo/npm/whatever) and some of the time rust is somehow | slower than C# and Java. It's a huge fuckup yet the community | turns a blind eye | staticassertion wrote: | This is exactly the way I see it. I view the bar for | developer responsibility to be much higher than some others | do. I suspect it's because I come from the world of | information security where we deal very directly with the | consequences of _not_ having a higher bar. | | To me, the idea that a new language would be anything other | than entirely memory safe, unless it is very domain specific | to areas where that's not important, is just another example | of developers lowering the bar. And I'm fine calling that out | and being called a zealot or whatever. | verdagon wrote: | You didn't explicitly say this, but for anyone who might | misinterpret your comment to imply that any memory-unsafe | should be specifically targeted at certain small domains: | | A vast swath of the programming world doesn't need the | level of memory safety that Rust has: apps and webapps are | generally sandboxed and only talk to a trusted first-party | domain, and games don't need memory safety if they're | single player, or even multiplayer co-op against AI. There | are a lot of aspects of the industry like this. | | We do need memory safety in any case that receives | untrusted input (for security reasons), cases that handle | multiple users' data (for privacy reasons), and safety | critical software. There are plenty more cases like these | too. Languages like Rust (or even more memory-safe | languages) are a stellar choice for this side of the | programming world, but not necessarily the other side. | | Creating simple languages to serve the purposes best served | by simple languages is a good thing, and I celebrate and | applaud Odin and Zig for that. It's up to the individual | developer to make a responsible choice based on their | situation, and any developer that uses the wrong language | for the wrong situation is indeed guilty of lowering the | bar. | staticassertion wrote: | Yeah, to be clear, some domains are different. Gaming is | one example of a domain where "best effort" memory safety | is perfectly acceptable. Would I _prefer_ full memory | safety? Sure, but we don 't see games as an attack vector | in the wild very often and there are good reasons for | that. | | If someone wants to build a game in a language that's | best-effort memory safety go for it. Similarly, CLI | applications are often never called from an untrusted | context and often provide semantic power that is | equivalent to code execution anyways, so again I'd love | to see memory safety, but I'm not going to care that | much. | | But these are exceptions - you'd still want to way | whatever you're getting from memory-unsafe-language-X | against just using a memory safe language. The default | should be memory safety. Given that we have Go, Rust, | Java, and more, there are few situations where memory | safety isn't an easy option. Not zero, just few. | verdagon wrote: | I'm not sure I'd characterize Rust as an easy enough | option for most people/cases, or that there's just a | "few" situations, but apart from that I like your | perspective, well said. | lifthrasiir wrote: | I think there is some similarity between information | security and Rust community; there had been the elephant in | the room, and they were the first to actively acknowledge | and get rid of it. Since it was long neglected by others, | the elimination of the elephant---vulnerability or memory | bugs---can be seen as the utmost goal to some (but not all) | of them. Others will complain, but as those others were | mostly who neglected the elephant from the beginning, their | complaints are mixed, some reasonable, some not. It's clear | that this situation is far from being ideal, both for who | are aware of the elephant and not, but I'm not sure how to | mend this gap in understanding. | dang wrote: | Related: | | _Odin Programming Language_ - | https://news.ycombinator.com/item?id=30394000 - Feb 2022 (42 | comments) | | _The Odin Programming Language_ - | https://news.ycombinator.com/item?id=22199942 - Jan 2020 (141 | comments) | | _The Odin Programming Language_ - | https://news.ycombinator.com/item?id=20075638 - June 2019 (3 | comments) | agluszak wrote: | > Odin has two idiomatic ways to allocate memory. The make and | new procedures. When destroying something created with make you | call delete. When destroying something created with new you call | free. This is a bit confusing if you come from C++ where new is | paired with delete. | | What's the difference between the make and new then? | | > There's no need for a build system, nor to explicitly call the | linker. The compiler does it all. | | Umm, so there _is_ a build system, but it 's just integrated into | the compiler? What is the benefit here? Rust has an excellent | build system out of the box (cargo), but it's still separate from | the compiler itself. | hsn915 wrote: | The distinction between make and new is similar to Go. | | new allocates memory for the given type. | | make allocates memory referenced by the type. | | For example, a slice is defined as: slice :: | struct { data: rawptr, length: int | } | | When you make a slice, you don't allocate space for the struct. | You allocate space for the data to be referenced by the struct. | | > Umm, so there is a build system, but it's just integrated | into the compiler? What is the benefit here? Rust has an | excellent build system out of the box (cargo), but it's still | separate from the compiler itself. | | What's the benefit of not having the build system integrated | into the compiler? | | Ideally if you are not linking to external libraries, and all | the code is in the language, you don't want to go through the | typical stages of the C compiler where it first produces object | files and then links them. You want to just produce the final | exe directly. I don't think Odin does this - at least at this | point - but anyway messing around with object files should be | considered obsolete. | agluszak wrote: | > What's the benefit of not having the build system | integrated into the compiler? | | Being able to write new/integrate with existing build systems | (i.e. Bazel). | | > Ideally if you are not linking to external libraries | | I'm afraid that's rarely the case. | | > messing around with object files should be considered | obsolete. | | Messing with them _yourself_ , as you need to do when you use | `make` in C/C++ (without cmake or anything more fancy) - I | agree, it should be considered obsolete. But what if I _want_ | to mess with them because, for example, I want to add support | for Odin to Bazel? | vinyl7 wrote: | There shouldn't be a buffet of build systems. How much time is | wasted bikeshedding on different build systems, not to mention | writing in their proprietary language/api and having to | maintain a separate program? | | There doesn't need to be a variety of build systems because all | they do is put out an executable. Its a simple thing that | doesnt benefit from having competing products | d_burfoot wrote: | I decided that if I ever get a dog, I will name him Odin. That | way, when he gets lost, I can walk around and yell | "Ooooodddddiiiiinnnnnn!!" | gnuvince wrote: | Odin and Zig are the two up-and-coming languages that I keep my | eye on. I like how they are trying to find a sweet spot where | they offer more than plain old C, but without becoming | overbearing like Ada, C++, or Rust. | tialaramex wrote: | You might want to also pay attention to Jai (or whatever | Jonathan Blow ends up naming it) | | Like the author of Odin, Blow has significant experience | writing software in a specific domain (video games), has strong | opinions about what's wrong with existing languages, and | decided he could do better. | | You can't actually use Jai yourself yet, it is as yet a closed | beta (though you might know somebody who can get you in), but | you can already get a flavour of it and I think it's probably | in the sphere of things you'd be interested in judging from | your comment. | | Personally I think we need to stop treating safety as optional, | as a C programmer for about 30 years, about 15-20 years of that | for pay, I found Rust very pleasant and would now always choose | it over C or these C replacements - although currently I get | paid to write Python and C# in my day job. | | But I'm clearly in the minority, for now at least, so I expect | at least one of these C replacements like Odin or Zig to get | significant adoption. Probably pays to know several of them, as | it's far from clear which will succeed and I doubt there's room | for all of them over the long term. | bsaul wrote: | I'm really surprised to see those language emerge after having | read so many praise about rust being a fantastic system | language. | | Although, from a personal standpoint, anytime i see rust code | or read about horror stories fighting the compiler i wonder how | that language gained so much popularity. | hsn915 wrote: | Odin emerged partly out of the "Handmade Network" which is a | group of people interested in a style of programming that is | very different from what is usually accepted by the rest of | the industry as best practice. | | See: https://www.youtube.com/watch?v=f4ioc8-lDc0&t=4407s | lucasyvas wrote: | It's popular _because_ the compiler is difficult - people | would rather suffer the pain at compile time instead of | runtime for particular projects, so it is very well suited to | those. | bsaul wrote: | there's good difficult, the one that forces you to clarify | your point, and there's bad one: the one that makes simple | constructs hard or impossible without going through hoops. | | From my understanding, rust has a little bit of both. | doliveira wrote: | I still feel that we're overdue for some paradigm shift in | programming languages. There's a nebulous feeling, probably | informed by my amateurishness, that some tasks just | shouldn't be this hard. Seeing the whole buzz around GitHub | Copilot, which seems to confirm that 90% of the typing we | do is useless, makes me think that there's another level of | "semantic abstraction" (?) we're missing. | afranchuk wrote: | I think an interesting distinction is this: it's not that | 90% of the typing you do is necessarily useless, it's | that it's been done before. Copilot is, in a sense, | drawing from a corpus of prior work. In that way you are | kind of using it as if you found a published library for | your more specific use cases. So it's allowing you to | draw on prior work without necessarily having external | packages split up with the granularity of many variations | of functions (which, ideally, would allow you to pull in | exactly what you need from prior work, but would | certainly be onerous in practice). | | That being said, I've never used Copilot myself, so I | can't speak very confidently about it. But from what I've | seen, it kind of allows you to incorporate every library | that's ever been made open source into your project, but | in a more granular fashion. Which naturally would save | you some typing :) | | P.S. I realize Copilot isn't necessarily copying other | code verbatim, though I assume pretty often it basically | ends up doing that, at least in pieces. | crdrost wrote: | This is an interesting take. One does need to draw a | distinction between "what everybody writes" (copilot | generates) and "what has to be written" (this code is the | 'useless' stuff bemoaned by the parent comment). | Obviously everybody writes what has to be written, but | you're right that there is a distinction. | | One gets a vision of the copilot autocomplete, as kind of | missing what would be an essential highlight, in the | templates it provides. "These parts highlighted in red, | do not change those, for some reason everybody does it | that way. But these parts highlighted in yellow, I've | seen a bunch of different takes on those; this is where | some variation occurs and where you might want to | customize it yourself." | | On this take, copilot is a poor way of providing | syntactic templates--macros--and indeed those macros take | the form of template substitution, which means they form | in theory a lambda calculus for that metalanguage. | | That is itself interesting because I only know of one | programming language which says "we are going to live out | here in la-la-functional-programming-math-land, but we | are going to describe values which are actual fragments | of computer programs," and that language is Haskell-- | though never used to write at this scale!. Interesting to | think that the Ur-goal of Copilot is to provide what you | were missing because you didn't wrap your language in | Haskell in the first place! | | With that said, a better way to start with metalanguage | design if this problem irks you is probably not Haskell | itself (it doesn't have an easy way to swap out its | compile target language from C-- to some other target, I | don't think) but something like Ometa2: | http://wiki.squeak.org/squeak/3930 | macintux wrote: | I feel like some languages make it much easier (or at | least require less code) to accomplish certain tasks | (Perl, for text processing, and Erlang for | concurrency/distributed systems), while other languages | make it more practical to do anything at all, but the | tradeoff is that they're much more verbose. | | Kitchen sink languages require you to build the kitchen | before you can cook. | zasdffaa wrote: | Very interesting. I don't have experience with copilot, | but with my own programming I tend to abstract heavily | until repetition is removed and there's little to repeat | (that said there are places where the language, C# in my | case, could support my style of programming better). I'll | check out some vids. | bsaul wrote: | a programming language (together with its standard | library) should guide you toward safe code, while keeping | an enjoyable experience. | | Saying "this thing is hard to get right, so the PL should | make you feel the pain" is only marginally better than a | language that pretends the problem isn't there at all. | taylorius wrote: | An interesting point. I wonder if the knowledge contained | in github copilot could somehow be extracted to come up | with suitable semantic abstractions. | Existenceblinks wrote: | It combines two things that appeal two large camps of | developers, ML style and performance. I even think that its | memory safety isn't that the main attractive point. It's like | a language that's aim to sell both ML family and C guys, and | that's over 50% of volume of devs' voice. | kaba0 wrote: | What I genuinely don't understand is why do we focus so much | on the low-level/system front? It is (or very much _should | be_ ) a small niche, and most business applications are | better served by managed languages that won't get a huge list | of vulnerabilities from memory corruption alone. | lostdog wrote: | Low-level and system programming are the areas most hurting | for better languages. | | High level has several modern scripting languages (Python, | Ruby, Javascript), and typed languages (Java, C#, Kotlin). | Low-level programming has had C and C++ for 30+ years, and | both have major problems that need fixing. Plus, there's | lots of interest in having programs run much much faster, | especially as people realize that every line of Python they | write could be 10x faster in a lower level language, with | minimal extra work. This is why Rust, and now Odin, get so | much attention. | | Of course, there's also a renaissance brewing for mid-level | languages, including Go, nim, and crystal. | shpongled wrote: | Personally, I would use Rust even if it was managed/not as | low level. Advanced ML type system + best-in-class | developer UX/tooling is the biggest selling point for me. | Hemospectrum wrote: | Systems-level programming is a frontier for language design | precisely _because_ higher-level domains are already so | well-served by established platforms. If your problem can | be solved in Python or JavaScript, you 're potentially | creating a lot of work for yourself by using a language | that's sort of like either of these, but not actually | compatible with their libraries. The internet is littered | with upstart languages that were made this way and withered | on the vine. On the other hand, if you're working in a | problem space with tighter performance constraints, and you | _already_ can 't touch these languages, and you can't even | count on libraries written in C or C++, then you suddenly, | paradoxically, have a lot more options. | jessermeyer wrote: | Odin has renewed my joy of programming. | | Built in bounds checking, slices, distinct typing, _no_ | undefined behavior, consistent semantics between optimization | modes, minimal implicit type conversions, context system and | the standard library tracking allocator combine together to | eliminate the majority of memory bugs I found use for | sanitizers in C /C++. Now I'm back to logic bugs, which neither | Rust nor sanitizers can help you directly with anyway because | they rely on program and not language semantics. Obviously | these features together do not eliminate those classes of bugs, | like Rust, but Odin chooses a different point on the efficient | frontier to target, and does so masterfully. | | To put the cherry on top, the sane package system, buttery | smooth syntax, sensible preprocessor (the 'constant system'), | generics, LLVM backend for debug info / optimization, open | source standard library, simply build system, engaging and well | intended community make day to day programming a pleasant | experience. | dunefox wrote: | > minimal type inference | | How is that a pro? That's a net negative. | Mountain_Skies wrote: | That depends on your goal. Want to move fast and break | things? Then type inference is great. Want to build things | that are solid and reliable? Type inference can be a very | bad thing in the wrong hands and most hands are the wrong | hands. | dunefox wrote: | How is type inference for "moving fast and breaking | things" but not for building solid and reliable things? | I'm not quite sure we're talking about the same concept | here. | Yoric wrote: | Having been a professional OCaml developer, a long time | ago, I found out that too much type inference gets into | the way of proper documentation and, to some extent, | proper naming conventions. Once code stabilized, we | started annotating everything, as in languages with much | more limited support for type inference, because it made | finding type-level (and sometimes runtime) issues easier. | | Perhaps that's the GP is referring to? | tialaramex wrote: | I'm comfortable with Rust's choice to infer types only | within a function, OCaml does sound like it has too much | inference. But I think the GP was just confused about | vocabulary and what they're really talking about is | coercion and they originally wrote "minimal type | inference" instead of "minimal type coercion". I think | they subsequently corrected to "minimal implicit type | conversions" which, is basically just more words for the | same thing. | | Unwanted type coercions are an infamous problem in C and | C++. | jessermeyer wrote: | I should clarify. I may have mistyped. | | Odin allows type inference at the declaration level `foo := | 1` for example, and a few other places, largely from the | constant system. 1 can be an integer, float, or even a | matrix, given the larger context. | | What I meant was implicit type conversion. Integers do not | automatically cast as booleans in Odin, as an example. | dunefox wrote: | > What I meant was implicit type conversion. | | Well, that's something completely different then. | hsn915 wrote: | Odin's stance towards undefined behavior was probably _the_ | decisive element that made me prefer it over the other | languages. | | https://twitter.com/TheGingerBill/status/1495004577531367425 | raphlinus wrote: | How does this work in practice? Is the behavior of a use- | after-free defined? Data races? The latter even more so for | objects that don't fit in one machine word, such as slices. | | While avoiding undefined behavior is a noble goal, my | personal feeling is that actually achieving that will be | much harder than it might first seem, and will probably end | up precluding a good deal of optimization. | | Of course, C has an entire class of UB that is much more | excessive than useful, for example left shift of a negative | integer. It's clearly and obviously possible to do much | better than C. I'm just skeptical that "no UB at all" is in | reach for a low-level, systems programming language that is | also portable and can be compiled with optimization | comparable to C. | guenthert wrote: | > for example left shift of a negative integer. It's | clearly and obviously possible to do much better than C. | | If you want to allow machine architectures using other | than two's complement while simultaneously striving for | efficient translations, that isn't all that obvious to | me. | elteto wrote: | Those machines can stay using whatever compiler/language | already works for them today. Enough of holding back the | rest of the world because of these fabled non-standard | architectures. | gingerBill wrote: | Name a machine that people still program for that is NOT | two's complement. | | And the latest version of C now requires two's complement | too! | astrange wrote: | Left shift with a negative shift count still has UB | issues unless you generate inefficient code. The way the | shift count is read is different on ARM, x86 scalar, and | x86 SIMD... so you can't autovectorize it without UB. | gingerBill wrote: | Odin does not allow for negative shifts to begin with. To | copy from the Overview[1]: | | > The right operand in a shift expression must have an | unsigned integer type or be an untyped constant | representable by a typed unsigned integer. | | and [2]: | | > The shift operators shift the left operand by the shift | count specified by the right operand, which must be non- | negative. The shift operators implement arithmetic shifts | if the left operand a signed integer and logical shifts | if the it is an unsigned integer. There is not an upper | limit on the shift count. Shifts behave as if the left | operand is shifted `n` times by `1` for a shift count of | `n`. Therefore, `x<<1` is the same as `x*2` and `x>>1` is | the same as `x/2` but truncated towards negative | infinity. | | [1] https://odin-lang.org/docs/overview/#arithmetic- | operators [2] https://odin- | lang.org/docs/overview/#integer-operators | jessermeyer wrote: | Odin forbids negative shifts. | hsn915 wrote: | I think the twitter thread I linked answers your | questions? | | "use after free" is an instance of a memory access | pattern that is considered invalid from the program's | point of view. | | What happens depends on the situation: | | Was the memory returned to the operating system? If so it | will probably result in a page fault and if you don't | have a thing to handle the signal then the OS will crash | your program. | | Was the memory part of an arena managed by the custom | allocator that still owns it? If so it will return | whatever value is contained in the address being | dereferenced. | lifthrasiir wrote: | Borrowing gingerbill's terminology, your "mini spec" is | still incomplete. And it is unclear how an implicit or | incomplete mini spec is different from UB, except for the | fact that the compiler can't take advantage of that. From | the user's perspective a gap in the mini spec is still a | gap that needs to be memorized, considered and avoided | much like UB. If you do somehow manage to define every | "mini spec", this poses another problem that your | specification limits what you can do in the future---for | example you would be unable to switch the memory | allocator. | glowcoil wrote: | This is fundamentally the same thing as undefined | behavior, regardless of whether Odin insists on calling | it by a different name. If you don't want behavior to be | undefined, you have to define it, and every part of the | compiler has to respect that definition. If a use-after- | free is not undefined behavior in Odin, what behavior is | it defined to have? | | As a basic example, if the compiler guarantees that the | write will result in a deterministic segmentation fault, | then that address must never be reused by future | allocations (including stack allocations!), and the | compiler is not allowed to perform basic optimizations | like dead store elimination and register promotion for | accesses to that address, because those can prevent the | segfault from occurring. | | If the compiler guarantees that the write will result in | either a segfault or a valid write to that memory | location, depending on the current state of the | allocator, what guarantees does the compiler make about | those writes? If some other piece of code is also | performing reads and writes at that location, is the | write guaranteed to be visible to that code? This | essentially rules out dead store elimination, register | promotion, constant folding, etc. for _both_ pieces of | code, because those optimizations can prevent one piece | of code from observing the other 's writes. Worse, what | if the two pieces of code are on different threads? And | so on. | | If the compiler doesn't guarantee a deterministic crash, | and it doesn't guarantee whether or not the write is | visible to other code using the same region of memory, | and it doesn't provide any ordering or atomicity | guarantees for the write if it does end up being visible | to other code, and then it performs a bunch of | optimizations that can affect all of those things in | surprising ways: congratulations, your language has | undefined behavior. You can insist on calling it | something else, but you haven't changed the fundamental | situation. | kaba0 wrote: | How is that different from specifying the behavior of | addition only when it won't overflow? The C spec might as | well say that it is invalid to overflow, and you are | responsible for checking for that (and that's kind of | what they do), but that's what UB is afaik. | raphlinus wrote: | The point of the C11 memory model is that it gives formal | bounds on what optimizations the compiler is and is not | able to do. In particular, it is free to reorder memory | operations as if the program was single-threaded, unless | the memory operations are explicitly marked as atomic. My | assertion is that if you do these optimizations and then | have a data race, it's functionally equivalent to | undefined behavior, even if you call it something | different and loudly proclaim that your language doesn't | have UB. | jessermeyer wrote: | Obviously Odin does not use C's memory model. And in | instances where LLVM optimizes Odin for UB, it is a bug, | and not a feature. Odin explicitly opts out of all | optimization passes that depend or leverage UB, but | that's a moving target. | | For example, as mentioned in the article, Odin does not | leverage LLVM's poison value optimizations, which are | derived from optimizations exploiting undefined behavior. | | Sure, some code is slowed. But can you point to a well | known and well used algorithm whose runtime | characteristics depend upon exploiting UB? If you code | goes fast because it's doing undefined things the | compiler strips away, that's a structural bug in the | application, in my view. | raphlinus wrote: | I'll give a concrete example. It's not the most | compelling optimization in the world, but I think | illustrates the tradeoffs clearly. The following is | pseudocode, not any particular language, but hopefully | will be clear. let i = mystruct.i; | if (i < array.length) { let x = | expensive_computation(i); array[i] = x; | } | | For the purpose of this example, let's say that the | expensive computation is register-intensive but doesn't | write any memory (so we don't need to get into alias | analysis issues). Because it is register-intensive, our | optimizer would like to free up the register occupied by | i, replacing the second use of i by another load from | mystruct.i. In C or unsafe Rust, this would be a | perfectly fine optimization. | | If another thread writes struct.i concurrently, we have a | time of check to time of use (TOCTOU) error. In C or | unsafe Rust, that's accounted for by the fact that a data | race is undefined behavior. One of the behaviors that's | allowed (because basically all behavior are allowed) is | for the two uses of i to differ, invalidating the bounds | check. | | Different languages deal with this in different ways. | Java succeeds in its goal of avoiding UB, disallowing | this optimization; the mechanism for doing so in LLVM is | to consider most memory accesses to have "unordered" | semantics. However, this comes with its own steep | tradeoff. To avoid tearing, all pointers must be "thin," | specifically disallowing slices. Go, by contrast, has | slices among its fat pointer types, so incurs UB when | there's a data race. It's otherwise a fairly safe | language, but this is one of the gaps in that promise. | | Basically, my argument is this. If you're _really_ | rigorous about avoiding UB, you essentially have to | define a memory model, then make sure your use of LLVM | (or whatever code generation technique) is actually | consistent with that memory model. That 's potentially an | enormous amount of work, very easy to get subtly wrong, | and at the end of the day gives you fewer optimization | opportunities than C or unsafe Rust. Thus, it's certainly | not a tradeoff I personally would make. | jessermeyer wrote: | Thanks. Currently Odin would cache i on the stack for | retrieval later, granting LLVM the ability to load it | into a register if profitable with knowledge that after | the read, `i` is constant, which bypasses the RAW hazard | after the initial read. | | My view is that undefined behavior is a trash fire and | serious effort should be undertaken to fix the situation | before it gets even more out of hand. | spacechild1 wrote: | > Obviously Odin does not use C's memory model. | | How is that obvious? And which memory model does Odin | use? Just to be clear: a "memory model" is not about | memory management, it is about the behaviour of | multithreaded programs: https://en.m.wikipedia.org/wiki/M | emory_model_(programming) | jessermeyer wrote: | Thanks. I was thinking of strict-aliasing and the | associated undefined behavior, which Odin forbids. Odin's | atomic intrinsics model after C11 closely (and require | the programmer to emit memory barriers when cache | behavior must be controlled by instructions). I believe | the final memory model will be platform defined. There is | no built-in atomic data type in Odin. Only atomic | operations are available, and most sync primitives are | implemented in the standard library wrapping OS | capabilities (WaitOnAddress), etc. | | The parent comment said: >then have a data race, it's | functionally equivalent to undefined behavior | | This is a matter of interpretation but there is a | categorical difference between "this read is undefined so | the compiler will silently omit it" and "this read will | read whatever value is in the CPU cache at this address, | even if the cache is stale". The difference is a matter | of undefined behavior from the _language_ versus the | _application_ being in an undefined state. | Yoric wrote: | My experience of Rust is that affine types go a long way | towards eliminating some classes of logic bugs. | | That being said, there's always space for exploring other | design choices! I haven't tried Odin yet, but it looks very | interesting. | dikaio wrote: | The way the article reads feels very authentic and the | transparency in conflict of the review is refreshing. ___________________________________________________________________ (page generated 2022-09-11 23:00 UTC)