Jesus Fucking Christ, now we're exploiting undefined behaviour to remove one compare instruction? You don't even need the jump, just use a conditional move. Just..
test rax, rax
add rax, $offset
cmovz rax, 0
Or keep the damn jump and expect the cpu to profile it correctly.
Sorry for sounding angry, but this kind of thing isn't what makes my programs faster, this kind of thing is what makes it harder to write correct programs.
That you need to invoke undefined behavior to remove the compare instruction is only the symptom. The actual disease is nullable pointers themselves, which create the need for a compare instruction at every pointer dereference in the first place.
No. Java has nullable pointers and almost all JVMs implement them with a pointer to address 0 and unmap the first couple of pages of the address space. That will cause a fault, leading to a signal, which is then handled to materialize and throw the exception. No compare. Just naked loads/stores with fixed offsets. The disease is multiple (implementation) inheritance.
> No. Java has nullable pointers and almost all JVMs implement them with a pointer to address 0 and unmap the first couple of pages of the address space.
This is just plain ugly. Why should the normal operation of a program written in a high-level language trigger page faults? But, leaving aesthetic concerns aside, I'm not even sure it works. How do you guarantee that the OS won't give you back the same pages you unmapped when you try to map new pages (say, to grow the heap)?
Yeah. Unfortunately the only really efficient protection mechanisms that modern processors have is virtual memory. C++ programs generally unmap the first few pages for exactly the same reason; to catch nullptr derefs.
> Why should the normal operation of a program written in a high-level language trigger page faults?
NullPointerExceptions are not considered normal operation. They are safety violations that have a controlled, well-defined semantics. BTW page faults happen all the time; the OS transparently handles them and maps in new pages as necessary. The problem you are referring occurs when a page fault happens and the OS knows there is no mapping for those addresses.
> How do you guarantee that the OS won't give you back the same pages you unmapped
Because the mapping is for an address space range (e.g. 0-N), and the OS does not overlap new request with existing mappings unless specified in the request.
> NullPointerExceptions are not considered normal operation. They are safety violations that have a controlled, well-defined semantics.
I'm not really buying this. My definition of “normal operation” is very simple: Everything but FFI calls. Normal operation in a safe language is supposed to be safe.
Sorry, yes. You can do this just through segment declarations in both ELF and MachO binary formats, to prevent anything getting accidentally mapped there before startup.
Garbage collectors (and various other parts of the JVM) use similar tricks to coordinate threads without using memory barriers. The compiler just inserts TEST instructions to some fixed memory address in strategic places in the code.
When the garbage collector needs to run it protects the page and waits until all threads have segfaulted and transferred control to the collector.
Sorry for sounding angry, but this kind of thing isn't what makes my programs faster, this kind of thing is what makes it harder to write correct programs.
Edit: clang does exactly this at -O1: https://godbolt.org/g/o6gh0M