You raise some good points. But your example using a = a - 2; is something done routinely, even in C. You'd need to show a better example of an abstract idea that's "low level" and that C doesn't support, that you consider would be too low level.
I didn't explain the a = a - 2 section very well.
Another (possibly better) example, would be a low level instruction, which counts how many bits in total, are set (1), in an unsigned, 64 bit integer. Which is useful for some types of algorithms and program types.
So the high level language, could include a low level statement, which counts up the bits set count, works in only 1 CPU clock cycle (because the initial CPU architecture has such an assembly language instruction), for the current CPU architecture, and so runs very quickly.
But the issue would be that if you included that statement in the language, and people start using it, even when it was not really needed. Any architectures, that DON'T have a 64 bit, bit count instruction, may run that instruction very inefficiently.
There are faster methods, but essentially the function would be performing something like:
// Code vaguely based on Pascal
// Count the total bits set in variable x
bit_count = 0;
for bit = 0 to 63 do
begin
if x and 1 == 1 then bit_count++;
x /= 2;
end
There are faster algorithms.
C does support embedded assembly language though, can one get any lower level than that?
That is an interesting question. Computers are getting rather complicated these days, because as well as the CPU which processes conventional instructions. There can be various other sections (System on a chip, SoC), such as AI/Machine learning accelerators (technical names can vary), hardware encryption, video encoders/decoders. Even finite state machines (FSMs), FPGAs, the list these days, seems to go on and on.
So I suppose it depends on ones definition. But anyway, technically speaking some of these SoC (names can vary), features, can be at a relatively simple/low level (lower than standard double floating point or 64 bit integers), but typically, it is done at incredibly high speeds. Compared to the main/normal/standard CPU instruction set.
OK I see, that's a better example. Yes the bit count thing is something I was reading about recently, they refer to it as "popcount". The concern then is, that a person might write code using that or copy/paste code using that and have no inkling that there might be a much higher cost than they would have thought.
So this raises the question of
intrinsic operations, how to deal with and implement these, its a good question, a very relevant issue, I agree.
Here's a
Microsoft article on this very issue of popcount.
Note:
If you run code that uses these intrinsics on hardware that doesn't support the popcnt instruction, the results are unpredictable.
You've raised a pretty good issue here, the very kind of thing that must be carefully thought about in a new language. It's an interesting area, there are no reserved words too so naming and adding intrinsic over time can't break existing code, this is a good example of why having no reserved words is a help, I know I may have seemed to dwell on this a lot, but if one doesn't design the grammar to do that there's no going back, incorporating this gives a great deal of power.
Now PL/I (and this new language - potential new language...) has what are called "builtin" functions. These are functions inherent in the language and they way they did that is clever.
To use a builtin function you MUST declare the function, declaring is very easy too, whatever the function is, whatever it returns and whatever arguments it takes, it is always declared as:
dcl Substr builtin;
dcl Sqrt builtin;
dcl Offset builtin;
Of course a bunch of those could easily be inside some utility include file. The attribute 'builtin' is sufficient, since the compiler knows already about the function's signature. This is how PL/I represents the kind of things we see in C with "stdlib.h" but is much slicker I think.
Now because keywords are not reserved, a user might create a function named 'Sqrt' and that's fine and legal, because unless they've already declared it as builtin, there's no issue, if they later wanted to use the real builtin one, they realize the conflict and resolve it. This means that adding new builtins also cannot break existing code, unless a builtin is explicitly declared it does no harm and a user cannot declare a function as builtin if its not yet present in the version of the language they're using.
Anyway, I'm thinking a similar idea could well be applied to intrinsics, naturally we'd consider something like:
dcl Popcount intrinsic;
dcl Emit intrinsic;
dcl Prefetch intrinsic;
Then during compilation we'd issue a diagnostic if the intrinsic was not available for the target CPU or if it is available but might be costly...