-
-
-
- 支援 TLCS-90,16 bit 處理器?
編譯
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
-
- IE8 開啟下載頁面會有問題,改用 Chrome。
- 將 gawk-4.1.0-bin.zip 解壓縮至 C:\Program Files (x86)\GnuWin32
-
- 此版本 awk 有問題,導致編譯 sdcpp 時發生錯誤 (Build Errors on Visual Studio 10)。改用 gnu-on-windows 或是 GNU utilities for Win32
- 錯誤原因是 awk 產生的 options.h 和 options.c 有問題,導致 sdcpp 編譯失敗。
- Download → Complete package, except sources
- 預設安裝到 C:\Program Files (x86)\GnuWin32
-
-
- 將 win_flex_bison-latest.zip 解壓縮到 C:\Program Files (x86)\GnuWin32
- 點選 all Project,依照 Visual Studio custom build rules 設置 Custom Build Rule。
- 將 C:\Program Files (x86)\GnuWin32\bin 和 C:\Program Files (x86)\GnuWin32 加至系統蒐尋路徑,登出並登入已使其生效。
- 安裝 Boost
- 部分 Project 依賴 Boost,需要修正頭檔路徑。
- sdcc, hc08, stm8, z80
- 目前可以單獨編譯 sdcc 和 sdcpp。
- 參考資料
Cygwin
- 透過 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
流程簡介
yyparse (SDCCmain.c) → AST (SDCCast.c) → iCode (SDCCicode.c) → BBlock (SDCCopt.c) → iCode (SDCCicode.c) → CodeGen
- yyparse (SDCCmain.c)
if (fullSrcFileName || options.c1mode) { preProcess (envp); initSymt (); initiCode (); initCSupport (); initBuiltIns (); initPeepHole (); if (options.verbose) printf ("sdcc: Generating code...\n"); yyparse ();
- 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 (SDCC.y): 返回型別。
- function_declarator (SDCC.y): 函數名和參數。
- function_body (SDCC.y): 函數體。
- addDecl (SDCCsymt.c)
- createFunctionDecl (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 (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 (SDCCicode.c) 將樹狀結構的 AST 轉成串列型式的 iCode。
/*-----------------------------------------------------------------*/ /* ast2iCode - creates an icodeList from an ast */ /*-----------------------------------------------------------------*/ operand * ast2iCode (ast * tree, int lvl) {
- 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 (SDCCBBlock.c)
- iCode2eBBlock (SDCCBBlock.c)
- 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 (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 (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 (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 (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 (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() (SDCCmain.c)。產生段及其中的變數,並將所有匯編合併 (glue) 成一份。
if (port->general.do_glue != NULL) (*port->general.do_glue) ();
- 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 (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 (SDCCglue.c)
/*-----------------------------------------------------------------*/ /* emitRegularMap - emit code for maps with no special cases */ /*-----------------------------------------------------------------*/ static void emitRegularMap (memmap * map, bool addPublics, bool arFlag)
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 // 根節點的型別鏈其末端節點
- createFunction (SDCCast.c)
body = resolveSymbols (body); /* resolve the symbols */ body = decorateType (body, RESULT_TYPE_NONE); /* propagateType & do semantic checks */
- 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 (SDCCast.c) 計算 (推導) AST 根節點的型別,並做語意分析 (semantic analysis)。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 (SDCCval.c) 由 struct sym_link 轉成 struct value。
- 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 (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 (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 輸出解說
範例一
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 的種類定義在 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
-
- 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
-
- 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
- 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 (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 (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 (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 信息 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
- 視 case 情況,選擇 compare and branch 或是 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}
範例二
__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 (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 (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 (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 (hc08/gen.c) 釋放 asmop。
- pushRegIfSurv (hc08/gen.c)。如果暫存器當前的值還需要被使用,則入棧加以保存。
- pullOrFreeReg (hc08/gen.c)。出棧回復暫存器值,否則將該暫存器釋放,以供之後配置使用。
- AOP (IC_LEFT (ic)): 取出 ic 中的 operand,再取出 operand 中的 asmop。
- regalloc_dry_run_cost: 累加指令所佔的 byte 數。本為 debug 輸出,現在為新版 register allocator 所調用。
-
- 暫存器都有此種 global struct。
-
- 將 asm operand 載入暫存器。
- 這裡又將情況依據 asm operand 的類別和暫存器類別,而有不同代碼生成。
-
- 將暫存器存放於 asm operand (暫存器或是棧)。
-
- 資料搬移有時候可以直接在暫存器之間移動。
-
- 調整棧空間。_G 存放相關信息。
-
- 產生函式調用代碼。
-
- 準備參數供函式調用。
-
- 被調用方 (callee) 的 prologue。
-
- 接受調用方傳送的參數。
-
- 被調用方 (callee) 的 epilogue。
-
- 滿足 C 語言要求的整型提升之要求。
-
- 函式指針調用。
-
- 如果 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 (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;
- dwarf2DebugFile (SDCCdwarf2.c ) 註冊 DWARF 除錯訊息相關函式。
DEBUGFILE dwarf2DebugFile = { &dwOpenFile, &dwCloseFile, &dwWriteModule, &dwWriteFunction, &dwWriteEndFunction, &dwWriteLabel, &dwWriteScope, &dwWriteSymbol, &dwWriteType, &dwWriteCLine, &dwWriteALine, &dwWriteFrameAddress };
- 預設為 CDB 格式 (SDCCdebug.c)
DEBUGFILE *debugFile = &cdbDebugFile;
- _hc08_parseOptions 根據命令行選項選擇 CBD 或是 DWARF 格式。(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; }
- 後端於代碼生成時,會產生相應的除錯資訊。(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 資料結構 (port.h),供 CDB 或是 DWARF 後端調用,其中 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 (hc08/main.c)
{ hc08_emitDebuggerSymbol, { hc08_dwarfRegNum, NULL, NULL, 4, /* addressSize */ 14, /* regNumRet */ 15, /* regNumSP */ -1, /* regNumBP */ 1, /* offsetSP */ }, },
- hc08_dwarfRegNum (hc08/main.c) 被 DWARF 後端函式 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 (stm8/main.c) 不支援 DWARF 格式,故只需提供 emitDebuggerSymbol。
{ stm8_emitDebuggerSymbol },
- stm8_emitDebuggerSymbol (stm8/gen.c) 被 CDB 後端函式 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
-
- src/SDCC.lex
- src/SDCC.y (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 ... }
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)
- checkTypeSanity
- 用於排除像是 long char 這一類非法型別。
資料結構
-
- 編譯器內部內存分配。
-
-
- 部分 storage class 是由目標平台各自定義 (3.5 SDCC Language Extensions)。
- S_DATA 和 S_XDATA 可能分別指 PAGE0 和 NSEC (以 6502 為例)。
-
- 表示型別或儲存類型。
-
- 各個命名空間皆有對應的資料結構 memmap,供其寫入變數。
-
- 定義函式或變數 (包函指針)。
-
- sym_link 包含 specifier 或 declarator,或是函式相關屬性。
-
- 符號,其中包含 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)
- 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. }
-
- 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 */
-
- 定義 struct 或 union。
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 (SDCCast.c)
- patch condition 語句的 true 和 false 的 goto label。
- PORT (port.h)
- 各個平台有各自的 port 資料結構。
警告和錯誤訊息
-
- 程式的邏輯有機會讓 auto 變數沒有初始值。
if (Flag==0x00) { X_axis = 0; Y_axis = 0; } if (X_axis) do_something();
移植簡介
- 選擇與目標平台 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 & 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
術語
-
- address space (定址空間)
- pointer (指針)
- stack (棧)
- register allocation
- accumlator
- iCode (SDCC IR)
- SDCCicode.h
- mcs51 (8051) 是最一開始的目標平台
硬件
-
- 為內存中一特定區塊,近似於 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.
-
- 4 bits
C 程式語言
-
- 代表 specifier 或是 declarator,最後用來修飾 symbol。
- DCL_XXX 用來存取或驗證 sym_link 的 declarator 屬性。
- SPEC_XXX 用來存取或驗證 sym_link 的 specifier 屬性。
- Overview of Declarators
int foo(); | | | -----> declarator v specifier
-
- 代表變數、函式名或是 label 一類的概念。
- symbol 內會帶用來修飾該 symbol 的 sym_link。
- IS_OP_XXX 用來存取或驗證 symbol 屬性。
SDCC 技術文件
-
-
- iCode Operands Description C Equivalent
- iCode 名稱。基本會有 genXXX 對應。
- Operands。genXXX 會用到的 operand,由 IC_XXX 從 ic 取得。(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))) { } }
- 舊資料
其它
-
- 討論何種架構適合實做在 SDCC 或是 GCC。
- SDCC 針對的是暫存器個數更少的架構,GCC 和 LLVM 所使用的 RA 不適合該種架構。
- SDCC 針對的是有多種定址空間的架構 (例: RAM 和 ROM)。
- SDCC 針對的是沒有通用棧的架構 (即本地參數會放置在全域內存,而非棧上)。
C 前處理器
- support/cpp/sdcpp.c
- 支援 multi-line comment
- 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 擴展語法
C 函式庫
- device/include & device/lib
- device/lib 目錄為通用 C 函式庫的實現。部分目標平台會有匯編實現的版本,其餘目標平台基本採用 C 實現的 C 函式庫。
- device/lib 子目錄為目標平台特定的函式庫。
- 如果欲以匯編實現現有 C 函式庫,參考 rxxxx
- 並用 peeph.def 替換 C 函式調用。
Printf
-
-
- 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 有所不同。
-
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 輸出功能。
-
- 如果欲實現類似於 printf 的函式,透過 va_start 將可變參數轉換成 va_list,再調用 vprintf 達成格式化輸出。