9
0
Fork 0
nuttx-bb/misc/pascal/pascal/pgen.c

642 lines
19 KiB
C

/**********************************************************************
* pgen.c
* P-Code generation logic
*
* Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
**********************************************************************/
/**********************************************************************
* Included Files
**********************************************************************/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h" /* Configuration */
#include "keywords.h" /* Standard types */
#include "pasdefs.h" /* Common types */
#include "ptdefs.h" /* Token / symbol table definitions */
#include "podefs.h" /* Logical opcode definitions */
#include "pedefs.h" /* error code definitions */
#include "pas.h" /* Global variables */
#include "poff.h" /* For POFF file format */
#include "pofflib.h" /* For poff*() functions*/
#include "pinsn.h" /* (DEBUG only) */
#include "perr.h" /* error() */
#include "pproc.h" /* for actualParameterSize */
#include "pgen.h" /* (to verify prototypes in this file) */
/**********************************************************************
* Pre-processor Definitions
**********************************************************************/
#define UNDEFINED_LEVEL (-1)
#define INVALID_PCODE (-1)
#define LEVEL_DEFINED(l) ((int32_t)(l) >= 0)
#define PCODE_VALID(p) ((int32_t)(p) >= 0)
/**********************************************************************
* Global Variables
**********************************************************************/
/**********************************************************************
* Private Variables
**********************************************************************/
static int32_t g_currentStackLevelReference = UNDEFINED_LEVEL;
static uint32_t g_nStackLevelReferenceChanges = 0;
/***********************************************************************
* Private Function Prototypes
***********************************************************************/
/***********************************************************************
* Private Functions
***********************************************************************/
/***********************************************************************/
/* Generate a stack reference opcode to a global variable residing at
* static nesting level zero.
*/
static void
pas_GenerateLevel0StackReference(enum pcode_e eOpCode, STYPE *pVar)
{
/* Sanity checking. Double check nesting level and also since this is
* a level zero reference, then the offset must be positive
*/
if ((pVar->sLevel != 0) || (pVar->sParm.v.offset < 0))
{
error(eHUH);
}
else
{
/* Generate the P-code */
insn_GenerateDataOperation(eOpCode, pVar->sParm.v.offset);
/* If the variable is undefined, also generate a relocation
* record.
*/
if ((pVar->sParm.v.flags & SVAR_EXTERNAL) != 0)
{
(void)poffAddRelocation(poffHandle, RLT_LDST,
pVar->sParm.v.symIndex, 0);
}
}
}
/***********************************************************************/
/* There are some special P-codes for accessing stack data at static
* nesting level 0. Check if the specified opcode is one of those. If
* so, return the mapped opcode. Otherwise, return INVALID_PCODE.
*/
static int32_t
pas_GetLevel0Opcode(enum pcode_e eOpCode)
{
switch (eOpCode)
{
case opLDS: return opLD;
case opLDSH: return opLDH;
case opLDSB: return opLDB;
case opLDSM: return opLDM;
case opSTS: return opST;
case opSTSB: return opSTB;
case opSTSM: return opSTM;
case opLDSX: return opLDX;
case opLDSXB: return opLDXB;
case opLDSXM: return opLDXM;
case opSTSX: return opSTX;
case opSTSXB: return opSTXB;
case opSTSXM: return opSTXM;
case opLAS: return opLA;
case opLASX: return opLAX;
default: return INVALID_PCODE;
}
}
/***********************************************************************/
/* A new static nesting level has been encountered. Check if we need
* to reset the level stack pointer (LSP) register (assuming that the
* architecutre has one).
*/
static void
pas_SetLevelStackPointer(uint32_t dwLevel)
{
if (dwLevel != g_currentStackLevelReference)
{
/* Set the level stack pointer (LSP) register */
insn_SetStackLevel(dwLevel);
/* Remember the setting so that we do not reset the LSP until
* the level changes (or it is invalidated).
*/
g_currentStackLevelReference = dwLevel;
g_nStackLevelReferenceChanges++;
}
}
/***********************************************************************
* Public Functions
***********************************************************************/
/***********************************************************************/
/* Return the current setting of the level stack pointer (LSP) register
* -- assuming that the underlying architecure may have one.
*/
int32_t pas_GetCurrentStackLevel(void)
{
return g_currentStackLevelReference;
}
/***********************************************************************/
/* Invalidate the current stack level register setting. This will cause
* us to reset the LSP when the next stack level reference is encountered.
*/
void pas_InvalidateCurrentStackLevel(void)
{
g_currentStackLevelReference = UNDEFINED_LEVEL;
g_nStackLevelReferenceChanges++;
}
/***********************************************************************/
/* Set the stack level pointer to known value. This is done when in
* while and for loop processing. The value of the LSP will be that
* as sampled at the top of the lop not necessarily the value at the
* bottom of the loop.
*/
void pas_SetCurrentStackLevel(int32_t dwLsp)
{
g_currentStackLevelReference = dwLsp;
g_nStackLevelReferenceChanges++;
}
/***********************************************************************/
/* Get the number of changes made to the level stack pointer. This is
* useful by compiler logic to determine if the stack level pointer was
* ever changed by any logic path.
*/
uint32_t pas_GetNStackLevelChanges(void)
{
return g_nStackLevelReferenceChanges;
}
/***********************************************************************/
/* Generate the most simple of all P-codes */
void pas_GenerateSimple(enum pcode_e eOpCode)
{
insn_GenerateSimple(eOpCode);
}
/***********************************************************************/
/* Generate a P-code with a single data argument */
void pas_GenerateDataOperation(enum pcode_e eOpCode, int32_t dwData)
{
insn_GenerateDataOperation(eOpCode, dwData);
}
/***********************************************************************/
/* This function is called just before a multiple register operation is
* is generated. This should generate logic to specify the size of the
* multiple register operation (in bytes, not registers). This may translate
* into different operations on different architectures. Typically,
* this would generate a push of the size onto the stack or, perhaps,
* setting of a dedicated count register.
*/
void pas_GenerateDataSize(int32_t dwDataSize)
{
insn_GenerateDataSize(dwDataSize);
}
/***********************************************************************/
/* Generate a floating point operation */
void pas_GenerateFpOperation(uint8_t fpOpcode)
{
insn_GenerateFpOperation(fpOpcode);
}
/***********************************************************************/
/* Generate an IO operation */
void pas_GenerateIoOperation(uint16_t ioOpcode, uint16_t fileNumber)
{
insn_GenerateIoOperation(ioOpcode, fileNumber);
}
/***********************************************************************/
/* Generate a pseudo call to a built-in, standard pascal function */
void pas_BuiltInFunctionCall(uint16_t libOpcode)
{
insn_BuiltInFunctionCall(libOpcode);
}
/***********************************************************************/
/* Generate a reference to data on the data stack using the specified
* level and offset.
*/
void pas_GenerateLevelReference(enum pcode_e eOpCode, uint16_t wLevel,
int32_t dwOffset)
{
/* Is this variable declared at level 0 (i.e., it has global scope)
* that is being offset via a nesting level?
*/
if (wLevel == 0)
{
int32_t level0Opcode = pas_GetLevel0Opcode(eOpCode);
if (PCODE_VALID(level0Opcode))
{
insn_GenerateDataOperation(level0Opcode, dwOffset);
return;
}
}
/* We get here if the reference is at some static nesting level
* other that zero OR if there is no special PCode to reference
* data at static nesting level 0 for this operation.
*
* Check if we have to change the level stack pointer (LSP) register
* (assuming that the architecture has one).
*/
pas_SetLevelStackPointer(wLevel);
/* Then generate the opcode passing the level in the event that the
* architecture does not have an LSP.
*/
insn_GenerateLevelReference(eOpCode, wLevel, dwOffset);
}
/***********************************************************************/
/* Generate a stack reference opcode, handling references to undefined
* stack offsets.
*/
void pas_GenerateStackReference(enum pcode_e eOpCode, STYPE *pVar)
{
/* Is this variable declared at level 0 (i.e., it has global scope)
* that is being offset via a nesting level?
*/
if (pVar->sLevel == 0)
{
int32_t level0Opcode = pas_GetLevel0Opcode(eOpCode);
if (PCODE_VALID(level0Opcode))
{
pas_GenerateLevel0StackReference(level0Opcode, pVar);
return;
}
}
/* We get here if the reference is at some static nesting level
* other that zero OR if there is no special PCode to reference
* data at static nesting level 0 for this operation.
*
* Check if we have to change the level stack pointer (LSP) register
* (assuming that the architecture has one).
*/
pas_SetLevelStackPointer(pVar->sLevel);
/* Generate the P-Code at the defined offset and with the specified
* static level offset (in case that the architecture does not have
* an LSP)
*/
insn_GenerateLevelReference(eOpCode, (level - pVar->sLevel),
pVar->sParm.v.offset);
}
/***********************************************************************/
/* Generate a procedure call and an associated relocation record if the
* called procedure is external.
*/
void
pas_GenerateProcedureCall(STYPE *pProc)
{
/* sLevel is the level at which the procedure was declared. We need
* to set the SLP to this value prior to the call (on some architectures
* where the SLP is pushed onto the stack by the procedure
* call).
*/
pas_SetLevelStackPointer(pProc->sLevel);
/* Then generate the procedure call (passing the level again for those
* architectures that do not support the SLP.
*/
insn_GenerateProcedureCall(pProc->sLevel, pProc->sParm.p.label);
/* If the variable is undefined, also generate a relocation
* record.
*/
#if 0 /* Not yet */
if ((pVar->sParm.p.flags & SVAR_EXTERNAL) != 0)
{
/* For now */
# error "Don't know what last parameter should be"
(void)poffAddRelocation(poffHandle, RLT_PCAL,
pVar->sParm.p.symIndex,
0);
}
#endif
/* Any logic after the procedure/function call return must assume
* that the last level reference is unknown.
*/
pas_InvalidateCurrentStackLevel();
}
/***********************************************************************/
void pas_GenerateLineNumber(uint16_t wIncludeNumber, uint32_t dwLineNumber)
{
insn_GenerateLineNumber(wIncludeNumber, dwLineNumber);
}
/***********************************************************************/
void pas_GenerateDebugInfo(STYPE *pProc, uint32_t dwReturnSize)
{
int i;
/* Allocate a container to pass the proc information to the library */
uint32_t nparms = pProc->sParm.p.nParms;
poffLibDebugFuncInfo_t *pContainer = poffCreateDebugInfoContainer(nparms);
/* Put the proc information into the container */
pContainer->value = pProc->sParm.p.label;
pContainer->retsize = dwReturnSize;
pContainer->nparms = nparms;
/* Add the argument information to the container */
for (i = 0; i < nparms; i++)
{
pContainer->argsize[i] = actualParameterSize(pProc, i+1);
}
/* Add the contained information to the library */
poffAddDebugFuncInfo(poffHandle, pContainer);
/* Release the container */
poffReleaseDebugFuncContainer(pContainer);
}
/***********************************************************************/
/* Generate description of a level 0 stack variable that can be
* exported by a unit.
*/
void pas_GenerateStackExport(STYPE *pVar)
{
poffLibSymbol_t symbol;
#if CONFIG_DEBUG
/* Get the parent type of the variable */
STYPE *typePtr = pVar->sParm.v.parent;
/* Perform some sanity checking:
* - Must have a parent type
* - Must not be declared external
* - Must be declared at static nesting level zero
*/
if ((!typePtr) ||
((pVar->sParm.v.flags & SVAR_EXTERNAL) != 0) ||
(pVar->sLevel != 0))
{
error(eSYMTABINTERNAL);
}
#endif
/* Create the symbol structure */
symbol.type = STT_DATA;
symbol.align = STA_8BIT; /* for now */
symbol.flags = STF_NONE;
symbol.name = pVar->sName;
symbol.value = pVar->sParm.v.offset;
symbol.size = pVar->sParm.v.size;
/* Add the symbol to the symbol table */
(void)poffAddSymbol(poffHandle, &symbol);
}
/***********************************************************************/
/* Generate description of a level 0 stack variable that must be
* imported by a program or unit from a unit.
*/
void pas_GenerateStackImport(STYPE *pVar)
{
poffLibSymbol_t symbol;
#if CONFIG_DEBUG
/* Get the parent type of the variable */
STYPE *typePtr = pVar->sParm.v.parent;
/* Perform some sanity checking
* - Must have a parent type
* - Must be declared external
* - Must be declared at static nesting level zero
*/
if ((!typePtr) ||
((pVar->sParm.v.flags & SVAR_EXTERNAL) == 0) ||
(pVar->sLevel != 0))
{
error(eSYMTABINTERNAL);
}
#endif
/* Create the symbol structure */
symbol.type = STT_DATA;
symbol.align = STA_8BIT; /* for now */
symbol.flags = STF_UNDEFINED;
symbol.name = pVar->sName;
symbol.value = pVar->sParm.v.offset; /* for now */
symbol.size = pVar->sParm.v.size;
/* Add the symbol to the symbol table */
pVar->sParm.v.symIndex = poffAddSymbol(poffHandle, &symbol);
}
/***********************************************************************/
/* Generate description of a level 0 procedure or function that can be
* exported by a unit.
*/
void pas_GenerateProcExport(STYPE *pProc)
{
poffLibSymbol_t symbol;
#if CONFIG_DEBUG
/* Get the parent type of the function (assuming it is a function) */
STYPE *typePtr = pProc->sParm.p.parent;
/* Perform some sanity checking */
/* Check for a function reference which must have a valid parent type */
if ((pProc->sKind == sFUNC) && (typePtr != NULL));
/* Check for a procedure reference which must not have a valid type */
else if ((pProc->sKind == sPROC) && (typePtr == NULL));
/* Anything else is an error */
else
error(eSYMTABINTERNAL);
/* The function / procedure should NOT be declared external and
* only procedures declared at static nesting level zero can
* be exported.
*/
if (((pProc->sParm.p.flags & SPROC_EXTERNAL) != 0) ||
(pProc->sLevel != 0))
error(eSYMTABINTERNAL);
#endif
/* Everthing looks okay. Create the symbol structure */
if (pProc->sKind == sPROC)
symbol.type = STT_PROC;
else
symbol.type = STT_FUNC;
symbol.align = STA_NONE;
symbol.flags = STF_NONE;
symbol.name = pProc->sName;
symbol.value = pProc->sParm.p.label;
symbol.size = 0;
/* Add the symbol to the symbol table */
(void)poffAddSymbol(poffHandle, &symbol);
}
/***********************************************************************/
/* Generate description of a level 0 procedure or function that must be
* imported by a program or unit from a unit.
*/
void pas_GenerateProcImport(STYPE *pProc)
{
poffLibSymbol_t symbol;
#if CONFIG_DEBUG
/* Get the parent type of the function (assuming it is a function) */
STYPE *typePtr = pProc->sParm.p.parent;
/* Perform some sanity checking */
/* Check for a function reference which must have a valid parent type */
if ((pProc->sKind == sFUNC) && (typePtr != NULL));
/* Check for a procedure reference which must not have a valid type */
else if ((pProc->sKind == sPROC) && (typePtr == NULL));
/* Anything else is an error */
else
error(eSYMTABINTERNAL);
/* The function / procedure should also be declared external and
* only procedures declared at static nesting level zero can
* be exported.
*/
if (((pProc->sParm.p.flags & SPROC_EXTERNAL) == 0) ||
(pProc->sLevel != 0))
error(eSYMTABINTERNAL);
#endif
/* Everthing looks okay. Create the symbol structure */
if (pProc->sKind == sPROC)
symbol.type = STT_PROC;
else
symbol.type = STT_FUNC;
symbol.align = STA_NONE;
symbol.flags = STF_UNDEFINED;
symbol.name = pProc->sName;
symbol.value = pProc->sParm.p.label;
symbol.size = 0;
/* Add the symbol to the symbol table */
pProc->sParm.p.symIndex = poffAddSymbol(poffHandle, &symbol);
}