Press enter to see results or esc to cancel.

Procedure Entry and Exit Code

Procedures and functions (including methods) also need initialization and finalization code.
This function generates entry code:

  • it generates code to create stack frame if needed
  • it generates code to copy all parameters by value
  • for constructor method it generates code to construct the object
Function GenerateProcedureEntryCode: Word;
Var NegativeSizeOfPassedParameters, StackFrameOffset: Word;
    DS_Changed: Boolean;
    N: Word;
    ProcedureIdentifierData: PProcedureIdentifierData;
    ProcedureParameterData: PProcedureParameterData;
    ParameterTypeDef: PTypeDefinition;
    ParameterTypeDefRec: PtrRec absolute ParameterTypeDef;

  Procedure CheckAndCopyParameterValue (TypeDef: PTypeDefinition; ParameterFlags: TVarFlagsSet);
  Var ValueParameterCopySize, OffsetTo_VMT_Offset: Word;
      ObjectTypeDefinition: PObjectTypeDefinition absolute TypeDef;

    Procedure CopyParameterValue;
    Var DataOffset: Byte;

      Procedure Multiply_AX (Factor: Word);
      Var Expression: TExpression;
      begin
        With Expression do
          begin
            Location := elRegister;
            DataType := itWord;
            UsedRegisters := [urAX];
            LocationData.Register := rAX;
            IntegerMultiplicationWithConstant (rAX, Factor);
          end;
      end;

    begin
      If not DS_Changed then
        begin
          DS_Changed := True;
          GenerateInstruction_Word ($D38C);
          GenerateInstruction_Word ($C38E);
          GenerateInstruction_Word ($DB8C);
          GenerateInstruction_Byte ($FC);
        end;
      If not (vfOpenParameter in ParameterFlags) then
        begin
          GenerateInstructionForStackFrameDisplacement ($8D, $3E, StackFrameOffset);
          GenerateInstructionForStackFrameDisplacement ($C5, $36, NegativeSizeOfPassedParameters);
          Case TypeDef^.BaseType of
            btString: begin
                        GenerateInstruction_Byte ($AC);
                        If TypeDef^.Size <> 0 then
                          begin
                            GenerateInstruction_TwoBytes ($3C, TypeDef^.Size - 1);
                            GenerateInstruction_Word ($0272);
                            GenerateInstruction_TwoBytes ($B0, TypeDef^.Size - 1);
                          end;
                        GenerateInstruction_Word ($91AA);
                        GenerateInstruction_Word ($ED30);
                      end;
            btSet: begin
                     PSetTypeDefinition (TypeDef)^.GetSetSizeAndLowestElementDataOffset (DataOffset);
                     If DataOffset <> 0 then
                       begin
                         GenerateInstruction_Word ($C683);
                         GenerateInstruction_Byte (DataOffset);
                       end;
                     GenerateInstruction_Byte ($B9);
                     GenerateInstruction_Word (TypeDef^.Size);
                   end;
            else begin
                   GenerateInstruction_Byte ($B9);
                   GenerateInstruction_Word (TypeDef^.Size);
                 end;
          end;
          GenerateInstruction_Word ($A4F3);
          Exit;
        end;
      GenerateInstructionForStackFrameDisplacement ($8B, $06, NegativeSizeOfPassedParameters);
      GenerateInstruction_Byte ($40);
      Multiply_AX (TypeDef^.Size);
      GenerateInstruction_Word ($C189);
      If StackChecking in StatementCompilerSwitches then
        begin
          GenerateInstruction_Word ($DB8E);
          GenerateInstruction_CALL_FAR (SysProc_StackCheck);
        end;
      GenerateInstruction_Word ($CC29);
      If TypeDef^.Size = 1 then
        begin
          GenerateInstruction_Word ($E483);
          GenerateInstruction_Byte ($FE);
        end;
      GenerateInstruction_Word ($E789);
      GenerateInstructionForStackFrameDisplacement ($C5, $36, NegativeSizeOfPassedParameters + 2);
      GenerateInstructionForStackFrameDisplacement ($89, $3E, NegativeSizeOfPassedParameters + 2);
      GenerateInstructionForStackFrameDisplacement ($8C, $06, NegativeSizeOfPassedParameters + 4);
      Inc (NumberOfLocalParameters);
      GenerateInstruction_Word ($A4F3);
    end;

  begin
    Dec (NegativeSizeOfPassedParameters, SizeOfPassedParameter (TypeDef, ParameterFlags, ValueParameterCopySize, False));
    Dec (StackFrameOffset, ValueParameterCopySize);
    If not (vfOpenParameter in ParameterFlags) or (ParameterFlags * [vfConst, vfVar] <> []) then
      begin
        If ValueParameterCopySize > 0 then
          begin
            If (ValueParameterCopySize <> 1) and (WordAlignment in ModuleCompilerSwitches) then
              StackFrameOffset := StackFrameOffset and $FFFE;
            CopyParameterValue;
          end;
      end else CopyParameterValue;
    If (ParameterFlags * [vfOpenParameter, vfVar] = []) and (TypeDef^.BaseType = btObject) and
       (ObjectTypeDefinition^.VMT_Size <> 0) then
      begin
        If ValueParameterCopySize <> 0 then
          OffsetTo_VMT_Offset := StackFrameOffset else
            OffsetTo_VMT_Offset := NegativeSizeOfPassedParameters;
        Inc (OffsetTo_VMT_Offset, PObjectTypeDefinition (TypeDef)^.OffsetOf_VMT_Offset);
        GenerateInstructionForStackFrameDisplacement (MOV_RegisterOrMemory_Immediate_16Bit,
                                               rmSS_BP_Displacement,
                                               OffsetTo_VMT_Offset);
      GenerateReference (Seg (TypeDef^), PObjectTypeDefinition (TypeDef)^.VMT_TypedConstantsBlockRecordOffset, 0,
                             [rfDataSegment, rfConstant, rfOffset]);
      end;
  end;

  Procedure ConstructObject (OffsetOf_VMT_Offset: Word);
  begin
    GenerateInstruction_MOV_16BitReg_Immediate (rDI, OffsetOf_VMT_Offset);
    GenerateInstruction_CALL_FAR (SysProc_Construct);
    GenerateCodeForNearJump (LastJumpToProgramBlockExit, $74);
  end;

begin
  ProcedureIdentifierData := Ptr (SymbolTable [stMain].Segment, ProcedureIdentifierDataOffset);
  With ProcedureIdentifierData^ do
    begin
      If pfStackFrame in Flags then CreateStackFrame;
      If (ProcedureTypeDefinition.NumberOfParameters <> 0) and not (pfAssembler in Flags) then
        begin
          NegativeSizeOfPassedParameters := OffsetAfterLastParameter;
          StackFrameOffset := FunctionResultNegativeSize;
          DS_Changed := False;
          ProcedureParameterData := PProcedureParameterData (PChar (ProcedureIdentifierData) + 10 + 14);
          For N := 1 to ProcedureTypeDefinition.NumberOfParameters do
            begin
              ParameterTypeDefRec.Seg := PUnitIdentifierData (Ptr (Seg (ProcedureParameterData^),
                                           ProcedureParameterData^.UnitTypeOffsets.UnitIdentifierData))^.UnitSegment;
              ParameterTypeDefRec.Ofs := ProcedureParameterData^.UnitTypeOffsets.TypeOffset;
              CheckAndCopyParameterValue (ParameterTypeDef, ProcedureParameterData^.VarFlags);
              Inc (ProcedureParameterData);
            end;
          If DS_Changed then GenerateInstruction_Word (MOV_DS_BX);
        end;
      If pfConstructor in Flags then
        ConstructObject (PObjectTypeDefinition (Ptr (Seg (ProcedureIdentifierData^),
                                                          OuterBlockProcedureIdentifier))^.OffsetOf_VMT_Offset);
    end;
  GenerateProcedureEntryCode := EndSubroutine
end;

This function generates exit code for procedure or function:

  • for constructors: a pointer to object is loaded to DX:AX register pair
  • for destructors: SysProc_Destruct is called to deallocate the object from heap
  • for functions: the result is loaded to registers or FPU stack
  • for all procedures it also destroyes stack frame
Function GenerateProcedureExitCode: Word;
Var ProcedureIdentifierData: PProcedureIdentifierData;
    ResultType: PTypeDefinition;
    FOpCode: WordRec;
    ReturnOpCode: Byte;

  Procedure LoadFunctionResultToRegisters (StackFrameOffset: ShortInt; ReturnSize: Word);
  begin
    Case ReturnSize of
      1: GenerateInstruction_Word ($468A);
      2: GenerateInstruction_Word ($468B);
      4: begin
           GenerateInstruction_Word ($468B);
           GenerateInstruction_Byte (StackFrameOffset);
           Inc (StackFrameOffset, 2);
           GenerateInstruction_Word ($568B);
         end;
      else begin
             GenerateInstruction_Word ($468B);
             GenerateInstruction_Byte (StackFrameOffset);
             Inc (StackFrameOffset, 2);
             GenerateInstruction_Word ($5E8B);
             GenerateInstruction_Byte (StackFrameOffset);
             Inc (StackFrameOffset, 2);
             GenerateInstruction_Word ($568B);
           end;
    end;
    GenerateInstruction_Byte (StackFrameOffset);
  end;

begin
  ProcedureIdentifierData := Ptr (SymbolTable [stMain].Segment, ProcedureIdentifierDataOffset);
  With ProcedureIdentifierData^ do
    begin
      If pfConstructor in Flags then
        begin
          GenerateInstruction_Word ($46C4);
          GenerateInstruction_Byte ($06);
          GenerateInstruction_Word ($C28C);
        end else If pfDestructor in Flags then
          begin
            GenerateInstruction_MOV_16BitReg_Immediate (rDI,
              PObjectTypeDefinition (Ptr (Seg (ProcedureIdentifierData^),
                                          OuterBlockProcedureIdentifier))^.OffsetOf_VMT_Offset);
            GenerateInstruction_CALL_FAR (SysProc_Destruct);
          end;
      If (ProcedureTypeDefinition.ResultTypeOffset.UnitIdentifierData <> 0) and not (pfAssembler in Flags) then
        begin
          ResultType := PointerFromOffsets (ProcedureTypeDefinition.ResultTypeOffset);
          Case ResultType^.BaseType of
            btPointer,
            btReal,
            btInteger..btEnumeration: LoadFunctionResultToRegisters (FunctionResultNegativeSize, ResultType^.Size);
            btExtended: begin
                          FOpCode.Word := $06D9 or Byte (ResultType^.DataType);
                          If fpExtended in ResultType^.DataType then FOpCode.ByteH := $2E;
                          GenerateFPUInstructionForStackFrameDisplacement (FOpCode.Word, FunctionResultNegativeSize);
                          GenerateInstruction_TwoBytes ($CD, $3D);
                        end;
          end;
        end;
      If pfStackFrame in Flags then DestroyStackFrame;
      If pfFar in Flags then ReturnOpCode := $CB else ReturnOpCode := $C3;
      Case PushedParametersSize of
        0: GenerateInstruction_Byte (ReturnOpCode);
        else begin
               GenerateInstruction_Byte (ReturnOpCode - 1);
               GenerateInstruction_Word (PushedParametersSize);
             end;
      end;
    end;
  GenerateProcedureExitCode := EndSubroutine;
end;

This function returns the size of passed parameter and the size of value parameter that needs to be copied to stack frame.

Function SizeOfPassedParameter (Var TypeDef: PTypeDefinition; Var ParameterFlags: TVarFlagsSet;
                                Var ValueParameterCopySize: Word; AssemblerProcedure: Boolean): Word;
Var Size: Word;
    ExitFlag: Boolean;

  Procedure PassPointer;
  begin
    ExitFlag := True;
    If (vfConst in ParameterFlags) or AssemblerProcedure then
      begin
        Include (ParameterFlags, vfVar);
        If TypeDef^.BaseType <> btSet then SizeOfPassedParameter := 4 else
          begin
            TypeDef := PointerFromOffsets (POrdinalTypeDefinition (
                         PointerFromOffsets (PSetTypeDefinition (TypeDef)^.BaseSetTypeOffset))^.OrdinalType);
            SizeOfPassedParameter := 4;
          end;
      end else begin
                 ValueParameterCopySize := Size;
                 SizeOfPassedParameter := 4;
               end;
  end;

begin
  ExitFlag := False;
  ValueParameterCopySize := 0;
  If vfOpenParameter in ParameterFlags then SizeOfPassedParameter := 6 else
    If vfVar in ParameterFlags then SizeOfPassedParameter := 4 else
      begin
        Size := TypeDef^.Size;
        Case TypeDef^.BaseType of
          btUntyped..btText: Case Size of
                               1, 2, 4:
                               else PassPointer;
                             end;
          btSet: If it32Bytes in TypeDef^.DataType then PassPointer;
          btString: PassPointer;
        end;
        If not ExitFlag then SizeOfPassedParameter := (Size + 1) and $FFFE;
      end;
end;