Start on llvm IR target

This commit is contained in:
Johan B.W. de Vries 2025-02-01 17:01:27 +01:00
parent f798fbe55f
commit b911d9a49c
14 changed files with 767 additions and 206 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/it0-out0.diff /it0-out0.diff
/it1-out* /it1-out*
/it2-out* /it2-out*
/it3-out*

View File

@ -40,30 +40,54 @@ it2-out0.py: it1-out.py it2-in.lang0
# No gcc step since the target is still python here # No gcc step since the target is still python here
it2-out1.c: it2-out0.py it2-in.lang0 it2-out1.c: it2-out0.py it2-in.lang0
cat it2-in.lang0 | python3 it2-out0.py > it2-out1.c cat it2-in.lang0 | python3 it2-out0.py > $@
# diff it2-out0.py it2-out1.py Do not diff - the target changed so the output will be different # diff it2-out0.py it2-out1.py Do not diff - the target changed so the output will be different
it2-out1: it2-out1.c it2-out1: it2-out1.c
gcc it2-out1.c -o it2-out1 $(GCC_FLAGS) gcc $^ -o $@ $(GCC_FLAGS)
it2-out2.c: it2-out1 it2-in.lang0 it2-out2.c: it2-out1 it2-in.lang0
cat it2-in.lang0 | ./it2-out1 > it2-out2.c cat it2-in.lang0 | ./it2-out1 > $@
diff it2-out1.c it2-out2.c diff it2-out1.c $@
it2-out2: it2-out2.c it2-out2: it2-out2.c
gcc it2-out2.c -o it2-out2 $(GCC_FLAGS) gcc $^ -o $@ $(GCC_FLAGS)
it2-out3.c: it2-out2 it2-in.lang0 it2-out3.c: it2-out2 it2-in.lang0
cat it2-in.lang0 | ./it2-out2 > it2-out3.c cat it2-in.lang0 | ./it2-out2 > it2-out3.c
diff it2-out2.c it2-out3.c diff it2-out2.c it2-out3.c
it2-out3: it2-out3.c it2-out3: it2-out3.c
gcc it2-out3.c -o it2-out3 $(GCC_FLAGS) gcc $^ -o $@ $(GCC_FLAGS)
it2-out: it2-out3 it2-out: it2-out3
cp it2-out3 it2-out cp it2-out3 it2-out
it3-out0.c: it2-out it3-in.lang0
cat it3-in.lang0 | ./it2-out > it3-out0.c
# gcc step since the target is still python here
it3-out0: it3-out0.c
gcc $^ -o $@ $(GCC_FLAGS)
it3-out1.ll: it3-out0 it3-in.lang0
cat it3-in.lang0 | ./it3-out0 > $@
it3-out1.s: it3-out1.ll
llc $^ -o $@
foo.ll: foo.c
clang -S -emit-llvm $^
foo.s: foo.ll
llc $^
foo: foo.s
clang $^ -o $@
clean: clean:
-rm it1-out* it2-out* -rm it1-out* it2-out*

View File

@ -113,6 +113,10 @@ Creates a new string with `b` appended to `a`.
Writes the given string to standard output. Writes the given string to standard output.
#### fileget path
Reads a file named `path` from the current directory into a string.
#### peek #### peek
Checks stdin for the next character and returns it. Checks stdin for the next character and returns it.

17
foo.c Normal file
View File

@ -0,0 +1,17 @@
void print(char * x)
{
return;
}
int sum(int a, int b)
{
return a + b;
}
int main()
{
char * foo = "Hello, world!";
print("This is a test.");
return sum(7, 15);
}

2
it0-in-footer.py Normal file
View File

@ -0,0 +1,2 @@
if __name__ == '__main__':
main()

51
it0-in-header.py Normal file
View File

@ -0,0 +1,51 @@
import os
import sys
def eq(a, b):
return a == b
def lt(a, b):
return a[0] < b[0]
def addstringchar(a, b):
return a + b[0]
def emit(string):
sys.stdout.write(string)
def trace(header, value):
if os.environ.get('TRACE'):
sys.stderr.write(f'{header}={value!r}\\n')
eof = chr(0)
eol = chr(10)
quote = chr(34)
PEEK = None
LINE = 1
def peek():
global PEEK
if PEEK is None:
char = sys.stdin.read(1)
trace('char', char)
if not char:
PEEK = eof
else:
PEEK = char
return PEEK
def skip():
global LINE
global PEEK
if eol == PEEK:
LINE += 1
PEEK = None
def skipchar(char):
global LINE
assert char == peek(), (LINE, char, peek())
skip()
def fileget(path):
with open(f'./{path}', encoding='ASCII') as fil:
return fil.read()

