From 68611b4df32d4c94bb4e5e0a2c24108b29db041f Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sun, 25 Jan 2026 15:10:26 +0100 Subject: [PATCH] More work --- 3-lang0ll/lang0ll.lang0 | 226 ++++++++++++--- 3-lang0ll/runtimetest.ll | 272 +++++++++++++++++++ Makefile | 2 + tests/Makefile | 22 +- tests/build/test_stdlib_functions/.gitignore | 8 + tests/generate-recipes.py | 1 + 6 files changed, 486 insertions(+), 45 deletions(-) create mode 100644 3-lang0ll/runtimetest.ll diff --git a/3-lang0ll/lang0ll.lang0 b/3-lang0ll/lang0ll.lang0 index 9f2548d..89479d6 100644 --- a/3-lang0ll/lang0ll.lang0 +++ b/3-lang0ll/lang0ll.lang0 @@ -1,4 +1,18 @@ -registerid id: +registerfunc id: + declare idname + declare newidx + + calc idname mapgetkey "REGISTERID" id "" + if idname + return idname + / + + declare idnamea + calc idnamea add "@id_" id + return idnamea +/ + +registervar id: declare idname declare newidx @@ -22,8 +36,8 @@ makelocalvar: declare prevvar declare newvar declare result - calc prevvar mapgetkey "localvarused" "" "" - calc newvar add prevvar "1" + calc prevvar mapgetkey "localvarused" "" "0" + calc newvar intinc prevvar mapsetkey "localvarused" "" newvar calc result add "%tmp" newvar return result @@ -33,8 +47,8 @@ makelocallabel: declare prevlabel declare newlabel declare result - calc prevlabel mapgetkey "locallabelused" "" "" - calc newlabel add prevlabel "1" + calc prevlabel mapgetkey "locallabelused" "" "0" + calc newlabel intinc prevlabel mapsetkey "locallabelused" "" newlabel calc result add "lbl" newlabel return result @@ -44,15 +58,15 @@ makestringconst str: declare constid declare isempty - calc constid mapgetkey "stringconst" str "" + calc constid mapgetkey "str_to_const_id" str "" calc isempty eq constid "" if isempty - calc constid mapgetkey "stringconst" "___count___" "0" - calc constid add constid "1" - mapsetkey "stringconst" "___count___" constid + calc constid mapgetkey "const_id_to_str" "___count___" "0" + calc constid intinc constid + mapsetkey "const_id_to_str" "___count___" constid / - mapsetkey "stringconst" str constid - mapsetkey "stringconst" constid str + mapsetkey "str_to_const_id" str constid + mapsetkey "const_id_to_str" constid str return constid / @@ -130,7 +144,7 @@ parseexprvarref: declare varid declare varname calc varname lexident - calc varid registerid varname + calc varid registervar varname emit "i8* " emit varid return @@ -148,9 +162,9 @@ parseexprcall: declare isquote declare isnotquote calc funcname lexident + calc funcid registerfunc funcname check mapgetkey "FUNCREG" funcname "" : "Function" funcname "does not exist" - emit "@id_" - emit funcname + emit funcid emit "(" set first "1" forever @@ -188,7 +202,7 @@ parsestatdeclare indent: calc varname lexident skipchar eol - calc varid registerid varname + calc varid registervar varname emit indent emit varid emitln " = alloca i8*" @@ -204,7 +218,7 @@ parsestatset indent: skipchar " " calc varname lexident - calc varid registerid varname + calc varid registervar varname skipchar " " emit indent emit tmpvar @@ -233,7 +247,7 @@ parsestatcalc indent: skipchar " " calc varname lexident - calc varid registerid varname + calc varid registervar varname skipchar " " emit indent emit tmpvar @@ -400,7 +414,7 @@ parsestatcheck indent: skipchar " " calc funcname lexident - calc funcid registerid funcname + calc funcid registerfunc funcname emit indent emit funcresvar @@ -464,7 +478,7 @@ parsestatcheck indent: emit indent emit " " emit stderrvar - emitln " = load %FILE*, %FILE** @stderrvar" + emitln " = load %FILE*, %FILE** @stderr" emit indent emit " call i32 @fputs(i8* @str.ERROR, %FILE* " @@ -527,7 +541,7 @@ parsestattrace indent: skipchar eol calc callconstid makestringconst varname - calc varid registerid varname + calc varid registervar varname emit indent emit "call void @__trace(i8* @str." @@ -551,6 +565,7 @@ parsestat indent: declare isnotspace declare isnotquote calc call lexident + calc callid registerfunc call trace call calc iscall eq call "declare" if iscall @@ -599,8 +614,8 @@ parsestat indent: / check mapgetkey "FUNCREG" call "" : "Function" call "does not exist" emit indent - emit "call i8* @id_" - emit call + emit "call i8* " + emit callid emit "(" set first "1" forever @@ -689,10 +704,11 @@ parsefunc: declare varid declare varname calc funcname lexident + calc funcid registerfunc funcname mapsetkey "FUNCREG" funcname "1" trace funcname - emit "define i8* @id_" - emit funcname + emit "define i8* " + emit funcid emit "(" set first "1" forever @@ -704,7 +720,7 @@ parsefunc: / skip calc varname lexident - calc varid registerid varname + calc varid registervar varname calc isfirst eq first "1" calc isnotfirst not isfirst if isnotfirst @@ -727,6 +743,7 @@ parsefunc: emitln ")" emitln "{" parseblock " " + emitln " ret i8* @__EOF" emitln "}" emit eol return @@ -757,14 +774,51 @@ emitheader: emit eol + emit "@__BACKSLASH = internal constant [2 x i8] c" + emit quote + emit backslash + emit backslash + emit backslash + emit "00" + emit quote + emitln "" + + emit "@__EOF = internal constant [2 x i8] c" + emit quote + emit backslash + emit backslash + emit backslash + emit "FF" + emit quote + emitln "" + emit "@__EOL = internal constant [2 x i8] c" emit quote - emit "\\0A\\00" + emit backslash + emit "0A" + emit backslash + emit "00" emitln quote emit "@__QUOTE = internal constant [2 x i8] c" emit quote - emit "\\22\\00" + emit backslash + emit "22" + emit backslash + emit "00" + emitln quote + + emit "@__FALSE = internal constant [1 x i8] c" + emit quote + emit backslash + emit "00" + emitln quote + + emit "@__TRUE = internal constant [2 x i8] c" + emit quote + emit "1" + emit backslash + emit "00" emitln quote emit eol @@ -774,6 +828,75 @@ emitheader: emitln " ret i8* %0" emitln "}" + emitln "define i8* @__eq(i8* %0, i8* %1)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__lt(i8* %0, i8* %1)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__not(i8* %0)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__add(i8* %0, i8* %1)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__emit(i8* %0)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__trace(i8* %0)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__peek()" + emitln "{" + emitln " ret i8* @__EOL ; STUB" + emitln "}" + + emitln "define i8* @__skip()" + emitln "{" + emitln " ret i8* @__EOL ; STUB" + emitln "}" + + emitln "define i8* @__stdinlineno()" + emitln "{" + emitln " ret i8* @__EOL ; STUB" + emitln "}" + + emitln "define i8* @__stdincolno()" + emitln "{" + emitln " ret i8* @__EOL ; STUB" + emitln "}" + + emitln "define i8* @__mapgetkey(i8* %0, i8* %1, i8* %2)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__mapsetkey(i8* %0, i8* %1, i8* %2)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__strlen(i8* %0)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" + + emitln "define i8* @__intinc(i8* %0)" + emitln "{" + emitln " ret i8* %0 ; STUB" + emitln "}" emit eol @@ -782,48 +905,58 @@ emitheader: emitfooter: declare constid + declare done + declare len + declare maxconstid declare str declare strlenvar declare dontusethisstr + emitln "define i64 @main() {" + emitln " call i8* @id_main()" + emitln " ret i64 0" + emitln "}" + emit "@str.ERROR = internal constant [7 x i8] c" emit quote - emit "ERROR:\\00" + emit "ERROR:" + emit backslash + emit "00" emit quote emitln "" emit "@str.EMPTY = internal constant [1 x i8] c" emit quote - emit "\\00" + emit backslash + emit "00" emit quote emitln "" - - set dontusethisstr "__dontusethisstr__" - - set constid "01" + set constid "0" + calc maxconstid mapgetkey "const_id_to_str" "___count___" "0" forever - calc str mapgetkey "stringconst" constid dontusethisstr + calc str mapgetkey "const_id_to_str" constid "0" - declare unused - calc unused eq str dontusethisstr - if unused - break - / - - calc strlen strlen str + calc len strlen str + calc len intinc len emit "@str." emit constid emit " = internal constant [" - emit strlen + emit len emit " x i8] c" emit quote emit str - emit "\\00" + emit backslash + emit "00" emitln quote - calc constid add constid "1" + calc done eq constid maxconstid + if done + break + / + + calc constid intinc constid / emit "" @@ -838,6 +971,7 @@ main: emitheader + mapsetkey "REGISTERID" "backslash" "@__BACKSLASH" mapsetkey "REGISTERID" "eof" "@__EOF" mapsetkey "REGISTERID" "eol" "@__EOL" mapsetkey "REGISTERID" "quote" "@__QUOTE" @@ -866,6 +1000,10 @@ main: mapsetkey "REGISTERID" "stdincolno" "@__stdincolno" mapsetkey "FUNCREG" "stdinlineno" "1" mapsetkey "REGISTERID" "stdinlineno" "@__stdinlineno" + mapsetkey "FUNCREG" "strlen" "1" + mapsetkey "REGISTERID" "strlen" "@__strlen" + mapsetkey "FUNCREG" "intinc" "1" + mapsetkey "REGISTERID" "intinc" "@__intinc" forever calc char peek diff --git a/3-lang0ll/runtimetest.ll b/3-lang0ll/runtimetest.ll new file mode 100644 index 0000000..20c7be9 --- /dev/null +++ b/3-lang0ll/runtimetest.ll @@ -0,0 +1,272 @@ +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%FILE = type opaque +@stdout = global %FILE* null + +declare void @exit(i32) +declare i32 @fputs(i8*, %FILE*) +declare %FILE* @fdopen(i32, i8*) +declare void @fclose(%FILE*) + +@__BACKSLASH = internal constant [2 x i8] c"\\\00" +@__EOF = internal constant [2 x i8] c"\\\FF" +@__EOL = internal constant [2 x i8] c"\0A\00" +@__QUOTE = internal constant [2 x i8] c"\22\00" +@__FALSE = internal constant [1 x i8] c"\00" +@__TRUE = internal constant [2 x i8] c"1\00" + +define i8* @__drop_i8_type(i8* %0) alwaysinline readnone +{ + ret i8* %0 +} +define i8* @__eq(i8* %0, i8* %1) +{ + %i = alloca i64, align 8 + + store i64 0, i64* %i + br label %loop +loop: + %index = load i64, i64* %i + %char0_adr = getelementptr i8, i8* %0, i64 %index + %char1_adr = getelementptr i8, i8* %1, i64 %index + + %char0 = load i8, i8* %char0_adr + %char1 = load i8, i8* %char1_adr + + %chars_differ = icmp ne i8 %char0, %char1 + br i1 %chars_differ, label %lbl_not_equal, label %lbl_equal +lbl_equal: + %string_end = icmp eq i8 %char0, 0 + br i1 %string_end, label %string_equal, label %next_char +string_equal: + ret i8* @__TRUE +next_char: + %next_index = add i64 %index, 1 + store i64 %next_index, i64* %i + br label %loop + +lbl_not_equal: + ret i8* @__FALSE +} +define i8* @__lt(i8* %0, i8* %1) +{ + %i = alloca i64, align 8 + + store i64 0, i64* %i + br label %loop +loop: + %index = load i64, i64* %i + %char0_adr = getelementptr i8, i8* %0, i64 %index + %char1_adr = getelementptr i8, i8* %1, i64 %index + + %char0 = load i8, i8* %char0_adr + %char1 = load i8, i8* %char1_adr + + %char0_eol = icmp eq i8 %char0, 0 + %char1_eol = icmp eq i8 %char1, 0 + + ; if char0 == 0 + ; if char1 == 0 + ; return FALSE ; "" < "" === False + ; else + ; return TRUE ; "" < "." === True + ; endif + ; else + ; if char1 == 0 + ; return FALSE ; "." < "" === False + ; else + ; if char0 < char1 + ; return TRUE ; "a" < "b" === True + ; else + ; if char0 > char1 + ; return FALSE ; "b" < "a" === False + ; else + ; jump next_char + ; endif + ; endif + ; endif + ; endif + br i1 %char0_eol, label %lbl_char0_eol, label %lbl_char0_not_eol +lbl_char0_eol: + br i1 %char1_eol, label %lbl_not_smaller, label %lbl_smaller +lbl_char0_not_eol: + br i1 %char1_eol, label %lbl_not_smaller, label %lbl_char0_not_eol_char1_not_eol +lbl_char0_not_eol_char1_not_eol: + %char0_lt_char1 = icmp ult i8 %char0, %char1 + br i1 %char0_lt_char1, label %lbl_smaller, label %lbl_char0_ge_char1 +lbl_char0_ge_char1: + %char0_gt_char1 = icmp ugt i8 %char0, %char1 + br i1 %char0_gt_char1, label %lbl_not_smaller, label %next_char +next_char: + %next_index = add i64 %index, 1 + store i64 %next_index, i64* %i + br label %loop + +lbl_smaller: + ret i8* @__TRUE +lbl_not_smaller: + ret i8* @__FALSE +} +define i8* @__not(i8* %0) +{ + %char0_adr = getelementptr i8, i8* %0, i64 0 + %char0 = load i8, i8* %char0_adr + %char0_eol = icmp eq i8 %char0, 0 + br i1 %char0_eol, label %lbl_empty, label %lbl_not_empty +lbl_empty: + ret i8* @__TRUE; +lbl_not_empty: + ret i8* @__FALSE; +} +define i8* @__add(i8* %0, i8* %1) +{ + ret i8* %0 ; STUB +} +define i8* @__emit(i8* %0) +{ + %stdout_ptr = load %FILE*, %FILE** @stdout + call i32 @fputs(i8* %0, %FILE* %stdout_ptr) + ret i8* @__FALSE +} +define i8* @__trace(i8* %0, i8* %1) +{ + ret i8* %0 ; STUB +} +define i8* @__peek() +{ + ret i8* @__EOL ; STUB +} +define i8* @__skip() +{ + ret i8* @__EOL ; STUB +} +define i8* @__stdinlineno() +{ + ret i8* @__EOL ; STUB +} +define i8* @__stdincolno() +{ + ret i8* @__EOL ; STUB +} +define i8* @__mapgetkey(i8* %0, i8* %1, i8* %2) +{ + ret i8* %0 ; STUB +} +define i8* @__mapsetkey(i8* %0, i8* %1, i8* %2) +{ + ret i8* %0 ; STUB +} +define i8* @__strlen(i8* %0) +{ + ret i8* %0 ; STUB +} +define i8* @__intinc(i8* %0) +{ + ret i8* %0 ; STUB +} + + +define void @test_result_1(i8* %func, i8* %arg0, i8* %result) +{ + call i32 @__emit(i8* %func) + call i32 @__emit(i8* @str.bracket) + call i32 @__emit(i8* %arg0) + call i32 @__emit(i8* @str.bracket_colon_space) + call i32 @__emit(i8* %result) + call i32 @__emit(i8* @__EOL) + ret void +} +define void @test_result_2(i8* %func, i8* %arg0, i8* %arg1, i8* %result) +{ + call i32 @__emit(i8* %func) + call i32 @__emit(i8* @str.bracket) + call i32 @__emit(i8* %arg0) + call i32 @__emit(i8* @str.comma_space) + call i32 @__emit(i8* %arg1) + call i32 @__emit(i8* @str.bracket_colon_space) + call i32 @__emit(i8* %result) + call i32 @__emit(i8* @__EOL) + ret void +} + +define void @test_eq(i8* %0, i8* %1) +{ + %result = call i8* @__eq(i8* %0, i8* %1) + call void @test_result_2(i8* @str.eq, i8* %0, i8* %1, i8* %result) + ret void +} +define void @test_lt(i8* %0, i8* %1) +{ + %result = call i8* @__lt(i8* %0, i8* %1) + call void @test_result_2(i8* @str.lt, i8* %0, i8* %1, i8* %result) + ret void +} +define void @test_not(i8* %0) +{ + %result = call i8* @__not(i8* %0) + call void @test_result_1(i8* @str.not, i8* %0, i8* %result) + ret void +} + + +define i64 @main() +{ + %stdout_ptr = call %FILE* @fdopen(i32 1, i8* @str.w) + store %FILE* %stdout_ptr, %FILE** @stdout + + call void @test_eq(i8* @str., i8* @str.0) + call void @test_eq(i8* @str.0, i8* @str.) + call void @test_eq(i8* @str.0, i8* @str.0) + call void @test_eq(i8* @str.a, i8* @str.a) + call void @test_eq(i8* @str.a, i8* @str.b) + call void @test_eq(i8* @str.a, i8* @str.c) + call void @test_eq(i8* @str.b, i8* @str.a) + call void @test_eq(i8* @str.b, i8* @str.b) + call void @test_eq(i8* @str.b, i8* @str.c) + call void @test_eq(i8* @str.c, i8* @str.a) + call void @test_eq(i8* @str.c, i8* @str.b) + call void @test_eq(i8* @str.c, i8* @str.c) + call void @test_eq(i8* @str.left, i8* @str.right) + call void @test_eq(i8* @str.left, i8* @str.left) + call void @test_eq(i8* @str.right, i8* @str.left) + call void @test_eq(i8* @str.right, i8* @str.right) + + call void @test_lt(i8* @str.0, i8* @str.0) + call void @test_lt(i8* @str.a, i8* @str.a) + call void @test_lt(i8* @str.a, i8* @str.b) + call void @test_lt(i8* @str.a, i8* @str.c) + call void @test_lt(i8* @str.b, i8* @str.a) + call void @test_lt(i8* @str.b, i8* @str.b) + call void @test_lt(i8* @str.b, i8* @str.c) + call void @test_lt(i8* @str.c, i8* @str.a) + call void @test_lt(i8* @str.c, i8* @str.b) + call void @test_lt(i8* @str.c, i8* @str.c) + ; `a` and `b` are expected to have length 1. + + call void @test_not(i8* @str.) + call void @test_not(i8* @str.0) + call void @test_not(i8* @str.1) + + + + call i32 @fclose(%FILE* %stdout_ptr) + + ret i64 0 +} + +@str. = internal constant [1 x i8] c"\00" +@str.0 = internal constant [2 x i8] c"0\00" +@str.1 = internal constant [2 x i8] c"1\00" +@str.a = internal constant [2 x i8] c"a\00" +@str.b = internal constant [2 x i8] c"b\00" +@str.c = internal constant [2 x i8] c"c\00" +@str.bracket = internal constant [2 x i8] c"(\00" +@str.bracket_colon_space = internal constant [4 x i8] c"): \00" +@str.comma_space = internal constant [3 x i8] c", \00" +@str.eq = internal constant [3 x i8] c"eq\00" +@str.left = internal constant [5 x i8] c"left\00" +@str.lt = internal constant [3 x i8] c"lt\00" +@str.not = internal constant [4 x i8] c"not\00" +@str.right = internal constant [6 x i8] c"right\00" +@str.w = internal constant [2 x i8] c"w\00" diff --git a/Makefile b/Makefile index 1e83fc8..84f2cb6 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,12 @@ all: $(MAKE) -C 0-lang0py all $(MAKE) -C 1-lang0py all $(MAKE) -C 2-lang0c all + $(MAKE) -C 3-lang0ll all $(MAKE) -C tests all clean: $(MAKE) -C 0-lang0py clean $(MAKE) -C 1-lang0py clean $(MAKE) -C 2-lang0c clean + $(MAKE) -C 3-lang0ll clean $(MAKE) -C tests clean diff --git a/tests/Makefile b/tests/Makefile index 15be89e..82d1ec5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -11,7 +11,7 @@ CFLAGS=-ggdb all: verify-results Makefile.mk: generate-recipes.py $(wildcard test_*/test_*) - python3 generate-recipes.py Makefile.mk it0 it1 it2 + python3 generate-recipes.py Makefile.mk it0 it1 it2 it3 include Makefile.mk @@ -29,6 +29,10 @@ clean: find build -name '*.it2.result' -delete find build -name '*.it2.stderr' -delete find build -name '*.it2.stdout' -delete + find build -name '*.it3' -delete + find build -name '*.it3.result' -delete + find build -name '*.it3.stderr' -delete + find build -name '*.it3.stdout' -delete find build -name '*.py' -delete find build -name '*.tmp' -delete @@ -85,3 +89,19 @@ build/%.it2.o: build/%.it2.c build/%.it2: build/%.it2.o $(CC) $(CFLAGS) -o $@ $^ -lpython$(PYVERSION) + +### +# it3 + +../3-lang0ll/lang0ll.exe: ../0-lang0py/lang0py.exe ../1-lang0py/lang0py.exe ../2-lang0c/lang0c.exe ../3-lang0ll/lang0ll.lang0 + $(MAKE) -C ../3-lang0ll + +build/%.it3.ll: %.lang0 ../3-lang0ll/lang0ll.exe + -cat $< | ../3-lang0ll/lang0ll.exe > $@.tmp 2> build/$*.it3.cmperr + mv $@.tmp $@ + +build/%.it3.s: build/%.it3.ll + llc --relocation-model=pic $^ + +build/%.it3: build/%.it3.s + clang $^ -o $@ diff --git a/tests/build/test_stdlib_functions/.gitignore b/tests/build/test_stdlib_functions/.gitignore index 74aeb99..32fa164 100644 --- a/tests/build/test_stdlib_functions/.gitignore +++ b/tests/build/test_stdlib_functions/.gitignore @@ -24,3 +24,11 @@ /*.it2.result /*.it2.stderr /*.it2.stdout +/*.it3 +/*.it3.ll +/*.it3.cmperr +/*.it3.ll.tmp +/*.it3.o +/*.it3.result +/*.it3.stderr +/*.it3.stdout diff --git a/tests/generate-recipes.py b/tests/generate-recipes.py index 164185f..eedacb4 100644 --- a/tests/generate-recipes.py +++ b/tests/generate-recipes.py @@ -6,6 +6,7 @@ STAGE0_MAP = { 'it0': '.py', 'it1': '.py', 'it2': '.c', + 'it3': '.ll', } class Rule: