mirror of
https://github.com/byteworksinc/ORCA-C.git
synced 2024-12-30 14:31:04 +00:00
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:
parent
ab368d442a
commit
3f450bdb80
@ -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;
|
||||
|
@ -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}
|
||||
|
10
Header.pas
10
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}
|
||||
|
||||
|
39
Parser.pas
39
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);
|
||||
|
@ -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';
|
||||
|
53
Symbol.pas
53
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
|
||||
|
@ -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);
|
||||
|
8
cc.notes
8
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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user