4 ms·
I am not very familiar with Uxn, but the one thing from the article that struck me as an actual problem was that the memory containing the program's code can be
by csande17 4y ago
I am not very familiar with Uxn, but the one thing from the article that struck me as an actual problem was that the memory containing the program's code can be modified at runtime. The downsides of that can heavily outweigh the benefits in many cases; it turns "write a uxn -> native code compiler" from a weekend project to a nearly impossible task, for example. This is probably relatively easy to fix, assuming uxn programs do not regularly use self-modifying code. (The article proposes an elaborate scheme involving alias analysis and bounds checks, but something like "store executable code and writable data in two separate 64KB regions" would work just as well.)
The article suggests that Uxn programs can write to arbitrary locations on the filesystem. If that is the case, it seems like it would be really easy to change that in the interpreter. Then Uxn's security model would be essentially identical to how the article describes WebAssembly: the interpreter serves as a "sandbox" that prevents programs from touching anything outside their own virtual memory. This is a good security model and it likely makes a lot of sense for Uxn.
Otherwise, the article seems to be more bluster than substance. Uxn is probably not going to be the fastest way to compute the Fibonacci sequence, nor the most secure way to protect a telecommunications network from foreign cyber-attacks, but it doesn't need to be either of those things to be useful and valuable as a way to write the kind of UI-focused personal computer software you want to write.
- tumult 4y agoThe second (or first, maybe) most popular Uxn emulator, Uxn32, has sandboxed filesystem access, and has had it since the beginning. The author of the original article doesn't know what they're talking about.
- entaloneralie 4y agoI use Uxn's self-modification powers quite a bit, I use them mostly for routines that runs thousands of times per frames, so I don't have to pull literals from the stack, I just can just write myself a literal in the future and have it available. I wonder, what about this makes a native code compiler difficult, is it because most programs protect against this sort of behaviors? or that programs are stored in read-only memory? Some of the emulator sandbox the file device, it is likely to be the way that all the emulators will work eventually.
- csande17 4y ago> I wonder, what about this makes a native code compiler difficult, is it because most programs protect against this sort of behaviors? or that programs are stored in read-only memory? Say you're trying to "translate" a Uxn program into x86 code. The easiest way to do this is by going through each of the instructions in the program one by one, then converting it to an equivalent sequence of x86 instructions. (There is a little complexity around handling jumps/branches, but it's not too bad.) But if the Uxn program is allowed to change its own code at runtime, that kind of conversion won't work. The Uxn program can't change the x86 code because it doesn't know what x86 code looks like--it only knows about Uxn code. There are some ways around this, but either they're really slow (eg by switching back to an interpreter when the Uxn code has been modified) or much more complex (eg a JIT compiler) or don't work all the time (due to the halting problem).
- syntheweave 4y agoIt's ultimately a hardware-dependent answer. Self-modifying code fell out of fashion in the 1990's, once pipelined and cached code execution became the norm in desktop computing. From that point forward, correct answers to how to optimally use your hardware become complex to reason about, but generally follow the paradigms of "data-driven" coding: you're designing code that the CPU understands how to pipeline(and therefore is light on branching and indirection during inner loops), and data that the CPU can cache predictably(which leads to flat, array-like structures that a loop will traverse forward through). Therefore what compilers will actually do is reorder program behavior towards optimal pipeline usage(where doing so doesn't break the spec). This has clear downsides for any programming style that relies on knowing instruction-level behavior. And it is so hard to keep up with the exactly optimal instructions across successive generations of CPU that in the majority of cases, humans just never get around to attempting hand optimization. The benefit of defining a VM is that you can define whatever is optimal in terms of pragmatic convenience - if you want to write programs that have a certain approach to optimization, you can make some instructions, uses of memory or styles of coding relatively faster or slower, and this leads to an interesting play space for programmers who wish to puzzle through optimization problems. But unless it also happens to represent the actual hardware, it's not going to achieve any particular goal for real performance or energy usage - at least, not immediately. Widespread adoption motivates successively more optimal implementations. But that logic makes it hard to justify any "starting over" kind of effort, because then you end up at the conclusion that the market is actually succeeding at gaining efficiency through its fast-moving generational improvements, even if it does simultaneously result in an environment of obsolescence as the ecosystem-as-a-whole moves forward and leaves some parts behind. An alternate path forward, one which can integrate with the market, is to define a much more narrow language for each application you have in mind, and be non-particular about the implementation, thus allowing the implementation to become as optimal as possible given a general specification. This task leads, in the large scale, towards something like the VPRI STEPS project, where layers of small languages build off of each other into a graphical desktop environment.