* [[JTC1/SC22/WG14 - C]] * [[wp>C (programming language)]] * [[http://sdcc.sourceforge.net/doc/sdccman.pdf|SDCC Compiler User Guide]] * [[wp>Small Device C Compiler]] * [[wp>Toshiba TLCS]] * 支援 TLCS-90,16 bit 處理器? * [[http://www.edn.com/electronics-products/other/4343471/09-24-98-Toshiba-TLCS-90-EDN-s-25th-Annual-Microprocessor-Microcontroller-Director|Toshiba TLCS-90: EDN's 25th Annual Microprocessor/Microcontroller Director]] ====== 編譯 ====== ===== Linux ===== # svn co http://svn.code.sf.net/p/sdcc/code/trunk/sdcc/ $ wget http://sourceforge.net/projects/sdcc/files/sdcc/3.4.0/sdcc-src-3.4.0.tar.bz2/download $ tar xvf download $ mkdir sdcc.build $ cd sdcc.build $ sudo apt-get install g++ flex bison libboost-all-dev $ ../sdcc/configure --disable-mcs51-port --disable-z80-port \ --disable-z180-port --disable-r2k-port --disable-r3ka-port \ --disable-gbz80-port --disable-tlcs90-port --disable-ds390-port \ --disable-ds400-port --disable-pic14-port --disable-pic16-port --disable-s08-port $ make $ ./bin/sdcc # 會被 strip 調除錯訊息。 $ make install * 編譯完 SDCC 之後,會再用 SDCC 編譯 C 函式庫。 -S Compile only; do not assemble or link -c --compile-only Compile and assemble, but do not link Internal debugging options: --dump-ast Dump front-end AST before generating i-code --dump-i-code Dump the i-code structure at all stages --dump-graphs Dump graphs (control-flow, conflict, etc) --i-code-in-asm Include i-code as comments in the asm file --fverbose-asm Include code generator comments in the asm output # 會列出產生代碼過程中,編譯器內部被調用的函式。 $ export SDCC_DEBUG_FUNCTION_POINTERS=true ===== Windows ===== ==== Visual Studio ==== * 安裝 [[http://www.visualstudio.com/zh-tw/downloads/download-visual-studio-vs#DownloadFamilies_4|Visual Studio 2010 Express]] * IE8 開啟下載頁面會有問題,改用 Chrome。 * 將 [[https://code.google.com/p/gnu-on-windows/downloads/detail?name=gawk-4.1.0-bin.zip&can=2&q=|gawk-4.1.0-bin.zip]] 解壓縮至 C:\Program Files (x86)\GnuWin32 * 安裝 [[http://gnuwin32.sourceforge.net/packages/gawk.htm|Gawk for Windows]] * 此版本 awk 有問題,導致編譯 sdcpp 時發生錯誤 ([[http://sourceforge.net/p/sdcc/discussion/1864/thread/f5d242f4/|Build Errors on Visual Studio 10]])。改用 [[https://code.google.com/p/gnu-on-windows/|gnu-on-windows]] 或是 [[http://unxutils.sourceforge.net/|GNU utilities for Win32]] * 錯誤原因是 awk 產生的 options.h 和 options.c 有問題,導致 sdcpp 編譯失敗。 * Download -> Complete package, except sources * 預設安裝到 C:\Program Files (x86)\GnuWin32 * 安裝 [[http://sourceforge.net/projects/winflexbison/|Win flex-bison]] * 將 win_flex_bison-latest.zip 解壓縮到 C:\Program Files (x86)\GnuWin32 * 點選 all Project,依照 [[http://sourceforge.net/p/winflexbison/wiki/Visual%20Studio%20custom%20build%20rules/|Visual Studio custom build rules]] 設置 Custom Build Rule。 * 將 C:\Program Files (x86)\GnuWin32\bin 和 C:\Program Files (x86)\GnuWin32 加至系統蒐尋路徑,登出並登入已使其生效。 * 安裝 Boost * [[http://andres.jaimes.net/718/how-to-install-the-c-boost-libraries-on-windows/|How to install the C++ Boost Libraries on Windows]] * 部分 Project 依賴 Boost,需要修正頭檔路徑。 * sdcc, hc08, stm8, z80 * 目前可以單獨編譯 sdcc 和 sdcpp。 * 參考資料 * [[http://www.di-mgt.com.au/flex_and_bison_in_msvc.html|Using flex and bison in MSVC++]] * [[http://blog.sina.com.cn/s/blog_6476250d0100wq2q.html|【计算机】在Visual Studio 2008中使用Flex、Bison工具的配置]] ==== Cygwin ==== - 透過 [[https://sourceware.org/cygwinports/|Cygwin Ports]] 安裝 Boost。 - 以 MinGW 交叉編譯。 $ wget http://sourceforge.net/projects/sdcc/files/sdcc/3.5.0/sdcc-src-3.5.0.tar.bz2/download $ tar xvf download $ mkdir sdcc.build; cd sdcc.build $ ../sdcc/configure --host=x86_64-w64-mingw32 \ --disable-mcs51-port --disable-z80-port \ --disable-z180-port --disable-r2k-port --disable-r3ka-port \ --disable-gbz80-port --disable-tlcs90-port --disable-ds390-port \ --disable-ds400-port --disable-pic14-port --disable-pic16-port --disable-s08-port $ make # 為解決 sdcc.exe: error while loading shared libraries: libstdc++-6.dll 的問題。 $ export PATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin/:$PATH * [[http://stackoverflow.com/questions/13636513/linking-libstdc-statically-any-gotchas|Linking libstdc++ statically: any gotchas?]] * [[http://blog.xuite.net/porpoise/blog/39514352-%5BLinux%5D+gcc+-rpath+%E7%9A%84%E7%94%A8%E8%99%95.|[Linux] gcc -rpath 的用處]] * [[wp>rpath]] ====== 流程簡介 ====== * [[https://ftp.fau.de/fosdem/2015/devroom-embedded/8bit_compiler.mp4|FOSDEM 2015 presentation on SDCC]] yyparse (SDCCmain.c) -> AST (SDCCast.c) -> iCode (SDCCicode.c) -> BBlock (SDCCopt.c) -> iCode (SDCCicode.c) -> CodeGen * 以 [[wp>Freescale 68HC08|hc08]] 為主要研究對象 ([[wp>Freescale S08|s08]] 是 hc08 的後續衍生型)。 - yyparse ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCmain.c#main|SDCCmain.c]]) if (fullSrcFileName || options.c1mode) { preProcess (envp); initSymt (); initiCode (); initCSupport (); initBuiltIns (); initPeepHole (); if (options.verbose) printf ("sdcc: Generating code...\n"); yyparse (); * function_definition ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCC.y#function_definition|SDCC.y]])。沒有返回執型別的函式,其預設型別為 int。C 的型別基本上是以 Type Link (Chain) 的方法表示。 function_definition : function_declarator { /* function type not specified */ /* assume it to be 'int' */ addDecl($1,0,newIntLink()); $1 = createFunctionDecl($1); } function_body { $$ = createFunction($1,$3); } | declaration_specifiers function_declarator { pointerTypes($2->type,copyLinkChain($1)); addDecl($2,0,$1); $2 = createFunctionDecl($2); } function_body { $$ = createFunction($2,$4); } ; * declaration_specifiers ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCC.y#declaration_specifiers|SDCC.y]]): 返回型別。 * function_declarator ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCC.y#function_declarator|SDCC.y]]): 函數名和參數。 * function_body ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCC.y#function_body|SDCC.y]]): 函數體。 * addDecl ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.c#addDecl|SDCCsymt.c]]) * createFunctionDecl ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#createFunctionDecl|SDCCast.c]]) * [[https://msdn.microsoft.com/en-us/library/tb971bed.aspx|Overview of Declarators]] - createFunction ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#createFunction|SDCCast.c]]) /*-----------------------------------------------------------------*/ /* createFunction - This is the key node that calls the iCode for */ /* generating the code for a function. Note code */ /* is generated function by function, later when */ /* add inter-procedural analysis this will change */ /*-----------------------------------------------------------------*/ ast * createFunction (symbol * name, ast * body) { /* snip */ ex = newAst_VALUE (symbolVal (name)); /* create name */ ex = newNode (FUNCTION, ex, body); ex->values.args = FUNC_ARGS (name->type); ex->decorated = 1; /* snip */ /* create the node & generate intermediate code */ GcurMemmap = code; // 代碼相關 memmap。 codeOutBuf = &code->oBuf; // 代碼輸出緩衝區。 piCode = iCodeFromAst (ex); // AST -> iCode name->generated = 1; eBBlockFromiCode (piCode); // iCode -> eBBlock /* snip */ - iCodeFromAst ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.c#iCodeFromAst|SDCCicode.c]]) /*-----------------------------------------------------------------*/ /* iCodeFromAst - given an ast will convert it to iCode */ /*-----------------------------------------------------------------*/ iCode * iCodeFromAst (ast * tree) { returnLabel = newiTempLabel ("_return"); entryLabel = newiTempLabel ("_entry"); ast2iCode (tree, 0); return reverseiCChain (); } * ast2iCode ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.c#iCodeFromAst|SDCCicode.c]]) 將樹狀結構的 AST 轉成串列型式的 iCode。 /*-----------------------------------------------------------------*/ /* ast2iCode - creates an icodeList from an ast */ /*-----------------------------------------------------------------*/ operand * ast2iCode (ast * tree, int lvl) { - eBBlockFromiCode ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCopt.c#eBBlockFromiCode|SDCCopt.c]]) /*-----------------------------------------------------------------*/ /* eBBlockFromiCode - creates extended basic blocks from iCode */ /* will return an array of eBBlock pointers */ /*-----------------------------------------------------------------*/ eBBlock ** eBBlockFromiCode (iCode * ic) { /* optimize the chain for labels & gotos this will eliminate redundant labels and will change jump to jumps by jumps */ ic = iCodeLabelOptimize (ic); /* break it down into basic blocks */ ebbi = iCodeBreakDown (ic); /* 底層架構無關的優化。 */ /* allocate registers & generate code */ port->assignRegisters (ebbi); // 分配暫存器,並產生代碼。 /* throw away blocks */ setToNull ((void *) &graphEdges); return NULL; } * iCodeBreakDown ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCBBlock.c#iCodeBreakDown|SDCCBBlock.c]]) * iCode2eBBlock ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCBBlock.c#iCode2eBBlock|SDCCBBlock.c]]) - assignRegisters ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/ralloc.c#hc08_assignRegisters|hc08/ralloc.c]])。存在新舊兩種暫存器分配器。 /*-----------------------------------------------------------------*/ /* assignRegisters - assigns registers to each live range as need */ /*-----------------------------------------------------------------*/ void hc08_assignRegisters (ebbIndex * ebbi) { #ifdef OLDRALLOC if (options.oldralloc) hc08_oldralloc (ebbi); else #endif hc08_ralloc (ebbi); } * hc08_oldralloc ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/ralloc.c#hc08_oldralloc|hc08/ralloc.c]]) /*-----------------------------------------------------------------*/ /* Old, obsolete register allocator */ /*-----------------------------------------------------------------*/ void hc08_oldralloc (ebbIndex * ebbi) { /* first determine for each live range the number of registers & the type of registers required for each */ regTypeNum (*ebbs); /* and serially allocate registers */ serialRegAssign (ebbs, count); /* snip */ /* now get back the chain */ ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); // 再將 eBBlock 轉成 iCode。 genhc08Code (ic); // 針對 iCode 產生代碼。 } * hc08_ralloc ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/ralloc.c#hc08_ralloc|hc08_ralloc]]) /*-----------------------------------------------------------------*/ /* New register allocator */ /*-----------------------------------------------------------------*/ void hc08_ralloc (ebbIndex * ebbi) { /* The new register allocator invokes its magic */ ic = hc08_ralloc2_cc (ebbi); /* now get back the chain */ ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); // 再將 eBBlock 轉成 iCode。 genhc08Code (ic); // 針對 iCode 產生代碼。 } - genhc08Code ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genhc08Code|hc08/gen.c]]) /*-----------------------------------------------------------------*/ /* genhc08Code - generate code for HC08 based controllers */ /*-----------------------------------------------------------------*/ void genhc08Code (iCode *lic) { for (ic = lic; ic; ic = ic->next) { /* snip */ genhc08iCode(ic); /* snip */ } } - genhc08iCode ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genhc08iCode|hc08/gen.c]]) /*---------------------------------------------------------------------------------------*/ /* genhc08iode - generate code for HC08 based controllers for a single iCode instruction */ /*---------------------------------------------------------------------------------------*/ static void genhc08iCode (iCode *ic) { /* depending on the operation */ switch (ic->op) { case '!': genNot (ic); break; - genNot ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genNot|hc08/gen.c]]) /*-----------------------------------------------------------------*/ /* genNot - generate code for ! operation */ /*-----------------------------------------------------------------*/ static void genNot (iCode * ic) { bool needpulla; D (emitcode ("; genNot", "")); /* assign asmOps to operand & result */ aopOp (IC_LEFT (ic), ic, FALSE); aopOp (IC_RESULT (ic), ic, TRUE); needpulla = pushRegIfSurv (hc08_reg_a); asmopToBool (AOP (IC_LEFT (ic)), TRUE); emitcode ("eor", one); regalloc_dry_run_cost += 2; storeRegToFullAop (hc08_reg_a, AOP (IC_RESULT (ic)), FALSE); pullOrFreeReg (hc08_reg_a, needpulla); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); } * 至此已產生出匯編。 - do_glue() ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCmain.c|SDCCmain.c]])。產生段及其中的變數,並將所有匯編合併 (glue) 成一份。 if (port->general.do_glue != NULL) (*port->general.do_glue) (); * glue ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCglue.c#glue|SDCCglue.c]]) /*-----------------------------------------------------------------*/ /* glue - the final glue that hold the whole thing together */ /*-----------------------------------------------------------------*/ void glue (void) { /* emit code for the all the variables declared */ emitMaps (); } * emitMaps ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCglue.c#emitMaps|SDCCglue.c]]) /*-----------------------------------------------------------------*/ /* emitMaps - emits the code for the data portion the code */ /*-----------------------------------------------------------------*/ void emitMaps (void) { namedspacemap *nm; int publicsfr = TARGET_IS_MCS51; /* Ideally, this should be true for all */ /* ports but let's be conservative - EEP */ inInitMode++; /* no special considerations for the following data, idata & bit & xdata */ emitRegularMap (data, TRUE, TRUE); * emitRegularMap ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCglue.c#emitRegularMap|SDCCglue.c]]) /*-----------------------------------------------------------------*/ /* emitRegularMap - emit code for maps with no special cases */ /*-----------------------------------------------------------------*/ static void emitRegularMap (memmap * map, bool addPublics, bool arFlag) ====== AST ====== * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.h#ast|ast]] * 常用巨集 #define LRVAL(x) x->left->rvalue // 左節點是否為右值 #define RRVAL(x) x->right->rvalue #define TRVAL(x) x->rvalue // 根節點是否為右值 #define LLVAL(x) x->left->lvalue #define RLVAL(x) x->right->lvalue // 右節點是否為左值 #define TLVAL(x) x->lvalue // 根節點是否為左值 #define RTYPE(x) x->right->ftype // 右節點的型別鏈其開頭節點 #define RETYPE(x) x->right->etype #define LTYPE(x) x->left->ftype #define LETYPE(x) x->left->etype // 左節點的型別鏈其末端節點 #define TTYPE(x) x->ftype // 根節點的型別鏈其開頭節點 #define TETYPE(x) x->etype // 根節點的型別鏈其末端節點 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.h#opval|opval]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCval.h#value|value]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.h#operand|operand]] * createFunction ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#createFunction|SDCCast.c]]) body = resolveSymbols (body); /* resolve the symbols */ body = decorateType (body, RESULT_TYPE_NONE); /* propagateType & do semantic checks */ * resolveSymbols ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#resolveSymbols|SDCCast.c]]) 檢視 AST 中所使用到的符號是否存在符號表中,若否,則報出錯誤。 /*-----------------------------------------------------------------*/ /* resolveSymbols - resolve symbols from the symbol table */ /*-----------------------------------------------------------------*/ ast * resolveSymbols (ast * tree) { /* walk the entire tree and check for values */ /* with symbols if we find one then replace */ /* symbol with that from the symbol table */ /* make sure we resolve the true & false labels for ifx */ if (tree->type == EX_OP && tree->opval.op == IFX) { symbol *csym; if (tree->trueLabel) { if ((csym = findSym (LabelTab, tree->trueLabel, tree->trueLabel->name))) tree->trueLabel = csym; else werrorfl (tree->filename, tree->lineno, E_LABEL_UNDEF, tree->trueLabel->name); } if (tree->falseLabel) { if ((csym = findSym (LabelTab, tree->falseLabel, tree->falseLabel->name))) tree->falseLabel = csym; else werrorfl (tree->filename, tree->lineno, E_LABEL_UNDEF, tree->falseLabel->name); } } * decorateType ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#decorateType|SDCCast.c]]) 計算 (推導) AST 根節點的型別,並做語意分析 ([[wp>Semantic analysis (compilers)|semantic analysis]])。[[wp>Attribute grammar]] /*--------------------------------------------------------------------*/ /* decorateType - compute type for this tree, also does type checking.*/ /* This is done bottom up, since type has to flow upwards. */ /* resultType flows top-down and forces e.g. char-arithmetic, if the */ /* result is a char and the operand(s) are int's. */ /* It also does constant folding, and parameter checking. */ /*--------------------------------------------------------------------*/ ast * decorateType (ast * tree, RESULT_TYPE resultType) { /* snip */ /*------------------------------------------------------------------*/ /*----------------------------*/ /* multiplication */ /*----------------------------*/ if (!IS_ARITHMETIC (LTYPE (tree)) || !IS_ARITHMETIC (RTYPE (tree))) { werrorfl (tree->filename, tree->lineno, E_INVALID_OP, "multiplication"); goto errorTreeReturn; } /* if they are both literal then */ /* rewrite the tree */ if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) { // 常數折疊 (constant folding)。 tree->type = EX_VALUE; tree->opval.val = valMult (valFromType (LETYPE (tree)), valFromType (RETYPE (tree))); tree->right = tree->left = NULL; TETYPE (tree) = getSpec (TTYPE (tree) = tree->opval.val->type); return tree; } /* if left is a literal exchange left & right */ if (IS_LITERAL (LTYPE (tree))) { ast *tTree = tree->left; tree->left = tree->right; tree->right = tTree; } /* if right is a literal and */ /* we can find a 2nd literal in a mul-tree then */ /* rearrange the tree */ if (IS_LITERAL (RTYPE (tree))) { ast *parent; ast *litTree = searchLitOp (tree, &parent, "*"); if (litTree) { DEBUG_CF ("mul") ast *tTree = litTree->left; litTree->left = tree->right; tree->right = tTree; /* both operands in litTree are literal now */ decorateType (parent, resultType); } } LRVAL (tree) = RRVAL (tree) = 1; tree->left = addCast (tree->left, resultTypeProp, FALSE); // 在左右子樹插入 Cast 節點。 tree->right = addCast (tree->right, resultTypeProp, FALSE); TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op)); return tree; * valFromType ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCval.c#valFromType|SDCCval.c]]) 由 struct sym_link 轉成 struct value。 * valMult ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCval.c#valMult|SDCCval.c]]) 對兩個 struct value 做乘法運算。 /*------------------------------------------------------------------*/ /* valMult - multiply constants */ /*------------------------------------------------------------------*/ value * valMult (value * lval, value * rval) { value *val; /* create a new value */ val = newValue (); val->type = val->etype = computeType (lval->etype, rval->etype, RESULT_TYPE_INT, '*'); SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ if (IS_FLOAT (val->type)) SPEC_CVAL (val->type).v_float = floatFromVal (lval) * floatFromVal (rval); * addCast ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#addCast|SDCCast.c]]) 視情況插入轉型節點。 /*-----------------------------------------------------------------*/ /* addCast - adds casts to a type specified by RESULT_TYPE */ /*-----------------------------------------------------------------*/ static ast * addCast (ast * tree, RESULT_TYPE resultType, bool promote) { sym_link *newLink; bool upCasted = FALSE; switch (resultType) { case RESULT_TYPE_NONE: /* if thing smaller than int must be promoted to int */ if (!promote || getSize (tree->etype) >= INTSIZE) /* promotion not necessary or already an int */ return tree; /* char and bits: promote to int */ newLink = newIntLink (); upCasted = TRUE; break; /* snip */ default: return tree; } tree->decorated = 0; tree = newNode (CAST, newAst_LINK (newLink), tree); tree->filename = tree->right->filename; tree->lineno = tree->right->lineno; /* keep unsigned type during cast to smaller type, but not when promoting from char to int */ if (!upCasted) SPEC_USIGN (tree->left->opval.lnk) = IS_UNSIGNED (tree->right->etype) ? 1 : 0; return decorateType (tree, resultType); } * computeType ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.c#computeType|SDCCsymt.c]]) /*------------------------------------------------------------------*/ /* computeType - computes the resultant type from two types */ /*------------------------------------------------------------------*/ sym_link * computeType (sym_link * type1, sym_link * type2, RESULT_TYPE resultType, int op) { ===== AST 輸出解說 ===== * createFunction ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#createFunction|SDCCast.c]]) ex = newAst_VALUE (symbolVal (name)); /* create name */ ex = newNode (FUNCTION, ex, body); ex->values.args = FUNC_ARGS (name->type); ex->decorated = 1; if (options.dump_ast) PA (ex); * ast_print ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#ast_print|SDCCast.c]]) ==== 範例一 ==== int main() { int a = 0; { int a = 0; int b = 1; int c = a + b; } return 0; } type chain (return type, storage class) | v FUNCTION (_main=0x9d81ab8) type (int fixed) args (void) | | | v ----> ast node address ----> args type chain function name tree (0x9d81a60) not decorated ----> left node is not decorated ---- L (level), B (block) | v (null):0:{ L1 B0 (null):0: DECLARE SYMBOL (L1 B1 a=0x9d7ef00) type (int fixed) (null):0: { L2 B1 (null):0: DECLARE SYMBOL (L2 B2 a=0x9d7f538) type (int fixed) (null):0: DECLARE SYMBOL (L2 B2 b=0x9d7fa78) type (int fixed) (null):0: DECLARE SYMBOL (L2 B2 c=0x9d7ffb8) type (int fixed) (null):0: } test.c:11: RETURN (0x9d80b50) type (int literal) test.c:0: CONSTANT (0x9d81550) value = 0, 0x0, 0.000000 type (int literal) (null):0:} ==== 範例二 ==== int main() { char a = 0; int b = 1; a + b; return 0; } FUNCTION (_main=0xa204f60) type (int fixed) args (void) tree (0xa204f08) not decorated (null):0:{ L1 B0 (null):0: DECLARE SYMBOL (L1 B1 a=0xa2030a0) type (char fixed) (null):0: DECLARE SYMBOL (L1 B1 b=0xa2035e0) type (int fixed) test.c:4: ADD (0xa2040c8) type (int fixed) test.c:4: CAST (0xa204878) from type (char fixed) to type (int fixed) test.c:4: SYMBOL (L1 B1 a=0xa203d68 @ 0xa2030a0) type (char fixed) test.c:4: SYMBOL (L1 B1 b=0xa204070 @ 0xa2035e0) type (int fixed) test.c:6: RETURN (0xa2042c0) type (int literal) test.c:0: CONSTANT (0xa2049f8) value = 0, 0x0, 0.000000 type (int literal) (null):0:} * 這裡可以看到 C 語言要求的整型提升。 ADD (int) / \ CAST SYMBOL (int) / SYMBOL (char) ====== iCode ====== yyparse (SDCCmain.c) → AST (SDCCast.c) → iCode (SDCCicode.c) → BBlock (SDCCopt.c) → iCode (SDCCicode.c) → CodeGen * iCode 的種類定義在 [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCC.y|SDCC.y]]。各檔案透過引用 SDCCy.h 得到。 %token IFX ADDRESS_OF GET_VALUE_AT_ADDRESS SPIL UNSPIL GETHBIT GETABIT GETBYTE GETWORD %token BITWISEAND UNARYMINUS IPUSH IPOP PCALL ENDFUNCTION JUMPTABLE %token RRC RLC %token CAST CALL PARAM NULLOP BLOCK LABEL RECEIVE SEND ARRAYINIT %token DUMMY_READ_VOLATILE ENDCRITICAL SWAP INLINE NORETURN RESTRICT SMALLC Z88DK_FASTCALL Z88DK_CALLEE ALIGNAS %token ASM * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.h#iCode|iCode]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.h#ulrrcnd|ulrrcnd]]: union, left, right, result, condition。 * 常用巨集 /* definition for intermediate code */ #define IC_RESULT(x) (x)->ulrrcnd.lrr.result // 取得 iCode 的結果 operand #define IC_LEFT(x) (x)->ulrrcnd.lrr.left // 取得 iCode 的左 operand #define IC_RIGHT(x) (x)->ulrrcnd.lrr.right // 取得 iCode 的右 operand #define IC_COND(x) (x)->ulrrcnd.cnd.condition // 取得 iCode 中的條件表達式 #define IC_TRUE(x) (x)->ulrrcnd.cnd.trueLabel // 取得 iCode 中,條件為真的 label #define IC_FALSE(x) (x)->ulrrcnd.cnd.falseLabel // 取得 iCode 中,條件為假的 label #define IC_LABEL(x) (x)->label // 取得 goto 跳轉到的 label #define IC_JTCOND(x) (x)->ulrrcnd.jmpTab.condition // 取得 jump table 中的條件表達式 #define IC_JTLABELS(x) (x)->ulrrcnd.jmpTab.labels // 取得 jump table 中,欲跳轉到的 label 所成之集合 #define IC_INLINE(x) (x)->inlineAsm #define IC_ARRAYILIST(x) (x)->arrayInitList * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.h#operand|operand]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.h#svt|svt]]: symbol, value, type * 常用巨集 #define OP_SYMBOL(op) validateOpType(op, "OP_SYMBOL", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand #define OP_SYMBOL_CONST(op) validateOpTypeConst(op, "OP_SYMBOL", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand #define OP_VALUE(op) validateOpType(op, "OP_VALUE", #op, VALUE, __FILE__, __LINE__)->svt.valOperand #define OP_SYM_TYPE(op) validateOpType(op, "OP_SYM_TYPE", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->type #define OP_SYM_ETYPE(op) validateOpType(op, "OP_SYM_ETYPE", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->etype #define SPIL_LOC(op) validateOpType(op, "SPIL_LOC", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->usl.spillLoc #define OP_LIVEFROM(op) validateOpType(op, "OP_LIVEFROM", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->liveFrom #define OP_LIVETO(op) validateOpType(op, "OP_LIVETO", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->liveTo #define OP_REQV(op) validateOpType(op, "OP_REQV", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->reqv #define OP_KEY(op) validateOpType(op, "OP_REQV", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->key #define OP_TYPE(op) validateOpType(op, "OP_TYPE", #op, TYPE, __FILE__, __LINE__)->svt.typeOperand ====== eBBlock ====== * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCBBlock.h#eBBlock|SDCCBBlock.h]] * eBBlockFromiCode ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCopt.c#eBBlockFromiCode|SDCCopt.c]]) * dumpraw0 /* optimize the chain for labels & gotos this will eliminate redundant labels and will change jump to jumps by jumps */ ic = iCodeLabelOptimize (ic); /* compute the control flow */ computeControlFlow (ebbi); /* dumpraw if asked for */ if (options.dump_i_code) dumpEbbsToFileExt (DUMP_RAW0, ebbi); * dumpraw1 /* replace the local variables with their register equivalents : the liveRange computation along with the register allocation will determine if it finally stays in the registers */ replaceRegEqv (ebbi); /* create loop regions */ loops = createLoopRegions (ebbi); /* dumpraw if asked for */ if (options.dump_i_code) dumpEbbsToFileExt (DUMP_RAW1, ebbi); * dumpcse optimizeCastCast (ebbi->bbOrder, ebbi->count); /* Burn the corpses, so the dead may rest in peace, safe from cse necromancy */ computeDataFlow (ebbi); killDeadCode (ebbi); /* do common subexpression elimination for each block */ change = cseAllBlocks (ebbi, FALSE); /* dumpraw if asked for */ if (options.dump_i_code) dumpEbbsToFileExt (DUMP_CSE, ebbi); * dumpdflow /* compute the data flow */ computeDataFlow (ebbi); /* dumpraw if asked for */ if (options.dump_i_code) dumpEbbsToFileExt (DUMP_DFLOW, ebbi); * dumpgcse /* global common subexpression elimination */ if (optimize.global_cse) { change += cseAllBlocks (ebbi, FALSE); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_GCSE, ebbi); } else { // compute the dataflow only assert(cseAllBlocks (ebbi, TRUE)==0); } * dumpdeadcode /* kill dead code */ kchange = killDeadCode (ebbi); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_DEADCODE, ebbi); * dumploop /* do loop optimizations */ change += (lchange = loopOptimizations (loops, ebbi)); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_LOOP, ebbi); * dumploopg & dumploopd /* recompute the data flow and apply global cse again if loops optimizations or dead code caused a change: loops will brings out of the loop which then may be available for use in the later blocks: dead code elimination could potentially disconnect some blocks conditional flow may be efected so we need to apply subexpression once more */ if (lchange || kchange) { computeDataFlow (ebbi); change += cseAllBlocks (ebbi, FALSE); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_LOOPG, ebbi); /* if loop optimizations caused a change then do dead code elimination once more : this will get rid of the extra assignments to the induction variables created during loop optimizations */ killDeadCode (ebbi); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_LOOPD, ebbi); } * dumplospre (Lifetime-optimal speculative partial redundancy elimination) offsetFoldGet (ebbi->bbOrder, ebbi->count); /* lospre */ computeControlFlow (ebbi); loops = createLoopRegions (ebbi); computeDataFlow (ebbi); computeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); adjustIChain (ebbi->bbOrder, ebbi->count); ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); if (optimize.lospre && (TARGET_Z80_LIKE || TARGET_HC08_LIKE || TARGET_IS_STM8)) /* Todo: enable for other ports. */ { lospre (ic, ebbi); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_LOSPRE, ebbi); /* GCSE, lospre and maybe other optimizations sometimes create temporaries that have non-connected live ranges, which is bad. Split them. */ ebbi = iCodeBreakDown (ic); computeControlFlow (ebbi); loops = createLoopRegions (ebbi); computeDataFlow (ebbi); recomputeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); adjustIChain (ebbi->bbOrder, ebbi->count); ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); separateLiveRanges (ic, ebbi); } * dumprange /* compute the live ranges */ recomputeLiveRanges (ebbi->bbOrder, ebbi->count, TRUE); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_RANGE, ebbi); * hc08_oldralloc ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/ralloc.c#hc08_oldralloc|hc08/ralloc.c]]) * dumppack /* change assignments this will remove some live ranges reducing some register pressure */ for (i = 0; i < count; i++) packRegisters (ebbs, i); /* liveranges probably changed by register packing so we compute them again */ recomputeLiveRanges (ebbs, count, FALSE); if (options.dump_i_code) dumpEbbsToFileExt (DUMP_PACK, ebbi); * dumprassign & dumprange /* first determine for each live range the number of registers & the type of registers required for each */ regTypeNum (*ebbs); /* and serially allocate registers */ serialRegAssign (ebbs, count); freeAllRegs (); /* after that create the register mask for each of the instruction */ createRegMask (ebbs, count); /* Convert the old sym->accuse flag into normal register assignments */ replaceAccuse (ebbs, count); /* redo that offsets for stacked automatic variables */ if (currFunc) { redoStackOffsets (); } if (options.dump_i_code) { dumpEbbsToFileExt (DUMP_RASSGN, ebbi); dumpLiveRanges (DUMP_LRANGE, liveRanges); } ===== eBBlock 輸出解說 ===== * eBBlockFromiCode ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCopt.c#eBBlockFromiCode|SDCCopt.c]]) * 將 iCode 分為數個 eBBlock,進行優化。 /* break it down into basic blocks */ ebbi = iCodeBreakDown (ic); /* compute the control flow */ computeControlFlow (ebbi); /* dumpraw if asked for */ if (options.dump_i_code) dumpEbbsToFileExt (DUMP_RAW0, ebbi); * dumpEbbsToFileExt ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCBBlock.c#dumpEbbsToFileExt|SDCCBBlock.c]]) * 輸出基本塊信息。 /*-----------------------------------------------------------------*/ /* dumpEbbsToFileExt - write all the basic blocks to a file */ /*-----------------------------------------------------------------*/ void dumpEbbsToFileExt (int id, ebbIndex * ebbi) { for (i = 0; i < count; i++) { fprintf (of, "\n----------------------------------------------------------------\n"); fprintf (of, "Basic Block %s (df:%d bb:%d lvl:%d): loopDepth=%d%s%s%s\n", ebbs[i]->entryLabel->name, ebbs[i]->dfnum, ebbs[i]->bbnum, ebbs[i]->entryLabel->level, ebbs[i]->depth, ebbs[i]->noPath ? " noPath" : "", ebbs[i]->partOfLoop ? " partOfLoop" : "", ebbs[i]->isLastInLoop ? " isLastInLoop" : ""); // a --nolabelopt makes this more readable fprintf (of, "\nsuccessors: "); for (bb = setFirstItem (ebbs[i]->succList); bb; bb = setNextItem (ebbs[i]->succList)) { fprintf (of, "%s ", bb->entryLabel->name); } fprintf (of, "\npredecessors: "); for (bb = setFirstItem (ebbs[i]->predList); bb; bb = setNextItem (ebbs[i]->predList)) { fprintf (of, "%s ", bb->entryLabel->name); } fprintf (of, "\ndominators: "); for (d = 0; d < ebbs[i]->domVect->size; d++) { if (bitVectBitValue (ebbs[i]->domVect, d)) { fprintf (of, "%s ", ebbi->bbOrder[d]->entryLabel->name); //ebbs[d]->entryLabel->name); } } fprintf (of, "\n"); fprintf (of, "\ndefines bitVector :"); bitVectDebugOn (ebbs[i]->defSet, of); fprintf (of, "\nlocal defines bitVector :"); bitVectDebugOn (ebbs[i]->ldefs, of); fprintf (of, "\npointers Set bitvector :"); bitVectDebugOn (ebbs[i]->ptrsSet, of); #if 0 fprintf (of, "\nin coming definitions :"); bitVectDebugOn (ebbs[i]->inDefs, of); fprintf (of, "\nout going definitions :"); bitVectDebugOn (ebbs[i]->outDefs, of); fprintf (of, "\ndefines used :"); bitVectDebugOn (ebbs[i]->usesDefs, of); #endif if (ebbs[i]->isLastInLoop) { fprintf (of, "\nInductions Set bitvector :"); bitVectDebugOn (ebbs[i]->linds, of); } fprintf (of, "\ninExprs:"); for (cseSet = ebbs[i]->inExprs; cseSet; cseSet = cseSet->next) { cseDef *item = cseSet->item; fprintf (of, " %s(%d)", OP_SYMBOL (item->sym)->name, item->diCode->key); if (item->fromGlobal) fprintf (of, "g"); } fprintf (of, "\noutExprs:"); for (cseSet = ebbs[i]->outExprs; cseSet; cseSet = cseSet->next) { cseDef *item = cseSet->item; fprintf (of, " %s(%d)", OP_SYMBOL (item->sym)->name, item->diCode->key); if (item->fromGlobal) fprintf (of, "g"); } fprintf (of, "\nkilledExprs:"); for (cseSet = ebbs[i]->killedExprs; cseSet; cseSet = cseSet->next) { cseDef *item = cseSet->item; fprintf (of, " %s(%d)", OP_SYMBOL (item->sym)->name, item->diCode->key); if (item->fromGlobal) fprintf (of, "g"); } fprintf (of, "\n----------------------------------------------------------------\n"); printiCChain (ebbs[i]->sch, of); } fflush (of); } * 再輸出 iCode 信息 [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.c#printiCChain|SDCCicode.c]] for (loop = icChain; loop; loop = loop->next) { if ((icTab = getTableEntry (loop->op))) { struct dbuf_s dbuf; fprintf (of, "%s(l%d:s%d:k%d:d%d:s%d)\t", loop->filename, loop->lineno, loop->seq, loop->key, loop->depth, loop->supportRtn); dbuf_init (&dbuf, 1024); icTab->iCodePrint (&dbuf, loop, icTab->printName); dbuf_write_and_destroy (&dbuf, of); fflush (of); } } * switch case * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.c#geniCodeSwitch|geniCodeSwitch]] * 視 case 情況,選擇 compare and branch 或是 [[wp>Branch table|jump table]] 兩種方式。 ==== 範例一 ==== int main() { return 0; } ---------------------------------------------------------------- Basic Block _entry (df:1 bb:0 lvl:1): loopDepth=0 successors: _return predecessors: dominators: _entry defines bitVector : local defines bitVector : pointers Set bitvector : inExprs: outExprs: killedExprs: ---------------------------------------------------------------- test.c(l1:s0:k0:d0:s0) _entry($2) : test.c(l1:s0:k1:d0:s0) proc _main [k1 lr0:0 so:0]{ ia0 a2p0 re0 rm0 nos0 ru0 dp0}{int function ( ) fixed} symbol 的內部名稱 symbol 屬性 k : key lr: live range so: stack (local or global symbol) symbol & operand 屬性 ia : is an address a2p: aggregate to pointer to aggregate re : is the register equivalent of a symbol rm : can be remateriazed nos: cannot be assigned a spil location ru : used in return statement only dp : (8051) data pointer symbol type storage class test.c(l3:s0:k2:d0:s0) ret 0x0 {int literal} ---------------------------------------------------------------- Basic Block _return (df:2 bb:1 lvl:1): loopDepth=0 successors: predecessors: _entry dominators: _entry _return defines bitVector : local defines bitVector : pointers Set bitvector : inExprs: outExprs: killedExprs: ---------------------------------------------------------------- test.c(l3:s0:k3:d0:s0) _return($1) : test.c(l3:s0:k4:d0:s0) eproc _main [k1 lr0:0 so:0]{ ia0 a2p0 re0 rm0 nos0 ru0 dp0}{int function ( ) fixed} * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCicode.c#dbuf_printOperand|dbuf_printOperand]] ==== 範例二 ==== __xdata int * p; int gint; /* This function does nothing useful. It is used for the purpose of explaining iCode */ short function (__data int *x) { short i=10; /* dead initialization eliminated */ short sum=10; /* dead initialization eliminated */ short mul; int j ; while (*x) *x++ = *p++; sum = 0 ; mul = 0; /* compiler detects i,j to be induction variables */ for (i = 0, j = 10 ; i < 10 ; i++, j--) { sum += i; mul += i * 3; /* this multiplication remains */ gint += j * 3; /* this multiplication changed to addition */ } return sum+mul; } ====== 代碼生成 ====== * asmop ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.h#asmop|hc08/gen.h]]) 針對 iCode 的 operand 生成 asm 指令所需的 operand (暫存器、常數 ... 等等)。 /* type asmop : a homogenised type for all the different spaces an operand can be in */ typedef struct asmop { * genNot ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genNot|hc08/gen.c]]) /*-----------------------------------------------------------------*/ /* genNot - generate code for ! operation */ /*-----------------------------------------------------------------*/ static void genNot (iCode * ic) { bool needpulla; D (emitcode ("; genNot", "")); /* assign asmOps to operand & result */ aopOp (IC_LEFT (ic), ic, FALSE); // 針對 ic 的左 operand 生成 asmop aopOp (IC_RESULT (ic), ic, TRUE); // 針對 ic 的 result 生成 asmop needpulla = pushRegIfSurv (hc08_reg_a); // 如果暫存器 a 當前的值還需要被使用,則入棧加以保存 asmopToBool (AOP (IC_LEFT (ic)), TRUE); // 針對 asmop 生成代碼,產生真假值。 emitcode ("eor", one); regalloc_dry_run_cost += 2; storeRegToFullAop (hc08_reg_a, AOP (IC_RESULT (ic)), FALSE); pullOrFreeReg (hc08_reg_a, needpulla); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); } * aopOp ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#aopOp|hc08/gen.c]]) 針對 operand 產生對應的 asmop,並記錄在 operand 之中。 /*-----------------------------------------------------------------*/ /* aopOp - allocates an asmop for an operand : */ /*-----------------------------------------------------------------*/ static void aopOp (operand *op, iCode * ic, bool result) { /* snip */ /* if this a literal */ if (IS_OP_LITERAL (op)) { op->aop = aop = newAsmop (AOP_LIT); // 注意! 生成的 asmop 記錄在 operand 的 aop 欄位。 aop->aopu.aop_lit = OP_VALUE (op); aop->size = getSize (operandType (op)); aop->op = op; return; } /* snip */ } * freeAsmop ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#freeAsmop|hc08/gen.c]]) 釋放 asmop。 * pushRegIfSurv ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#pushRegIfSurv|hc08/gen.c]])。如果暫存器當前的值還需要被使用,則入棧加以保存。 * pullOrFreeReg ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#pullOrFreeReg|hc08/gen.c]])。出棧回復暫存器值,否則將該暫存器釋放,以供之後配置使用。 * AOP (IC_LEFT (ic)): 取出 ic 中的 operand,再取出 operand 中的 asmop。 * regalloc_dry_run_cost: 累加指令所佔的 byte 數。本為 debug 輸出,現在為新版 register allocator 所調用。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/ralloc.c#hc08_reg_a|hc08_reg_a]] * 暫存器都有此種 global struct。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#loadRegFromAop|loadRegFromAop]] * 將 asm operand 載入暫存器。 * 這裡又將情況依據 asm operand 的類別和暫存器類別,而有不同代碼生成。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#storeRegToAop|storeRegToAop]] * 將暫存器存放於 asm operand (暫存器或是棧)。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#transferRegReg|transferRegReg]] * 資料搬移有時候可以直接在暫存器之間移動。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#adjustStack|adjustStack]] * 調整棧空間。_G 存放相關信息。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genCall|genCall]] * 產生函式調用代碼。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genSend|genSend]] * 準備參數供函式調用。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genFunction|genFunction]] * 被調用方 (callee) 的 prologue。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genReceive|genReceive]] * 接受調用方傳送的參數。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genEndFunction|genEndFunction]] * 被調用方 (callee) 的 epilogue。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genCast|genCast]] * 滿足 C 語言要求的整型提升之要求。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genPcall|genPcall]] * 函式指針調用。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genIpush|genIpush]] * 如果 callee 符合特定條件 (如: 可變參數),前端產生 iCode 時,即會使用 IPUSH 傳參。 ====== Overlay ====== * SDCC 會針對 leaf function 做 overlay,生成 overlay_name (OSEC) 段。 * emitOverlay (SDCCglue.c) ====== 除錯訊息 ====== SDCC 似乎存在有兩套除錯資訊,cdbFile 和 SDCCdwarf2。前者是另外寫至以 .adb 和 .cdb 後綴的檔案 (開啟 ''--debug'' 選項即會使用),後者則是將除錯資訊寫至匯編 (hc08 另外再開啟 ''--out-fmt-elf'' 選項下會使用)。 * DEBUGFILE ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCdebug.h#DebugFile|SDCCdebug.h]]) 定義除錯訊息操作函式。 typedef struct DebugFile { int (*openFile) (const char *file); int (*closeFile) (void); int (*writeModule) (const char *name); int (*writeFunction) (symbol *pSym, iCode *ic); int (*writeEndFunction) (symbol *pSym, iCode *ic, int offset); int (*writeLabel) (symbol *pSym, iCode *ic); int (*writeScope) (iCode *ic); int (*writeSymbol) (symbol *pSym); int (*writeType) (structdef *sdef, int block, int inStruct, const char *tag); int (*writeCLine) (iCode *ic); int (*writeALine) (const char *module, int Line); int (*writeFrameAddress) (const char *variable, struct reg_info *reg, int offset); }DEBUGFILE; * cdbDebugFile ([[http://code.metager.de/source/xref/sdcc/sdcc/src/cdbFile.c#cdbDebugFile|cdbFile.c]]) 註冊 [[http://sdcc.sourceforge.net/mediawiki/index.php/CDB_File_Format|CDB]] 除錯訊息相關函式。 DEBUGFILE cdbDebugFile = { &cdbOpenFile, &cdbCloseFile, &cdbWriteModule, &cdbWriteFunction, &cdbWriteEndFunction, &cdbWriteLabel, &cdbWriteScope, &cdbWriteSymbol, &cdbWriteType, &cdbWriteCLine, &cdbWriteALine, &cdbWriteFrameAddress }; * dwarf2DebugFile ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCdwarf2.c#dwarf2DebugFile|SDCCdwarf2.c]] ) 註冊 [[wp>DWARF]] 除錯訊息相關函式。 DEBUGFILE dwarf2DebugFile = { &dwOpenFile, &dwCloseFile, &dwWriteModule, &dwWriteFunction, &dwWriteEndFunction, &dwWriteLabel, &dwWriteScope, &dwWriteSymbol, &dwWriteType, &dwWriteCLine, &dwWriteALine, &dwWriteFrameAddress }; * 預設為 CDB 格式 ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCdebug.c|SDCCdebug.c]]) DEBUGFILE *debugFile = &cdbDebugFile; * _hc08_parseOptions 根據命令行選項選擇 CBD 或是 DWARF 格式。([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/main.c#_hc08_parseOptions|hc08/main.c]]) static bool _hc08_parseOptions (int *pargc, char **argv, int *i) { if (!strcmp (argv[*i], "--out-fmt-elf")) { options.out_fmt = 'E'; debugFile = &dwarf2DebugFile; return TRUE; } if (!strcmp (argv[*i], "--oldralloc")) { options.oldralloc = TRUE; return TRUE; } return FALSE; } * 後端於代碼生成時,會產生相應的除錯資訊。([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genEndFunction|hc08/gen.c]]) /*-----------------------------------------------------------------*/ /* genEndFunction - generates epilogue for functions */ /*-----------------------------------------------------------------*/ static void genEndFunction (iCode * ic) { symbol *sym = OP_SYMBOL (IC_LEFT (ic)); if (IFFUNC_ISNAKED (sym->type)) { emitcode (";", "naked function: no epilogue."); if (options.debug && currFunc) debugFile->writeEndFunction (currFunc, ic, 0); return; } * 後端需要指定 debugger 資料結構 ([[http://code.metager.de/source/xref/sdcc/sdcc/src/port.h#debugger|port.h]]),供 CDB 或是 DWARF 後端調用,其中 dwarf 資料結構 ([[http://code.metager.de/source/xref/sdcc/sdcc/src/port.h#dwarf|port.h]]) 是專供 DWARF 後端調用。emitDebuggerSymbol 基本上是用來針對源代碼中的某一行,輸出除錯訊息 (字串)。 struct { void (*emitDebuggerSymbol) (const char *); struct { int (*regNum) (const struct reg_info *); bitVect *cfiSame; bitVect *cfiUndef; int addressSize; int regNumRet; int regNumSP; int regNumBP; int offsetSP; } dwarf; } debugger; * hc08 ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/main.c#hc08_port|hc08/main.c]]) { hc08_emitDebuggerSymbol, { hc08_dwarfRegNum, NULL, NULL, 4, /* addressSize */ 14, /* regNumRet */ 15, /* regNumSP */ -1, /* regNumBP */ 1, /* offsetSP */ }, }, * hc08_dwarfRegNum ([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/main.c#hc08_dwarfRegNum|hc08/main.c]]) 被 DWARF 後端函式 dwWriteFrameAddress ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCdwarf2.c#dwWriteFrameAddress|SDCCdwarf2.c]]) 調用。 /*-----------------------------------------------------------------------*/ /* dwWriteFrameAddress - note the current position of the frame pointer */ /* address. The base address can be specified by */ /* either a register or pointer variable, leaving */ /* the other as NULL. If both are NULL, there is */ /* no current frame pointer address defined. */ /*-----------------------------------------------------------------------*/ int dwWriteFrameAddress(const char *variable, struct reg_info *reg, int offset) { /* snip */ else if (reg) /* frame pointer based from a register */ { regNum = port->debugger.dwarf.regNum (reg); /* snip */ * stm8 ([[http://code.metager.de/source/xref/sdcc/sdcc/src/stm8/main.c#stm8_port|stm8/main.c]]) 不支援 DWARF 格式,故只需提供 emitDebuggerSymbol。 { stm8_emitDebuggerSymbol }, * stm8_emitDebuggerSymbol ([[http://code.metager.de/source/xref/sdcc/sdcc/src/stm8/gen.c#stm8_emitDebuggerSymbol|stm8/gen.c]]) 被 CDB 後端函式 cdbWriteFunction ([[http://code.metager.de/source/xref/sdcc/sdcc/src/cdbFile.c#cdbWriteFunction|cdbFile.c]]) 調用。 int cdbWriteFunction (symbol *pSym, iCode *ic) { char debugSym[INITIAL_INLINEASM]; if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) fprintf (stderr, "cdbFile.c:cdbWriteFunction()\n"); if (!cdbFilePtr) return 0; if (IS_STATIC (pSym->etype)) sprintf (debugSym, "F%s$%s$0$0", moduleName, pSym->name); else sprintf (debugSym, "G$%s$0$0", pSym->name); emitDebuggerSymbol (debugSym); return cdbWriteBasicSymbol (pSym, FALSE, TRUE); } ====== Lex & Yacc ====== * [[http://www.cs.utsa.edu/~wagner/CS3723/grammar/c_syntax.html|The syntax of C in Backus-Naur Form]] * src/SDCC.lex * src/SDCC.y ([[https://msdn.microsoft.com/en-us/library/a037k1zd.aspx|Overview of C Statements]]) statement : labeled_statement | compound_statement | expression_statement | selection_statement | iteration_statement | jump_statement | critical_statement | asm_statement ; * labeled_statement exit: case 10: break; * compound_statement { // ... code ... } * selection_statement if (cond) { // ... code ... } else { // ... code .. } switch (cond) { // ... code ... } * iteration_statement while (cond) { // ... code ... } do { // ... code ... } while (cond); for ( init; cond; exp) { // ... code ... } * jump_statement goto exit; for ( init; cond; exp) { // ... code ... continue; } for ( init; cond; exp) { // ... code ... break; } func() { return; } selection_statement : IF '(' expr ')' { seqPointNo++;} statement else_statement { noLineno++; $$ = createIf ($3, $6, $7 ); $$->lineno = $3->lineno; $$->filename = $3->filename; noLineno--; } /*-----------------------------------------------------------------*/ /* createIf - creates the parsetree for the if statement */ /*-----------------------------------------------------------------*/ ast * createIf (ast * condAst, ast * ifBody, ast * elseBody) { static int Lblnum = 0; ast *ifTree; symbol *ifTrue, *ifFalse, *ifEnd; struct dbuf_s dbuf; /* if neither exists */ if (!elseBody && !ifBody) { // if there are no side effects (i++, j() etc) if (!hasSEFcalls (condAst)) { return condAst; } } /* create the labels */ dbuf_init (&dbuf, 128); dbuf_printf (&dbuf, "_iffalse_%d", Lblnum); ifFalse = newSymbol (dbuf_c_str (&dbuf), NestLevel); dbuf_destroy (&dbuf); /* if no else body then end == false */ if (!elseBody) ifEnd = ifFalse; else { dbuf_init (&dbuf, 128); dbuf_printf (&dbuf, "_ifend_%d", Lblnum); ifEnd = newSymbol (dbuf_c_str (&dbuf), NestLevel); dbuf_destroy (&dbuf); } dbuf_init (&dbuf, 128); dbuf_printf (&dbuf, "_iftrue_%d", Lblnum); ifTrue = newSymbol (dbuf_c_str (&dbuf), NestLevel); dbuf_destroy (&dbuf); Lblnum++; /* attach the ifTrue label to the top of it body */ ifBody = createLabel (ifTrue, ifBody); /* attach a goto end to the ifBody if else is present */ if (elseBody) { ifBody = newNode (NULLOP, ifBody, newNode (GOTO, newAst_VALUE (symbolVal (ifEnd)), NULL)); /* put the elseLabel on the else body */ elseBody = createLabel (ifFalse, elseBody); /* out the end at the end of the body */ elseBody = newNode (NULLOP, elseBody, createLabel (ifEnd, NULL)); } else { ifBody = newNode (NULLOP, ifBody, createLabel (ifFalse, NULL)); } condAst = backPatchLabels (condAst, ifTrue, ifFalse); if (IS_IFX (condAst)) ifTree = condAst; else ifTree = newIfxNode (condAst, ifTrue, ifFalse); return newNode (NULLOP, ifTree, newNode (NULLOP, ifBody, elseBody)); } while : WHILE { /* create and push the continue , break & body labels */ static int Lblnum = 0; /* continue */ SNPRINTF (lbuff, sizeof(lbuff), "_whilecontinue_%d",Lblnum); STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); /* break */ SNPRINTF (lbuff, sizeof(lbuff), "_whilebreak_%d",Lblnum); STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); /* body */ SNPRINTF (lbuff, sizeof(lbuff), "_whilebody_%d",Lblnum++); $$ = newSymbol(lbuff,NestLevel); } ; iteration_statement : while '(' expr ')' { seqPointNo++;} statement { noLineno++; $$ = createWhile ( $1, STACK_POP(continueStack), STACK_POP(breakStack), $3, $6 ); $$->lineno = $1->lineDef; $$->filename = $1->fileDef; noLineno--; } * continueStack 和 breakStack 用於記錄 continue 和 break 所需跳至的 label。 /*-----------------------------------------------------------------*/ /* createWhile - creates parse tree for while statement */ /* the while statement will be created as follows */ /* */ /* _while_continue_n: */ /* condition_expression +-> trueLabel -> _while_boby_n */ /* | */ /* +-> falseLabel -> _while_break_n */ /* _while_body_n: */ /* statements */ /* goto _while_continue_n */ /* _while_break_n: */ /*-----------------------------------------------------------------*/ ast * createWhile (symbol * trueLabel, symbol * continueLabel, symbol * falseLabel, ast * condExpr, ast * whileBody) { ast *whileTree; /* put the continue label */ condExpr = backPatchLabels (condExpr, trueLabel, falseLabel); if (condExpr && !IS_IFX (condExpr)) { condExpr = newNode (IFX, condExpr, NULL); /* put the true & false labels in place */ condExpr->trueLabel = trueLabel; condExpr->falseLabel = falseLabel; } whileTree = createLabel (continueLabel, condExpr); whileTree->filename = NULL; whileTree->lineno = 0; /* put the body label in front of the body */ if(whileBody && whileBody->type == EX_VALUE && !whileBody->left && !whileBody->right) whileBody = newNode (NULLOP, NULL, whileBody); whileBody = createLabel (trueLabel, whileBody); whileBody->filename = NULL; whileBody->lineno = 0; /* put a jump to continue at the end of the body */ /* and put break label at the end of the body */ whileBody = newNode (NULLOP, whileBody, newNode (GOTO, newAst_VALUE (symbolVal (continueLabel)), createLabel (falseLabel, NULL))); /* put it all together */ return newNode (NULLOP, whileTree, whileBody); } * 將 symbol 和 ast 關聯,生成新的 ast。 statement_list : statement | statement_list statement { $$ = newNode(NULLOP,$1,$2);} ; ast ^ | val ^ | symbol <- lnk (描述 symbol 型別和屬性) (identifier) * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h|symbol]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h|sym_link]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCval.h|value]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.h|ast]] * checkTypeSanity * 用於排除像是 long char 這一類非法型別。 ====== 資料結構 ====== * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCmem.h|SDCCmem.h]] * 編譯器內部內存分配。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h|SDCCsymt.h]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#STORAGE_CLASS|STORAGE_CLASS]] * 部分 storage class 是由目標平台各自定義 ([[http://sdcc.sourceforge.net/doc/sdccman.pdf#page=38|3.5 SDCC Language Extensions]])。 * S_DATA 和 S_XDATA 可能分別指 PAGE0 和 NSEC (以 6502 為例)。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#specifier|specifier]] * 表示型別或儲存類型。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCmem.h#memmap|memmap]] * 各個命名空間皆有對應的資料結構 memmap,供其寫入變數。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#declarator|declarator]] * 定義函式或變數 (包函指針)。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#sym_link|sym_link]] * sym_link 包含 specifier 或 declarator,或是函式相關屬性。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#symbol|symbol]] * 符號,其中包含 sym_link 描述該 symbol 相關屬性。 * 定義巨集以便處理 symbol 相關屬性。 #define FUNC_HASVARARGS(x) (x->funcAttrs.hasVargs) #define IFFUNC_HASVARARGS(x) (IS_FUNC(x) && FUNC_HASVARARGS(x)) * 定義巨集以便處理 specifier 相關屬性。 #define SPEC_NOUN(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.noun #define SPEC_LONG(x) validateLink(x, "SPEC_LONG", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_long * 定義巨集以便處理 declarator 相關屬性。 #define DCL_TYPE(l) validateLink(l, "DCL_TYPE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.dcl_type #define DCL_ELEM(l) validateLink(l, "DCL_ELEM", #l, DECLARATOR, __FILE__, __LINE__)->select.d.num_elem * 定義巨集以便型別檢查。 #define IS_FUNC(x) (IS_DECL(x) && DCL_TYPE(x) == FUNCTION) #define IS_LONG(x) (IS_SPEC(x) && x->select.s.b_long) * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.c#validateLink|validateLink]] sym_link * validateLink (sym_link * l, const char *macro, const char *args, const char select, const char *file, unsigned line) { if (l && l->xclass == select) { return l; // 如果 sym_link l 的 xclass 同 select,是 specifier 或 declarator,返回 l。 } fprintf (stderr, "Internal error: validateLink failed in %s(%s) @ %s:%u:" " expected %s, got %s\n", macro, args, file, line, DECLSPEC2TXT (select), l ? DECLSPEC2TXT (l->xclass) : "null-link"); exit (EXIT_FAILURE); return l; // never reached, makes compiler happy. } * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.c|SDCCsymt.c]] bucket *SymbolTab[256]; /* the symbol table */ bucket *StructTab[256]; /* the structure table */ bucket *TypedefTab[256]; /* the typedef table */ bucket *LabelTab[256]; /* the Label table */ bucket *enumTab[256]; /* enumerated table */ bucket *AddrspaceTab[256]; /* the named address space table */ * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#bucket|bucket]] * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#structdef|structdef]] * 定義 struct 或 union。 * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.h|SDCCast.h]] function_definition : function_declarator { /* function type not specified */ /* assume it to be 'int' */ addDecl($1,0,newIntLink()); $1 = createFunctionDecl($1); } function_body { $$ = createFunction($1,$3); } | declaration_specifiers function_declarator { pointerTypes($2->type,copyLinkChain($1)); addDecl($2,0,$1); $2 = createFunctionDecl($2); } function_body { $$ = createFunction($2,$4); } ; function_declarator2 : declarator2 '(' ')' { addDecl ($1, FUNCTION, NULL); } | declarator2 '(' { NestLevel++; STACK_PUSH(blockNum, currBlockno); btree_add_child(currBlockno, ++blockNo); currBlockno = blockNo; seqPointNo++; /* not a true sequence point, but helps resolve scope */ } parameter_type_list ')' { sym_link *funcType; addDecl ($1, FUNCTION, NULL); funcType = $1->type; while (funcType && !IS_FUNC(funcType)) funcType = funcType->next; assert (funcType); FUNC_HASVARARGS(funcType) = IS_VARG($4); FUNC_ARGS(funcType) = reverseVal($4); /* nest level was incremented to take care of the parms */ NestLevel--; currBlockno = STACK_POP(blockNum); seqPointNo++; /* not a true sequence point, but helps resolve scope */ // if this was a pointer (to a function) if (!IS_FUNC($1->type)) cleanUpLevel(SymbolTab, NestLevel + 1); $$ = $1; } | declarator2 '(' identifier_list ')' { werror(E_OLD_STYLE,$1->name); /* assume it returns an int */ $1->type = $1->etype = newIntLink(); $$ = $1; } ; iteration_statement : while '(' expr ')' { seqPointNo++;} statement { noLineno++; $$ = createWhile ( $1, STACK_POP(continueStack), STACK_POP(breakStack), $3, $6 ); $$->lineno = $1->lineDef; $$->filename = $1->fileDef; noLineno--; } ; while : WHILE { /* create and push the continue , break & body labels */ static int Lblnum = 0; /* continue */ SNPRINTF (lbuff, sizeof(lbuff), "_whilecontinue_%d",Lblnum); STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); /* break */ SNPRINTF (lbuff, sizeof(lbuff), "_whilebreak_%d",Lblnum); STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); /* body */ SNPRINTF (lbuff, sizeof(lbuff), "_whilebody_%d",Lblnum++); $$ = newSymbol(lbuff,NestLevel); } ; /*-----------------------------------------------------------------*/ /* createWhile - creates parse tree for while statement */ /* the while statement will be created as follows */ /* */ /* _while_continue_n: */ /* condition_expression +-> trueLabel -> _while_boby_n */ /* | */ /* +-> falseLabel -> _while_break_n */ /* _while_body_n: */ /* statements */ /* goto _while_continue_n */ /* _while_break_n: */ /*-----------------------------------------------------------------*/ ast * createWhile (symbol * trueLabel, symbol * continueLabel, symbol * falseLabel, ast * condExpr, ast * whileBody) { ast *whileTree; /* put the continue label */ condExpr = backPatchLabels (condExpr, trueLabel, falseLabel); if (condExpr && !IS_IFX (condExpr)) { condExpr = newNode (IFX, condExpr, NULL); /* put the true & false labels in place */ condExpr->trueLabel = trueLabel; condExpr->falseLabel = falseLabel; } whileTree = createLabel (continueLabel, condExpr); whileTree->filename = NULL; whileTree->lineno = 0; /* put the body label in front of the body */ whileBody = createLabel (trueLabel, whileBody); whileBody->filename = NULL; whileBody->lineno = 0; /* put a jump to continue at the end of the body */ /* and put break label at the end of the body */ whileBody = newNode (NULLOP, whileBody, newNode (GOTO, newAst_VALUE (symbolVal (continueLabel)), createLabel (falseLabel, NULL))); /* put it all together */ return newNode (NULLOP, whileTree, whileBody); } 透過 --dump-ast 輸出 AST 可以幫助了解上述代碼。 void main() { int i, j; bool flag = true; if (flag == false) { i = 1; } else { j = 2; } } /*-----------------------------------------------------------------*/ /* createIf - creates the parsetree for the if statement */ /* */ /* condition_expression +-> ifTrue */ /* | -> ifEnd */ /* +-> ifFalse */ /*-----------------------------------------------------------------*/ ast * createIf (ast * condAst, ast * ifBody, ast * elseBody) { static int Lblnum = 0; ast *ifTree; symbol *ifTrue, *ifFalse, *ifEnd; struct dbuf_s dbuf; /* if neither exists */ if (!elseBody && !ifBody) { // if there are no side effects (i++, j() etc) if (!hasSEFcalls (condAst)) { return condAst; } } /* create the labels */ dbuf_init (&dbuf, 128); dbuf_printf (&dbuf, "__iffalse_%d", Lblnum); ifFalse = newSymbol (dbuf_c_str (&dbuf), NestLevel); dbuf_destroy (&dbuf); /* if no else body then end == false */ if (!elseBody) { ifEnd = ifFalse; } else { dbuf_init (&dbuf, 128); dbuf_printf (&dbuf, "__ifend_%d", Lblnum); ifEnd = newSymbol (dbuf_c_str (&dbuf), NestLevel); dbuf_destroy (&dbuf); } dbuf_init (&dbuf, 128); dbuf_printf (&dbuf, "__iftrue_%d", Lblnum); ifTrue = newSymbol (dbuf_c_str (&dbuf), NestLevel); dbuf_destroy (&dbuf); Lblnum++; /* attach the ifTrue label to the top of it body */ ifBody = createLabel (ifTrue, ifBody); /* attach a goto end to the ifBody if else is present */ if (elseBody) { ifBody = newNode (NULLOP, ifBody, newNode (GOTO, newAst_VALUE (symbolVal (ifEnd)), NULL)); /* put the elseLabel on the else body */ elseBody = createLabel (ifFalse, elseBody); /* out the end at the end of the body */ elseBody = newNode (NULLOP, elseBody, createLabel (ifEnd, NULL)); } else { ifBody = newNode (NULLOP, ifBody, createLabel (ifFalse, NULL)); } condAst = backPatchLabels (condAst, ifTrue, ifFalse); if (IS_IFX (condAst)) ifTree = condAst; else ifTree = newIfxNode (condAst, ifTrue, ifFalse); return newNode (NULLOP, ifTree, newNode (NULLOP, ifBody, elseBody)); } * backPatchLabels ([[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCast.c#backPatchLabels|SDCCast.c]]) * patch condition 語句的 true 和 false 的 goto label。 * PORT (port.h) * 各個平台有各自的 port 資料結構。 ====== 警告和錯誤訊息 ====== * [[http://comments.gmane.org/gmane.comp.compilers.sdcc.user/1149|'auto' variable 'timeout' may be used before initialization]] * 程式的邏輯有機會讓 auto 變數沒有初始值。 if (Flag==0x00) { X_axis = 0; Y_axis = 0; } if (X_axis) do_something(); * [[http://sourceforge.net/p/sdcc/discussion/1865/thread/da3bb76d/|conditional flow changed by optimizer]] ====== 移植簡介 ====== * 選擇與目標平台 Y 相近的現存後端 X,將其拷貝至新目錄 Y。 $ cd sdcc-3.4.0/src/ $ mkdir Y $ cp X/* Y/ * 將新目錄 Y 底下檔案更名,並將檔案中現存後端 X 更換成目標平台 Y。 // Y 必須符合 C 識別字命名規則,不可以數字開頭。 * configure 腳本加入目標平台 Y,運行 autoconf 重新生成 configure 腳本。 $ cd sdcc-3.4.0/ $ vim configure.in $ autoconf * 修改剩餘檔案,新增目標平台 Y 至 SDCC 主程式。 $ vi clean.mk $ vi port.h $ vi SDCCmain.c * 編譯試運行。修正編譯過程的錯誤 $ ../sdcc-3.4.0/configure $ make $ ./bin/sdcc -h Special options for the Y port: --out-fmt-elf Output executable in ELF format --oldralloc Use old register allocator $ ./bin/sdcc -mY -S test.c * 依據目標平台 Y 實際情況,逐步修改後端。 * 新增 port 目錄。 * hc08.h * main.h & [[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/main.c|main.c]] * _hc08_defaultRules * peephole 規則 * _hc08_keywords * 支援的關鍵字 * hc08_assignRegisters * register allocator 接口 * _hc08_regparm * 決定參數是否可以放在暫存器 * ralloc.h, ralloc.c & ralloc2.cc * gen.h & gen.c * peeph.def ====== 術語 ====== * [[http://web.archive.org/web/20090617011643/http://www.circuitcellar.com/SA/Articles2/Dutta-121.pdf|Anatomy of a Compiler. A Retargetable ANSI-C Compiler]] * address space (定址空間) * pointer (指針) * stack (棧) * register allocation * accumlator * iCode (SDCC IR) * SDCCicode.h * mcs51 (8051) 是最一開始的目標平台 ===== 硬件 ===== * [[wp>Special function register| SFR (Special function register)]] * [[http://www.keil.com/support/man/docs/c51/c51_le_sfrs.htm|Special Function Registers]] * 為內存中一特定區塊,近似於 IO 埠。The 8051 family of microcontrollers provides a distinct memory area for accessing Special Function Registers (SFRs). SFRs are used in your program to control timers, counters, serial I/Os, port I/Os, and peripherals. * sbit 可以用來指定 IO 埠特定 Pin 腳。With typical 8051 applications, it is often necessary to access individual bits within an SFR. The sbit type provides access to bit-addressable SFRs and other bit-addressable objects. * [[wp>Nibble]] * 4 bits ===== C 程式語言 ===== * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#sym_link|sym_link]] * 代表 specifier 或是 declarator,最後用來修飾 symbol。 * DCL_XXX 用來存取或驗證 sym_link 的 declarator 屬性。 * SPEC_XXX 用來存取或驗證 sym_link 的 specifier 屬性。 * [[http://msdn.microsoft.com/en-us/library/tb971bed.aspx|Overview of Declarators]] int foo(); | | | -----> declarator v specifier * [[http://code.metager.de/source/xref/sdcc/sdcc/src/SDCCsymt.h#symbol|symbol]] * 代表變數、函式名或是 label 一類的概念。 * symbol 內會帶用來修飾該 symbol 的 sym_link。 * IS_OP_XXX 用來存取或驗證 symbol 屬性。 ====== SDCC 技術文件 ====== * [[http://sdcc.sourceforge.net/doc/sdccman.pdf|SDCC Compiler User Guide]] * [[http://sdcc.sourceforge.net/doc/sdccman.pdf|8.3 Retargetting for other Processors]] * [[http://sdcc.sourceforge.net/doc/sdccman.pdf|8.1.14 Peephole Optimizer]] * [[http://sdcc.sourceforge.net/doc/sdccman.pdf|9.1 The anatomy of the compiler]] * iCode Operands Description C Equivalent * iCode 名稱。基本會有 genXXX 對應。 * Operands。genXXX 會用到的 operand,由 IC_XXX 從 ic 取得。([[http://code.metager.de/source/xref/sdcc/sdcc/src/hc08/gen.c#genCall|genCall]]) /*-----------------------------------------------------------------*/ /* genCall - generates a call statement */ /*-----------------------------------------------------------------*/ static void genCall (iCode * ic) { sym_link *dtype; sym_link *etype; // bool restoreBank = FALSE; // bool swapBanks = FALSE; D (emitcode (";", "genCall")); /* if caller saves & we have not saved then */ if (!ic->regsSaved) saveRegisters (ic); dtype = operandType (IC_LEFT (ic)); etype = getSpec (dtype); /* if send set is not empty then assign */ if (_G.sendSet && !regalloc_dry_run) { if (IFFUNC_ISREENT (dtype)) /* need to reverse the send set */ { genSend (reverseSet (_G.sendSet)); } else { genSend (_G.sendSet); } _G.sendSet = NULL; } /* make the call */ if (IS_LITERAL (etype)) { emitcode ("jsr", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); regalloc_dry_run_cost += 3; } else { bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); emitcode (jump ? "jmp" : "jsr", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); regalloc_dry_run_cost += 3; } hc08_dirtyReg (hc08_reg_a, FALSE); hc08_dirtyReg (hc08_reg_hx, FALSE); /* if we need assign a result value */ if ((IS_ITEMP (IC_RESULT (ic)) && (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) { } } * 舊資料 * [[http://www.gtoal.com/compilers101/small_c/gbdk/sdcc/doc/sdccman.html/sdccman.html|SDCC Compiler User Guide]] * [[http://www.gtoal.com/compilers101/small_c/gbdk/sdcc/doc/sdccman.html/node48.html|6. Retargetting for other MCUs.]] * [[http://sdcc.sourceforge.net/mediawiki/index.php/Random_notes_about_sdcc_and_how_it_works|Random notes about sdcc and how it works]] ====== 其它 ====== * [[http://sourceforge.net/p/sdcc/mailman/message/31601719/|[sdcc-devel] Why not gcc port?]] * 討論何種架構適合實做在 SDCC 或是 GCC。 * SDCC 針對的是暫存器個數更少的架構,GCC 和 LLVM 所使用的 RA 不適合該種架構。 * [[http://pholia.tdi.informatik.uni-frankfurt.de/~philipp/publications/register-2.pdf|Optimal Register Allocation in Polynomial Time]] * SDCC 針對的是有多種定址空間的架構 (例: RAM 和 ROM)。 * SDCC 針對的是沒有通用棧的架構 (即本地參數會放置在全域內存,而非棧上)。 * [[http://sourceforge.net/p/sdcc/mailman/message/29701885/|[sdcc-devel] struct assignment]] * [[http://sourceforge.net/p/sdcc/mailman/message/32888441/|[sdcc-user] Question about genCall]] * [[http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-May/062053.html|[LLVMdev] Fw: Accepting iCode as input to SDCC]] * [[http://stackoverflow.com/questions/2419763/do-all-c-compilers-allow-functions-to-return-structures|Do all C compilers allow functions to return structures?]] ===== C 前處理器 ===== * support/cpp/sdcpp.c * [[http://gcc.gnu.org/onlinedocs/cppinternals.pdf|Cpplib Internals]] * 支援 multi-line comment * [[http://stackoverflow.com/questions/19105350/what-is-the-meaning-of-multi-line-comment-warnings-in-c|What is the meaning of multi-line comment warnings in C?]] * support/cpp/libcpp/lex.c else if (c == '/' && (CPP_OPTION (pfile, cplusplus_comments) || cpp_in_system_header (pfile))) { /* Warn about comments only if pedantically GNUC89, and not in system headers. */ if (CPP_OPTION (pfile, lang) == CLK_GNUC89 && CPP_PEDANTIC (pfile) && ! buffer->warned_cplusplus_comments) { cpp_error (pfile, CPP_DL_PEDWARN, "C++ style comments are not allowed in ISO C90"); cpp_error (pfile, CPP_DL_PEDWARN, "(this will be reported only once per input file)"); buffer->warned_cplusplus_comments = 1; } if (skip_line_comment (pfile) && CPP_OPTION (pfile, warn_comments)) cpp_warning (pfile, CPP_W_COMMENTS, "multi-line comment"); } ===== C 擴展語法 ===== * [[http://blog.sina.com.cn/s/blog_4ab84c99010009ov.html|关于sdcc sfr at (0x80) P0]] * [[http://jyhshin3.blogspot.tw/2009/03/sdcc-keil-c.html|SDCC 與 Keil-C 相異之處]] * [[http://ecee.colorado.edu/~mcclurel/Using_SDCC_3-03-2008.pdf|Using SDCC for Embedded System Design]] ===== C 函式庫 ===== * device/include & device/lib * device/lib 目錄為通用 C 函式庫的實現。部分目標平台會有匯編實現的版本,其餘目標平台基本採用 C 實現的 C 函式庫。 * device/lib 子目錄為目標平台特定的函式庫。 * 如果欲以匯編實現現有 C 函式庫,參考 rxxxx * 並用 peeph.def 替換 C 函式調用。 ===== Printf ===== * [[http://www.cnblogs.com/liu_xf/archive/2011/04/14/2015726.html|(整理)如何在单片机上使用printf函数(printf)(avr)(stm)(lpc)(单片机)]] * [[http://www.keil.com/forum/56766/|How printf function can sends a data using UART?]] * [[http://www.microchip.com/forums/m235683.aspx|redirect printf to UART]] * [[wp>Universal asynchronous receiver/transmitter|UART]] * [[https://www.freebsd.org/doc/en/articles/serial-uart/|Serial and UART Tutorial]] * [[http://www.tldp.org/HOWTO/Serial-HOWTO-18.html|What Are UARTs? How Do They Affect Performance?]] * [[wp>Baud|Baud Rate]] * [[http://sourceforge.net/p/sdcc/mailman/message/11634446/|[Sdcc-user] printf for 8051]] * [[http://jyhshin3.blogspot.tw/2009/04/sdcc-printf.html|SDCC printf 函數介紹]] * [[http://sdcc.sourceforge.net/doc/sdccman.pdf|3.18.2 Stdclib functions (puts, printf, strcat etc.)]] * printf_small (pic16/libc/stdio/printf_small.c) 是用 C 語言實現的精簡版 printf,其餘版本基本上是用匯編撰寫。 - vprintf.c static void put_char_to_stdout (char c, void* p) _REENTRANT { p; //make compiler happy putchar (c); } int vprintf (const char *format, va_list ap) { return _print_format (put_char_to_stdout, NULL, format, ap); } int printf (const char *format, ...) { va_list arg; int i; va_start (arg, format); i = _print_format (put_char_to_stdout, NULL, format, arg); va_end (arg); return i; } - printf_large.c int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap) { while( c=*format++ ) { if ( c=='%' ) { /* 處理 format string */ } else { // nothing special, just output the character OUTPUT_CHAR( c, p ); } } return charsOutputted; } * printf 和 vprintf 底層都是調用 _print_format。_print_format 調用 put_char_to_stdout,put_char_to_stdout 最後再調用底層相關的 putchar 輸出字串符至裝置。 * sprintf/vsprintf (device/lib/sprintf.c) 和 fprintf (device/lib/pic16/libc/stdio/fprintf.c) 都是類似的實現機制,只是傳給 _print_format 的第一個參數 put_char_to_xxx 有所不同。 * [[http://www.sparetimelabs.com/printfrevisited/printfrevisited.php|A tiny printf revisited]] * The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. Zero padding and field width (limited to 255) are also supported. * %[flags][width]specifier * specifier: 'd' 'u' 'c' 's' 'x' 'X'。 // 分析 fmt 字串,取得型別資訊,再透過 var_arg 從可變參數取出對應值。 void tfp_printf(char *fmt, ...) { va_list va; char ch; char* p; va_start(va,fmt); while ((ch=*(fmt++))) { if (ch!='%') { putchar(ch); } else { char lz=0; // lz: left zero char w=0; // width ch=*(fmt++); if (ch=='0') { ch=*(fmt++); lz=1; // Left-pads the number with zeroes (0) instead of spaces } if (ch>='0' && ch<='9') { w=0; // Minimum number of characters to be printed. while (ch>='0' && ch<='9') { w=(((w<<2)+w)<<1)+ch-'0'; ch=*fmt++; } } bf=buf; p=bf; zs=0; // 遇到特定 specifier,從 va_arg 抓取參數。 switch (ch) { case 0: goto abort; case 'u': case 'd' : num=va_arg(va, unsigned int); if (ch=='d' && (int)num<0) { num = -(int)num; out('-'); } divOut(10000); divOut(1000); divOut(100); divOut(10); outDgt(num); break; case 'x': case 'X' : uc= ch=='X'; // 判斷 16 進制是否需要 uppercase。 num=va_arg(va, unsigned int); divOut(0x1000); // 將 num 與 0x1000 (4096) 相除取商,並印出。 divOut(0x100); divOut(0x10); outDgt(num); // 印出最後一位。 break; case 'c' : out((char)(va_arg(va, int))); break; case 's' : p=va_arg(va, char*); break; case '%' : out('%'); default: break; } *bf=0; bf=p; // 計算 width 需要補上的長度。 while (*bf++ && w > 0) w--; // 視條件,以 0 或是空白補充 width。 while (w-- > 0) putchar(lz ? '0' : ' '); // 印出參數對應的字串。 while ((ch= *p++)) putchar(ch); } } abort:; va_end(va); } 前述兩個 printf 函式,皆要求使用者自行提供 putchar,其中實做 IO 輸出功能。 * [[http://stackoverflow.com/questions/1485805/whats-the-difference-between-the-printf-and-vprintf-function-families-and-when|what's the difference between the printf and vprintf function families, and when should I use one over the other?]] * 如果欲實現類似於 printf 的函式,透過 va_start 將可變參數轉換成 va_list,再調用 vprintf 達成格式化輸出。 ====== 外部連結 ====== * [[http://code.metager.de/source/xref/sdcc/sdcc/|Cross Reference for SDCC]] * [[http://jyhshin3.blogspot.tw/search/label/SDCC|邱小新の單晶片筆記 - SDCC]] * [[http://jyhshin3.blogspot.tw/search/label/keil|邱小新の單晶片筆記 - Keli]] * [[http://bellard.org/tcc/|Tiny C Compiler]] * 不用 Lex & Yacc * [[Compiler]]