Oberon-0 compiler written in C++. The compiler reads Oberon-0 code and produces assembly code for the Netwide Assembler (nasm). At the moment only arithmetic operations are implemented, no procedure calls. The produced assembly performs arithmetic by using the processor as a stack machine instead of using the registers efficiently.
For an expression c := a + b * c
as input the compiler produces assembly code similar to the following:
mov rax, @c ; get c
push rax ; stack: c
mov rax, @b ; get b
push rax ; stack: b,c
pop rax ; rax := b
pop rbx ; rbx := c
imul rax, rbx ; rax := b * c
push rax ; stack: b*c
mov rax, @a ; get a
push rax ; stack: a, b*c
pop rax ; rax := a
pop rbx ; rbx := b*c
add rax, rbx ; rax := a + b*c
push rax ; stack: a+b*c
pop rax ; rax := a+b*c
mov @c, rax ; c := a+b*c
This is clearly not optimized. For example the lines push rax
followed by pop rax
are completely wasted and can be
omitted. Most of these sequences of push
and pop
could be eliminated easily by replacing them with a mov
or
omission. In this example this would still not be optimal because all operations can take place within the registers
only. To achieve an even better result a register allocation algorithm would be necessary which could lead to code as
follows:
mov rax, @c ; rax := c
mov rbx, @b ; rbx := b
imul rax, rbx ; rax := rax * rbx
mov rbx, @a ; rbx := a
add rax, rbx ; rax := a+b*c
mov @c, rax ; c := rax
First build the oberon0c
target with cmake. This will produce a oberon0c
executable.
To build an executable from your oberon0 program three steps are necessary:
- Compilation with
oberon0c
: oberon0 -> assembly - Assembly with
nasm
: assembly -> object - Linking with
link
orgcc
: object -> executable
To compile a file invoke the compiler with the input file as first argument and output file as second arg. Example:
./oberon0c Test.Mod Test.asm
.
This section assumes that gcc is available. On Linux we want to create an executable using the elf
object format for a
64bit machine.
nasm -felf64 Test.asm && gcc Test.o -o Test && ./Test
This section assumes that Visual Studio 2019 is installed, such that its linker can be used. To make the linker
available VS provides a script called vcvars64.bat
. This will set up some variables, including the linker and required
locations of binaries. Therefore this must be executed first before the linker can be run.
On windows we must tell nasm
to output a win64
object file.
C:\Program\ Files\ (x86)\Microsoft\ Visual\ Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
nasm -f win64 "Test.asm"
link /machine:x64 "Test.obj" msvcrt.lib legacy_stdio_definitions.lib
.\Test.exe
Running in a Powershell environment is a bit tricky because the vcvars64
script does not work here. It can be invoked
but the variables set in the script will not be visible in Powershell. Therefore the script is executed in a command
window. Then set is called to print all environment variables. This output is piped into a Powershell block that sets
all of the variables in the Powershell environment.
$vs = "`"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat`""
cmd /c "$vs > nul 2>&1 && set" | . { process {
if ($_ -match '^([^=]+)=(.*)') {
[System.Environment]::SetEnvironmentVariable($matches[1], $matches[2])
}
}}
nasm -f win64 "Test.asm"
link /machine:x64 "Test.obj" msvcrt.lib legacy_stdio_definitions.lib
.\Test.exe