4 ms·
It may be impossible for you to get what you're looking for out of a "portable assembler." There's a fundamental tension between low-level control, optimization
by lambda 8y ago
It may be impossible for you to get what you're looking for out of a "portable assembler." There's a fundamental tension between low-level control, optimization, and portability. Either you will have code that will run dog-slow on certain architectures, as the compiler has to jump through hoops to do things like unaligned reads and writes that you ask it to do, or it will simply fail to compile certain architectures, or you will encounter undefined behavior on other architectures.
Whichever way it happens, you'd have to rewrite the code for different architectures. If you're rewriting the code on different systems, why aren't you just using an assembler instead of a portable assembler?
However, Rust may be the answer you're looking for.
Right now, in safe code, all operations are well defined; there are a few compiler and standard library bugs which allow undefined behavior, but that's not intentional and many of them are just waiting on some compiler refactoring to land before they will be fixed.
In safe code in Rust, you can pass around references (bare pointers with no GC), work with byte arrays, allocate things on the stack or on the heap, etc. It does provide some restrictions in what you can do to make it possible to check statically that all of your code is safe.
There is also an unsafe subset; this allows the "I know what I'm doing" kind of manipulations you can do in C. Of course, this means that you could encounter undefined behavior; if you do something incorrectly, it could cause arbitrary behavior. The compiler has to be able to rely on certain guarantees that you uphold, because otherwise it could perform not even the most basic of optimizations, nor in many cases even know how to correctly compile the code.
There is an active effort to specify the exact guarantees applied to unsafe code. Right now it's "whatever the current compiler and LLVM happen to do", which is not particularly good in terms of guarantees.
The idea is to try to make something which will be amenable to validating the code. If undefined behavior is sufficiently well specified, then you can compile the code in an instrumented mode which will detect undefined behavior (at significant cost in speed), and you can run your test suite in that mode to get reasonably good guarantees that you don't have UB in your program (as long as your test suite has sufficient coverage).
Clang and GCC have similar modes, undefined behavior sanitizers, that compile code with instrumentation that can detect certain classes of UB. Since C's memory model wasn't designed with this in mind, and C has a fairly weak type system, they can't cover everything.
The hope with the current work on Rust is that a memory model can be formally defined such that all UB could be detected by such a sanitizer, so as long as you have sufficient test coverage, you can be confident of avoiding UB.
Of course, there are other tools you can use as well to avoid UB, like formal proofs of correctness, but it can be quite difficult to fully specify the language in a formal system, and writing proofs of correctness in such systems is not a trivial task.