TinyCC 的設計原理 -- 整體架構

TinyCC 編譯器

簡介

MinGW 測試

整體架構

編譯器

組譯器

目的檔

連結器

整合測試

訊息

相關網站

參考文獻

最新修改

簡體版

English

檔案列表

檔案 必要性 說明
tcc.c O 編譯器主程式 (參數處理)
tcc.h O tcc.c 的定義檔
tccpp.c O* 巨集處理器 (PreProcessor) (2935 行)
tccgen.c O* 程式碼產生與 Parser 剖析函數 (5121 行)
libtcc.h O* 雜湊表、符號表、字串函數,還包含 tcc_compile() 等編譯器函數,Parser 等 (2260 行)
tcctok.h O Tokenizer (Scanner)
il-gen.c O 中間碼產生 (Intermediate Language)
tccasm.c X 組譯器 (Assembler)
tcccoff.c X COFF 目的碼產生
tccelf.c X ELF 目的碼產生
tccpe.c X PE 目的碼產生
arm-gen.c X ARM 目的碼產生
c67-gen.c X C67 目的碼產生
x86_64-gen.c X x86 64 bit 程式碼產生
i386-gen.c X i386 程式碼產生
i386-asm.c X i386 組譯器
i386-asm.h X i386 組譯器定義檔
elf.h X ELF 定義檔
il-opcodes.h X 中間碼定義檔
stab.h X Indicate the GNU stab.h is in use.

必要性指的是對開放電腦的必要性。

功能 檔案 說明
編譯器 (Compiler) tcc.c,
剖析器 (Parser) tccpp.c
組譯器 (Assembler) tccasm.c , i386-asm.c
中間碼產生 (P-Code) tccgen.c, il-gen.c
目的檔格式 (ObjCode) tccelf.c, tcccoff.c, tccpe.c
程式碼產生 (Code-Gen) tccgen.c, il-gen.c, arm-gen.c, c67-gen.c, x86_64-gen.c, i386-gen.c

TinyCC 架構追蹤解讀:

tcc.c:main() => tcc_add_file() => libtcc:tcc_add_file_internal() => tcc_preprocess() or tcc_compile() or tcc_assemble()

tcc_compile() => 奇怪的事情,tcc_compile() 當中竟然找不到剖析動作的開始點。???

但事實上不是,真正的剖析起始點是 tcc_compile() 中位於 libtcc:1216 行的 decl(VT_CONST) 這個指令。

而 decl() 的宣告在 tccgen:4942 行的 static void decl(int l) ,其中的參數 l 可能是 VT_LOCAL or VT_CONST ,代表是在全域變數區還是區域變數區。


看來像是 tccgen:gen_inline_functions() => gen_function() /* parse a function defined by symbol 'sym' and generate its code in
'cur_text_section' */
=> i386-gen.c:gfunc_prolog(&sym->type); … libtcc:block(NULL, NULL, NULL, NULL, 0, 0);… i386-gen.c:gfunc_epilog();
=> 賓果:真正的剖析器主程式在 block 函數當中,剖析 { …. } 的程式區塊。(tccgen.c:3790~4132行, 後續到 5121 行檔案結束都是剖析程式)

很可惜 TinyCC 在程式中沒有列出對應 C 語言的 EBNF 語法,這使得程式並不容易讀懂。可惜阿可惜!

若要將 TinyCC 移植到新的 CPU 上,似乎只要加入一個 xxx-gen.c 就行了,不知道這樣的猜測對不對呢?

移植時似乎必須有下列函數。

void o(unsigned int c)
void gsym_addr(int t, int a)
void gsym(int t)
void load(int r, SValue *sv)
void store(int r, SValue *v)
static void gadd_sp(int val)
static void gcall_or_jmp(int is_jmp)
void gfunc_call(int nb_args)
void gfunc_prolog(CType *func_type)
void gfunc_epilog(void)
int gjmp(int t)
void gjmp_addr(int a)
int gtst(int inv, int t)
void gen_opi(int op)
void gen_opf(int op)
void gen_cvt_itof(int t) // arm-gen.c 當中沒有
void gen_cvt_ftoi(int t)
void gen_cvt_ftof(int t)
void ggoto(void)
void gen_bounded_ptr_add(void) // arm-gen.c 當中沒有

/* compile the C file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1)
{
    Sym *define_start;
    char buf[512];
    volatile int section_sym;

#ifdef INC_DEBUG
    printf("%s: **** new file\n", file->filename);
#endif
    preprocess_init(s1);

    cur_text_section = NULL;
    funcname = "";
    anon_sym = SYM_FIRST_ANOM; 

    /* file info: full path + filename */
    section_sym = 0; /* avoid warning */
    if (s1->do_debug) {
        section_sym = put_elf_sym(symtab_section, 0, 0, 
                                  ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, 
                                  text_section->sh_num, NULL);
        getcwd(buf, sizeof(buf));
#ifdef _WIN32
        normalize_slashes(buf);
#endif
        pstrcat(buf, sizeof(buf), "/");
        put_stabs_r(buf, N_SO, 0, 0, 
                    text_section->data_offset, text_section, section_sym);
        put_stabs_r(file->filename, N_SO, 0, 0, 
                    text_section->data_offset, text_section, section_sym);
    }
    /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
       symbols can be safely used */
    put_elf_sym(symtab_section, 0, 0, 
                ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, 
                SHN_ABS, file->filename);

    /* define some often used types */
    int_type.t = VT_INT;

    char_pointer_type.t = VT_BYTE;
    mk_pointer(&char_pointer_type);

    func_old_type.t = VT_FUNC;
    func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);

#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
    float_type.t = VT_FLOAT;
    double_type.t = VT_DOUBLE;

    func_float_type.t = VT_FUNC;
    func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD);
    func_double_type.t = VT_FUNC;
    func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD);
#endif

#if 0
    /* define 'void *alloca(unsigned int)' builtin function */
    {
        Sym *s1;

        p = anon_sym++;
        sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW);
        s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
        s1->next = NULL;
        sym->next = s1;
        sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
    }
#endif

    define_start = define_stack;
    nocode_wanted = 1;

    if (setjmp(s1->error_jmp_buf) == 0) {
        s1->nb_errors = 0;
        s1->error_set_jmp_enabled = 1;

        ch = file->buf_ptr[0];
        tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
        parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM;
        next();
        decl(VT_CONST);
        if (tok != TOK_EOF)
            expect("declaration");

        /* end of translation unit info */
        if (s1->do_debug) {
            put_stabs_r(NULL, N_SO, 0, 0, 
                        text_section->data_offset, text_section, section_sym);
        }
    }
    s1->error_set_jmp_enabled = 0;

    /* reset define stack, but leave -Dsymbols (may be incorrect if
       they are undefined) */
    free_defines(define_start); 

    gen_inline_functions();

    sym_pop(&global_stack, NULL);
    sym_pop(&local_stack, NULL);

    return s1->nb_errors != 0 ? -1 : 0;
}

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License