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.
This commit is contained in:
Stephen Heumann 2022-11-19 23:02:50 -06:00
parent ab368d442a
commit 3f450bdb80
8 changed files with 97 additions and 34 deletions

View File

@ -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;

View File

@ -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}

View File

@ -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}

View File

@ -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);

View File

@ -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';

View File

@ -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

View File

@ -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 <stdio.h>
@ -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);

View File

@ -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.