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;