Fixed function parameter checking.

Fixed function return type checking.
This commit is contained in:
acqn
2020-08-15 06:27:11 +08:00
committed by Oliver Schmidt
parent 4e61ae5b36
commit 0a96ffc878
6 changed files with 210 additions and 130 deletions

View File

@@ -410,8 +410,10 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg)
(AutoCDecl ? (AutoCDecl ?
IsQualFastcall (E->Type) : IsQualFastcall (E->Type) :
!IsQualCDecl (E->Type))) { !IsQualCDecl (E->Type))) {
/* Will use registers depending on the last param. */ /* Will use registers depending on the last param. If the last
switch (CheckedSizeOf (D->LastParam->Type)) { ** param has incomplete type, just assume __EAX__.
*/
switch (SizeOf (D->LastParam->Type)) {
case 1u: case 1u:
*Use = REG_A; *Use = REG_A;
break; break;

View File

@@ -1704,7 +1704,6 @@ static void ParseAnsiParamList (FuncDesc* F)
static FuncDesc* ParseFuncDecl (void) static FuncDesc* ParseFuncDecl (void)
/* Parse the argument list of a function. */ /* Parse the argument list of a function. */
{ {
unsigned Offs;
SymEntry* Sym; SymEntry* Sym;
SymEntry* WrappedCall; SymEntry* WrappedCall;
unsigned char WrappedCallData; unsigned char WrappedCallData;
@@ -1751,23 +1750,10 @@ static FuncDesc* ParseFuncDecl (void)
*/ */
F->LastParam = GetSymTab()->SymTail; F->LastParam = GetSymTab()->SymTail;
/* Assign offsets. If the function has a variable parameter list, /* It is allowed to use incomplete types in function prototypes, so we
** there's one additional byte (the arg size). ** won't always get to know the parameter sizes here and may do that later.
*/ */
Offs = (F->Flags & FD_VARIADIC)? 1 : 0;
Sym = F->LastParam;
while (Sym) {
unsigned Size = CheckedSizeOf (Sym->Type);
if (SymIsRegVar (Sym)) {
Sym->V.R.SaveOffs = Offs;
} else {
Sym->V.Offs = Offs;
}
Offs += Size;
F->ParamSize += Size;
Sym = Sym->PrevSym;
}
/* Leave the lexical level remembering the symbol tables */ /* Leave the lexical level remembering the symbol tables */
RememberFunctionLevel (F); RememberFunctionLevel (F);

View File

@@ -346,6 +346,9 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
int FrameOffs = 0; /* Offset into parameter frame */ int FrameOffs = 0; /* Offset into parameter frame */
int Ellipsis = 0; /* Function is variadic */ int Ellipsis = 0; /* Function is variadic */
/* Make sure the size of all parameters are known */
int ParamComplete = F_CheckParamList (Func, 1);
/* As an optimization, we may allocate the complete parameter frame at /* As an optimization, we may allocate the complete parameter frame at
** once instead of pushing into each parameter as it comes. We may do that, ** once instead of pushing into each parameter as it comes. We may do that,
** if... ** if...
@@ -359,7 +362,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
** (instead of pushing) is enabled. ** (instead of pushing) is enabled.
** **
*/ */
if (IS_Get (&CodeSizeFactor) >= 200) { if (ParamComplete && IS_Get (&CodeSizeFactor) >= 200) {
/* Calculate the number and size of the parameters */ /* Calculate the number and size of the parameters */
FrameParams = Func->ParamCount; FrameParams = Func->ParamCount;
@@ -424,65 +427,68 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
/* Evaluate the argument expression */ /* Evaluate the argument expression */
hie1 (&Expr); hie1 (&Expr);
/* If we don't have a prototype, accept anything; otherwise, convert /* Skip to the next parameter if there are any incomplete types */
** the actual argument to the parameter type needed. if (ParamComplete) {
*/ /* If we don't have an argument spec., accept anything; otherwise,
Flags = CF_NONE; ** convert the actual argument to the type needed.
if (!Ellipsis) {
/* Convert the argument to the parameter type if needed */
TypeConversion (&Expr, Param->Type);
/* If we have a prototype, chars may be pushed as chars */
Flags |= CF_FORCECHAR;
} else {
/* No prototype available. Convert array to "pointer to first
** element", and function to "pointer to function".
*/ */
Expr.Type = PtrConversion (Expr.Type); Flags = CF_NONE;
if (!Ellipsis) {
} /* Convert the argument to the parameter type if needed */
TypeConversion (&Expr, Param->Type);
/* Handle struct/union specially */ /* If we have a prototype, chars may be pushed as chars */
if (IsClassStruct (Expr.Type)) { Flags |= CF_FORCECHAR;
/* Use the replacement type */
Flags |= TypeOf (GetStructReplacementType (Expr.Type));
} else {
/* Use the type of the argument for the push */
Flags |= TypeOf (Expr.Type);
}
/* Load the value into the primary if it is not already there */
LoadExpr (Flags, &Expr);
/* If this is a fastcall function, don't push the last argument */
if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) {
unsigned ArgSize = sizeofarg (Flags);
if (FrameSize > 0) {
/* We have the space already allocated, store in the frame.
** Because of invalid type conversions (that have produced an
** error before), we can end up here with a non-aligned stack
** frame. Since no output will be generated anyway, handle
** these cases gracefully instead of doing a CHECK.
*/
if (FrameSize >= ArgSize) {
FrameSize -= ArgSize;
} else {
FrameSize = 0;
}
FrameOffs -= ArgSize;
/* Store */
g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal);
} else { } else {
/* Push the argument */
g_push (Flags, Expr.IVal); /* No prototype available. Convert array to "pointer to first
** element", and function to "pointer to function".
*/
Expr.Type = PtrConversion (Expr.Type);
} }
/* Calculate total parameter size */ /* Handle struct/union specially */
PushedSize += ArgSize; if (IsClassStruct (Expr.Type)) {
/* Use the replacement type */
Flags |= TypeOf (GetStructReplacementType (Expr.Type));
} else {
/* Use the type of the argument for the push */
Flags |= TypeOf (Expr.Type);
}
/* Load the value into the primary if it is not already there */
LoadExpr (Flags, &Expr);
/* If this is a fastcall function, don't push the last argument */
if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) {
unsigned ArgSize = sizeofarg (Flags);
if (FrameSize > 0) {
/* We have the space already allocated, store in the frame.
** Because of invalid type conversions (that have produced an
** error before), we can end up here with a non-aligned stack
** frame. Since no output will be generated anyway, handle
** these cases gracefully instead of doing a CHECK.
*/
if (FrameSize >= ArgSize) {
FrameSize -= ArgSize;
} else {
FrameSize = 0;
}
FrameOffs -= ArgSize;
/* Store */
g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal);
} else {
/* Push the argument */
g_push (Flags, Expr.IVal);
}
/* Calculate total parameter size */
PushedSize += ArgSize;
}
} }
/* Check for end of argument list */ /* Check for end of argument list */

View File

@@ -105,6 +105,62 @@ static void FreeFunction (Function* F)
int F_CheckParamList (FuncDesc* D, int RequireAll)
/* Check and set the parameter sizes.
** If RequireAll is true, emit errors on parameters of incomplete types.
** Return true if all parameters have complete types.
*/
{
unsigned I = 0;
unsigned Offs;
SymEntry* Param;
unsigned ParamSize = 0;
unsigned IncompleteCount = 0;
/* Assign offsets. If the function has a variable parameter list,
** there's one additional byte (the arg size).
*/
Offs = (D->Flags & FD_VARIADIC) ? 1 : 0;
Param = D->LastParam;
while (Param) {
unsigned Size = SizeOf (Param->Type);
if (RequireAll && IsIncompleteESUType (Param->Type)) {
if (D->Flags & FD_UNNAMED_PARAMS) {
Error ("Parameter %u has incomplete type '%s'",
D->ParamCount - I,
GetFullTypeName (Param->Type));
} else {
Error ("Parameter '%s' has incomplete type '%s'",
Param->Name,
GetFullTypeName (Param->Type));
}
++IncompleteCount;
}
if (SymIsRegVar (Param)) {
Param->V.R.SaveOffs = Offs;
} else {
Param->V.Offs = Offs;
}
Offs += Size;
ParamSize += Size;
Param = Param->PrevSym;
++I;
}
/* If all parameters have complete types, set the total size description
** and return true.
*/
if (IncompleteCount == 0) {
D->ParamSize = ParamSize;
return 1;
}
/* Otherwise return false */
return 0;
}
const char* F_GetFuncName (const Function* F) const char* F_GetFuncName (const Function* F)
/* Return the name of the current function */ /* Return the name of the current function */
{ {
@@ -378,9 +434,11 @@ static void F_EmitDebugInfo (void)
void NewFunc (SymEntry* Func, FuncDesc* D) void NewFunc (SymEntry* Func, FuncDesc* D)
/* Parse argument declarations and function body. */ /* Parse argument declarations and function body. */
{ {
int ParamComplete; /* If all paramemters have complete types */
int C99MainFunc = 0;/* Flag for C99 main function returning int */ int C99MainFunc = 0;/* Flag for C99 main function returning int */
SymEntry* Param; SymEntry* Param;
const Type* RType; /* Real type used for struct parameters */ const Type* RType; /* Real type used for struct parameters */
const Type* ReturnType; /* Return type */
/* Remember this function descriptor used for definition */ /* Remember this function descriptor used for definition */
GetFuncDesc (Func->Type)->FuncDef = D; GetFuncDesc (Func->Type)->FuncDef = D;
@@ -391,6 +449,21 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Reenter the lexical level */ /* Reenter the lexical level */
ReenterFunctionLevel (D); ReenterFunctionLevel (D);
/* Check return type */
ReturnType = F_GetReturnType (CurrentFunc);
if (IsIncompleteESUType (ReturnType)) {
/* There are already diagnostics on returning arrays or functions */
if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) {
Error ("Function has incomplete return type '%s'",
GetFullTypeName (ReturnType));
}
}
/* Check and set the parameter sizes. All parameter must have complete
** types now.
*/
ParamComplete = F_CheckParamList (D, 1);
/* Check if the function header contains unnamed parameters. These are /* Check if the function header contains unnamed parameters. These are
** only allowed in cc65 mode. ** only allowed in cc65 mode.
*/ */
@@ -429,7 +502,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* If cc65 extensions aren't enabled, don't allow a main function that /* If cc65 extensions aren't enabled, don't allow a main function that
** doesn't return an int. ** doesn't return an int.
*/ */
if (IS_Get (&Standard) != STD_CC65 && CurrentFunc->ReturnType[0].C != T_INT) { if (IS_Get (&Standard) != STD_CC65 && ReturnType[0].C != T_INT) {
Error ("'main' must always return an int"); Error ("'main' must always return an int");
} }
@@ -472,16 +545,11 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
unsigned Flags; unsigned Flags;
/* Generate the push */ /* Generate the push */
if (IsTypeFunc (D->LastParam->Type)) { /* Handle struct/union specially */
/* Pointer to function */ if (IsClassStruct (D->LastParam->Type)) {
Flags = CF_PTR; Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR;
} else { } else {
/* Handle struct/union specially */ Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR;
if (IsClassStruct (D->LastParam->Type)) {
Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR;
} else {
Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR;
}
} }
g_push (Flags, 0); g_push (Flags, 0);
} }
@@ -497,48 +565,50 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Setup the stack */ /* Setup the stack */
StackPtr = 0; StackPtr = 0;
/* Walk through the parameter list and allocate register variable space /* Emit code to handle the parameters if all of them have complete types */
** for parameters declared as register. Generate code to swap the contents if (ParamComplete) {
** of the register bank with the save area on the stack. /* Walk through the parameter list and allocate register variable space
*/ ** for parameters declared as register. Generate code to swap the contents
Param = D->SymTab->SymHead; ** of the register bank with the save area on the stack.
while (Param && (Param->Flags & SC_PARAM) != 0) { */
Param = D->SymTab->SymHead;
while (Param && (Param->Flags & SC_PARAM) != 0) {
/* Check if we need copy for struct/union type */ /* Check if we need copy for struct/union type */
RType = Param->Type; RType = Param->Type;
if (IsClassStruct (RType)) { if (IsClassStruct (RType)) {
RType = GetStructReplacementType (RType); RType = GetStructReplacementType (RType);
/* If there is no replacement type, then it is just the address. /* If there is no replacement type, then it is just the address.
** We don't currently support this case. ** We don't currently support this case.
*/ */
if (RType == Param->Type) { if (RType == Param->Type) {
Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type));
}
} }
}
/* Check for a register variable */
if (SymIsRegVar (Param)) {
/* Check for a register variable */ /* Allocate space */
if (SymIsRegVar (Param)) { int Reg = F_AllocRegVar (CurrentFunc, RType);
/* Allocate space */ /* Could we allocate a register? */
int Reg = F_AllocRegVar (CurrentFunc, RType); if (Reg < 0) {
/* No register available: Convert parameter to auto */
CvtRegVarToAuto (Param);
} else {
/* Remember the register offset */
Param->V.R.RegOffs = Reg;
/* Could we allocate a register? */ /* Generate swap code */
if (Reg < 0) { g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType));
/* No register available: Convert parameter to auto */ }
CvtRegVarToAuto (Param);
} else {
/* Remember the register offset */
Param->V.R.RegOffs = Reg;
/* Generate swap code */
g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType));
} }
}
/* Next parameter */ /* Next parameter */
Param = Param->NextSym; Param = Param->NextSym;
}
} }
/* Need a starting curly brace */ /* Need a starting curly brace */

