From 3f450bdb807cd735ce3446131c883c6d497b4177 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Sat, 19 Nov 2022 23:02:50 -0600 Subject: [PATCH] Support "inline" function definitions without static or extern. This is a minimal implementation that does not actually inline anything, but it is intended to implement the semantics defined by the C99 and later standards. One complication is that a declaration that appears somewhere after the function body may create an external definition for a function that appeared to be an inline definition when it was defined. To support this while preserving ORCA/C's general one-pass compilation strategy, we generate code even for inline definitions, but treat them as private and add the prefix "~inline~" to the name. If they are "un-inlined" based on a later declaration, we generate a stub with external linkage that just jumps to the apparently-inline function. --- CCommon.pas | 2 +- Expression.pas | 2 +- Header.pas | 10 +++++-- Parser.pas | 39 +++++++++++++------------- Scanner.pas | 2 +- Symbol.pas | 53 +++++++++++++++++++++++++++++++---- Tests/Conformance/c99inline.c | 15 ++++++++-- cc.notes | 8 +++++- 8 files changed, 97 insertions(+), 34 deletions(-) diff --git a/CCommon.pas b/CCommon.pas index a7ea74c..6e707e3 100644 --- a/CCommon.pas +++ b/CCommon.pas @@ -374,7 +374,7 @@ type parameter: (pln: integer; {paramater label #} pdisp: integer; {disp of parameter} pnext: identPtr); {next parameter} - external: (); + external: (inlineDefinition: boolean); {(potential) inline definition of function?} global,private: (); none: (anonMemberField: boolean); {field from an anonymous struct/union member?} end; diff --git a/Expression.pas b/Expression.pas index f36d956..646b171 100644 --- a/Expression.pas +++ b/Expression.pas @@ -1008,7 +1008,7 @@ var {fnPtr^.dispatcher := 0;} np := pointer(GMalloc(length(fToken.name^)+1)); CopyString(pointer(np), pointer(fToken.name)); - id := NewSymbol(np, fnPtr, ident, variableSpace, declared); + id := NewSymbol(np, fnPtr, ident, variableSpace, declared, false); if ((lint & lintUndefFn) <> 0) or ((lint & lintC99Syntax) <> 0) then Error(51); end {if} diff --git a/Header.pas b/Header.pas index 7b53efb..69bf312 100644 --- a/Header.pas +++ b/Header.pas @@ -18,7 +18,7 @@ uses CCommon, MM, Scanner, Symbol, CGI; {$segment 'HEADER'} const - symFileVersion = 32; {version number of .sym file format} + symFileVersion = 33; {version number of .sym file format} var inhibitHeader: boolean; {should .sym includes be blocked?} @@ -1106,7 +1106,9 @@ procedure EndInclude {chPtr: ptr}; WriteByte(ord(ip^.class)); WriteByte(ord(ip^.storage)); if ip^.storage = none then - WriteByte(ord(ip^.anonMemberField)); + WriteByte(ord(ip^.anonMemberField)) + else if ip^.storage = external then + WriteByte(ord(ip^.inlineDefinition)); end; {WriteIdent} @@ -1808,7 +1810,9 @@ var sp^.class := tokenEnum(ReadByte); sp^.storage := storageType(ReadByte); if sp^.storage = none then - sp^.anonMemberField := boolean(ReadByte); + sp^.anonMemberField := boolean(ReadByte) + else if sp^.storage = external then + sp^.inlineDefinition := boolean(ReadByte); ReadIdent := sp; end; {ReadIdent} diff --git a/Parser.pas b/Parser.pas index c1d4d54..818d2c2 100644 --- a/Parser.pas +++ b/Parser.pas @@ -1547,7 +1547,7 @@ var end; {if} if token.kind = ident then begin pvar := NewSymbol(token.name, nil, ident, variableSpace, - declared); + declared, false); pvar^.storage := parameter; pvar^.pnext := lastParameter; lastParameter := pvar; @@ -1809,7 +1809,8 @@ if madeFunctionTable then begin table := table^.next; end; {if} if newName <> nil then {declare the variable} - variable := NewSymbol(newName, tPtr, declSpecifiers.storageClass, space, state) + variable := NewSymbol(newName, tPtr, declSpecifiers.storageClass, + space, state, inlinesy in declSpecifiers.declarationModifiers) else if unnamedParm then variable^.itype := tPtr else begin @@ -2873,7 +2874,7 @@ var or (unionsy in fieldDeclSpecifiers.declarationModifiers)) then begin variable := NewSymbol(@'~anonymous', tPtr, ident, - fieldListSpace, defined); + fieldListSpace, defined, false); anonMember := true; end; {if} end {if} @@ -3248,7 +3249,7 @@ while token.kind in allowedTokens do begin tPtr^.qualifiers := []; tPtr^.kind := enumType; variable := - NewSymbol(ttoken.name, tPtr, ident, tagSpace, defined); + NewSymbol(ttoken.name, tPtr, ident, tagSpace, defined, false); end {if} else if token.kind <> lbracech then Error(9); @@ -3263,8 +3264,8 @@ while token.kind in allowedTokens do begin tPtr^.qualifiers := []; tPtr^.kind := enumConst; if token.kind = ident then begin - variable := - NewSymbol(token.name, tPtr, ident, variableSpace, defined); + variable := NewSymbol(token.name, tPtr, ident, variableSpace, + defined, false); NextToken; end {if} else @@ -3352,7 +3353,7 @@ while token.kind in allowedTokens do begin {structTypePtr^.constMember := false;} {structTypePtr^.flexibleArrayMember := false;} structPtr := NewSymbol(ttoken.name, structTypePtr, ident, - tagSpace, defined); + tagSpace, defined, false); structTypePtr^.sName := structPtr^.name; declaredTagOrEnumConst := true; end; @@ -3765,9 +3766,6 @@ if isFunction then begin SkipStatement; goto 1; end; {if} - if isInline then - if not (declSpecifiers.storageClass in [staticsy,externsy]) then - Error(120); if isInline or isNoreturn then if not (isNewDeskAcc or isClassicDeskAcc or isCDev or isNBA or isXCMD) then if variable^.name^ = 'main' then @@ -3918,16 +3916,19 @@ if isFunction then begin end; {if} foundFunction := true; {got one...} segType := ord(variable^.class = staticsy) * $4000; - if fnType^.isPascal then begin + if (variable^.storage = external) and variable^.inlineDefinition then begin + new(fname); + fname^ := concat('~inline~', variable^.name^); + segType := $4000; + end {if} + else if fnType^.isPascal then begin fName := pointer(Malloc(length(variable^.name^)+1)); CopyString(pointer(fName), pointer(variable^.name)); for i := 1 to length(fName^) do if fName^[i] in ['a'..'z'] then fName^[i] := chr(ord(fName^[i]) & $5F); - Gen2Name (dc_str, segType, 0, fName); - end {if} - else - Gen2Name (dc_str, segType, 0, variable^.name); + end; {if} + Gen2Name(dc_str, segType, 0, fName); doingMain := variable^.name^ = 'main'; hasVarargsCall := false; firstCompoundStatement := true; @@ -3989,7 +3990,7 @@ if isFunction then begin {set up struct/union area} if variable^.itype^.ftype^.kind in [structType,unionType] then begin lp := NewSymbol(@'@struct', variable^.itype^.ftype, staticsy, - variablespace, declared); + variablespace, declared, false); tk.kind := ident; tk.class := identifier; tk.name := @'@struct'; @@ -4007,7 +4008,7 @@ if isFunction then begin functionTable := table; if fnType^.varargs then begin {make internal va info for varargs funcs} lp := NewSymbol(@'__orcac_va_info', vaInfoPtr, autosy, - variableSpace, declared); + variableSpace, declared, false); lp^.lln := GetLocalLabel; Gen2(dc_loc, lp^.lln, ord(vaInfoPtr^.size)); Gen2(pc_lda, lastParameterLLN, lastParameterSize); @@ -4732,7 +4733,7 @@ tp^.size := len; tp^.kind := arrayType; tp^.aType := constCharPtr; tp^.elements := len; -id := NewSymbol(@'__func__', tp, staticsy, variableSpace, initialized); +id := NewSymbol(@'__func__', tp, staticsy, variableSpace, initialized, false); sval := pointer(GCalloc(len + sizeof(integer))); sval^.length := len; @@ -4777,7 +4778,7 @@ else class := staticsy; name := pointer(Malloc(25)); name^ := concat('~CompoundLiteral', cnvis(compoundLiteralNumber)); -id := NewSymbol(name, tp, class, variableSpace, defined); +id := NewSymbol(name, tp, class, variableSpace, defined, false); compoundLiteralNumber := compoundLiteralNumber + 1; if compoundLiteralNumber = 0 then Error(57); diff --git a/Scanner.pas b/Scanner.pas index 966e411..00176c6 100644 --- a/Scanner.pas +++ b/Scanner.pas @@ -715,7 +715,7 @@ if list or (numErr <> 0) then begin 117: msg := @'field cannot have incomplete or function type'; 118: msg := @'flexible array must be last member of structure'; 119: msg := @'inline specifier is only allowed on functions'; - 120: msg := @'inline functions without ''static'' or ''extern'' are not supported'; + {120: msg := @'inline functions without ''static'' or ''extern'' are not supported';} 121: msg := @'invalid digit for binary constant'; 122: msg := @'arithmetic is not allowed on a pointer to an incomplete or function type'; 123: msg := @'array element type may not be an incomplete or function type'; diff --git a/Symbol.pas b/Symbol.pas index 5f9d70a..6197519 100644 --- a/Symbol.pas +++ b/Symbol.pas @@ -234,7 +234,8 @@ function Unqualify (tp: typePtr): typePtr; function NewSymbol (name: stringPtr; itype: typePtr; class: tokenEnum; - space: spaceType; state: stateKind): identPtr; + space: spaceType; state: stateKind; isInline: boolean): + identPtr; { insert a new symbol in the symbol table } { } @@ -1991,7 +1992,8 @@ end; {Unqualify} function NewSymbol {name: stringPtr; itype: typePtr; class: tokenEnum; - space: spaceType; state: stateKind): identPtr}; + space: spaceType; state: stateKind; isInline: boolean): + identPtr}; { insert a new symbol in the symbol table } { } @@ -2016,6 +2018,35 @@ var np: stringPtr; {for forming static name} p: identPtr; {work pointer} tk: tokenType; {fake token; for FindSymbol} + + procedure UnInline; + + { Generate a non-inline definition for a function previously } + { defined with an (apparent) inline definition. } + + var + fName: stringPtr; {name of function} + i: integer; {loop variable} + + begin {UnInline} + if cs^.iType^.isPascal then begin + fName := pointer(Malloc(length(name^)+1)); + CopyString(pointer(fName), pointer(name)); + for i := 1 to length(fName^) do + if fName^[i] in ['a'..'z'] then + fName^[i] := chr(ord(fName^[i]) & $5F); + end {if} + else + fName := name; + Gen2Name(dc_str, 0, 0, fName); + code^.s := m_jml; + code^.q := 0; + code^.r := ord(longabsolute); + new(code^.lab); + code^.lab^ := concat('~inline~',name^); + Gen0(pc_nat); + Gen0(dc_enp); + end; {UnInline} begin {NewSymbol} needSymbol := true; {assume we need a symbol} @@ -2028,7 +2059,9 @@ if space <> fieldListSpace then begin {are we defining a function?} if (itype <> nil) and (itype^.kind = functionType) then begin isFunction := true; if class in [autosy, ident] then - class := externsy; + class := externsy + else {If explicit storage class is given,} + isInline := false; {this is not an inline definition. } end {if} else if (itype <> nil) and (itype^.kind in [structType,unionType]) and (itype^.fieldList = nil) and doingParameters then begin @@ -2052,6 +2085,14 @@ if space <> fieldListSpace then begin {are we defining a function?} if class = externsy then if cs^.class = staticsy then class := staticsy; + if cs^.storage = external then + if isInline then + isInline := cs^.inlineDefinition + else if cs^.inlineDefinition then + if iType^.kind = functionType then + if cs^.state = defined then + if table = globalTable then + UnInline; p := cs; needSymbol := false; end; {else} @@ -2124,8 +2165,10 @@ else if class = ident then begin else p^.storage := global; end {else if} -else if class = externsy then - p^.storage := external +else if class = externsy then begin + p^.storage := external; + p^.inlineDefinition := isInline; + end {else if} else if class = staticsy then p^.storage := private else diff --git a/Tests/Conformance/c99inline.c b/Tests/Conformance/c99inline.c index 762bcf6..6b4466e 100644 --- a/Tests/Conformance/c99inline.c +++ b/Tests/Conformance/c99inline.c @@ -1,8 +1,5 @@ /* * Test inline function specifier (C99). - * - * This only tests "static inline" and "extern inline", - * which are the only forms currently supported by ORCA/C. */ #include @@ -15,9 +12,14 @@ inline int extern g(void) { return 2; } +inline int h(int i) { + return i+5; +} + int main(void) { int (*p)(void) = f; int (*q)(void) = g; + int (*r)(int) = h; if (f() + g() != 3) goto Fail; @@ -25,9 +27,16 @@ int main(void) { if (p() + q() != 3) goto Fail; + if (h(2) != 7) + goto Fail; + if (r(23) != 28) + goto Fail; + printf ("Passed Conformance Test c99inline\n"); return 0; Fail: printf ("Failed Conformance Test c99inline\n"); } + +extern inline int h(int i); diff --git a/cc.notes b/cc.notes index 42bad75..ba554db 100644 --- a/cc.notes +++ b/cc.notes @@ -451,7 +451,13 @@ The flexible array member does not contribute to the size of the struct as repor (Kelvin Sherlock) -6. (C99) Functions may be declared as "static inline" or "extern inline". These have the same semantics as if "inline" was omitted. The "inline" function specifier suggests (but does not require) that calls to the function should be inlined or otherwise optimized. ORCA/C currently does not inline these functions or apply any other special optimizations, but future versions might introduce such features. Note that inline functions without a "static" or "extern" storage-class specifier are not currently supported. +6. (C99) Functions may be declared with the "inline" function specifier, which suggests (but does not require) that calls to the function should be inlined. + +Functions declared as "static inline" or "extern inline" have the same semantics as if "inline" was omitted. + +Other function definitions with the "inline" specifier provide an inline definition of the function, which is potentially usable only within the source file containing it. There should also be a non-inline definition of the function in another source file, and it is unspecified which definition will be used for calls to the function within the file containing the inline definition. + +ORCA/C currently does not inline any functions and does not generate calls to inline definitions of functions, but future versions might add such features. 7. (Draft C23) Integer constants may be written in binary, with a "0b" or "0B" prefix followed by binary digits. The type of these constants is determined by the same rules as for octal and hexadecimal constants.