lcc原始碼解析只decl.c
本文解析lcc中最複雜的模組之一decl.c,作為語法分析的一部分,它處理各個宣告。
#include "c.h" static char rcsid[] = "$Id: decl.c,v 1.1 2002/08/28 23:12:42 drh Exp $"; #define add(x,n) (x > inttype->u.sym->u.limits.max.i-(n) ? (overflow=1,x) : x+(n)) #define chkoverflow(x,n) ((void)add(x,n)) #define bits2bytes(n) (((n) + 7)/8) static int regcount; static List autos, registers; Symbol cfunc; /* current function */ Symbol retv; /* return value location for structs */ static void checkref(Symbol, void *); static Symbol dclglobal(int, char *, Type, Coordinate *); static Symbol dcllocal(int, char *, Type, Coordinate *); static Symbol dclparam(int, char *, Type, Coordinate *); static Type dclr(Type, char **, Symbol **, int); static Type dclr1(char **, Symbol **, int); static void decl(Symbol (*)(int, char *, Type, Coordinate *)); extern void doconst(Symbol, void *); static void doglobal(Symbol, void *); static void doextern(Symbol, void *); static void exitparams(Symbol []); static void fields(Type); static void funcdefn(int, char *, Type, Symbol [], Coordinate); static void initglobal(Symbol, int); static void oldparam(Symbol, void *); static Symbol *parameters(Type); static Type specifier(int *); static Type structdcl(int); static Type tnode(int, Type); /* *迴圈地分析所有源程式,把所有宣告和語義都分析出來,並且在遇到函式的定義時,就會呼叫後端生成彙編程式碼 */ void program(void) { int n; //作用域為全域性 level = GLOBAL; //分析源程式到檔案尾 for (n = 0; t != EOI; n++)//它的終止條件是分析源程式檔案到結束 //判斷開始的詞素是否合法 if (kind[t] == CHAR || kind[t] == STATIC || t == ID || t == '*' || t == '(') { //宣告開始 decl(dclglobal); //刪除分配記憶體空間 deallocate(STMT); if (!(glevel >= 3 || xref)) deallocate(FUNC); //取得記號t為分號,那這種情況是出錯的,發出警告給軟體開發人員,說明寫了一行空行程式碼,接著獲取下一個記號 } else if (t == ';') { warning("empty declaration\n"); t = gettok(); //處理錯誤的宣告,因為不能處理這種記號開始的宣告 } else { error("unrecognized declaration\n"); t = gettok(); } //當整個檔案沒有寫一行程式碼的情況給出警告 if (n == 0) warning("empty input file\n"); } //輸入了獲取宣告的儲存型別引數sclass static Type specifier(int *sclass) { //一個宣告變數有可能出現的說明符就有以下幾種: //儲存型別、常量說明、符號說明、型別大小、型別、是否優化 int cls, cons, sign, size, type, vol; //定義返回型別儲存變數 Type ty = NULL; //把所有型別說明初始化為0值,表示沒有出現這種說明 cls = vol = cons = sign = size = type = 0; //把儲存型別設定為自動型別。由於在C語言裡,所有變數宣告如果沒有明確地指明儲存型別時,預設就是自動型別AUTO if (sclass == NULL) cls = AUTO; for (;;) { int *p, tt = t; switch (t) { case AUTO: //當全域性變數宣告而又宣告為暫存器型別,就出會提示出錯 case REGISTER: if (level <= GLOBAL && cls == 0) error("invalid use of `%k'\n", t); //儲存當前識別出來的型別說明並獲取下一個詞素 p = &cls; t = gettok(); break; case STATIC: case EXTERN: case TYPEDEF: p = &cls; t = gettok(); break; case CONST: p = &cons; t = gettok(); break; //識別優化限制說明,當指定這個屬性時,表示這個變數不能優化掉的 case VOLATILE: p = &vol; t = gettok(); break; case SIGNED: case UNSIGNED: p = &sign; t = gettok(); break; case LONG: if (size == LONG) { size = 0; tt = LONG+LONG; } p = &size; t = gettok(); break; case SHORT: p = &size; t = gettok(); break; case VOID: case CHAR: case INT: case FLOAT: case DOUBLE: p = &type; ty = tsym->type; t = gettok(); break; case ENUM: p = &type; ty = enumdcl(); break;//列舉的宣告處理 case STRUCT: case UNION: p = &type; ty = structdcl(t); break;//共用體和結構體放在一起處理 //當把所有的說明符分析完成後,最後肯定是剩下一個ID,如果不是就有可能出錯 //處理ID是自己定義的型別處理,比如用typedef定義的ID型別,就需要在那裡識別出型別的屬性 case ID: if (istypename(t, tsym) && type == 0 && sign == 0 && size == 0) { use(tsym, src); ty = tsym->type; if (isqual(ty) && ty->size != ty->type->size) { ty = unqual(ty); if (isconst(tsym->type)) ty = qual(CONST, ty); if (isvolatile(tsym->type)) ty = qual(VOLATILE, ty); tsym->type = ty; } p = &type; t = gettok(); } else//如果這個ID是變數 p = NULL; break; default: p = NULL; } if (p == NULL) break; if (*p) error("invalid use of `%k'\n", tt); //意味著整個宣告已經分析完成,接著就是把所有分析出來的說明符組成屬性結構,儲存了到相應的符號表裡 *p = tt; } //儲存儲存型別返回給呼叫函式 if (sclass) *sclass = cls; //設定型別為預設值 if (type == 0) { type = INT; ty = inttype; } //判斷宣告組合是否合法,如果不合法的組合,就需要提示出錯 if (size == SHORT && type != INT || size == LONG+LONG && type != INT || size == LONG && type != INT && type != DOUBLE || sign && type != INT && type != CHAR) error("invalid type specification\n"); //根據符號和型別來判斷宣告的型別,由於型別的大小不同,符號位不同,而組成不同的型別。這些型別都是C編譯器預先定義好的 if (type == CHAR && sign) ty = sign == UNSIGNED ? unsignedchar : signedchar; else if (size == SHORT) ty = sign == UNSIGNED ? unsignedshort : shorttype; else if (size == LONG && type == DOUBLE) ty = longdouble; else if (size == LONG+LONG) { ty = sign == UNSIGNED ? unsignedlonglong : longlong; if (Aflag >= 1) warning("`%t' is a non-ANSI type\n", ty); } else if (size == LONG) ty = sign == UNSIGNED ? unsignedlong : longtype; else if (sign == UNSIGNED && type == INT) ty = unsignedtype; //新增常量屬性 if (cons == CONST) ty = qual(CONST, ty);//構造屬性限定型別 //新增不可優化屬性 if (vol == VOLATILE) ty = qual(VOLATILE, ty);//構造屬性限定型別 //把宣告的型別返回給呼叫函式 return ty; } static void decl(Symbol (*dcl)(int, char *, Type, Coordinate *)) { //定義儲存儲存型別的區域性變數 int sclass; Type ty, ty1; //定義語法出錯時停止的條件 static char stop[] = { CHAR, STATIC, ID, 0 }; //呼叫函式specifier進行語法的宣告處理 ty = specifier(&sclass); //在宣告分析之後,也就是識別了說明符之後,記號開始一定是ID、'*'、'('、'['等幾種型別了。如果不是這樣的記號,說明語法出錯 if (t == ID || t == '*' || t == '(' || t == '[') { char *id; Coordinate pos; //file info, pan , tilt id = NULL; //儲存源程式分析的位置 pos = src; //根據當前作用域來分別處理,區域性宣告的作用域是沒有引數處理的 if (level == GLOBAL) { Symbol *params = NULL; //調宣告處理函式dclr來分析全域性變數和函式的宣告 ty1 = dclr(ty, &id, ¶ms, 0); //判斷是否函式的宣告,如果是函式的宣告,就再進一步判斷是否函式定義的複合語句 if (params && id && isfunc(ty1) && (t == '{' || istypename(t, tsym) || (kind[t] == STATIC && t != TYPEDEF))) { if (sclass == TYPEDEF) { error("invalid use of `typedef'\n"); sclass = EXTERN;//錯誤處理 } if (ty1->u.f.oldstyle)//處理舊時風格的函式引數方式 exitscope(); /*處理函式定義,並生成彙編程式碼 *sclass 是函式返回儲存型別 *id 是函式宣告的名稱 *ty1是返回型別 *params是函式的引數列表 *pos是函式定義的位置 */ funcdefn(sclass, id, ty1, params, pos); return; //處理引數列表,退出引數的作用域 } else if (params) exitparams(params); } else//區域性函式和變數 ty1 = dclr(ty, &id, NULL, 0); //用來處理宣告的變數是逗號表示式時並列處理 for (;;) { if (Aflag >= 1 && !hasproto(ty1)) warning("missing prototype\n"); //處理宣告沒有ID的出錯情況 if (id == NULL) error("missing identifier\n"); //處理typedef定義的型別宣告 else if (sclass == TYPEDEF) { //從符號表identifiers裡查詢這個ID是否已經聲明瞭, //如果有宣告過並且作用域一樣,就表示重複聲明瞭同一個ID Symbol p = lookup(id, identifiers); if (p && p->scope == level) error("redeclaration of `%s'\n", id); //儲存這個ID到符號表identifiers p = install(id, &identifiers, level, level < LOCAL ? PERM : FUNC); p->type = ty1;//儲存宣告的型別 p->sclass = TYPEDEF;//儲存儲存的型別 p->src = pos;//儲存源程式位置 } else //呼叫全域性函式dclglobal,或者區域性函式dcllocal,或者引數函式dclparam來處理變數ID (void)(*dcl)(sclass, id, ty1, &pos); if (t != ',') break; t = gettok(); id = NULL; pos = src; ty1 = dclr(ty, &id, NULL, 0); } } //處理出錯的情況 else if (ty == NULL || !(isenum(ty) || isstruct(ty) && (*unqual(ty)->u.sym->name < '1' || *unqual(ty)->u.sym->name > '9'))) error("empty declaration\n"); test(';', stop);//測試當前的記號是否語句結束符,如果不是,就會跳過錯誤直到下一句語句再進行分析處理 } /* ------------------>主要用來分析全域性函式 int sclass -->是這個函式名稱儲存型別。 char *id -->是函式的名稱。 Type ty -->是函式的型別,包括返回型別。 Coordinate *pos -->是函式定義的源程式中位置 */ static Symbol dclglobal(int sclass, char *id, Type ty, Coordinate *pos) { Symbol p; if (sclass == 0) sclass = AUTO;//預設儲存型別 else if (sclass != EXTERN && sclass != STATIC) { error("invalid storage class `%k' for `%t %s'\n", sclass, ty, id); sclass = AUTO; } //在id符號表中查詢這個全域性函式或者變數 p = lookup(id, identifiers); if (p && p->scope == GLOBAL) { if (p->sclass != TYPEDEF && eqtype(ty, p->type, 1)) ty = compose(ty, p->type); else error("redeclaration of `%s' previously declared at %w\n", p->name, &p->src); if (!isfunc(ty) && p->defined && t == '=') error("redefinition of `%s' previously defined at %w\n", p->name, &p->src); if (p->sclass == EXTERN && sclass == STATIC || p->sclass == STATIC && sclass == AUTO || p->sclass == AUTO && sclass == STATIC) warning("inconsistent linkage for `%s' previously declared at %w\n", p->name, &p->src); } if (p == NULL || p->scope != GLOBAL) { Symbol q = lookup(id, externals); if (q) { if (sclass == STATIC || !eqtype(ty, q->type, 1)) warning("declaration of `%s' does not match previous declaration at %w\n", id, &q->src); p = relocate(id, externals, globals); p->sclass = sclass; } else { p = install(id, &globals, GLOBAL, PERM); p->sclass = sclass; (*IR->defsymbol)(p); } //統計全域性函式定義的個數 if (p->sclass != STATIC) { static int nglobals; nglobals++; if (Aflag >= 2 && nglobals == 512) warning("more than 511 external identifiers\n"); } } else if (p->sclass == EXTERN) p->sclass = sclass; //處理函式初始化 p->type = ty; p->src = *pos; if (t == '=' && isfunc(p->type)) { error("illegal initialization for `%s'\n", p->name); t = gettok(); initializer(p->type, 0); } else if (t == '=') { initglobal(p, 0); if (glevel > 0 && IR->stabsym) { (*IR->stabsym)(p); swtoseg(p->u.seg); } } else if (p->sclass == STATIC && !isfunc(p->type) && p->type->size == 0) error("undefined size for `%t %s'\n", p->type, p->name); return p;//返回這個函式屬性符號 } static void initglobal(Symbol p, int flag) { Type ty; if (t == '=' || flag) { if (p->sclass == STATIC) { for (ty = p->type; isarray(ty); ty = ty->type) ; defglobal(p, isconst(ty) ? LIT : DATA); } else defglobal(p, DATA); if (t == '=') t = gettok(); ty = initializer(p->type, 0); if (isarray(p->type) && p->type->size == 0) p->type = ty; if (p->sclass == EXTERN) p->sclass = AUTO; } } void defglobal(Symbol p, int seg) { p->u.seg = seg; swtoseg(p->u.seg); if (p->sclass != STATIC) (*IR->export)(p); (*IR->global)(p); p->defined = 1; } /* */ static Type dclr(Type basety, char **id, Symbol **params, int abstract) { Type ty = dclr1(id, params, abstract); //用for迴圈把所有型別新增到一個連結串列basety裡 for ( ; ty; ty = ty->type) switch (ty->op) { case POINTER: basety = ptr(basety);//呼叫ptr來新增型別到連結串列 break; case FUNCTION: basety = func(basety, ty->u.f.proto, ty->u.f.oldstyle); break; case ARRAY: basety = array(basety, ty->size, 0); break; case CONST: case VOLATILE: basety = qual(ty->op, basety); break; default: assert(0); } if (Aflag >= 2 && basety->size > 32767) warning("more than 32767 bytes in `%t'\n", basety); return basety;//返回這個指標連結串列的型別,這樣就可以給後面判斷語法和語義了 } //構造一個型別 static Type tnode(int op, Type type) { Type ty; NEW0(ty, STMT); ty->op = op; ty->type = type; return ty; } /* */ static Type dclr1(char **id, Symbol **params, int abstract) { Type ty = NULL; switch (t) { case ID: if (id) *id = token; //global variable in lex.c, current token else error("extraneous identifier `%s'\n", token); t = gettok(); break; case '*': t = gettok(); //遞迴解析表示式,知道得出結論 if (t == CONST || t == VOLATILE) { Type ty1; ty1 = ty = tnode(t, NULL);//構造一個指標型別 while ((t = gettok()) == CONST || t == VOLATILE)//處理前置限制 ty1 = tnode(t, ty1); ty->type = dclr1(id, params, abstract); ty = ty1; } else ty = dclr1(id, params, abstract); ty = tnode(POINTER, ty); break; case '(': t = gettok(); if (abstract && (t == REGISTER || istypename(t, tsym) || t == ')')) { Symbol *args; ty = tnode(FUNCTION, ty); enterscope(); if (level > PARAM) enterscope(); args = parameters(ty);//處理函式引數 exitparams(args); } else { ty = dclr1(id, params, abstract); expect(')'); if (abstract && ty == NULL && (id == NULL || *id == NULL)) return tnode(FUNCTION, NULL); } break; case '[': break; default: return ty; } while (t == '(' || t == '[') switch (t) { //func declaration case '(': //specifier函式裡就已經可以把型別和ID識別出來,並判斷語法的合法性,這裡處理括號和引數列表 t = gettok(); { Symbol *args; ty = tnode(FUNCTION, ty); enterscope(); if (level > PARAM)/*判斷是否在函式聲明裡再宣告一個函式,如是就需要再增加它的作用域*/ enterscope(); //分析引數列表 args = parameters(ty); if (params && *params == NULL)/*儲存分析後的引數列表返回,以便後面建構函式宣告的型別*/ *params = args; else exitparams(args); } break; case '[': t = gettok(); { int n = 0; if (kind[t] == ID) { n = intexpr(']', 1); if (n <= 0) { error("`%d' is an illegal array size\n", n); n = 1; } } else expect(']'); ty = tnode(ARRAY, ty); ty->size = n; } break; default: assert(0); } return ty; } //分析函式引數列表 static Symbol *parameters(Type fty) { List list = NULL; Symbol *params; //判斷引數型別是否合法,主要是儲存型別或者型別宣告 if (kind[t] == STATIC || istypename(t, tsym)) { int n = 0; Type ty1 = NULL; //一個一個地分析逗號分隔的引數 for (;;) { Type ty; int sclass = 0; char *id = NULL; if (ty1 && t == ELLIPSIS) //處理引數裡可變引數(…) { static struct symbol sentinel; if (sentinel.type == NULL) { sentinel.type = voidtype; sentinel.defined = 1; } if (ty1 == voidtype) error("illegal formal parameter types\n"); list = append(&sentinel, list); t = gettok(); break; /*C編譯器為了處理可變引數需要從右向左地壓引數入棧,以便計算有多少個引數。 ty1的判斷是表示在可變引數之前,一定需要有一個引數,如果只有一個可變引數是非法的。 處理可變引數很簡單,就是把它型別sentinel新增到引數列表list*/ } //判斷引數是否有宣告型別,如果沒有就出錯 if (!istypename(t, tsym) && t != REGISTER) error("missing parameter type\n"); n++;//增加引數的計算,也可以說引數的名稱 //遞迴呼叫型別宣告函式dclr來分析這個引數定義是否合法,而在函式dclr呼叫之前,就需要先遞迴呼叫說明符處理函式specifier來處理 ty = dclr(specifier(&sclass), &id, NULL, 1); if ( ty == voidtype && (ty1 || id) || ty1 == voidtype)//處理宣告為void型別語法錯誤處理 error("illegal formal parameter types\n"); if (id == NULL)//處理宣告引數變數是空時處理,採用自定義的資料作為名稱 id = stringd(n); /*判斷宣告的型別不為空型別的話,就說明已經定義了一個引數變數, 需要檢查這個引數變數是否已經在前面的區域性變數或者全域性變數裡聲明瞭嗎? 要解決定這個疑問就需要呼叫引數變數宣告函式dclparam來處理*/ if (ty != voidtype) list = append(dclparam(sclass, id, ty, &src), list); if (Aflag >= 1 && !hasproto(ty)) warning("missing prototype\n"); if (ty1 == NULL) ty1 = ty; //判斷是否還有引數變數宣告,如果沒有就跳出for迴圈,處理引數完畢 if (t != ',') break; t = gettok(); } fty->u.f.proto = newarray(length(list) + 1, sizeof (Type *), PERM);//建立引數型別原型列表 params = ltov(&list, FUNC); for (n = 0; params[n]; n++)//儲存所有引數變數的型別 fty->u.f.proto[n] = params[n]->type; fty->u.f.proto[n] = NULL; fty->u.f.oldstyle = 0;//指定這個函式宣告是新型別的函式宣告 } else { if (t == ID) for (;;) { Symbol p; if (t != ID) { error("expecting an identifier\n"); break; } p = dclparam(0, token, inttype, &src); p->defined = 0; list = append(p, list); t = gettok(); if (t != ',') break; t = gettok(); } params = ltov(&list, FUNC); fty->u.f.proto = NULL; fty->u.f.oldstyle = 1; } if (t != ')') {//判斷是否函式宣告結束 static char stop[] = { CHAR, STATIC, IF, ')', 0 }; expect(')'); skipto('{', stop); } if (t == ')') t = gettok(); return params; } static void exitparams(Symbol params[]) { assert(params); if (params[0] && !params[0]->defined) error("extraneous old-style parameter list\n"); if (level > PARAM) exitscope(); exitscope(); } static Symbol dclparam(int sclass, char *id, Type ty, Coordinate *pos) { Symbol p; if (isfunc(ty))//判斷這個引數變數是否宣告為函式型別,如果是就需要建立新型別 ty = ptr(ty); else if (isarray(ty))//判斷這個引數變數是否宣告為資料型別 ty = atop(ty); //***************************************************************// //都是判斷儲存型別。如果沒有宣告儲存型別,預設為AUTO型別。 //其它幾個識別為暫存器型別REGISTER、不可刪除屬性、結構型別 if (sclass == 0) sclass = AUTO; else if (sclass != REGISTER) { error("invalid storage class `%k' for `%t%s\n", sclass, ty, stringf(id ? " %s'" : "' parameter", id)); sclass = AUTO; } else if (isvolatile(ty) || isstruct(ty)) { warning("register declaration ignored for `%t%s\n", ty, stringf(id ? " %s'" : "' parameter", id)); sclass = AUTO; } //***************************************************************// //查詢這個引數變數是否已經在其它地方宣告過,如果宣告過就從lookup返回給p, //如果作用域一樣的宣告肯定就是重複宣告,這是出錯的 p = lookup(id, identifiers); if (p && p->scope == level) error("duplicate declaration for `%s' previously declared at %w\n", id, &p->src); else//如果沒有這個引數變數是合法的,就把它儲存到宣告表格identifiers裡 p = install(id, &identifiers, level, FUNC); p->sclass = sclass; p->src = *pos; p->type = ty; p->defined = 1; if (t == '=') {//標準C裡是不允許引數變數初始化 error("illegal initialization for parameter `%s'\n", id); t = gettok(); (void)expr1(0); } return p; } static Type structdcl(int op) { char *tag; Type ty; Symbol p; Coordinate pos; t = gettok(); pos = src; if (t == ID) { tag = token; t = gettok(); } else tag = "";//沒有tag,struct後的結構名 if (t == '{') { static char stop[] = { IF, ',', 0 }; ty = newstruct(op, tag);//建立結構的型別ty ty->u.sym->src = pos; ty->u.sym->defined = 1; t = gettok(); if (istypename(t, tsym))//判斷語句是否型別開始的記號,如果不是就是出錯的定義 fields(ty);//如果是型別,就需要呼叫函式fields(ty)來處理所有的欄位定義 else error("invalid %k field declarations\n", op); test('}', stop); } else if (*tag && (p = lookup(tag, types)) != NULL && p->type->op == op) { ty = p->type; if (t == ';' && p->scope < level) ty = newstruct(op, tag); } else { if (*tag == 0) error("missing %k tag\n", op); ty = newstruct(op, tag); } if (*tag && xref) use(ty->u.sym, pos); return ty; } static void fields(Type ty) { { int n = 0; while (istypename(t, tsym)) //判斷是否型別定義,如果是的話就不斷地進行欄位列表處理 { static char stop[] = { IF, CHAR, '}', 0 }; Type ty1 = specifier(NULL);//這裡也是遞迴呼叫的。主要用來分析一行程式碼的宣告處理 for (;;) { Field p; char *id = NULL; Type fty = dclr(ty1, &id, NULL, 0);//進行一個宣告的處理,也是呼叫函式dclr來遞迴處理 p = newfield(id, ty, fty); if (Aflag >= 1 && !hasproto(p->type)) warning("missing prototype\n"); if (t == ':') { if (unqual(p->type) != inttype && unqual(p->type) != unsignedtype) { error("`%t' is an illegal bit-field type\n", p->type); p->type = inttype; } t = gettok(); p->bitsize = intexpr(0, 0); if (p->bitsize > 8*inttype->size || p->bitsize < 0) { error("`%d' is an illegal bit-field size\n", p->bitsize); p->bitsize = 8*inttype->size; } else if (p->bitsize == 0 && id) { warning("extraneous 0-width bit field `%t %s' ignored\n", p->type, id); p->name = stringd(genlabel(1)); } p->lsb = 1; } else { if (id == NULL) error("field name missing\n"); else if (isfunc(p->type)) error("`%t' is an illegal field type\n", p->type); else if (p->type->size == 0) error("undefined size for field `%t %s'\n", p->type, id); } if (isconst(p->type)) ty->u.sym->u.s.cfields = 1; if (isvolatile(p->type)) ty->u.sym->u.s.vfields = 1; n++; if (Aflag >= 2 && n == 128) warning("more than 127 fields in `%t'\n", ty); if (t != ',') break; t = gettok(); } test(';', stop); } } { int bits = 0, off = 0, overflow = 0; Field p, *q = &ty->u.sym->u.s.flist; ty->align = IR->structmetric.align; for (p = *q; p; p = p->link) { int a = p->type->align ? p->type->align : 1; if (p->lsb) a = unsignedtype->align; if (ty->op == UNION) off = bits = 0; else if (p->bitsize == 0 || bits == 0 || bits - 1 + p->bitsize > 8*unsignedtype->size) { off = add(off, bits2bytes(bits-1)); bits = 0; chkoverflow(off, a - 1); off = roundup(off, a); } if (a > ty->align) ty->align = a; p->offset = off; if (p->lsb) { if (bits == 0) bits = 1; if (IR->little_endian) p->lsb = bits; else p->lsb = 8*unsignedtype->size - bits + 1 - p->bitsize + 1; bits += p->bitsize; } else off = add(off, p->type->size); if (off + bits2bytes(bits-1) > ty->size) ty->size = off + bits2bytes(bits-1); if (p->name == NULL || !('1' <= *p->name && *p->name <= '9')) { *q = p; q = &p->link; } } *q = NULL; chkoverflow(ty->size, ty->align - 1); ty->size = roundup(ty->size, ty->align); if (overflow) { error("size of `%t' exceeds %d bytes\n", ty, inttype->u.sym->u.limits.max.i); ty->size = inttype->u.sym->u.limits.max.i&(~(ty->align - 1)); } } } /*-------------------> 處理函式定義,並生成彙編程式碼 *int sclass --> 是函式返回儲存型別 *char *id --> 是函式宣告的名稱 *Type ty --> 是返回型別 *Symbol params[] --> 是函式的引數列表 *Coordinate pt --> 是函式定義的位置 */ static void funcdefn(int sclass, char *id, Type ty, Symbol params[], Coordinate pt) { int i, n; Symbol *callee, *caller, p; Type rty = freturn(ty);//處理函式返回的型別 if (isstruct(rty) && rty->size == 0)//什麼情況下會出現返回值型別為結構,但是size為0 ??? error("illegal use of incomplete type `%t'\n", rty); for (n = 0; params[n]; n++) ; //然後設定引數列表結束位置 if (n > 0 && params[n-1]->name == NULL) params[--n] = NULL; //引數過多告警 if (Aflag >= 2 && n > 31) warning("more than 31 parameters in function `%s'\n", id); //下面程式碼是生成舊風格和新風格的引數callee和caller, //第一個是傳入函式的引數列表,第二個是返回給呼叫函式的引數列表 if (ty->u.f.oldstyle) { if (Aflag >= 1) warning("old-style function definition for `%s'\n", id); caller = params; callee = newarray(n + 1, sizeof *callee, FUNC); memcpy(callee, caller, (n+1)*sizeof *callee); enterscope(); assert(level == PARAM); while (kind[t] == STATIC || istypename(t, tsym)) decl(dclparam); foreach(identifiers, PARAM, oldparam, callee); for (i = 0; (p = callee[i]) != NULL; i++) { if (!p->defined) callee[i] = dclparam(0, p->name, inttype, &p->src); *caller[i] = *p; caller[i]->sclass = AUTO; caller[i]->type = promote(p->type); } p = lookup(id, identifiers); if (p && p->scope == GLOBAL && isfunc(p->type) && p->type->u.f.proto) { Type *proto = p->type->u.f.proto; for (i = 0; caller[i] && proto[i]; i++) { Type ty = unqual(proto[i]); if (eqtype(isenum(ty) ? ty->type : ty, unqual(caller[i]->type), 1) == 0) break; else if (isenum(ty) && !isenum(unqual(caller[i]->type))) warning("compatibility of `%t' and `%t' is compiler dependent\n", proto[i], caller[i]->type); } if (proto[i] || caller[i]) error("conflicting argument declarations for function `%s'\n", id); } else { Type *proto = newarray(n + 1, sizeof *proto, PERM); if (Aflag >= 1) warning("missing prototype for `%s'\n", id); for (i = 0; i < n; i++) proto[i] = caller[i]->type; proto[i] = NULL; ty = func(rty, proto, 1); } }//endof if (ty->u.f.oldstyle) else { callee = params; caller = newarray(n + 1, sizeof *caller, FUNC); for (i = 0; (p = callee[i]) != NULL && p->name; i++) { NEW(caller[i], FUNC); *caller[i] = *p; if (isint(p->type)) caller[i]->type = promote(p->type); caller[i]->sclass = AUTO; if ('1' <= *p->name && *p->name <= '9') error("missing name for parameter %d to function `%s'\n", i + 1, id); } caller[i] = NULL; } //判斷引數傳送的型別是否出錯 for (i = 0; (p = callee[i]) != NULL; i++) if (p->type->size == 0) { error("undefined size for parameter `%t %s'\n", p->type, p->name); caller[i]->type = p->type = inttype; } //處理main函式定義出錯的處理 if (Aflag >= 2 && sclass != STATIC && strcmp(id, "main") == 0) { if (ty->u.f.oldstyle) warning("`%t %s()' is a non-ANSI definition\n", rty, id); else if (!(rty == inttype && (n == 0 && callee[0] == NULL || n == 2 && callee[0]->type == inttype && isptr(callee[1]->type) && callee[1]->type->type == charptype && !variadic(ty)))) warning("`%s' is a non-ANSI definition\n", typestring(ty, id)); } //上面的程式碼是判斷函式是否重複宣告 p = lookup(id, identifiers); if (p && isfunc(p->type) && p->defined) error("redefinition of `%s' previously defined at %w\n", p->name, &p->src); //處理函式全域性定義 cfunc = dclglobal(sclass, id, ty, &pt); //儲存函式的屬性 cfunc->u.f.label = genlabel(1); cfunc->u.f.callee = callee; cfunc->u.f.pt = src; cfunc->defined = 1; if (xref) use(cfunc, cfunc->src); if (Pflag) printproto(cfunc, cfunc->u.f.callee); if (ncalled >= 0) ncalled = findfunc(cfunc->name, pt.file); //準備生成程式碼 labels = table(NULL, LABELS); stmtlabs = table(NULL, LABELS); refinc = 1.0; regcount = 0; codelist = &codehead; codelist->next = NULL; if (!IR->wants_callb && isstruct(rty)) retv = genident(AUTO, ptr(unqual(rty)), PARAM); compound(0, NULL, 0);//分析函式定義裡的複合語句 definelab(cfunc->u.f.label); if (events.exit) apply(events.exit, cfunc, NULL); walk(NULL, 0, 0); exitscope(); assert(level == PARAM); foreach(identifiers, level, checkref, NULL); if (!IR->wants_callb && isstruct(rty)) { Symbol *a; a = newarray(n + 2, sizeof *a, FUNC); a[0] = retv; memcpy(&a[1], callee, (n+1)*sizeof *callee); callee = a; a = newarray(n + 2, sizeof *a, FUNC); NEW(a[0], FUNC); *a[0] = *retv; memcpy(&a[1], caller, (n+1)*sizeof *callee); caller = a; } if (!IR->wants_argb) for (i = 0; caller[i]; i++) if (isstruct(caller[i]->type)) { caller[i]->type = ptr(caller[i]->type); callee[i]->type = ptr(callee[i]->type); caller[i]->structarg = callee[i]->structarg = 1; } if (glevel > 1) for (i = 0; callee[i]; i++) callee[i]->sclass = AUTO; if (cfunc->sclass != STATIC) (*IR->export)(cfunc); if (glevel && IR->stabsym) { swtoseg(CODE); (*IR->stabsym)(cfunc); } swtoseg(CODE); (*IR->function)(cfunc, caller, callee, cfunc->u.f.ncalls); if (glevel && IR->stabfend) (*IR->stabfend)(cfunc, lineno); foreach(stmtlabs, LABELS, checklab, NULL); exitscope(); expect('}'); labels = stmtlabs = NULL; retv = NULL; cfunc = NULL; } static void oldparam(Symbol p, void *cl) { int i; Symbol *callee = cl; for (i = 0; callee[i]; i++) if (p->name == callee[i]->name) { callee[i] = p; return; } error("declared parameter `%s' is missing\n", p->name); } void compound(int loop, struct swtch *swp, int lev) { Code cp; int nregs; //復位一些前面使用過的全域性變數,以便後面可以正確地分析 walk(NULL, 0, 0);//這步實際上什麼都沒幹啊 ????? //建立程式碼開始塊cp,並儲存這塊程式碼塊到程式碼列表codelist裡 cp = code(Blockbeg); enterscope(); assert(level >= LOCAL); if (level == LOCAL && events.entry) apply(events.entry, cfunc, NULL); definept(NULL); //測試是否複合語句的開始,如果是,獲取下一個詞素 expect('{'); autos = registers = NULL; if (level == LOCAL && IR->wants_callb && isstruct(freturn(cfunc->type))) { retv = genident(AUTO, ptr(unqual(freturn(cfunc->type))), level); retv->defined = 1; retv->ref = 1; registers = append(retv, registers); } //區域性變數宣告處理 while (kind[t] == CHAR || kind[t] == STATIC || istypename(t, tsym) && getchr() != ':') decl(dcllocal);//遞迴地呼叫decl來處理宣告 {//處理自動型別和暫存器型別的區域性變數 int i; Symbol *a = ltov(&autos, STMT); nregs = length(registers); for (i = 0; a[i]; i++) registers = append(a[i], registers); cp->u.block.locals = ltov(®isters, FUNC); } if (events.blockentry)//定義了事件響應處理 apply(events.blockentry, cp->u.block.locals, NULL); while (kind[t] == IF || kind[t] == ID) statement(loop, swp, lev);//遞迴呼叫語句分析函式statement walk(NULL, 0, 0);//復位所有使用全部變數 foreach(identifiers, level, checkref, NULL);//統計變數的引用計數 { int i = nregs, j; Symbol p; for ( ; (p = cp->u.block.locals[i]) != NULL; i++) { for (j = i; j > nregs && cp->u.block.locals[j-1]->ref < p->ref; j--) cp->u.block.locals[j] = cp->u.block.locals[j-1]; cp->u.block.locals[j] = p; } } if (level == LOCAL) {//處理區域性函式返回值 Code cp; for (cp = codelist; cp->kind < Label; cp = cp->prev) ; if (cp->kind != Jump) {//如果程式碼的型別不是跳轉,就需生成返回程式碼 if (freturn(cfunc->type) != voidtype) { warning("missing return value\n"); retcode(cnsttree(inttype, 0L)); } else retcode(NULL); } } //儲存複合語句的程式碼屬性,就處理完成了複合語句 if (events.blockexit) apply(events.blockexit, cp->u.block.locals, NULL); cp->u.block.level = level; cp->u.block.identifiers = identifiers; cp->u.block.types = types; code(Blockend)->u.begin = cp; if (reachable(Gen)) definept(NULL); if (level > LOCAL) { exitscope(); expect('}'); } } static void checkref(Symbol p, void *cl) { if (p->scope >= PARAM && (isvolatile(p->type) || isfunc(p->type))) p->addressed = 1; if (Aflag >= 2 && p->defined && p->ref == 0) { if (p->sclass == STATIC) warning("static `%t %s' is not referenced\n", p->type, p->name); else if (p->scope == PARAM) warning("parameter `%t %s' is not referenced\n", p->type, p->name); else if (p->scope >= LOCAL && p->sclass != EXTERN) warning("local `%t %s' is not referenced\n", p->type, p->name); } if (p->sclass == AUTO && (p->scope == PARAM && regcount == 0 || p->scope >= LOCAL) && !p->addressed && isscalar(p->type) && p->ref >= 3.0) p->sclass = REGISTER; if (level == GLOBAL && p->sclass == STATIC && !p->defined && isfunc(p->type) && p->ref) error("undefined static `%t %s'\n", p->type, p->name); assert(!(level == GLOBAL && p->sclass == STATIC && !p->defined && !isfunc(p->type))); } /* 區域性函式和變數的宣告處理 */ static Symbol dcllocal(int sclass, char *id, Type ty, Coordinate *pos) { Symbol p, q; if (sclass == 0) sclass = isfunc(ty) ? EXTERN : AUTO; else if (isfunc(ty) && sclass != EXTERN) { error("invalid storage class `%k' for `%t %s'\n", sclass, ty, id); sclass = EXTERN; } else if (sclass == REGISTER && (isvolatile(ty) || isstruct(ty) || isarray(ty))) { warning("register declaration ignored for `%t %s'\n", ty, id); sclass = AUTO; } q = lookup(id, identifiers); if (q && q->scope >= level || q && q->scope == PARAM && level == LOCAL) if (sclass == EXTERN && q->sclass == EXTERN && eqtype(q->type, ty, 1)) ty = compose(ty, q->type); else error("redeclaration of `%s' previously declared at %w\n", q->name, &q->src); assert(level >= LOCAL); p = install(id, &identifiers, level, sclass == STATIC || sclass == EXTERN ? PERM : FUNC); p->type = ty; p->sclass = sclass; p->src = *pos; switch (sclass) { case EXTERN: q = lookup(id, globals); if (q == NULL || q->sclass == TYPEDEF || q->sclass == ENUM) { q = lookup(id, externals); if (q == NULL) { q = install(p->name, &externals, GLOBAL, PERM); q->type = p->type; q->sclass = EXTERN; q->src = src; (*IR->defsymbol)(q); } } if (!eqtype(p->type, q->type, 1)) warning("declaration of `%s' does not match previous declaration at %w\n", q->name, &q->src); p->u.alias = q; break; case STATIC: (*IR->defsymbol)(p); initglobal(p, 0); if (!p->defined) if (p->type->size > 0) { defglobal(p, BSS); (*IR->space)(p->type->size); } else error("undefined size for `%t %s'\n", p->type, p->name); p->defined = 1; break; case REGISTER: registers = append(p, registers); regcount++; p->defined = 1; break; case AUTO: autos = append(p, autos); p->defined = 1; if (isarray(ty)) p->addressed = 1; break; default: assert(0); } if (t == '=') { Tree e; if (sclass == EXTERN) error("illegal initialization of `extern %s'\n", id); t = gettok(); definept(NULL); if (isscalar(p->type) || isstruct(p->type) && t != '{') { if (t == '{') { t = gettok(); e = expr1(0); expect('}'); } else e = expr1(0); } else { Symbol t1; Type ty = p->type, ty1 = ty; while (isarray(ty1)) ty1 = ty1->type; if (!isconst(ty) && (!isarray(ty) || !isconst(ty1))) ty = qual(CONST, ty); t1 = genident(STATIC, ty, GLOBAL); initglobal(t1, 1); if (isarray(p->type) && p->type->size == 0 && t1->type->size > 0) p->type = array(p->type->type, t1->type->size/t1->type->type->size, 0); e = idtree(t1); } walk(root(asgn(p, e)), 0, 0); p->ref = 1; } if (!isfunc(p->type) && p->defined && p->type->size <= 0) error("undefined size for `%t %s'\n", p->type, id); return p; } void finalize(void) { foreach(externals, GLOBAL, doextern, NULL); foreach(identifiers, GLOBAL, doglobal, NULL); foreach(identifiers, GLOBAL, checkref, NULL); foreach(constants, CONSTANTS, doconst, NULL); } static void doextern(Symbol p, void *cl) { (*IR->import)(p); } static void doglobal(Symbol p, void *cl) { if (!p->defined && (p->sclass == EXTERN || isfunc(p->type) && p->sclass == AUTO)) (*IR->import)(p); else if (!p->defined && !isfunc(p->type) && (p->sclass == AUTO || p->sclass == STATIC)) { if (isarray(p->type) && p->type->size == 0 && p->type->type->size > 0) p->type = array(p->type->type, 1, 0); if (p->type->size > 0) { defglobal(p, BSS); (*IR->space)(p->type->size); if (glevel > 0 && IR->stabsym) (*IR->stabsym)(p); } else error("undefined size for `%t %s'\n", p->type, p->name); p->defined = 1; } if (Pflag && !isfunc(p->type) && !p->generated && p->sclass != EXTERN) printdecl(p, p->type); } void doconst(Symbol p, void *cl) { if (p->u.c.loc) { assert(p->u.c.loc->u.seg == 0); defglobal(p->u.c.loc, LIT); if (isarray(p->type) && p->type->type == widechar) { unsigned int *s = p->u.c.v.p; int n = p->type->size/widechar->size; while (n-- > 0) { Value v; v.u = *s++; (*IR->defconst)(widechar->op, widechar->size, v); } } else if (isarray(p->type)) (*IR->defstring)(p->type->size, p->u.c.v.p); else (*IR->defconst)(p->type->op, p->type->size, p->u.c.v); p->u.c.loc = NULL; } } void checklab(Symbol p, void *cl) { if (!p->defined) error("undefined label `%s'\n", p->name); p->defined = 1; } Type enumdcl(void) { char *tag; Type ty; Symbol p; Coordinate pos; t = gettok(); pos = src; if (t == ID) { tag = token; t = gettok(); } else tag = ""; if (t == '{') { static char follow[] = { IF, 0 }; int n = 0; long k = -1; List idlist = 0; ty = newstruct(ENUM, tag); t = gettok(); if (t != ID) error("expecting an enumerator identifier\n"); while (t == ID) {//迴圈處理列舉中的每一個欄位 char *id = token; Coordinate s; if (tsym && tsym->scope == level) error("redeclaration of `%s' previously declared at %w\n", token, &tsym->src); s = src; t = gettok(); if (t == '=') {//處理賦值列舉 t = gettok(); k = intexpr(0, 0); } else { if (k == inttype->u.sym->u.limits.max.i) error("overflow in value for enumeration constant `%s'\n", id); k++; } //裝入id符號表 p = install(id, &identifiers, level, level < LOCAL ? PERM : FUNC); p->src = s; p->type = ty; p->sclass = ENUM; p->u.value = k; idlist = append(p, idlist);//將p加入連結串列 n++; if (Aflag >= 2 && n == 128)//列舉欄位超過限定值 warning("more than 127 enumeration constants in `%t'\n", ty); if (t != ',') break; t = gettok();//獲取下一個詞素,用於下一輪迴圈 if (Aflag >= 2 && t == '}') warning("non-ANSI trailing comma in enumerator list\n"); } test('}', follow);//宣告結束 ty->type = inttype; ty->size = ty->type->size; ty->align = ty->type->align; ty->u.sym->u.idlist = ltov(&idlist, PERM); ty->u.sym->defined = 1; } else if ((p = lookup(tag, types)) != NULL && p->type->op == ENUM) { ty = p->type; if (t == ';') error("empty declaration\n"); } else { error("unknown enumeration `%s'\n", tag); ty = newstruct(ENUM, tag); ty->type = inttype; } if (*tag && xref) use(p, pos); return ty; } Type typename(void) { Type ty = specifier(NULL); if (t == '*' || t == '(' || t == '[') { ty = dclr(ty, NULL, NULL, 1); if (Aflag >= 1 && !hasproto(ty)) warning("missing prototype\n"); } return ty; }
相關推薦
lcc原始碼解析只decl.c
本文解析lcc中最複雜的模組之一decl.c,作為語法分析的一部分,它處理各個宣告。 #include "c.h" static char rcsid[] = "$Id: decl.c,v 1.1 2002/08/28 23:12:42 drh Exp $"; #def
lcc原始碼解析之sym.c
lcc是一款小巧的工業級編譯器,程式碼精簡,程式碼開源,相比gcc更適合編譯器初學者閱讀。 你可以在這裡搞到程式碼:https://github.com/drh/lcc 但是,怎麼說呢,這個程式碼防盜性較強,幾乎沒有註釋, 我在閱讀原始碼中參考了其他前輩關於lcc的文章以及
lcc原始碼解析之expr.c
又憋了一個周天,終於大概搞明白了表示式解析這一編譯器中我個人認為也許最迷人的部分。 之所以程式碼讀起來費勁,主要還是在於理論上沒有搞清楚,確實很繞,需要反覆理解。 所以,打算在本篇之後寫一篇理論的科普文,儘管因為我文字表達能力太爛,這是我一直竭力避免的。 ---------
lcc 原始碼讀書筆記之c語言的語義檢測
LCC在語法分析過程中進行了不少的語義檢測,基本集中在一元及二元操作符這裡.C語言的語義檢查主要包括隱式轉換,型別檢測和計算順序。那就讓我們從LCC編譯器的角度研究C語言的語義規則 轉換: c語言在進行二元計算的時候常常包括返回型別及運算元的型別轉換,下面貼出
lcc原始碼解析之x86後端
在前面的文章中已經介紹過,lcc中跟硬體平臺相關的配置由src中*.md配置,本文以x86為例,詳解這一部分的工作機制。 熟悉彙編的同學都知道32位x86機器有八個通用暫存器: eax ebx ecx edx esi edi esp ebp 而ebp和esp兩個暫存器是有固
YOLO原始碼解析之yolo.c
yolo.c是YOLO模型原始碼的主檔案,該檔案包括以下函式: train_yolo void train_yolo(char *cfgfile, char *weightfile) { char *train_images = "/data/voc
C#軟體授權、註冊、加密、解密模組原始碼解析並製作註冊機生成license
最近做了一個綠色免安裝軟體,領導臨時要求加個註冊機制,不能讓現場工程師隨意複製。事出突然,只能在現場開發(離開現場軟體就不受我們控了)。花了不到兩個小時實現了簡單的註冊機制,稍作整理。 基本原理:1.軟體一執行就把計算機的CPU、主機板、BIOS、MAC地
jdk8原始碼解析第一天(簡化版,只記自己理解的要點)
1.String類: 實現了serilizable,comparable介面,seriliazable僅用於標誌,comparable的comparableTo方法用於比較字串大小。 底層是通過final char[] 實現字串的,其所有方法均是用字元陣列相關方法實現的。
ceph/crush/mapper.c 原始碼解析
ceph/crush/mapper.c 原始碼解析 (1)crush_find_rule函式 int crush_find_rule(const struct crush_map *map, int ruleset, int type, int size) crush_find
貼上JNA—JNI終結者、深入解析JNA—模擬C語言結構體兩篇文章的完整原始碼
原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599854.aspx 貼上 JNA—JNI 終結者 、 深入解析 JNA— 模擬 C 語言結構體 兩篇文章的完整原始碼 C 語言
貼上 JNA—JNI 終結者 、 深入解析 JNA— 模擬 C 語言結構體 兩篇文章的完整原始碼 (續)...
原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599854.aspx /** * @param args */ public static void main(String[] args
uC/OS-II原始碼解析(ucos_ii.c)
/* ** ver: 2.52 ** file: uCOS_II.C ** brief: C檔案集合 */ #define OS_GLOBALS /* 宣告全域性變數 防止重複定義
Python核心原始碼解析與C/CPP-API拓展程式設計(一)PyObject
開發十年,就只剩下這套架構體系了! >>>
曹工說Redis原始碼(2)-- redis server 啟動過程解析及簡單c語言基礎知識補充
文章導航 Redis原始碼系列的初衷,是幫助我們更好地理解Redis,更懂Redis,而怎麼才能懂,光看是不夠的,建議跟著下面的這一篇,把環境搭建起來,後續可以自己閱讀原始碼,或者跟著我這邊一起閱讀。由於我用c也是好幾年以前了,些許錯誤在所難免,希望讀者能不吝指出。 曹工說Redis原始碼(1)-- redi
ghost後只剩下C盤DEF盤的文件如何恢復
gho 新的 文件 保存 圖片 bubuko src 系統 .com GHOST後只剩C盤是由於在ghost系統時,選擇了錯誤的選項導致把整個硬盤當成C盤來裝,裝完之後自然就只剩下C盤。想要恢復丟失盤的資料,需要註意,別往現在的這個C盤存入新的文件(因為現在存入的文件可能會
Netty進階:Futrue&Promise原始碼解析
文章目錄 1. Future&Promise 2. AbstractFuture 3.Completefuture 4.Channelfuture&Completechannel
大資料基礎(1)zookeeper原始碼解析
五 原始碼解析 public enum ServerState { LOOKING, FOLLOWING, LEADING, OBSERVING;}zookeeper伺服器狀態:剛啟動LOOKING,follower是FOLLOWING,leader是LEADING,observer是
Android框架原始碼解析之(四)Picasso
這次要分析的原始碼是 Picasso 2.5.2 ,四年前的版本,用eclipse寫的,但不影響這次我們對其原始碼的分析 地址:https://github.com/square/picasso/tree/picasso-parent-2.5.2 Picasso的簡單使用
Android框架原始碼解析之(三)ButterKnife
注:所有分析基於butterknife:8.4.0 原始碼目錄:https://github.com/JakeWharton/butterknife 其中最主要的3個模組是: Butterknife註解處理器https://github.com/JakeWharton/
Android框架原始碼解析之(二)OKhttp
原始碼在:https://github.com/square/okhttp 包實在是太多了,OKhttp核心在這塊https://github.com/square/okhttp/tree/master/okhttp 直接匯入Android Studio中即可。 基本使用: