16 ms·
The new Clang _ExtInt feature provides exact bitwidth integer types
- rightbyte 6y agoI feel this better be compiler extensions. Writing FPGA code has so much specialness anyway.
- saagarjha 6y agoI wonder if this could help standardize some vectorized code as well.
- beefhash 6y agoNote that the spec[1] requires that this tops at an implementation-defined size of integers, so you're likely not getting out of writing bignum code yourself (and even fifimplemented, the bignum operations may likely be variable-time and thus unsuitable for any kind of cryptography). Making the size completely implementation-defined also sounds like it'll be unreliable in practice, I feel like making it at least match the bit size of int would be a worthwhile trade-off between predictability for the programmer aiming for portability and simplicity of implementation. [1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2472.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2472.pdf
- deleted 6y ago[deleted]
- sgeisler 6y agoSince I had to implement something like that in rust for a base32 codec [1] a few years ago I really like the idea. Although my main concern was ensuring that invariants are checked by the type system, which might not be as much a concern in c with its implicit conversions? [1] https://github.com/rust-bitcoin/rust-bech32/blob/master/src/lib.rs#L64 https://github.com/rust-bitcoin/rust-bech32/blob/master/src/...
- captainbland 6y agoJudging by the motivation section, the motivation is primarily for FPGAs which I guess is why they want to allow these sub-int sized bit values. You might come up with some custom C-programmable operator that is only 3-bits wide where before you're presumably forced to use the smallest available power of 2 word size which would waste resources. So I think actually the idea is that this is for code which is not supposed to be portable at all, but rather hyper-optimised for custom devices.
- wongarsu 6y agoBoth Clang and gcc already support a 128bit integer type, so it's certainly possible that "implementation-defined" will end up being 128-bit or 256-bit for x64 targets on common compilers (provided MSVC plays along).
- TazeTSchnitzel 6y agoLLVM already supports completely arbitrary integer sizes up to 2^23-1 in its IR (https://llvm.org/docs/LangRef.html#integer-type https://llvm.org/docs/LangRef.html#integer-type), and I think it can “lower” any integer size to fit what the hardware actually supports. So if Clang doesn't add an artificial constraint on top, in theory you could use a one million-bit integer size if you wanted to?
- xyzzy2020 6y agoHow does this not break sizeof ?
- saagarjha 6y agoIt’d probably round up to the nearest byte, as it already does with the boolean types.
- barbegal 6y agoWhere does the spec say that it does that? As far as I can tell C only allows objects to have sizes in whole number of bytes, and that includes booleans. Although a _Bool type can be used for a bit field (having size of 1 bit) but you can't use sizeof with a bit field.
- monocasa 6y agoYeah, the unit of sizeof is number of chars, which is usually a byte.
- _kst_ 6y agoIn C and C++, a char is by definition a byte. A byte is CHAR_BIT bits, where CHAR_BIT is required to be at least 8 (and is exactly 8 for the vast majority of implementations). The word "byte" is commonly used to mean exactly 8 bits, but C and C++ don't define it that way. If you want to refer to exactly 8 bits without ambiguity, that's an "octet".
- hermitdev 6y agoI think you worded this pretty well. One thing I'd add (and that annoys me about C & C++) is that the size guarantees for integral types boil down to is that CHAR_BIT = sizeof(char) and that sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long). sizeof(T*) (for any T) is not even defined well, and can be OS/compiler specific. Makes cross-platform 32/64-bit support painful, especially because there were no strictly sized integer types before C11 & C++11. Although C11 & C++11 define types like int32_t and int64_t, they're not actually required to be those sizes! The various x-bit types only have to at least be large enough to store x-bits. So, on a hypothetical 40-bit CPU, sizeof(int32_t) could vary well be 40-bits, if that's the natural "word" size for the CPU. The devil is always in the details, and the devil is very, very annoying...
- Traster 6y agoI'd love to know if there's any use to this beyond FPGAs. This just seems to be another case of porting the complexity of RTL design into C syntax so that they can claim they have an HLS product that compiles C to gates. It's not C to gates if you had to rewrite all your C to manually specific the bit widths of every single signal. I really wonder how far we can keep going before the naming police break into Intel Headquarters and rename all their marketing material with "Low Level Synthesis".
- jschwartzi 6y agoIt provides another way to represent individual register fields without using bitfields. And probably gives you stronger guarantees about what happens when the bitfield overflows. It also provides a way to pass those values around without passing the whole register struct around.
- deleted 6y ago[deleted]
- pjc50 6y agoI was wondering that, and share your skepticism of autotranslation (it basically never works, and the only reason people like it is that the HDLs are stuck in the 80s). But I think the "no automatic promotion or conversion" combined with "will error if combined with different width" could actually make extint(8) and extint(16) useful - it's a massive hint to autovectorisers and lets you generate the SIMD instructions for those widths. Doubly so if they make sure never to write the words "undefined" where they mean "implementation-defined" for extint. At the moment normal arithmetic in C (x = x+1) is potentially undefined behaviour.
- qppo 6y agoHigh level synthesis works perfectly fine, just not from C. HDLs have chugged along too, it's just the toolchains are ridiculously expensive and risky to change. That's why hardware tech stacks lag behind the state-of-the-art. I share the skepticism of high level synthesis from C as being a bad motivation. The workflow is more like metaprogramming, and C is terrible at that.
- 0xTJ 6y agoI'm very much in support of this. One thing I like about Zig[1] s that integers are explicitly given sizes. I've been playing recently with it, but I'm waiting for a specific "TODO" in the compiler to be fixed. [1] https://ziglang.org/ https://ziglang.org/
- detaro 6y agoWhat was wrong with the actual title? > The New Clang _ExtInt Feature Provides Exact Bitwidth Integer Types
- moonchild 6y agoIt implements a proposed feature to the c language, which is the more interesting part of it.
- deleted 6y ago[deleted]
- pjmlp 6y agoNot all proposed features get accepted, specially in how conservative WG 14 tends to be.
- SloopJon 6y agoC++ has sped up the pace of its releases, but I don't have a sense of where C is. I didn't realize until I looked it up just now that there's a C18, although I gather that this is even smaller a change than C95 was. Safe to say that a feature like this would be standardized by 2022 at the earliest?
- GTP 6y agoI just found out about C18 thanks to your comment, I was still at C11. Thanks. Anyway I think you're right, except for the fact that I don't like when language designers release versions too quickly. I don't know the situation in the C++ land, but as an example I think that Java took the wrong way.
- jcelerier 6y ago> but as an example I think that Java took the wrong way. I wonder what is the right way then ? Java is apparently too fast for you, and yet it gets improvements so slowly that it is getting its marketshare eaten by other JVM languages moving much faster. If it was even slower it could as well be put directly next to the dusty COBOL and RPG boxes in the IBM attic.
- saagarjha 6y agoThis is recent, though. The releases of Java 1-7 were fairly slow and conservative.
- pjmlp 6y agoDusty Cobol?!? https://www.microfocus.com/en-us/products/visual-cobol/overview https://www.microfocus.com/en-us/products/visual-cobol/overv... The lastest standard revision is from 2018, and you can even do OOP if you feel so inclined.
- GTP 6y agoI think that language designers should release a new version when they actually have something new and not because somebody decided that there has to be a new version every x months. Also I think that the popularity of other JVM languages, eg. Kotlin, derives not from their quick releases, but from the fact that they observed what programmers didn't like about Java and designed the language from the start keeping in mind those problems and addreesing them right from the beginning.
- ralusek 6y agoA lot of people don't know this, but `BigInt`s are supported in modern JavaScript; integers of arbitrarily large precision. Try in your browser console: 2n ** 4096n // output (might have to scroll right) 1044388881413152506691752710716624382579964249047383780384233483283953907971557456848826811934997558340890106714439262837987573438185793607263236087851365277945956976543709998340361590134383718314428070011855946226376318839397712745672334684344586617496807908705803704071284048740118609114467977783598029006686938976881787785946905630190260940599579453432823469303026696443059025015972399867714215541693835559885291486318237914434496734087811872639496475100189041349008417061675093668333850551032972088269550769983616369411933015213796825837188091833656751221318492846368125550225998300412344784862595674492194617023806505913245610825731835380087608622102834270197698202313169017678006675195485079921636419370285375124784014907159135459982790513399611551794271106831134090584272884279791554849782954323534517065223269061394905987693002122963395687782878948440616007412945674919823050571642377154816321380631045902916136926708342856440730447899971901781465763473223850267253059899795996090799469201774624817718449867455659250178329070473119433165550807568221846571746373296884912819520317457002440926616910874148385078411929804522981857338977648103126085903001302413467189726673216491511131602920781738033436090243804708340403154190336n To use, just add `n` after the number as literal notation, or can cast any Number x with BigInt(x). BigInts may only do operations with other BigInts, so make sure to cast any Numbers where applicable. I know this is about C, I thought I'd just mention it, since many people seem to be unaware of this.
- justicz 6y agoHm, does this work in Safari? https://caniuse.com/#feat=bigint https://caniuse.com/#feat=bigint
- jakear 6y ago“Syntax Error: No identifiers allowed directly after numeric literal”
- The_rationalist 6y agoDoes X work in Safari can almost always be answered by 'no' iff the feature is from a recent(less than a decade) spec
- segfaultbuserr 6y agoI think it's funny. C was originally invented in an era when machines didn't have a standard integer size, 36-bit architectures were at their heydays, so C integers - char, short, int, and long - only have a guaranteed minimum size that could be taken for granted, but nothing else, to achieve portability. But after the computers of world have converged to multiple-of-8-bit integers, the inability to specify a particular size of an integer become an issue. As a result, in modern C programming, the standard is to use uint8_t, uint16_t, uint32_t, etc., defined in <stdint.h>, C's inherent support of different integer sizes are basically abandoned - no one needs it anymore, and it only creates confusions in practice, especially in the bitmasking and bitshifting world of low-level programming. Now, if N-bit integers are introduced to C, it's kind of a negation-of-the-negation, and we complete a full cycle - the ability to work on non-multiple-of-8-bit integers will come back (although the original integer-size independence and portability will not come back).
- GTP 6y agoWhy portability will not come back? Also when the ability of working on non-multiple-of-8-bit integers was lost?
- gumby 6y agoAFAIK the last machine in widespread production that handled multi-length integers was the PDP-10/20 and its clones which essentially died around 1984. I say "around" because though DEC canceled the 20 line, some clones remained (that was Cisco's original business plan, for example)
- ajuc 6y agoThere was a series of Polish mineframes called Odra with 24bit integers which also died out in 80s essentially, but some of them were still used till 2010 in some specialized railway station software, and there was a short series of faster replacement processors for them made in late 80s-early 90s called SKOK. Of course they weren't "widespread" for most meanings of that word :)
- waynecochran 6y agoAt some point they need to branch off and not call it C anymore. C should stay relatively small -- small enough that a competent programmer could write a compiler and RTS for it.
- weinzierl 6y agoYes, keep C clean and add all the cruft to another language derived from C. We could call the new language C++.
- Gibbon1 6y agoMy opinion is C and C++ need a divorce. So that C can be modernized with features that make sense in the context of the language. And not constrained as a broken subset of C++.
- detaro 6y agoThat has already happened though?
- Gibbon1 6y agoI think it's starting to happen because C++ has become so grossly Byzantine. C refuses to relinquish a bunch of niche applications. The heyday of OOP is past. 10 years ago the attitude was C is going to die any day now. Now it more like since C isn't dying it needs improvements. And none of them are backports from C++ nor make sense in C++.
- detaro 6y agoC99 already isn't a subset of C++ (although I think it took until C++14 to finally give up on that), so any alignment is courtesy now, and it's recognized they'll deviate where necessary.
- wongarsu 6y agoA lot of the world still runs on C99, and a lot of (toy or academic) compilers are written for C0 (a simple, small, safe C subset). Even when a new C version gains more features you can still develop against C99, C0, or whatever version you prefer.
- derefr 6y ago> While the spelling is undecided, we intend something like: 1234X would result in an integer literal with the value 1234 represented in an _ExtInt(11), which is the smallest type capable of storing this value. That “smallest type capable of storing this value” is a disappointing approach, IMHO. It’d be a lot more powerful to just be able to pass in bit patterns (base-2 literals) and have the resulting type match the lexical width of the literal. 0b0010X should have a bit-width of 4, not 2.
- saagarjha 6y agoI wonder if the suffix could be Xn where n is an integer specifying the width.
- nybble41 6y agoWould 0X12 then be a 12-bit integer with the value zero or a hexadecimal `int` literal with a base-10 value of 18? Does this work for other bases (0X12X12)? I'm not sure why they picked a letter which can already occur in integer literals rather than one of the many unused letters. Given the focus on FPGAs and HDL it's also worth noting that X is commonly used in binary or hexadecimal constants in HDLs to denote undefined or "don't care" values, which could lead to confusion. Rust integer literal syntax would be perfect here (1234u11 or 1234i11) since it already includes the bit width and is compatible with any base prefix.
- floatingatoll 6y agoI think your proposal for 0b0010X makes an excellent addition to the 1234X proposal. Has it already been discussed by the working group? If not, you should email someone to ask them to consider it!
- Taniwha 6y ago"smallest type" doesn't go far enough for HDL languages - consider a 4-bit counter in verilog reg [3:0]r; r = 4'hf; r = r+1; if (r == 0) .... if r is really 8 bits r+1 will have a non-0 value .... However all the LLVM people may be saying here is that they're providing the minimal support for arbitrary size math and expect language implementers to generate the masking where required (ie that r+1 above is really (r+1)&4'hf ) I'll note that for Verilog in particular the standard Verilog C-level APIs for accessing data imply that integers are not stored contiguously, instead they're stored in 32-bit chunks with a min size of 32-bits for 1 to 32-bit values - a 33-64 bit value will be stored in 2 non-contiguous 32-bit words ('packed' values are different from this). To be useful any back end support needs to be able to understand stuff stored this way.
- SlowRobotAhead 6y ago> These tools take C or C++ code and produce a transistor layout to be used by the FPGA. Hmm, I haven’t been following that but it seems that... > The result is massively larger FPGA/HLS programs than the programmer needed And there it is. Really seems odd to me to try and force procedural C into non-linear execution of FPGA. Like it seems super odd, and when talking about changes to C to help that... I really don’t get it. This isn’t what C is for. What is the performance advantage over Verilog? How many people want n-bit into in C when automatically handled structures work well for most people. Maybe I’m just not seeing the bigger picture here and that example was just poor?
- Cyph0n 6y agoNot to mention that the first statement is simply false... The final result is a bitstream that determines which LUTs (lookup tables) and BRAM (memory/block RAM) to use on the chip, and how they should be connected/routed. The FPGA fabric itself is made of transistors, but your C/C++ (HLS) or HDL code is not directly controlling these transistors. This is what makes FPGAs so flexible relative to ASICs.
- marcan_42 6y agoThere is no performance advantage over Verilog. The reason why (some) people want C is because Verilog and VHDL are unquestionably terrible languages (they weren't even intended to be HDLs, that use case came later) which are damn near impossible to write complex systems in without spending most of your time writing bug-prone boilerplate. Every big name IC designer shop ends up wrapping them in layers of metaprogramming to make them palatable. So, since those languages suck, people familiar with the procedural side of things end up asking for C. Which is an even bigger impedance mismatch than Verilog, but since you need a smarter backend to even begin to implement it, it can make life easier by that alone. Personally, I prefer stuff like nMigen, which is basically Python metaprogramming a synthesis-oriented subset of Verilog constructs. Compiles down to Verilog behind the scenes.
- krupan 6y ago"Verilog and VHDL are unquestionably terrible languages (they weren't even intended to be HDLs, that use case came later)" They were intended to be HDLs (for simulation of hardware), but they were never intended to be automatically translated into gates/schematics (i.e., synthesized)
- fortran77 6y agoI love Erlang for the ability to deal with _bits_. To see this in a compiled language would be wonderful. Of course, you can get down to the bit level with bitwise logical operations, but to be able to express it more naturally would be a great boon to people writing low-level network stuff, and will probably reduce programming errors.
- drfuchs 6y agoSo, if I have an array of extint(3), does it pack them nicely into 10-per-32-bit-word? Or 21-per-64-bit-word? Will a struct with six extint(5) fields fit into 4 bytes? What about just a few global variables of extint(1)? Will they get packed into a single byte? Did I miss where this is covered?
- tom_mellior 6y agohttp://www.open-std.org/jtc1/sc22/wg14/www/docs/n2472.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2472.pdf does not mention structs at all, which is disappointing. I quoted this language below: "_ExtInt types are bit-aligned to the next greatest power-of-2 up to 64 bits: the bit alignment A is min(64, next power-of-2(>=N)). The size of these types is the smallest multiple of the alignment greater than or equal to N. Formally, let M be the smallest integer such that A * M >= N. The size of these types for the purposes of layout and sizeof is the number of bits aligned to this calculated alignment, A * M. This permits the use of these types in allocated arrays using the common sizeof(Array)/sizeof(ElementType) pattern." But to be honest I don't understand what it's trying to say. If bit width N = 3, the next power of 2 is 4, so would that mean that "bit alignment(?)" A = 4? Then M = 1 is the smallest integer such that A * M >= 3. Then the size of the type would be 4 bits? That wouldn't fly with sizeof.
- nabla9 6y agoI think most commonly used languages with and without standards, C,C++, JavaScript/Wasm, Python, Java, etc. should standardize new primitive type represetations together (with hardware people included). If you have different representations in different languages it just creates unnecessary impedance mismatch. It would be better for everyone if you could just pass these types from language to language.
- einpoklum 6y agoC++ can have arbitrary-width integers as library types; it would not be that big of a deal IMHO. If `optional`, `variant` and `any` (and maybe soon, `bit`) are not in the language itself, no reason why n-bit-integer should be. (Of course, this is written from the "we can jerry-rig the existing language to do what you want" perspective with which so much is achievable efficiently in C++.)
- nabla9 6y ago> no reason why n-bit-integer should be. If standard is agreed, it could be pragma similar to calling conventions.
- hermitdev 6y agoBoost Multiprecision [0] is an example of such a library type. It offers a compile-time arbitrarily wide integers (with predefined types up to 1024 bits) and a C++ wrapper around the GMP or MPIR libraries, which supports arbitrary sizes at runtime (not sure how it's implemented, but probably on top of an array of ints or BCD (binary-coded decimals)). C++ has had `optional` and `variant` since (I think) C++11, maybe 14. I don't think `any` made the cut. All of these types originated (for C++ standardization) in Boost, as well. I'd caution against using `any`, though. From personal experience, the runtime overhead is quite high, and holding any non-none type is a dynamic allocation. Performance is far better with `variant` at the development cost of needing to know all the types you're going to support at compile-time. [0] https://www.boost.org/doc/libs/1_72_0/libs/multiprecision/doc/html/index.html https://www.boost.org/doc/libs/1_72_0/libs/multiprecision/do...
- waltpad 6y agoFirst of all, I suppose that it will be possible to make them unsigned (just like for standard types). Is this correct? Also, what's the relationship between standard types and the new _ExtInts? Are _ExtInt(16) equivalent to shorts, or are they considered distinct and require explicit cast? > In order to be consistent with the C Language, expressions that include a standard type will still follow integral promotion and conversion rules. All types smaller than int will be promoted, and the operation will then happen at the largest type. This can be surprising in the case where you add a short and an _ExtInt(15), where the result will be int. However, this ends up being the most consistent with the C language specification. For instance, what if I choose to replace short by _ExtInt(16) in the above? What would be the promotion rule then? Note that it was already possible to implement arbitrary sized ints for a size <= 64, by using bitfields (although it's possible that you could fall into UB territory in some situations, I've never used that to do modular arithmetic). Edit: Ah, there's this notion of underlying type: one may use the nearest upper type to implement a given size, but nothing prevents to use a larger type, for instance: struct short3_s { short value:3; }; struct longlong3_s { long long value:3; }; I don't know what the C standard says about that, but clearly these two types are not identical (sizeof will probably gives different results). What's will it be for _ExtInt? How these types will be converted? Another idea: what about struct extint13_3_s { _ExtInt(13) value:3; }; Will the above be possible? In other words, will it be possible to combine bitfields with this new feature? I guess it's a much more complicated problem that it appears to be at first.
- jhj 6y agoMuch of my time is spent writing Mentor Catapult HLS for ASIC designs these days. Every HLS vendor or language has their own, incompatible arbitrary bitwidth integer type at present. SystemC sc_int is different from Xilinx Vivado ap_int is different from Mentor Catapult ac_int is different from whatever Intel had for their Altera FPGAs. It's a real mess. I'm hoping this is another small step to slowly move the industry into a more unified representation, or at least if LLVM support for this at the type level could enable faster simulation of designs on CPU by improving the CPU code that is emitted. What probably matters most for HLS though are the operations which are performed on the types (static or dynamic bit slicing, etc).
- mshockwave 6y agothe title is a little misleading. Since _ExtInst is just an extension of Clang not a standard. GCC and Clang both have some hidden features that are not in standard.
- dang 6y agoWe've changed it now (https://news.ycombinator.com/item?id=22948380 https://news.ycombinator.com/item?id=22948380).
- mappu 6y agoIt is on the standards track, though, even if N2472 was not completely accepted it seems like there is a process for this (or something very much like it) to become a standard.
- dang 6y agoSpeaking of C, if you missed last week's thread with C Committee members, it was rather amazing: https://news.ycombinator.com/item?id=22865357 https://news.ycombinator.com/item?id=22865357. Click 'More' at the bottom to page through it; it just keeps going.
- pjmlp 6y agoCurrently clang is getting it, if ISO C gets it, it is another matter.
- rurban 6y agoThat's what I've wrote to their reddit post: The feature is of course fantastic. But the syntax still looks bit overblown. Type system-wise this seems to be more correct: _ExtInt(a) + _ExtInt(b) => _ExtInt(MAX(a, b) +1) And int + _ExtInt(15) might need a pragma or warning flag to warn about that promotion. One little int, or automatic int pollutes all.
- Traster 6y agoProblem is: _ExtInt(16) + _ExtInt(15) => ExtInt(17) _ExtInt(17) + _ExtInt(15) => ExtInt(18) So let's say we have a,b and c. a is 16 bits, b and c are 14bits. a + (b + c) => ExtInt(17) (a + b) + c => ExtInt(18) Now obviously this a trivial example, but it highlights the fact that unless you're actually willing to carry the true ranges around in your type system, your calculation of bit widths are going to vary due to the details of which operations are done in which order with which intermediary variables.
- rurban 6y agoI see, it's now the same problem as with floats. addition is not commutative anymore. But isn't that the same problem in HW also? With the right order you are saving transistors.
- Traster 6y agoWell, it's not quite a problem with whether the operations are commutative - both of those phrasings of the problem will give the correct answer in hardware 100% of the time. The only difference is one made an efficient decision about the order of operations with knowledge of how bit growth rules work and the ranges of the inputs. You do have the same problem in hardware, which is hardware designers job. The difference RTLs don't claim to be high level languages. This is an instance where there's a high level intention and a low level implementation, and the high level synthesis tool has just ported new language constructs into the high level language to do low level optimisation, rather than actually doing the synthesis optimisations that are expected.
- Someone 6y ago”Likewise, if a Binary expression involves operands which are both _ExtInt, rather than promoting both operands to int the narrower operand will be promoted to match the size of the wider operand, and the result of the binary operation is the wider type.“ I don’t understand that choice. The result should be of the wider type, yes, but, for example, multiplying a _ExtInt(1) by a _ExtInt(1000) should take less hardware than multiplying two ExtInt(1000)s. So, why promote the narrower one to the wider type?
- ndesaulniers 6y agoCongrats Erich! One thing I'd be curious about is the ergonomics (or lack of) of explicit integer promotions and conversions for these types, as I find the current rules for implicit integer promotions a little confusing and hard to remember. For a fun compiler bug in LLVM due to representation of arbitrary width integers, see: https://nickdesaulniers.github.io/blog/2020/04/06/off-by-two/ https://nickdesaulniers.github.io/blog/2020/04/06/off-by-two...
- senozhatsky 6y agoKnuth's MIX computer with its 6-bit bytes and 5-byte words (IIRC) came to my mind [0] [0] https://en.wikipedia.org/wiki/MIX https://en.wikipedia.org/wiki/MIX