View File

@@ -81,6 +81,12 @@ extern Function* CurrentFunc;
int F_CheckParamList (FuncDesc* D, int RequireAll);
/* Check and set the parameter sizes.
** If RequireAll is true, emit errors on parameters of incomplete types.
** Return true if all parameters have complete types.
*/
const char* F_GetFuncName (const Function* F); const char* F_GetFuncName (const Function* F);
/* Return the name of the current function */ /* Return the name of the current function */

View File

@@ -318,28 +318,38 @@ static void ReturnStatement (void)
/* Evaluate the return expression */ /* Evaluate the return expression */
hie0 (&Expr); hie0 (&Expr);
/* If we return something in a void function, print an error and /* If we return something in a function with void or incomplete return
** ignore the value. Otherwise convert the value to the type of the ** type, print an error and ignore the value. Otherwise convert the
** return. ** value to the type of the return.
*/ */
if (F_HasVoidReturn (CurrentFunc)) { if (F_HasVoidReturn (CurrentFunc)) {
Error ("Returning a value in function with return type void"); Error ("Returning a value in function with return type 'void'");
} else { } else {
/* Convert the return value to the type of the function result */
TypeConversion (&Expr, F_GetReturnType (CurrentFunc));
/* Load the value into the primary */ /* Check the return type first */
if (IsClassStruct (Expr.Type)) { ReturnType = F_GetReturnType (CurrentFunc);
/* Handle struct/union specially */ if (IsIncompleteESUType (ReturnType)) {
ReturnType = GetStructReplacementType (Expr.Type); /* Avoid excess errors */
if (ReturnType == Expr.Type) { if (ErrorCount == 0) {
Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); Error ("Returning a value in function with incomplete return type");
} }
LoadExpr (TypeOf (ReturnType), &Expr);
} else { } else {
/* Convert the return value to the type of the function result */
TypeConversion (&Expr, ReturnType);
/* Load the value into the primary */ /* Load the value into the primary */
LoadExpr (CF_NONE, &Expr); if (IsClassStruct (Expr.Type)) {
/* Handle struct/union specially */
ReturnType = GetStructReplacementType (Expr.Type);
if (ReturnType == Expr.Type) {
Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type));
}
LoadExpr (TypeOf (ReturnType), &Expr);
} else {
/* Load the value into the primary */
LoadExpr (CF_NONE, &Expr);
}
} }
} }