View File

@ -41,6 +41,10 @@ def skipchar(char):
assert char == peek(), (LINE, char, peek()) assert char == peek(), (LINE, char, peek())
skip() skip()
def fileget(path):
with open(f'./{path}', encoding='ASCII') as fil:
return fil.read()
def lexident(): def lexident():
word = '' word = ''
while True: while True:
@ -280,58 +284,12 @@ def parsefunc():
emit("\n") emit("\n")
def emitheader(): def emitheader():
emit("import os\n") header = fileget("it0-in-header.py")
emit("import sys\n") emit(header)
emit("\n")
emit("def eq(a, b):\n")
emit(" return a == b\n")
emit("\n")
emit("def lt(a, b):\n")
emit(" return a[0] < b[0]\n")
emit("\n")
emit("def addstringchar(a, b):\n")
emit(" return a + b[0]\n")
emit("\n")
emit("def emit(string):\n")
emit(" sys.stdout.write(string)\n")
emit("\n")
emit("def trace(header, value):\n")
emit(" if os.environ.get('TRACE'):\n")
emit(" sys.stderr.write(f'{header}={value!r}\\n')\n")
emit("\n")
emit("eof = chr(0)\n")
emit("eol = chr(10)\n")
emit("quote = chr(34)\n")
emit("PEEK = None\n")
emit("LINE = 1\n")
emit("\n")
emit("def peek():\n")
emit(" global PEEK\n")
emit(" if PEEK is None:\n")
emit(" char = sys.stdin.read(1)\n")
emit(" trace('char', char)\n")
emit(" if not char:\n")
emit(" PEEK = eof\n")
emit(" else:\n")
emit(" PEEK = char\n")
emit(" return PEEK\n")
emit("\n")
emit("def skip():\n")
emit(" global LINE\n")
emit(" global PEEK\n")
emit(" if eol == PEEK:\n")
emit(" LINE += 1\n")
emit(" PEEK = None\n")
emit("\n")
emit("def skipchar(char):\n")
emit(" global LINE\n")
emit(" assert char == peek(), (LINE, char, peek())\n")
emit(" skip()\n")
emit("\n")
def emitfooter(): def emitfooter():
emit("if __name__ == '__main__':\n") footer = fileget("it0-in-footer.py")
emit(" main()\n") emit(footer)
def main(): def main():
emitheader() emitheader()

2
it1-in-footer.py Normal file
View File

@ -0,0 +1,2 @@
if __name__ == '__main__':
main()

51
it1-in-header.py Normal file
View File

@ -0,0 +1,51 @@
import os
import sys
def eq(a, b):
return a == b
def lt(a, b):
return a[0] < b[0]
def addstringchar(a, b):
return a + b[0]
def emit(string):
sys.stdout.write(string)
def trace(header, value):
if os.environ.get('TRACE'):
sys.stderr.write(f'{header}={value!r}\\n')
eof = chr(0)
eol = chr(10)
quote = chr(34)
PEEK = None
LINE = 1
def peek():
global PEEK
if PEEK is None:
char = sys.stdin.read(1)
trace('char', char)
if not char:
PEEK = eof
else:
PEEK = char
return PEEK
def skip():
global LINE
global PEEK
if eol == PEEK:
LINE += 1
PEEK = None
def skipchar(char):
global LINE
assert char == peek(), (LINE, char, peek())
skip()
def fileget(path):
with open(f'./{path}', encoding='ASCII') as fil:
return fil.read()

View File

@ -320,59 +320,13 @@ parsefunc:
/ /
emitheader: emitheader:
emit "import os\n" calc header fileget "it1-in-header.py"
emit "import sys\n" emit header
emit "\n"
emit "def eq(a, b):\n"
emit " return a == b\n"
emit "\n"
emit "def lt(a, b):\n"
emit " return a[0] < b[0]\n"
emit "\n"
emit "def addstringchar(a, b):\n"
emit " return a + b[0]\n"
emit "\n"
emit "def emit(string):\n"
emit " sys.stdout.write(string)\n"
emit "\n"
emit "def trace(header, value):\n"
emit " if os.environ.get('TRACE'):\n"
emit " sys.stderr.write(f'{header}={value!r}\\n')\n"
emit "\n"
emit "eof = chr(0)\n"
emit "eol = chr(10)\n"
emit "quote = chr(34)\n"
emit "PEEK = None\n"
emit "LINE = 1\n"
emit "\n"
emit "def peek():\n"
emit " global PEEK\n"
emit " if PEEK is None:\n"
emit " char = sys.stdin.read(1)\n"
emit " trace('char', char)\n"
emit " if not char:\n"
emit " PEEK = eof\n"
emit " else:\n"
emit " PEEK = char\n"
emit " return PEEK\n"
emit "\n"
emit "def skip():\n"
emit " global LINE\n"
emit " global PEEK\n"
emit " if eol == PEEK:\n"
emit " LINE += 1\n"
emit " PEEK = None\n"
emit "\n"
emit "def skipchar(char):\n"
emit " global LINE\n"
emit " assert char == peek(), (LINE, char, peek())\n"
emit " skip()\n"
emit "\n"
/ /
emitfooter: emitfooter:
emit "if __name__ == '__main__':\n" calc header fileget "it1-in-footer.py"
emit " main()\n" emit header
/ /
main: main:

