Press enter to see results or esc to cancel.

Saving and Restoring Expressions

During expression calculation Turbo Pascal stores the value in registers. However, they are limited and some operations can be performed only with specific registers. Therefore, Turbo Pascal compiler needs some way to save and restore expressions. Using stack is an elegant way to temprarily save expressions until registers are needed for other calculations. The power of this mechanism lies in the possibility to use non-linear way of generating code. This means that the compiler can generate code for some part of the calculation and then according to used registers (which are tracked) proceed with next calculation or save it to the stack to free registers. The first procedure saves the expression and pushes 2 or 4 bytes to stack.

Procedure TExpression.Push;
begin
  Case Location of
    elMemory: begin
                If not (it16Bit in DataType) then
                  begin
                    LoadExpressionToRegisters (urAX);
                    GenerateInstruction_Byte (PUSH_AX);
                  end else begin
                             If it32Bit in DataType then
                               begin
                                 Inc (Value.Offset, 2);
                                 GenerateInstructionWithExpressionInMemOrReg (PUSH_RegMem, PUSH_Mem);
                                 Dec (Value.Offset, 2);
                               end;
                             GenerateInstructionWithExpressionInMemOrReg (PUSH_RegMem, PUSH_Mem);
                           end;
              end;
    elRegister: begin
                  If it32Bit in DataType then GenerateInstruction_Byte (PUSH_DX);
                  GenerateInstruction_Byte (PUSH_AX);
                end;
    elPointerToMemory: begin
                         If not (efSegment in LocationData.Flags) then
                           begin
                             LoadOffsetTo_DI;
                             If not (it32Bit in DataType) then
                               begin
                                 GenerateInstruction_Byte (PUSH_DI);
                                 Location := elStack;
                                 Exit;
                               end;
                           end;
                         If LocationData.Flags * SegmentMask = [] then
                           begin
                             GenerateInstructionWithSegment (MOV_AX_Immediate);
                             GenerateInstruction_Byte (PUSH_AX);
                           end else GenerateInstruction_Byte (PUSH_SegmentRegister or ExpressionSegmentRegister);
                         If it32Bit in DataType then GenerateInstruction_Byte (PUSH_DI);
                       end;
    else begin
           If Instructions80286 in StatementCompilerSwitches then
             begin
               If it32Bit in DataType then GenerateInstruction_PUSH_Immediate (Value.LongRec.WordH);
               If it16Bit in DataType then GenerateInstruction_PUSH_Immediate (Value.LongRec.WordL) else
                 GenerateInstruction_PUSH_Immediate (Value.ShortInt);
             end else begin
                        LoadExpressionToRegisters (urAX);
                        If it32Bit in DataType then GenerateInstruction_Byte (PUSH_DX);
                        GenerateInstruction_Byte (PUSH_AX);
                      end;
         end;
  end;
  Location := elStack;
end;

This procedure calculates the expression and pushes it on the stack.

Procedure TExpression.PushExpression;
begin
  Calculate;
  Push;
  EndIntermediateCodeSubroutine;
end;

This procedure restores pushed expression to registers. It avoids already used registers and pops data to free ones.

Procedure TExpression.PopToRegisters (AlreadyUsedRegisters: TUsedRegistersSet);
Var OpCode, OpCode2: Byte;
    NewUsedRegs: TUsedRegistersSet;
    Register: Byte;
begin
  If Location = elStack then
    begin
      Location := elRegister;
      Case it32Bit in DataType of
        True: begin
                OpCode := POP_AX;
                OpCode2 := POP_DX;
                Register := rDX_AX;
                NewUsedRegs := [urDX, urAX];
                If [urDX, urAX] * AlreadyUsedRegisters <> [] then
                  begin
                    OpCode := POP_CX;
                    OpCode2 := POP_BX;
                    Register := rBX_CX;
                    NewUsedRegs := [urBX, urCX];
                  end;
                GenerateInstruction_TwoBytes (OpCode, OpCode2);
              end;
        else begin
               Case urAX in AlreadyUsedRegisters of
                 True: Case urDX in AlreadyUsedRegisters of
                         True: Case urCX in AlreadyUsedRegisters of
                                 True: begin
                                         OpCode := POP_BX;
                                         Register := rBX;
                                         NewUsedRegs := [urBX];
                                       end;
                                 else begin
                                        OpCode := POP_CX;
                                        Register := rCX;
                                        NewUsedRegs := [urCX];
                                      end;
                               end;
                         else begin
                                OpCode := POP_DX;
                                Register := rDX;
                                NewUsedRegs := [urDX];
                              end;
                       end;
                 else begin
                        OpCode := POP_AX;
                        Register := rAX;
                        NewUsedRegs := [urAX];
                      end;
               end;
               GenerateInstruction_Byte (OpCode);
             end;
      end;
      UsedRegisters := UsedRegisters + NewUsedRegs;
      LocationData.Register := Register;
    end;
end;