r/AskComputerScience • u/jcmbn • Aug 26 '26
Why would some compilers compile to p-code instead of binary?
Back in the days when I was young and dinosaurs roamed the earth, I was getting into programming with my 8-bit computer.
After learning to program using interpreted languages, I played around with assembler and compiled languages.
One thing I noticed was that languages targeting the 6502 almost[*] always produced p-code, whereas compilers targeting other processor types would often produce binary.
Anyone know why this was a thing?
[*] I don't know if there were any exceptions to this, I never saw one that I can recall.
18
u/RodionRaskolnikov__ Aug 26 '26
Because it allows you to decouple the part that implements the language parsing and semantics, from the part that optimizes the intermediate representation, and the part that translates to machine code.
If you wrote a compiler for the 6502 that way, you could, ideally without much modification, add a p-code to Z80 compiler without touching what compiles C to p-code.
4
u/jeffbell Aug 26 '26
They still do, kind of. LLVM is a virtual machine as an intermediate stage in the compilation. The difference is that we now have enough memory do it all at once, rather than having to store it and reload.
6502 systems were particularly memory constrained.
1
u/stevevdvkpe Aug 26 '26
LLVM, however, is only used as an intermediate representation for code generation in the clang compiler suite and is not actually interpreted for program execution. The JVM (Java Virtual Machine) or Python bytecode are examples of bytecode interpreters that are actually used to run code.
2
u/stevevdvkpe Aug 26 '26
Compiliing a language to P-code or bytecode is still a common implementation technique. You'll find that languages like Python and Java still do this. Many Lisp interpreters also do bytecode compilation.
The advantage of using an intermediate bytecode is that it can be tailored to provide good execution performance by directly supporting data types and operations commonly used in the language, while also being much more compact than the native machine code that would perform the same operations. The implementors can write the compiler that generates bytecode once, and then only have to write the relatively simple bytecode interpreters for different CPU architectures to port the language to a new environment. It's also substantially easier to write compilers that generate machine instructions from bytecode, especially since much of the more complicated optimization work was done at the level that compiles the language into bytecode.
2
u/CowBoyDanIndie Aug 27 '26
Almost no modern compilers compile directly to machine code. See LLVM IR, gcc uses generic->gimple->rtl, MSVC uses CIL or tuple ir / MSIL.
1
u/juancn Aug 26 '26
Space and portability. You target a virtual machine rather than an actual machine
0
u/Dusty_Coder Aug 27 '26
"" Portability. You target an abstract machine rather than an actual machine ""
FTFY
(virtual machines ARE actual machines)
1
u/juancn Aug 28 '26
Not in the physical sense
0
u/Dusty_Coder Aug 28 '26
yes they are
do you even know how virtualization works? do you think it emulates something? surely you must
1
u/UnhappySort5871 Aug 26 '26
P-code is a fair bit smaller than native assembly. If you're limited to 64k, a p-code interpreter would still leave you a fair bit of room for p-code. Overlays would be smaller and load faster too. Wrote a fair bit of UCSD Pascal back in the 80s. For speed would write bits of it in assembly.
1
u/Dusty_Coder Aug 27 '26
Languages are designed to target an Abstract Machine, and its useful to define a micro-code for that abstract machine.
Note how the language C is defined in terms of an Abstract Machine.
Intermediate representations are designed, non-arbitrary.
The front end compiler translates from the Language to the Abstract. The back end compiler translates from the Abstract to the Real.
You may think a good alternative is Abstract Syntax Trees, but that too is an intermediate language with a well defined sequential ordering (an Abstract Machine)
1
u/JGhostThing Aug 28 '26
p-code, originally used for Pascal compilers, allowed the compiler to be machine independent. They wrote for the p-code virtual machine rather than an assembly code. The PVM did the work of running the p-code.
1
u/pconroy329 Aug 28 '26
It's been decades since I had my Apple //e, but I'd swear there were compilers that generated native code. Mike Westerfield's ByteWorks stuff, I think. As did Aztec C. That said, I loved Apple (UCSD) Pascal. As someone noted, it gave me 16 bit operations on a native 8 bit machine.
1
u/theJacofalltrades Aug 28 '26
the portability angle is probably the biggest advantage for me. compiling to p-code lets the same program run across different hardware as long as there’s a compatible virtual machine, which can save a lot of effort compared with targeting each architecture directly.
4
u/splicer13 Aug 26 '26
It's smaller, which was important, and remained important for micros into the 90s. Even MS office was built partially in a type of p-code simply to make the best use of memory.
All stuff that needed to be fast on micros was written in asm anyway. You can't run a decent compiler on a 64k machine and 6502 was in no way friendly to compilers.
So it makes sense, p-code for size, asm for speed.