104
it2-in-header.c Normal file
View File

@ -0,0 +1,104 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char var_eof[2] = {-1, 0};
char var_eol[2] = {10, 0};
char var_quote[2] = {34, 0};
char var_false[1] = {0};
char var_true[2] = {'1', 0};
char * eq(char * a, char * b)
{
return (strcmp(a, b) == 0) ? var_true : var_false;
}
char * lt(char * a, char * b)
{
return (strcmp(a, b) < 0) ? var_true : var_false;
}
char * not(char * a)
{
return (strcmp(a, var_true) != 0) ? var_true : var_false;
}
char * addstringchar(char * str, char * chr)
{
int str_len = strlen(str);
assert(strlen(chr) == 1);
char * res = malloc((str_len + 2) * sizeof(char));
strcpy(res, str);
res[str_len + 0] = chr[0];
res[str_len + 1] = 0;
return res;
}
char * emit(char * str)
{
fputs(str, stdout);
return 0;
}
char * trace(char * var_name, char * var_value)
{
const char * env_trace = getenv("TRACE");
if( !env_trace ) return 0;
fputs(var_name, stderr);
fputs("=", stderr);
fputs(var_value, stderr);
fputs(var_eol, stderr);
return 0;
}
int LINE = 1;
char * peek()
{
char * res = malloc(2*sizeof(char));
res[0] = getc(stdin);
res[1] = 0;
ungetc(res[0], stdin);
return res;
}
void skip()
{
char c = getc(stdin);
if( c == 10 ) LINE += 1;
}
void skipchar(char * chr)
{
assert(strlen(chr) == 1);
char * act = peek();
assert(strlen(act) == 1);
if( chr[0] == act[0] ) {skip(); return;};
fprintf(stderr, "Expected '%c' on line %d but saw '%c' instead\n", chr[0], LINE, act[0]);
exit(1);
}
char * fileget(char * var_path)
{
FILE * file_ptr;
int file_size;
char * res;
file_ptr = fopen(var_path, "r");
if( NULL == file_ptr ) {
fprintf(stderr, "Could not load file %s", var_path);
exit(1);
}
fseek(file_ptr, 0, SEEK_END);
file_size = ftell(file_ptr);
fseek(file_ptr, 0, SEEK_SET);
res = malloc(file_size * sizeof(char));
fread(res, sizeof(char), file_size, file_ptr);
fclose(file_ptr);
return res;
}

View File

