3 #define TEXT_SIZE 20000
5 #define SYM_TABLE_SIZE 10000
6 #define VAR_TABLE_SIZE 4096
8 /* vac: offset of variables
10 loc : local variable index
11 glo : global variable index
12 parm : parameter variable index
14 lsym: loop symbol stack
17 astk: arg position stack
19 int tok
, *vac
, *vat
, *lsym
, rsym
,
20 prog
, ind
, loc
, glo
, file
, vt
,
21 vc
, *macro_stack
, *macro_stack_ptr
, line_num
;
22 char *idtable
, *idptr
, *idlast
, *filename
;
24 /* The current value can be: */
25 #define VT_CONST 0x0002 /* constant in vc */
26 #define VT_VAR 0x0004 /* value is in eax */
27 #define VT_LOCAL 0x0008 /* offset on stack */
29 #define VT_LVAL 0x0010 /* const or var is an lvalue */
30 #define VT_CMP 0x0020 /* the value is stored in processor flags (in vc) */
31 #define VT_FORWARD 0x0040 /* value is forward reference (only used for functions) */
32 #define VT_JMP 0x0080 /* value is the consequence of jmp. bit 0 is set if inv */
34 #define VT_LVALN -17 /* ~VT_LVAL */
38 * VT_FUNC indicates a function. The return type is the stored type. A
39 * function pointer is stored as a 'char' pointer.
41 * If VT_PTRMASK is non nul, then it indicates the number of pointer
42 * iterations to reach the basic type.
46 * VT_BYTE indicate a char
49 * otherwise integer type is assumed.
52 #define VT_BYTE 0x00001 /* byte pointer. HARDCODED VALUE */
53 #define VT_PTRMASK 0x00f00 /* pointer mask */
54 #define VT_PTRINC 0x00100 /* pointer increment */
55 #define VT_FUNC 0x01000 /* function type */
56 #define VT_TYPE 0x01f01 /* type mask */
58 #define VT_TYPEN 0xffffe0fe /* ~VT_TYPE */
59 #define VT_FUNCN -4097
62 #define VT_DEFINE 0x02000 /* special value for #defined symbols */
72 #define TOK_RETURN 263
73 #define TOK_DEFINE 264
77 #define TOK_EQ 0x94 /* warning: depend on asm code */
78 #define TOK_NE 0x95 /* warning: depend on asm code */
79 #define TOK_LT 0x9c /* warning: depend on asm code */
80 #define TOK_GE 0x9d /* warning: depend on asm code */
81 #define TOK_LE 0x9e /* warning: depend on asm code */
82 #define TOK_GT 0x9f /* warning: depend on asm code */
88 #define TOK_MID 0xa3 /* inc/dec, to void constant */
94 /* assignement operators : normal operator or 0x80 */
95 #define TOK_A_MOD 0xa5
96 #define TOK_A_AND 0xa6
97 #define TOK_A_MUL 0xaa
98 #define TOK_A_ADD 0xab
99 #define TOK_A_SUB 0xad
100 #define TOK_A_DIV 0xaf
101 #define TOK_A_XOR 0xde
102 #define TOK_A_OR 0xfc
103 #define TOK_A_SHL 0x81
104 #define TOK_A_SHR 0x82
107 #define expr_eq() expr()
124 return (c
>= 'a' & c
<= 'z') |
125 (c
>= 'A' & c
<= 'Z') |
131 return c
>= '0' & c
<= '9';
135 /* XXX: use stderr ? */
136 void error(char *msg
)
138 printf("%s:%d: %s\n", filename
, line_num
, msg
);
142 void warning(char *msg
)
144 printf("%s:%d: warning: %s\n", filename
, line_num
, msg
);
150 printf("%s:%d: '%c' expected\n", filename
, line_num
, c
);
159 error("lvalue expected\n");
164 #define skip(c) next()
165 #define test_lvalue()
181 /* single line comments */
184 } else if (c
== '*') {
186 while ((c
= inp()) >= 0) {
194 } else if (c
== '\n')
205 /* preprocessor: we handle only define */
207 if (tok
== TOK_DEFINE
) {
209 /* now tok is the macro symbol */
210 vat
[tok
] = VT_DEFINE
;
211 vac
[tok
] = ftell(file
);
213 /* ignore preprocessor or shell */
218 /* end of line : check if we are in macro state. if so,
219 pop new file position */
220 if (macro_stack_ptr
> macro_stack
)
221 fseek(file
, *--macro_stack_ptr
, 0);
224 } else if (c
!= ' ' & c
!= 9)
230 while(isid(c
) | isnum(c
)) {
239 if (strcmp(p
, idptr
) == 0)
244 /* if not found, add symbol */
248 if (vat
[tok
] & VT_DEFINE
) {
249 *macro_stack_ptr
++ = ftell(file
);
250 fseek(file
, vac
[tok
], 0);
255 q
= "<=\236>=\235!=\225++\244--\242==\224";
257 q
= "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374";
262 if (*q
== c
& q
[1] == v
) {
264 if (tok
== TOK_SHL
| tok
== TOK_SHR
) {
276 /* single char substitutions */
299 /* output a symbol and patch all calls to it */
304 n
= *(int *)t
; /* next value */
305 *(int *)t
= ind
- t
- 4;
310 /* psym is used to put an instruction with a data field which is a
311 reference to a symbol. It is in fact the same as oad ! */
314 /* instruction + 4 bytes data. Return the address of the data */
330 /* generate a value in eax from vt and vc */
337 if ((vt
& VT_TYPE
) == VT_BYTE
)
338 o(0xbe0f); /* movsbl x, %eax */
340 o(0x8b); /* movl x,%eax */
343 else if (vt
& VT_LOCAL
)
349 oad(0xb8, vc
); /* mov $xx, %eax */
350 } else if (vt
& VT_LOCAL
) {
351 oad(0x858d, vc
); /* lea xxx(%ebp), %eax */
352 } else if (vt
& VT_CMP
) {
353 oad(0xb8, 0); /* mov $0, %eax */
354 o(0x0f); /* setxx %al */
359 else if (vt
& VT_JMP
) {
361 oad(0xb8, t
); /* mov $1, %eax */
362 oad(0xe9, 5); /* jmp after */
364 oad(0xb8, t
^ 1); /* mov $0, %eax */
368 vt
= (vt
& VT_TYPE
) | VT_VAR
;
371 /* generate a test. set 'inv' to invert test */
372 /* XXX: handle constant */
376 /* fast case : can jump directly since flags are set */
378 t
= psym((vc
- 16) ^ inv
, t
);
382 /* && or || optimization */
390 if ((vt
& (VT_CONST
| VT_LVAL
)) == VT_CONST
) {
391 /* constant jmp optimization */
392 if ((vc
!= 0) != inv
)
398 o(0xc085); /* test %eax, %eax */
400 t
= psym(0x85 ^ inv
, t
);
405 /* return the size in bytes of a given type */
408 if ((t
& VT_PTRMASK
) > VT_PTRINC
| (t
& VT_TYPE
) == VT_PTRINC
)
414 #define POST_ADD 0x1000
417 /* a defines POST/PRE add. c is the token ++ or -- */
423 o(0x018bc189); /* movl %eax, %ecx ; mov (%ecx), %eax */
424 o(0x408d | a
); /* leal x(%eax), %eax/%edx */
425 g((c
- TOK_MID
) * type_size(vt
));
426 o(0x0189 | a
); /* mov %eax/%edx, (%ecx) */
429 /* XXX: handle ptr sub and 'int + ptr' case (only 'ptr + int' handled) */
430 /* XXX: handle constant propagation (need to track live eax) */
436 o(0x50); /* push %eax */
445 o(0x59); /* pop %ecx */
446 if (op
== '+' | op
== '-') {
447 /* XXX: incorrect for short (futur!) */
448 if (type_size(t
) == 4)
449 o(0x02e0c1); /* shl $2, %eax */
451 o(0xd8f7); /* neg %eax */
452 o(0xc801); /* add %ecx, %eax */
454 } else if (op
== '&')
461 o(0xc1af0f); /* imul %ecx, %eax */
463 else if (op
== TOK_SHL
| op
== TOK_SHR
) {
464 o(0xd391); /* xchg %ecx, %eax, shl/sar %cl, %eax */
471 else if (op
== '/' | op
== '%') {
472 o(0x91); /* xchg %ecx, %eax */
473 o(0xf9f799); /* cltd, idiv %ecx, %eax */
475 o(0x92); /* xchg %edx, %eax */
477 o(0xc139); /* cmp %eax,%ecx */
482 /* return 0 if no type declaration. otherwise, return the basic type
484 XXX: A '2' is ored to ensure non zero return if int type.
490 if (tok
== TOK_INT
| tok
== TOK_CHAR
| tok
== TOK_VOID
) {
493 return (t
!= TOK_INT
) | 2;
499 /* Read a type declaration (except basic type), and return the
500 type. If v is true, then also put variable name in 'vc' */
505 t
= t
& -3; /* suppress the ored '2' */
512 /* XXX: incorrect if abstract type for functions (e.g. 'int ()') */
519 /* type identifier */
525 /* function declaration */
529 n
= vc
; /* must save vc there */
531 /* read param name and compute offset */
533 t
= typ(1, t
); /* XXX: should accept both arg/non arg if v == 0 */
540 vat
[vc
] = VT_LOCAL
| VT_LVAL
| t
;
545 next(); /* skip ')' */
555 /* read a number in base b */
617 vset(VT_CONST
, getn(tok
, t
));
619 vset(VT_CONST
, getn(tok
, 10));
625 vset(VT_CONST
, getq(inp()));
626 next(); /* skip char */
631 vset(VT_CONST
| VT_PTRINC
| VT_BYTE
, glo
);
632 while (tok
== '\"') {
633 while((n
= inp()) != 34) {
634 *(char *)glo
= getq(n
);
640 glo
= (glo
+ 4) & -4; /* align heap */
650 vt
= (vt
& VT_TYPEN
) | ft
;
655 } else if (t
== '*') {
660 if (!(vt
& VT_PTRMASK
))
661 error("pointer expected");
663 vt
= (vt
- VT_PTRINC
) | VT_LVAL
;
664 } else if (t
== '&') {
667 vt
= (vt
& VT_LVALN
) + VT_PTRINC
;
675 vset(VT_JMP
, gtst(1, 0));
679 if ((vt
& (VT_CONST
| VT_LVAL
)) == VT_CONST
)
690 if (t
== TOK_INC
| t
== TOK_DEC
) {
693 } else if (t
== '-') {
695 if ((vt
& (VT_CONST
| VT_LVAL
)) == VT_CONST
)
699 o(0xd8f7); /* neg %eax */
703 vset(vat
[t
], vac
[t
]);
704 /* forward reference or external reference ? */
706 n
= dlsym(0, idlast
);
708 vset(VT_CONST
| VT_FORWARD
| VT_LVAL
, vac
+ t
);
710 vset(VT_CONST
| VT_LVAL
, n
);
715 /* post operations */
716 if (tok
== TOK_INC
| tok
== TOK_DEC
) {
722 if (!(vt
& VT_PTRMASK
))
723 error("pointer expected");
726 /* dereference pointer */
727 vt
= (vt
- VT_PTRINC
) | VT_LVAL
;
732 /* lvalue is implied */
734 if ((vt
& VT_CONST
) == 0) {
735 /* evaluate function address */
737 o(0x50); /* push %eax */
748 o(0x50); /* push %eax */
753 /* horrible, but needed : convert to native ordering (could
754 parse parameters in reverse order, but would cost more
759 oad(0x24848b, p
); /* mov x(%esp,1), %eax */
760 oad(0x248487, n
); /* xchg x(%esp,1), %eax */
761 oad(0x248489, p
); /* mov %eax, x(%esp,1) */
766 /* forward reference */
768 *(int *)fc
= psym(0xe8, *(int *)fc
);
770 oad(0xe8, fc
- ind
- 5);
772 oad(0x2494ff, t
); /* call *xxx(%esp) */
777 /* return value is variable, int */
788 (tok
>= TOK_A_MOD
& TOK_A_DIV
) |
789 tok
== TOK_A_XOR
| tok
== TOK_A_OR
|
790 tok
== TOK_A_SHL
| tok
== TOK_A_SHR
) {
794 b
= (vt
& VT_TYPE
) == VT_BYTE
;
796 o(0x50); /* push %eax */
801 if ((vt
& VT_PTRMASK
) != (ft
& VT_PTRMASK
))
802 warning("incompatible type");
804 gv(); /* generate value */
806 gen_op(tok
& 0x7f, -2); /* XXX: incorrect, must call expr_eq */
809 o(0x59); /* pop %ecx */
810 o(0x0189 - b
); /* mov %eax/%al, (%ecx) */
811 } else if (ft
& VT_LOCAL
)
812 oad(0x8589 - b
, fc
); /* mov %eax/%al,xxx(%ebp) */
814 oad(0xa3 - b
, fc
); /* mov %eax/%al,xxx */
827 while ((l
== 0 & (tok
== '*' | tok
== '/' | tok
== '%')) |
828 (l
== 1 & (tok
== '+' | tok
== '-')) |
830 (l
== 2 & (tok
== TOK_SHL
| tok
== TOK_SHR
)) |
832 (l
== 3 & (tok
>= TOK_LT
& tok
<= TOK_GT
)) |
833 (l
== 4 & (tok
== TOK_EQ
| tok
== TOK_NE
)) |
834 (l
== 5 & tok
== '&') |
835 (l
== 6 & tok
== '^') |
836 (l
== 7 & tok
== '|')) {
855 if (tok
!= TOK_LAND
) {
875 if (tok
!= TOK_LOR
) {
934 d
= psym(0xe9, 0); /* jmp */
937 gsym(d
); /* patch else jmp */
940 } else if (tok
== TOK_WHILE
) {
946 *++lsym
= gtst(1, 0);
948 oad(0xe9, d
- ind
- 5); /* jmp */
950 } else if (tok
== '{') {
957 } else if (tok
== TOK_RETURN
) {
964 rsym
= psym(0xe9, rsym
); /* jmp */
965 } else if (tok
== TOK_BREAK
) {
967 *lsym
= psym(0xe9, *lsym
);
972 if (tok
== TOK_FOR
) {
992 oad(0xe9, d
- ind
- 5); /* jmp */
997 oad(0xe9, c
- ind
- 5); /* jmp */
1008 /* 'l' is true if local declarations */
1014 while (1) { /* iterate thru each declaration */
1017 /* patch forward references (XXX: does not work for
1018 function pointers) */
1021 /* put function address */
1022 vat
[vc
] = VT_CONST
| VT_LVAL
| vt
;
1025 o(0xe58955); /* push %ebp, mov %esp, %ebp */
1026 a
= oad(0xec81, 0); /* sub $xxx, %esp */
1030 o(0xc3c9); /* leave, ret */
1031 *a
= loc
; /* save local variables */
1035 vat
[vc
] = l
| VT_LVAL
| vt
;
1036 if (l
== VT_LOCAL
) {
1053 int main(int c
, char **v
)
1057 printf("usage: tc src\n");
1062 file
= fopen(filename
, "r");
1070 idtable
= malloc(SYM_TABLE_SIZE
);
1073 "int\0void\0char\0if\0else\0while\0break\0return\0define\0main", 53);
1074 idptr
= idtable
+ 53;
1077 "int\0void\0char\0if\0else\0while\0break\0return\0define\0main\0for", 57);
1078 idptr
= idtable
+ 57;
1080 glo
= malloc(DATA_SIZE
);
1081 prog
= malloc(TEXT_SIZE
);
1082 vac
= malloc(VAR_TABLE_SIZE
);
1083 vat
= malloc(VAR_TABLE_SIZE
);
1085 macro_stack
= malloc(256);
1086 macro_stack_ptr
= macro_stack
;
1094 f
= fopen(v
[1], "w");
1095 fwrite((void *)prog
, 1, ind
- prog
, f
);
1103 error("main() not defined");
1105 return (*t
)(c
- 1, v
);