summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2021-10-13 20:32:29 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2021-10-13 20:32:29 +0200
commit62924274b391dbfd16f4349bd46a2136f2a78b0e (patch)
tree0a5aefa2d23bf402fff822f5bc2f93743cfaf1b4
parentf895cabf52336db0f2a259bbe6fae6e6fc81c98a (diff)
downloadcompilertests-62924274b391dbfd16f4349bd46a2136f2a78b0e.tar.gz
compilertests-62924274b391dbfd16f4349bd46a2136f2a78b0e.tar.bz2
cc: some support for putchar and char store/load
-rw-r--r--miniany/README.html4
-rw-r--r--miniany/cc.c62
-rw-r--r--miniany/cc.wg12
-rw-r--r--miniany/test1.c4
4 files changed, 64 insertions, 18 deletions
diff --git a/miniany/README.html b/miniany/README.html
index 7be5708..ca5cdf7 100644
--- a/miniany/README.html
+++ b/miniany/README.html
@@ -12,7 +12,7 @@
<p>Similarly we simplify the C language to not use certain features which can cause trouble when bootstrapping:</p>
<ul>
<li><i>variable </i><i>arguments</i>: though simple in principle (just some pointers into the stack if you use a stack for function parameters), it is not typesafe. And the only example in practice it's really heavily used for is in printf-like functions.</li>
-<li><i>preprocessor</i>: it needs a filesystem, we take this outside of the compiler by feeding it an (eventually) concatenated list of *.c files. Note: in the hosted environemt we (and glibc) can use as many preprocessor features as they want, they just don't have to get visible in our code.</li>
+<li><i>preprocessor</i>: it needs a filesystem, we take this outside of the compiler by feeding it an (eventually) concatenated list of *.c files. Note: in the hosted environment we (and glibc) can use as many preprocessor features as they want, they just don't have to get visible in our code.</li>
<li><i>two </i><i>types</i>: int and char, so we can interpret memory as words or as bytes.</li>
</ul>
<h2>Local version of C4</h2>
@@ -34,7 +34,7 @@
<li>de-POSIX-ified, no <i>open/read/close</i>, use <i>getchar </i>from stdin only (don't assume the existence of a file system), this also means we had to create sort of an old style tape-file with FS markers to separate the files piped to c4.</li>
</ul>
<p>The reason for all those adaptions is to minimize the dependency on the host system and to be able to use <i>libc-freestanding.c</i>.</p>
-<h3><i>Note: </i>only too late I discovered that there was a C5 version of the same compiler, which would maybe have served better as a basis.</h3>
+<p>Note: Only too late I discovered that there was a C5 version of the same compiler, which would maybe have served better as a basis.</p>
<h2>Examples</h2>
<h3>Running on the host system using the hosts C compiler</h3>
<p>Compiled in either hosted (host libc) or freestanding (our own libc, currently IA-32 Linux kernel only syscalls):</p>
diff --git a/miniany/cc.c b/miniany/cc.c
index e977bc9..c72d290 100644
--- a/miniany/cc.c
+++ b/miniany/cc.c
@@ -32,6 +32,7 @@ enum {
S_WHILE,
S_DO,
S_PUTINT,
+ S_PUTCHAR,
S_IDENT = 60,
S_NUM = 70,
S_EOI = 98,
@@ -194,6 +195,8 @@ int keyword( char *ident )
case 'p':
if( strcmp( ident, "putint" ) == 0 ) {
return S_PUTINT;
+ } else if( strcmp( ident, "putchar" ) == 0 ) {
+ return S_PUTCHAR;
}
break;
@@ -599,7 +602,7 @@ void putlow8reg( struct Generator *generator, int reg )
break;
default:
scannerPrintErrorHeader( generator->scanner );
- putstring( "error using low 8-bit subreguster of reguster '" );
+ putstring( "error using low 8-bit subreguster of register '" );
putstring( generator->regName[reg] );
putstring( "'" );
putnl( );
@@ -739,27 +742,41 @@ int genLoadImm( struct Generator *generator, int intval )
return reg;
}
-int genLoadIdent( struct Generator *generator, char *ident )
+int genLoadIdent( struct Generator *generator, struct Symbol *sym )
{
int reg;
reg = genAllocReg( generator );
putstring( "mov " );
- putreg( generator, reg );
+ switch( sym->type ) {
+ case S_INT:
+ putreg( generator, reg );
+ break;
+ case S_CHAR:
+ putlow8reg( generator, reg );
+ break;
+ }
putstring( ", [" );
- putstring( ident );
+ putstring( sym->name );
putstring( "]" );
putnl( );
return reg;
}
-int genStoreIdent( struct Generator *generator, int reg, char *ident )
+int genStoreIdent( struct Generator *generator, int reg, struct Symbol *sym )
{
putstring( "mov [" );
- putstring( ident );
+ putstring( sym->name );
putstring( "], " );
- putreg( generator, reg );
+ switch( sym->type ) {
+ case S_INT:
+ putreg( generator, reg );
+ break;
+ case S_CHAR:
+ putlow8reg( generator, reg );
+ break;
+ }
putnl( );
return reg;
@@ -921,10 +938,10 @@ int generateFromAST( struct Generator *generator, struct ASTnode *node, int inre
reg = genLoadImm( generator, node->intval );
break;
case A_IDENT:
- reg = genLoadIdent( generator, node->sym->name );
+ reg = genLoadIdent( generator, node->sym );
break;
case A_LVIDENT:
- reg = genStoreIdent( generator, inreg, node->sym->name );
+ reg = genStoreIdent( generator, inreg, node->sym );
break;
case A_ADD:
reg = genAdd( generator, leftreg, rightreg );
@@ -979,6 +996,14 @@ void genPrologue( struct Compiler *compiler )
putstring( "use32" ); putnl( );
putstring( "org $1000000" ); putnl( );
putstring( "jmp _start" ); putnl( );
+ putstring( "putchar:" ); putnl( );
+ putstring( "mov [putint_string_fmt], eax" ); putnl( );
+ putstring( "mov eax, 4" ); putnl( );
+ putstring( "mov ebx, 1" ); putnl( );
+ putstring( "mov ecx, putint_string_fmt" ); putnl( );
+ putstring( "mov edx, 1" ); putnl( );
+ putstring( "int 0x80" ); putnl( );
+ putstring( "ret" ); putnl( );
putstring( "putint:" ); putnl( );
putstring( "mov esi, 0" ); putnl( );
putstring( "lea edi, [putint_string_fmt]" ); putnl( );
@@ -1301,6 +1326,23 @@ void parsePutint( struct Compiler *compiler )
freeASTnode( node );
}
+void parsePutchar( struct Compiler *compiler )
+{
+ struct Parser *parser;
+ struct ASTnode *node;
+
+ parser = compiler->parser;
+ parserExpect( parser, S_PUTCHAR, "putchar" );
+ parserExpect( parser, S_LPAREN, "(" );
+ node = parseExpression( parser, 0 );
+ parserExpect( parser, S_RPAREN, ")" );
+ parserExpect( parser, S_SEMICOLON, ";" );
+ generateFromAST( compiler->generator, node, NOREG );
+ putstring( "call putchar" ); putnl( );
+ genFreeAllRegs( compiler->generator );
+ freeASTnode( node );
+}
+
/* TODO c4: forward reference of function */
void parseStatementBlock( struct Compiler *compiler );
@@ -1437,6 +1479,8 @@ void parseStatement( struct Compiler *compiler )
parseAssignment( compiler );
} else if( parser->token == S_PUTINT ) {
parsePutint( compiler );
+ } else if( parser->token == S_PUTCHAR ) {
+ parsePutchar( compiler );
} else if( parser->token == S_IF ) {
parseIf( compiler );
} else if( parser->token == S_WHILE ) {
diff --git a/miniany/cc.wg b/miniany/cc.wg
index f09157a..2a0ad6d 100644
--- a/miniany/cc.wg
+++ b/miniany/cc.wg
@@ -29,16 +29,16 @@ WordGrinder dumpfile v3: this is a text file; diff me!
.clipboard.margin: 0
.clipboard.viewmode: 1
.clipboard.wordcount: 6
-.documents.1.co: 9
-.documents.1.cp: 205
-.documents.1.cw: 21
+.documents.1.co: 7
+.documents.1.cp: 31
+.documents.1.cw: 25
.documents.1.margin: 0
.documents.1.name: "main"
.documents.1.sticky_selection: false
.documents.1.viewmode: 1
.documents.1.wordcount: 2863
.fileformat: 8
-.findtext: "Typedefs"
+.findtext: "Note"
.menu.accelerators.^@: "ZM"
.menu.accelerators.^B: "SB"
.menu.accelerators.BACKSPACE: "ZDPC"
@@ -160,7 +160,7 @@ LB exit: terminate the process, return does not always work in all combination
LB read/write: read from stdin linearly, write to stdout linearly, this is essentially a model using an input and an output tape. Those two functions must really exist. This basically eliminates the need for a file system which we might not have during early bootstrapping.
P Similarly we simplify the C language to not use certain features which can cause trouble when bootstrapping:
LB variable arguments: though simple in principle (just some pointers into the stack if you use a stack for function parameters), it is not typesafe. And the only example in practice it's really heavily used for is in printf-like functions.
-LB preprocessor: it needs a filesystem, we take this outside of the compiler by feeding it an (eventually) concatenated list of *.c files. Note: in the hosted environemt we (and glibc) can use as many preprocessor features as they want, they just don't have to get visible in our code.
+LB preprocessor: it needs a filesystem, we take this outside of the compiler by feeding it an (eventually) concatenated list of *.c files. Note: in the hosted environment we (and glibc) can use as many preprocessor features as they want, they just don't have to get visible in our code.
LB two types: int and char, so we can interpret memory as words or as bytes.
H2 Local version of C4
P The local version of C4 has the following adaoptions and extensions:
@@ -179,7 +179,7 @@ LB converted printf to putstring/putint/putnl and some helper functions for
LB removed all memory leaks
LB de-POSIX-ified, no open/read/close, use getchar from stdin only (don't assume the existence of a file system), this also means we had to create sort of an old style tape-file with FS markers to separate the files piped to c4.
P The reason for all those adaptions is to minimize the dependency on the host system and to be able to use libc-freestanding.c.
-H3 Note: only too late I discovered that there was a C5 version of the same compiler, which would maybe have served better as a basis.
+P Note: Only too late I discovered that there was a C5 version of the same compiler, which would maybe have served better as a basis.
H2 Examples
H3 Running on the host system using the hosts C compiler
P Compiled in either hosted (host libc) or freestanding (our own libc, currently IA-32 Linux kernel only syscalls):
diff --git a/miniany/test1.c b/miniany/test1.c
index 2358298..30650f0 100644
--- a/miniany/test1.c
+++ b/miniany/test1.c
@@ -39,5 +39,7 @@ void main( )
i = i+1;
} while( i <= 5 );
- c = 42;
+ c = 41;
+ c = c + 1;
+ putchar( c );
}