@ -290,6 +290,11 @@ parsestat indent:
break break
/ /
skip skip
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
calc char peek calc char peek
calc isquote eq char quote calc isquote eq char quote
calc isnotquote not isquote calc isnotquote not isquote
@ -299,11 +304,6 @@ parsestat indent:
if isnotquote if isnotquote
parseexprvarref parseexprvarref
/ /
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
set first "0" set first "0"
/ /
skipchar eol skipchar eol
@ -402,105 +402,13 @@ parsefunc:
/ /
emitheader: emitheader:
emit "#include <assert.h>\n" declare header
emit "#include <stdio.h>\n" calc header fileget "it2-in-header.c"
emit "#include <stdlib.h>\n" emit header
emit "#include <string.h>\n"
emit eol
emit eol
emit "char var_eof[2] = {-1, 0};\n"
emit "char var_eol[2] = {10, 0};\n"
emit "char var_quote[2] = {34, 0};\n"
emit "char var_false[1] = {0};\n"
emit "char var_true[2] = {'1', 0};\n"
emit eol
emit eol
emit "char * eq(char * a, char * b)\n"
emit "{\n"
emit " return (strcmp(a, b) == 0) ? var_true : var_false;\n"
emit "}\n"
emit "\n"
emit "char * lt(char * a, char * b)\n"
emit "{\n"
emit " return (strcmp(a, b) < 0) ? var_true : var_false;\n"
emit "}\n"
emit "char * not(char * a)\n"
emit "{\n"
emit " return (strcmp(a, var_true) != 0) ? var_true : var_false;\n"
emit "}\n"
emit "\n"
emit "char * addstringchar(char * str, char * chr)\n"
emit "{\n"
emit " int str_len = strlen(str);\n"
emit " assert(strlen(chr) == 1);\n"
emit " char * res = malloc((str_len + 2) * sizeof(char));\n"
emit " strcpy(res, str);\n"
emit " res[str_len + 0] = chr[0];\n"
emit " res[str_len + 1] = 0;\n"
emit " return res;\n"
emit "}\n"
emit "\n"
emit "char * emit(char * str)\n"
emit "{\n"
emit " fputs(str, stdout);\n"
emit " return 0;\n"
emit "}\n"
emit "\n"
emit "char * trace(char * var_name, char * var_value)\n"
emit "{\n"
emit " const char * env_trace = getenv("
emit quote
emit "TRACE"
emit quote
emit ");\n"
emit " if( !env_trace ) return 0;\n"
emit " fputs(var_name, stderr);\n"
emit " fputs("
emit quote
emit "="
emit quote
emit ", stderr);\n"
emit " fputs(var_value, stderr);\n"
emit " fputs(var_eol, stderr);\n"
emit " return 0;\n"
emit "}\n"
emit eol
emit "int LINE = 1;\n"
emit eol
emit "char * peek()\n"
emit "{\n"
emit " char * res = malloc(2*sizeof(char));\n"
emit " res[0] = getc(stdin);\n"
emit " res[1] = 0;\n"
emit " ungetc(res[0], stdin);\n"
emit " return res;\n"
emit "}\n"
emit "\n"
emit "void skip()\n"
emit "{\n"
emit " char c = getc(stdin);\n"
emit " if( c == 10 ) LINE += 1;\n"
emit "}\n"
emit "\n"
emit "void skipchar(char * chr)\n"
emit "{\n"
emit " assert(strlen(chr) == 1);\n"
emit " char * act = peek();\n"
emit " assert(strlen(act) == 1);\n"
emit " if( chr[0] == act[0] ) {skip(); return;};\n"
emit " fprintf(stderr, "
emit quote
emit "Expected '%c' on line %d but saw '%c' instead%c"
emit quote
emit ", chr[0], LINE, act[0], 10);\n"
emit " exit(1);\n"
emit "}\n"
emit "\n"
return return
/ /
emitfooter: emitfooter:
emit ""
return return
/ /

466
it3-in.lang0 Normal file
View File

