* [[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]]