zaskar9 / oberon-lang Goto Github PK
View Code? Open in Web Editor NEWAn LLVM frontend for the Oberon programming language
License: MIT License
An LLVM frontend for the Oberon programming language
License: MIT License
I modified line 41 in Sema.cpp:
if (logger_.getErrorCount() == 0 && !config_.isJit()) {
This seems logical to add to me.
I added/replaced the following lines to src\codegen\llvm\LLVMCodeGen.cpp from line 254:
std::string name = config_.getOutputFile();
if (name == "") {
name = path.replace_extension(ext).string();
}
This seems to fix the issue.
While porting some code and unittests from XDS I triggered a crash in the compiler and
created a test module:
MODULE M;
CONST
CCHAR* = 041X;
CINT* = 100;
CLINT* = 0FFFFFFFFFFFFFFFFH;
CREAL* = 1.1;
CREALMAX* = 1.7976931348623158E308;
CBOOL* = TRUE;
CSET* = {0, 31};
CSTR* = "testing123";
VAR
C* : CHAR;
(* X* : BYTE; Not supported *)
I* : INTEGER;
LI* : LONGINT;
R* : REAL;
LR* : LONGREAL;
B* : BOOLEAN;
S* : SET;
STR* : ARRAY 25 OF CHAR;
BEGIN
C := CCHAR;
I := CINT;
LI := CLINT;
R := CREAL;
LR := CREALMAX;
B := CBOOL;
S := CSET;
STR := CSTR
END M.
And a main module:
MODULE TestImport;
IMPORT M, Out;
VAR
c : CHAR;
i : INTEGER;
l : LONGINT;
r : REAL;
lr : LONGREAL;
b : BOOLEAN;
s : SET;
str : ARRAY 25 OF CHAR;
BEGIN
(* Test CONST *)
c := M.CCHAR;
Out.Char(c); Out.Ln;
i := M.CINT;
Out.Int(i, 0); Out.Ln;
i := M.CLINT;
Out.Int(i, 0); Out.Ln;
l := M.CLINT;
Out.Long(l, 0); Out.Ln;
r := M.CREAL;
Out.Real(r, 9); Out.Ln;
lr := M.CREAL;
Out.LongReal(lr, 17); Out.Ln;
r := SHORT(M.CREALMAX); (* SHORT needed here *)
Out.Real(r, 9); Out.Ln;
lr := M.CREALMAX;
Out.LongReal(lr, 17); Out.Ln;
b := M.CBOOL;
Out.Int(ORD(b), 0); Out.Ln;
s := M.CSET;
Out.Set(s); Out.Ln;
str := M.CSTR;
Out.String(str); Out.Ln;
(* Test VAR *)
(* c := M.C *) (* This gives segmentation fault *)
END TestImport.
This creates the following output:
A
100
-1
-1
1.10E+00
1.100000024E+000
INF
1.797693135E+308
1
{ 0 31 }
testing123
LONGINT
, so that the problem with sign extension where the integer value1.1
is inexact represented in base 2 floating point representation and the constant willREAL
or LONGREAL
.1.1
and 1.1E0
is REAL
, while 1.1D0
is LONGREAL
.REAL
not allowed to be exported. This is probably done to keep the compiler as simple as possible and I do not see any logical reason for that.EDIT : Added notes on Oberon-07.
Consider the following code (simplified larger module):
MODULE Test;
IMPORT Out;
TYPE
TEST =
RECORD
title : ARRAY 64 OF CHAR;
tests : LONGINT
END;
VAR
test : TEST;
PROCEDURE Length(str: ARRAY OF CHAR) : INTEGER;
VAR i: INTEGER;
BEGIN
i := 0;
WHILE (i < LEN(str)) & (str[i] # 00X) DO INC(i) END;
RETURN i
END Length;
PROCEDURE FillString(VAR s : ARRAY OF CHAR);
VAR
i : INTEGER;
BEGIN
i := 0;
WHILE i < LEN(s) DO
s[i] := CHR(SHORT(i MOD 20) + 65);
INC(i)
END
END FillString;
PROCEDURE Run1(VAR test: TEST);
VAR
str : ARRAY 257 OF CHAR;
BEGIN
str := "test";
Out.String("1.1 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
FillString(str);
Out.String("1.2 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
END Run1;
PROCEDURE Run2(VAR test: TEST);
VAR
str : ARRAY 257 OF CHAR;
PROCEDURE Assert(b: BOOLEAN);
BEGIN
ASSERT(b)
END Assert;
BEGIN
str := "test";
Out.String("2.1 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
FillString(str);
Out.String("2.2 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
END Run2;
PROCEDURE Run3(VAR test: TEST);
VAR
str : ARRAY 257 OF CHAR;
i : LONGINT;
res : BOOLEAN;
BEGIN
str := "test";
Out.String("3.1 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
FillString(str);
Out.String("3.2 : Length(str) = "); Out.Int(Length(str), 0); Out.Ln;
END Run3;
BEGIN
Run1(test);
Run2(test);
Run2(test)
END Test.
This outputs unexpected result:
1.1 : Length(str) = 4
1.2 : Length(str) = 257
2.1 : Length(str) = 257
2.2 : Length(str) = 257
2.1 : Length(str) = 257
2.2 : Length(str) = 257
The following code:
MODULE Char ;
CONST
NUL* = 00X;
TAB* = 09X;
LF* = 0AX;
CR* = 0DX;
SPC* = 020X;
END Char.
Fails with:
sym\Char.smb: error: Cannot export constant NUL*.
sym\Char.smb: error: Cannot export constant TAB*.
sym\Char.smb: error: Cannot export constant LF*.
sym\Char.smb: error: Cannot export constant CR*.
sym\Char.smb: error: Cannot export constant SPC*.
Compilation failed: 5 error(s), 0 warning(s), 0 message(s).
It seems CHAR is not supported in symbol files.
I was thinking how to support the diverse number of target the LLVM infrastructure is supporting.
There is targets for 64bit, 32bit and 16bit. 8bit target (AVR) is currently marked as "experimental".
(8bit may sound crazy, but these platforms is actually in use very
much today in new designs as it replaces transistor/CMOS logic due
to the low cost (order of 30 cents). Your christmas lights is probably
run on one of these.)
We have the type CHAR
and BYTE
which should be fixed 8bit.
Then there is SHORTINT
, INTEGER
and LONGINT
. I believe it makes
sense to let LONGINT
follow the platform pointer size and array size
limit. The LEN
procedure and SYSTEM.ADR
all return a LONGINT
.
SET
should probably follow LONGINT
size?
Proposal:
Platform | 8Bit | 16Bit | 32Bit | 64Bit |
---|---|---|---|---|
BYTE | 8bit | 8bit | 8bit | 8bit |
CHAR | 8bit | 8bit | 8bit | 8bit |
SHORTINT | 8bit | 8bit | 8bit | 8bit |
INTEGER | 16bit | 16bit | 16bit | 32bit |
LONGINT | 16bit | 16bit | 32bit | 64bit |
SET | 16bit | 16bit | 32bit | 64bit |
REAL | 32bit | 32bit | 32bit | 32bit |
LONGREAL | 64bit | 64bit | 64bit | 64bit |
Legacy Oberon-2 code probably have some assumptions tied to 32bit I
guess and could fail on newer 64bit platforms as 64bit platforms was
not so common. Legacy Oberon-2 code should work fine on 32bit platform
as the bit sizes here are similar to the XDS compiler.
Then if Oberon-07 is to be followed with only REAL
, BYTE
, INTEGER
& SET
.
INTEGER
could be alias for LONGINT
and LEN
& SYSTEM.ADR
etc should
work on all platforms.
For Oberon-07 REAL
should probably be then possible to select if 32bit or 64bit.
For interface with OS functionality or hardware registers probably fixed integer sizes
should be added to SYSTEM
. Otherwise this would be to fragile and 64bit integer
is missing on 32bit platforms.
I see that many "modern" languages or modernized (C++11) now use fixed sizes.
This creates the need for additional types for array sizes and pointers etc and I guess
any library code which should be cross platform probably use some kind logic to select
a suitable fast integer size for the target platform.
Not looked into any implementation details other than I found the compiler is smart enough to
detect when upper bits is discarded in a return statement and acts accordingly for earlier operations.
EDIT : Changed SHORTINT
to 8bit on all platforms.
Found this when building on MSYS2:
C:/msys64/home/rute/oberon-lang/src/main.cpp:97:36: error: expected ';' at end of declaration
97 | std::string separator = ";"
| ^
| ;
1 error generated.
The unit tests fail to execute when I execute the following commands on my MacBook (macOS Sonoma 14.3.1).
cmake .. -G "Unix Makefiles"
make test
I get the following error message.
usage: lit [-h] [--version] [-j N] [--config-prefix NAME] [-D NAME=VAL] [-q] [-s] [-v] [-vv] [-a] [-o PATH]
[--no-progress-bar] [--show-excluded] [--show-skipped] [--show-unsupported] [--show-pass]
[--show-flakypass] [--show-xfail] [--path PATH] [--vg] [--vg-leak] [--vg-arg ARG] [--time-tests]
[--no-execute] [--xunit-xml-output XUNIT_XML_OUTPUT] [--resultdb-output RESULTDB_OUTPUT]
[--time-trace-output TIME_TRACE_OUTPUT] [--timeout MAXINDIVIDUALTESTTIME] [--max-failures MAX_FAILURES]
[--allow-empty-runs] [--ignore-fail] [--max-tests N] [--max-time N] [--order {lexical,random,smart}]
[--shuffle] [-i] [--filter REGEX] [--filter-out REGEX] [--xfail LIST] [--xfail-not LIST] [--num-shards M]
[--run-shard N] [--debug] [--show-suites] [--show-tests] [--show-used-features]
TEST_PATH [TEST_PATH ...]
lit: error: argument -s/--succinct: ignored explicit argument ' -v'
make[3]: *** [test/CMakeFiles/test] Error 2
make[2]: *** [test/CMakeFiles/test.dir/all] Error 2
make[1]: *** [test/CMakeFiles/test.dir/rule] Error 2
make: *** [test] Error 2
I am using lit 17.0.6 and filecheck 17.0.6.
Furthermore, when I try to execute a single test, for example with lit -a codegen/array_1.mod
, I also get an error that seems to indicate that the lib
and include
paths are concatenated the with ;
(which is platform-specific to Windows). On Linux and macOS, these paths should be concatenated with :
.
Right now the functions createNewCall
, createFreeCall
in src\codegen\llvm\LLVMIRBuilder.cpp
has hard coded references to malloc
, free
, abort
and exit
. This can be made more flexible by utilizing the linker support for weak symbols.
I replaced malloc
with OberonRuntime_Allocate
:
Value *
LLVMIRBuilder::createNewCall(TypeNode *type, llvm::Value *param) {
auto fun = module_->getFunction("OberonRuntime_Allocate");
if (!fun) {
auto funTy = FunctionType::get(builder_.getPtrTy(), { builder_.getInt64Ty() }, false);
fun = Function::Create(funTy, GlobalValue::ExternalLinkage, "OberonRuntime_Allocate", module_);
fun->addFnAttr(Attribute::getWithAllocSizeArgs(builder_.getContext(), 0, {}));
fun->addParamAttr(0, Attribute::NoUndef);
}
...
and free
with OberonRuntime_Deallocate
:
Value *
LLVMIRBuilder::createFreeCall([[maybe_unused]] TypeNode *type, Value *param) {
auto fun = module_->getFunction("OberonRuntime_Deallocate");
#ifdef _LLVM_LEGACY
auto voidTy = PointerType::get(builder_.getVoidTy(), 0);
#else
auto voidTy = builder_.getPtrTy();
#endif
if (!fun) {
auto funTy = FunctionType::get(builder_.getVoidTy(), {voidTy}, false);
fun = Function::Create(funTy, GlobalValue::ExternalLinkage, "OberonRuntime_Deallocate", module_);
fun->addParamAttr(0, Attribute::NoUndef);
}
...
Then creates a default implementation OberonRuntimeDefault.c:
#include <stdlib.h>
#include <stdio.h>
__attribute__((weak)) void *OberonRuntime_Allocate( size_t size ) {
printf("OberonRuntimeDefault.Allocate\n");
return malloc(size);
}
__attribute__((weak)) void OberonRuntime_Deallocate( void *ptr ) {
printf("OberonRuntimeDefault.Deallocate\n");
free(ptr);
}
Functions here are decorated with the weak attribute.
This is then just compiled to a static library and works as expected when linked.
This can then exploited to replace the functionality with for example a Garbage Collector (Boehm):
#include <stdlib.h>
#include <stdio.h>
#include <gc.h>
void *OberonRuntime_Allocate( size_t size ) {
printf("OberonRuntimeGC.Allocate\n");
return GC_malloc(size);
}
void OberonRuntime_Deallocate( void *ptr ) {
printf("OberonRuntimeGC.Deallocate\n");
// Skip
}
By linking this as a static library, these definitions take precedence over the weak version.
(This could also be done with an Oberon module.)
In order for this to work as expected with the JIT implementation, default functions need to be injected.
Perhaps default functions can just be defined in the main
module.
Also this can be expanded to cover putchar
etc., so that the Out
module can be redirected.
Consider the following code:
MODULE Test4;
(* Fails to compile in line 38 *)
PROCEDURE Length (str: ARRAY OF CHAR) : LONGINT;
VAR i: LONGINT;
BEGIN
i := 0;
WHILE (i < LEN(str)) & (str[i] # 00X) DO INC(i) END;
RETURN i
END Length;
PROCEDURE Append(VAR dst : ARRAY OF CHAR; src : ARRAY OF CHAR);
VAR
i, n: LONGINT;
ch : CHAR;
BEGIN
n := Length(dst);
i := 0;
ch := src[i];
WHILE (ch # 00X) & (i < LEN(src)) & (i + n < LEN(dst)) DO
dst[i + n] := ch;
INC(i);
ch := src[i]
END
END Append;
PROCEDURE Test();
VAR
str : ARRAY 32 OF CHAR;
sch : ARRAY 1 OF CHAR;
ch : CHAR;
BEGIN
str := "test";
sch := "1";
ch := "3";
Append(str, sch);
Append(str, "2"); (* Char literal works *)
Append(str, ch) (* Fails. Should probably work? *)
END Test;
BEGIN
Test()
END Test4.
Should this work?
This issue is intended to track the current state of the unit tests. Below is a summary of all tests that need work. They fall into the Unsupported, Failed, and Unexpectedly Passed categories. For every test, there is a brief summary of the problem or a description of the feature that is missing.
Unsupported
loop_1.mod
: LOOP
and EXIT
statement missing โ infinite loopmodule_unterminated.mod
: no terminating dot at end of module โ infinite loopprocedure_missing_return.mod
: no error on missing/unreachable RETURN
Failed
arithmetic_12.mod
: data type BYTE
missingarray_5.mod
: data type PROCEDURE
missingcase_1.mod
: CASE
statement missingloop_2.mod
: LOOP
statement missingsystem_6.mod
: problem in Out.Real
(NaN
instead of INF
/-INF
)system_7.mod
: problem with signedness of 64-bit hexadecimal valuesfail_on_extra_semicolon.mod
: expected failUnexpectedly Passed
arithmetic_3.mod
: should trigger integer overflow traparithmetic_5.mod
: divisor cannot be negative (cf. O07.8.2.2)arithmetic_6.mod
: divisor cannot be negative (cf. O07.8.2.2)arithmetic_9.mod
: should trigger integer overflow traparithmetic_10.mod
: should trigger division by zero traparithmetic_11.mod
: Oberon has no implicit type conversions, should be disabled by defaultarithmetic_15.mod
: should trigger division by zero traparray_6.mod
: should trigger out-of-bounds trapprocedure_4.mod
: Oberon-07 scoping rules not enforced: intermediate declarations cannot be accessed in nested proceduresif_with_trailing_end.mod
: parser does not complain about superfluous END
in IF
-ELSIF
-ELSE
statementOverall State
Unsupported : 2
Passed : 68
Expectedly Failed : 7
Failed : 5
Unexpectedly Passed: 9
Windows 10 MSYS 2, LLVM version 17.0.6:
C:/msys64/home/rute/dev/oberon-lang/src/sema/Sema.cpp:659:112: error: implicit conversion changes signedness: 'unsigned int'
to 'long' [-Werror,-Wsign-conversion]
659 | assertInBounds(dynamic_cast<const IntegerLiteralNode *>(index.get()), 0, array->getDimension() - 1);
| ~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~^~~
I made a simple example for embedded use which could be useful to add to the wiki as
this is not trivial to setup from scratch.
Embedded.md
Consider the following code:
MODULE Test;
PROCEDURE Run1();
VAR
str : ARRAY 25 OF CHAR;
PROCEDURE Assert(b: BOOLEAN);
BEGIN
ASSERT(b)
END Assert;
BEGIN
str := "test1";
END Run1;
PROCEDURE Run2();
VAR
str : ARRAY 25 OF CHAR;
PROCEDURE Assert(b: BOOLEAN);
BEGIN
ASSERT(b)
END Assert;
BEGIN
str := "test2";
END Run2;
BEGIN
Run1();
Run2()
END Test.
This fails to compile with the following error:
Test.ob07:17:5: error: Function Test__Assert already defined.
I found this problem with treating INTEGER
as unsigned integers in order to print hex string.
The following tests module reproduce the problem:
(*
RUN: %oberon -I "%S%{pathsep}%inc" -L "%S%{pathsep}%lib" -l oberon --run %s | filecheck %s
Fails : Current DIV and MOD implementation is not in line with Oberon report.
Ref. link : https://lists.inf.ethz.ch/pipermail/oberon/2019/013353.html
*)
MODULE Arithmetic17;
IMPORT Out;
(* From Texts.Mod : JG 21.11.90 / NW 11.7.90 / 24.12.95 / 22.11.10 / 18.11.2014 / 10.1.2019 / AP 15.9.20 Extended Oberon*)
PROCEDURE WriteHex* (x: INTEGER);
VAR i: INTEGER; y, base: INTEGER;
a: ARRAY 20 OF CHAR;
BEGIN
i := 0; base := 16;
REPEAT
y := x MOD base;
IF y < 10 THEN
a[i] := CHR(SHORT(y) + 30H)
ELSE
a[i] := CHR(SHORT(y) + 37H)
END;
x := x DIV base; INC(i)
UNTIL i = 8;
REPEAT DEC(i); Out.Char(a[i]) UNTIL i = 0;
Out.Ln;
END WriteHex;
PROCEDURE Test();
BEGIN
WriteHex(07FFFFFFFH); (* OK *)
WriteHex(08FFFFFFFH); (* Fails due to sign bit *)
WriteHex(0FFFFFFFFH); (* Fails due to sign bit *)
Out.Int(5 DIV 3, 0); Out.Ln;
Out.Int(5 MOD 3, 0); Out.Ln;
Out.Int((-5) DIV 3, 0); Out.Ln;
Out.Int((-5) MOD 3, 0); Out.Ln;
Out.Int(-5 DIV 3, 0); Out.Ln; (* expected : -1, same as -(5 DIV 3) *)
Out.Int(-5 MOD 3, 0); Out.Ln; (* expected : -2, same as -(5 MOD 3) *)
END Test;
BEGIN
Test
END Arithmetic17.
(*
CHECK: 7FFFFFFF
CHECK: 8FFFFFFF
CHECK: FFFFFFFF
CHECK: 1
CHECK: 2
CHECK: -2
CHECK: 1
CHECK: -1
CHECK: -2
*)
I believe the DIV
and MOD
operators is defined in this way in Oberon in order to be used for integers treated as
unsigned values. Ref. the WriteHex
procedure.
The above expected values was checked with Extended Oberon on an emulator.
The Oberon-07 behaviour of DIV
and MOD
implementation with C/C++ operators which I guess LLVM is based
on is described in this post and should be straightforward to implement.
However there is also an related issue with mixed type integer operations which is currently allowed.
This will sign extend the smaller type and the calculation could fail.
Also if the WriteHex
had an LONGINT
argument then passing an INTEGER
would be sign extended to LONGINT
and the procedure will print the wrong value if the INTEGER
value had the sign bit set.
This type inclusion was removed in the Oberon-07 report except for BYTE
type which is treated as a sub-range of INTEGER
.
Maybe we should add an explicit CAST
or VAL
procedure?
EDIT : I am currently busy porting code and unittests from the XDS library to oberon-lang compiler.
Can look at fix in the end of the week.
EDIT : Adde note onBYTE
type.
I have checked Oberon compiler.
Hello World with LLVM backend is working fine.
Linux Mint 21.2 Cinnamon
Linux laptop 5.15.0-91-generic #101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
cmake (3.22.1-1ubuntu1.22.04.1)
libboost-all-dev (1.74.0.3ubuntu7)
llvm (1:14.0-55~exp2)
clang (1:14.0-55~exp2)
sudo apt install cmake llvm libboost-all-dev clang
cmake src
make
cp test/oberon/HelloWorld.Mod ./liboberon/
mv oberon-lang ./liboberon/
cd liboberon/
./oberon-lang --filetype ll Out.Mod
./oberon-lang --filetype ll HelloWorld.Mod
clang -o hello.elf Out.ll HelloWorld.ll
./hello.elf
Hoi Niklaus!
Successfully built on latest ArchLinux distribution.
Version:
oberon-lang version 0.1.0
Target: x86_64-pc-linux-gnu
Includes: Boost 1.83.0, LLVM 17.0.6
Small fixes performed on src/global.h
due to missing include:
#include <sstream>
#include <string>
+#include <cstdint>
Small fixes performed on src/data/ast/ASTContext.h
due to missing include:
#include <string>
#include <vector>
+#include <algorithm>
Small fixes performed on src/CMakeLists.txt
due to different LLVM library structure:
- target_link_libraries(${OBERON_LANG} PRIVATE ${llvm_libs})
+ target_link_libraries(${OBERON_LANG} PRIVATE "LLVM-17")
On ArchLinux LLVM is build to a single dynamic library LLVM-17.so
.
Not sure how to reliable detect this.
Small fix performed on liboberon/Makefile
to proceed with the build:
- O7CFLAGS = -q -O3 --reloc=pic -fenable-extern -fenable-varargs # --target $(TGT)
+ O7CFLAGS = -q -I. -O3 --reloc=pic -fenable-extern -fenable-varargs # --target $(TGT)
-install : dist Oberon.smb Out.smb Random.smb Math.smb Reals.smb Texts.smb
+install : dist Oberon.smb Reals.smb Texts.smb Out.smb Random.smb Math.smb
These changes are probably generic and could be added to the main repository.
Output summary from make test
:
Failed Tests (4):
Oberon :: codegen/arithmetic_12.mod
Oberon :: codegen/array_5.mod
Oberon :: codegen/case_1.mod
Oberon :: codegen/loop_2.mod
Unexpectedly Passed Tests (7):
Oberon :: codegen/arithmetic_11.mod
Oberon :: codegen/arithmetic_15.mod
Oberon :: codegen/arithmetic_3.mod
Oberon :: codegen/arithmetic_5.mod
Oberon :: codegen/arithmetic_6.mod
Oberon :: codegen/arithmetic_9.mod
Oberon :: codegen/procedure_4.mod
Total Discovered Tests: 92
Unsupported : 2 (2.17%)
Passed : 70 (76.09%)
Expectedly Failed : 9 (9.78%)
Failed : 4 (4.35%)
Unexpectedly Passed: 7 (7.61%)
$ mkdir build
$ cd build
$ cmake .. -G "Unix Makefiles"
-- The C compiler identification is GNU 13.2.1
-- The CXX compiler identification is GNU 13.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Boost: /usr/lib/cmake/Boost-1.83.0/BoostConfig.cmake (found version "1.83.0") found components: filesystem program_options
-- Performing Test HAVE_FFI_CALL
-- Performing Test HAVE_FFI_CALL - Success
-- Found FFI: /usr/lib/libffi.so
-- Performing Test Terminfo_LINKABLE
-- Performing Test Terminfo_LINKABLE - Success
-- Found Terminfo: /usr/lib/libtinfo.so
-- Found ZLIB: /usr/lib/libz.so (found version "1.3")
-- Found zstd: /usr/lib/libzstd.so
-- Found LibXml2: /usr/lib/libxml2.so (found version "2.12.3")
-- Found LLVM: 16.0.6
-- Found Boost: /usr/lib/cmake/Boost-1.83.0/BoostConfig.cmake (found version "1.83.0") found components: filesystem
-- Configuring done (0.6s)
-- Generating done (0.0s)
-- Build files have been written to: /home/user/install/oberon-lang/build
$ make
[ 1%] Building CXX object src/CMakeFiles/oberon-lang.dir/main.cpp.o
[ 3%] Building CXX object src/CMakeFiles/oberon-lang.dir/logging/Logger.cpp.o
[ 5%] Building CXX object src/CMakeFiles/oberon-lang.dir/scanner/Scanner.cpp.o
[ 6%] Building CXX object src/CMakeFiles/oberon-lang.dir/scanner/Token.cpp.o
[ 8%] Building CXX object src/CMakeFiles/oberon-lang.dir/scanner/LiteralToken.cpp.o
[ 10%] Building CXX object src/CMakeFiles/oberon-lang.dir/scanner/IdentToken.cpp.o
[ 11%] Building CXX object src/CMakeFiles/oberon-lang.dir/scanner/UndefinedToken.cpp.o
[ 13%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/Ident.cpp.o
[ 15%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/Selector.cpp.o
[ 16%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/NodeVisitor.cpp.o
[ 18%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/Node.cpp.o
[ 20%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/ExpressionNode.cpp.o
[ 22%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/TypeNode.cpp.o
[ 23%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/ArrayTypeNode.cpp.o
[ 25%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/BasicTypeNode.cpp.o
[ 27%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/ProcedureTypeNode.cpp.o
[ 28%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/RecordTypeNode.cpp.o
[ 30%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/PointerTypeNode.cpp.o
[ 32%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/BlockNode.cpp.o
[ 33%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/ModuleNode.cpp.o
[ 35%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/ProcedureNode.cpp.o
[ 37%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/DeclarationNode.cpp.o
[ 38%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/NodeReference.cpp.o
[ 40%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/StatementNode.cpp.o
[ 42%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/AssignmentNode.cpp.o
[ 44%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/IfThenElseNode.cpp.o
[ 45%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/LoopNode.cpp.o
[ 47%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/StatementSequenceNode.cpp.o
[ 49%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/ImportNode.cpp.o
[ 50%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/ast/NodePrettyPrinter.cpp.o
[ 52%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/symtab/SymbolTable.cpp.o
[ 54%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/symtab/Scope.cpp.o
[ 55%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/symtab/SymbolFile.cpp.o
[ 57%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/symtab/SymbolExporter.cpp.o
[ 59%] Building CXX object src/CMakeFiles/oberon-lang.dir/data/symtab/SymbolImporter.cpp.o
[ 61%] Building CXX object src/CMakeFiles/oberon-lang.dir/parser/Parser.cpp.o
[ 62%] Building CXX object src/CMakeFiles/oberon-lang.dir/system/OberonSystem.cpp.o
[ 64%] Building CXX object src/CMakeFiles/oberon-lang.dir/system/PredefinedProcedure.cpp.o
[ 66%] Building CXX object src/CMakeFiles/oberon-lang.dir/analyzer/Analyzer.cpp.o
[ 67%] Building CXX object src/CMakeFiles/oberon-lang.dir/analyzer/SemanticAnalysis.cpp.o
[ 69%] Building CXX object src/CMakeFiles/oberon-lang.dir/analyzer/LambdaLifter.cpp.o
[ 71%] Building CXX object src/CMakeFiles/oberon-lang.dir/codegen/CodeGen.cpp.o
[ 72%] Building CXX object src/CMakeFiles/oberon-lang.dir/codegen/llvm/LLVMIRBuilder.cpp.o
[ 74%] Building CXX object src/CMakeFiles/oberon-lang.dir/codegen/llvm/LLVMCodeGen.cpp.o
[ 76%] Building CXX object src/CMakeFiles/oberon-lang.dir/compiler/CompilerFlags.cpp.o
[ 77%] Building CXX object src/CMakeFiles/oberon-lang.dir/compiler/CompilationStatus.cpp.o
[ 79%] Building CXX object src/CMakeFiles/oberon-lang.dir/compiler/Compiler.cpp.o
[ 81%] Linking CXX executable oberon-lang
/usr/bin/ld: cannot find -lLLVMCore: No such file or directory
/usr/bin/ld: cannot find -lLLVMPasses: No such file or directory
/usr/bin/ld: cannot find -lLLVMAArch64CodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMAArch64AsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMAArch64Desc: No such file or directory
/usr/bin/ld: cannot find -lLLVMAArch64Disassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMAArch64Info: No such file or directory
/usr/bin/ld: cannot find -lLLVMAArch64Utils: No such file or directory
/usr/bin/ld: cannot find -lLLVMAMDGPUCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMAMDGPUAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMAMDGPUDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMAMDGPUDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMAMDGPUInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMAMDGPUUtils: No such file or directory
/usr/bin/ld: cannot find -lLLVMARMCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMARMAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMARMDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMARMDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMARMInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMARMUtils: No such file or directory
/usr/bin/ld: cannot find -lLLVMAVRCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMAVRAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMAVRDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMAVRDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMAVRInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMBPFCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMBPFAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMBPFDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMBPFDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMBPFInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMHexagonCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMHexagonAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMHexagonDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMHexagonDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMHexagonInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMLanaiCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMLanaiAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMLanaiDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMLanaiDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMLanaiInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMLoongArchCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMLoongArchAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMLoongArchDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMLoongArchDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMLoongArchInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMMipsCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMMipsAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMMipsDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMMipsDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMMipsInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMMSP430CodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMMSP430AsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMMSP430Desc: No such file or directory
/usr/bin/ld: cannot find -lLLVMMSP430Disassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMMSP430Info: No such file or directory
/usr/bin/ld: cannot find -lLLVMNVPTXCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMNVPTXDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMNVPTXInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMPowerPCCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMPowerPCAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMPowerPCDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMPowerPCDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMPowerPCInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMRISCVCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMRISCVAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMRISCVDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMRISCVDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMRISCVInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMSparcCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMSparcAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMSparcDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMSparcDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMSparcInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMSystemZCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMSystemZAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMSystemZDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMSystemZDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMSystemZInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMVECodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMVEAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMVEDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMVEDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMVEInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMWebAssemblyCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMWebAssemblyAsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMWebAssemblyDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMWebAssemblyDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMWebAssemblyInfo: No such file or directory
/usr/bin/ld: cannot find -lLLVMWebAssemblyUtils: No such file or directory
/usr/bin/ld: cannot find -lLLVMX86CodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMX86AsmParser: No such file or directory
/usr/bin/ld: cannot find -lLLVMX86Desc: No such file or directory
/usr/bin/ld: cannot find -lLLVMX86Disassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMX86Info: No such file or directory
/usr/bin/ld: cannot find -lLLVMXCoreCodeGen: No such file or directory
/usr/bin/ld: cannot find -lLLVMXCoreDesc: No such file or directory
/usr/bin/ld: cannot find -lLLVMXCoreDisassembler: No such file or directory
/usr/bin/ld: cannot find -lLLVMXCoreInfo: No such file or directory
collect2: error: ld returned 1 exit status
make[2]: *** [src/CMakeFiles/oberon-lang.dir/build.make:841: src/oberon-lang] Error 1
make[1]: *** [CMakeFiles/Makefile2:104: src/CMakeFiles/oberon-lang.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
$ llvm-config --libs
-lLLVM-16
This code fails to compile due to the extra semicolon:
MODULE Test3;
(* Fails to compile in line 15 *)
VAR
res : LONGINT;
PROCEDURE Test() : LONGINT;
VAR i, j : LONGINT;
BEGIN
i := 0; j := 6;
WHILE i < 10 DO
IF i = 8 THEN
RETURN i
ELSIF i = 7 THEN
RETURN i; (* Error triggered by this extra semicolon *)
ELSIF i = j THEN
RETURN i
END;
INC(i)
END;
END Test;
BEGIN
res := Test();
END Test3.
This is not so easy to reproduce.
I have a custom procedure defined which return a string literal.
From OberonSystem.cpp
:
this->createProcedure(ProcKind::COMPILER_ENV, "Env", {{stringType, false}}, stringType, false, true);
And in LLVMIRBuilder.cpp
:
Value *
LLVMIRBuilder::createCompilerEnvCall(vector<unique_ptr<ExpressionNode>> &actuals) {
auto param = actuals[0].get();
auto arg = dynamic_cast<StringLiteralNode*>(param);
if (!arg) {
logger_.error(param->pos(), "expected constant string");
return value_;
}
std::string val;
auto str = arg->value();
if (str == "HOST") {
val = sys::getProcessTriple();
} else if (str == "FILE") {
val = param->pos().fileName;
} else {
logger_.error(param->pos(), "unknown argumen '" + str + "'");
return value_;
}
auto len = val.size() + 1;
auto type = StructType::get(builder_.getInt64Ty(), ArrayType::get(builder_.getInt8Ty(), len));
value_ = strings_[val];
if (!value_) {
auto initializer = ConstantStruct::get(type, {builder_.getInt64(len), ConstantDataArray::getRaw(val, len, builder_.getInt8Ty())});
auto str = new GlobalVariable(*module_, type, true, GlobalValue::InternalLinkage, initializer, ".str");
str->setAlignment(module_->getDataLayout().getPrefTypeAlign(type));
strings_[val] = str;
value_ = strings_[val];
}
return value_;
}
The following works until the code crashes on assignment in global scope.
MODULE Test;
IMPORT SYSTEM, COMPILER, RTS := RUNTIME;
VAR
str : ARRAY 64 OF CHAR;
PROCEDURE test(s : ARRAY OF CHAR);
BEGIN
str := s;
END test;
BEGIN
RTS.Println(COMPILER.Env("FILE")); (* OK *)
test(COMPILER.Env("FILE")); (* OK *)
RTS.Println(str);
str := COMPILER.Env("FILE"); (* Crash *)
RTS.Println(str);
END Test.
Assignment in the test
procedure works.
I did not find a way to make a pull request for the wiki and therefore attach the page to this issue.
MSYS2.md
Added the following to ieee754.h:
#elif (defined(_WIN32) || defined(_WIN64))
#include <winsock2.h>
#ifdef GNUC
#include <sys/param.h>
#endif
Fixes compilation.
The following gives an error undefined identifier x:
MODULE Test;
PROCEDURE Max (x, y : REAL) : REAL;
VAR ret : REAL;
BEGIN
IF x > y THEN
ret := x
ELSE
ret := y
END;
RETURN ret
END Max;VAR
x, y, z : REAL;BEGIN
x := -2.5;
y := 1.0;
z := Max(x, y)
END Test.
Moving the module level VAR section to top of the file fixes the
problem, but perhaps the order of declaration should be enforced here
and give another error message?
As far as I can see it seems the only cross platform way to access argc
& argv
is to store these variables
in the main
procedure. Right now the main procedure is without arguments. This would then need to
changed and argc
, argc
stored in external visible variables.
Alternative access from external visible functions in similar way to outline in #46. This way the argument
can be modified by the runtime for example when running on embedded MCU in semihosting mode.
Also in the JIT mode the original argv
should be modified so that perhaps all arguments after --
from the
command line is propagated to the main
function.
The following code:
MODULE Test;
IMPORT Out;
VAR
str : ARRAY 25 OF CHAR;PROCEDURE Capacity(str : ARRAY OF CHAR): LONGINT;
BEGIN RETURN LEN(str)
END Capacity;PROCEDURE CapacityVar(VAR str : ARRAY OF CHAR): LONGINT;
BEGIN RETURN LEN(str)
END CapacityVar;BEGIN
Out.Long(LEN(str), 0); Out.Ln();
Out.Long(Capacity(str), 0); Out.Ln();
Out.Long(CapacityVar(str), 0); Out.Ln();
str := "test";
Out.Long(LEN(str), 0); Out.Ln();
Out.Long(Capacity(str), 0); Out.Ln();
Out.Long(CapacityVar(str), 0); Out.Ln()
END Test.
Outputs:
25
25
25
25
5
5
The array seems to be changed when passed as argument.
Hi,
I was able to easily build the oberon-lang repo on MSYS2/CLANG64 platform:
pacman -S mingw-w64-clang-x86_64-toolchain
pacman -S mingw-w64-clang-x86_64-boost
pacman -S mingw-w64-clang-x86_64-cmake
git clone https://github.com/zaskar9/oberon-lang.git
cd oberon-lang/
mkdir build
cd build
cmake .. -G "MSYS Makefiles"
make
Makefiles could be updated with support:
uname -s
MINGW64_NT-10.0-19045
MSYS2/CLANG64 platform is new, but seems to be stable and is an good option on the windows platform.
I was curious if I could use this compiler for embedded development and found some issues affecting this:
I was thinking to build the compiler with the LLVM-embedded-toolchain-for-Arm
project as the Oberon language is really suited to this role with it simplicity and limited footguns compared to well known languages :-)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.