@ -0,0 +1,466 @@
increaseindent indent:
calc indent addstringchar indent " "
calc indent addstringchar indent " "
calc indent addstringchar indent " "
calc indent addstringchar indent " "
return indent
/
lexident:
declare char
declare isbeforea
declare isafterz
declare word
set word ""
forever
calc char peek
calc isbeforea lt char "a"
if isbeforea
break
/
calc isafterz lt "z" char
if isafterz
break
/
calc word addstringchar word char
skip
/
return word
/
parseconststring:
declare char
declare iseof
declare isquote
skipchar quote
forever
calc char peek
calc iseof eq char eof
if iseof
break
/
calc isquote eq char quote
if isquote
break
/
skip
/
skipchar quote
emit "i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0)"
return
/
parseexprvarref:
declare varname
calc varname lexident
emit "i8* %"
emit varname
return
/
parseexprcall:
declare char
declare first
declare funcname
declare isspace
declare isnotspace
declare isfirst
declare isnotfirst
declare isquote
declare isnotquote
calc funcname lexident
emit funcname
emit "("
set first "1"
forever
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isnotspace
break
/
skip
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
set first "0"
/
emit ")"
return
/
parsestatdeclare indent:
declare var
skipchar " "
calc var lexident
emit indent
emit "char * var_"
emit var
emit ";"
emit eol
skipchar eol
return
/
parsestatset indent:
declare var
skipchar " "
calc var lexident
skipchar " "
emit indent
emit "var_"
emit var
emit " = "
parseconststring
emit ";"
emit eol
skipchar eol
return
/
parsestatcalc indent:
declare var
skipchar " "
calc var lexident
skipchar " "
emit indent
emit "%"
emit var
emit ".1 = call i8* @"
parseexprcall
emit ";"
emit eol
skipchar eol
return
/
parseblock indent/
parsestatif indent:
declare indentt
skipchar " "
emit indent
emit "if ( 0 < strlen("
parseexprvarref
emit ") )\n"
emit indent
emit "{\n"
skipchar eol
calc indentt increaseindent indent
parseblock indentt
emit indent
emit "}\n"
return
/
parsestatforever indent:
declare indentt
emit indent
emit "while (1)\n"
emit indent
emit "{\n"
skipchar eol
calc indentt increaseindent indent
parseblock indentt
emit indent
emit "}\n"
return
/
parsestatbreak indent:
emit indent
emit "break;\n"
skipchar eol
return
/
parsestatreturn indent:
declare char
declare isspace
declare isnotspace
emit indent
emit "ret "
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isspace
skip
parseexprvarref
/
if isnotspace
emit "i18* 0"
/
emit ";"
emit eol
skipchar eol
return
/
parsestattrace indent:
declare varname
emit indent
emit "trace("
emit quote
skipchar " "
calc varname lexident
emit varname
emit quote
emit ", var_"
emit varname
emit ");\n"
skipchar eol
return
/
parsestat indent:
declare call
declare char
declare first
declare iscall
declare isfirst
declare isspace
declare isquote
declare isnotfirst
declare isnotspace
declare isnotquote
calc call lexident
trace call
calc iscall eq call "declare"
if iscall
parsestatdeclare indent
return
/
calc iscall eq call "set"
if iscall
parsestatset indent
return
/
calc iscall eq call "calc"
if iscall
parsestatcalc indent
return
/
calc iscall eq call "if"
if iscall
parsestatif indent
return
/
calc iscall eq call "forever"
if iscall
parsestatforever indent
return
/
calc iscall eq call "break"
if iscall
parsestatbreak indent
return
/
calc iscall eq call "return"
if iscall
parsestatreturn indent
return
/
calc iscall eq call "trace"
if iscall
parsestattrace indent
return
/
emit indent
emit call
emit "("
set first "1"
forever
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isnotspace
break
/
skip
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
set first "0"
/
skipchar eol
emit ");\n"
return
/
parseblock indent:
declare char
declare copy
declare iseol
declare isnoteol
declare isdone
declare iseoblock
forever
forever
calc char peek
calc iseol eq char eol
calc isnoteol not iseol
if isnoteol
break
/
skip
/
set copy " "
forever
calc isdone eq copy indent
if isdone
break
/
skipchar "\t"
calc copy increaseindent copy
/
calc char peek
calc iseoblock eq char "/"
if iseoblock
skip
skipchar eol
break
/
skipchar "\t"
parsestat indent
/
return
/
parsefunc:
declare char
declare first
declare funcname
declare iseoblock
declare isfirst
declare isspace
declare isnotfirst
declare isnotspace
declare var
mapclear "funcvarused"
calc funcname lexident
trace funcname
emit "define i8* @"
emit funcname
emit "("
set first "1"
forever
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isnotspace
break
/
skip
calc var lexident
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
emit "i8* %"
emit var
mapsetkey "funcvarused" "var" "0"
emit "_0"
set first "0"
/
calc char peek
calc iseoblock eq char "/"
if iseoblock
skipchar "/"
skipchar eol
emit ");"
emit "\n"
return
/
skipchar ":"
skipchar eol
emit ")\n{\n"
parseblock " "
emit "}\n\n"
return
/
emitheader:
emit "target datalayout = "
emit quote
emit "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
emit quote
emit eol
emit "target triple = "
emit quote
emit "x86_64-pc-linux-gnu"
emit quote
emit eol
emit eol
emit "define i8* @addstringchar(i8* %0, i8* %1)"
emit eol
emit "{"
emit eol
emit " ; todo"
emit eol
emit " ret i8* %0;"
emit eol
emit "}"
emit eol
emit eol
return
/
emitfooter:
emit ""
return
/
main:
declare char
declare iseof
declare iseol
declare isnoteol
emitheader
forever
calc char peek
calc iseof eq char eof
if iseof
break
/
forever
calc char peek
calc iseol eq char eol
calc isnoteol not iseol
if isnoteol
break
/
skip
/
parsefunc
/
emitfooter
return
/

19
notes.ll Normal file
View File

@ -0,0 +1,19 @@
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"
define i8* @addstringchar(i8* %0, i8* %1)
{
; todo
ret i8* %0;
}
define i8* @increaseindent(i8* %indent)
{
%indent.1 = call i8* @addstringchar(i8* %indent, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
%indent.2 = call i8* @addstringchar(i8* %indent.1, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
%indent.3 = call i8* @addstringchar(i8* %indent.2, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
%indent.4 = call i8* @addstringchar(i8* %indent.3, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
ret i8* %indent.4;
}
@.str = private unnamed_addr constant [14 x i8] c"Hello, world!\00", align 1