Set Operations
Constant sets are located into temprary buffer and expression Value
holds offset to this buffer. Non-constant sets are always expanded to the stack frame. This procedure calculates operation between two constant sets.
Procedure ConstantSetOperations;
Var LeftSet, RightSet: PSetOfByte;
begin
LeftSet := Ptr (DSeg, LeftExpression.Value.Word);
RightSet := Ptr (DSeg, RightExpression.Value.Word);
Case Operation of
Calc_Add: LeftSet^ := LeftSet^ + RightSet^;
Calc_Subtract: LeftSet^ := LeftSet^ - RightSet^;
Calc_Multiply: LeftSet^ := LeftSet^ * RightSet^;
Calc_IsEqual: LeftExpression.SetConstantBooleanExpression (LeftSet^ = RightSet^);
Calc_IsNotEqual: LeftExpression.SetConstantBooleanExpression (LeftSet^ <> RightSet^);
Calc_IsLowerOrEqual: LeftExpression.SetConstantBooleanExpression (LeftSet^ <= RightSet^);
Calc_IsGreaterOrEqual: LeftExpression.SetConstantBooleanExpression (LeftSet^ >= RightSet^);
end;
Case Operation of
Calc_Add,
Calc_Subtract,
Calc_Multiply: LeftExpression.SetDataTypeOfSet;
end;
end;
This procedure checks expression for small set and sets its data type accordingly.
Procedure TExpression.SetDataTypeOfSet;
Var SetAddress: PWord;
First16: Word;
N: Byte;
SizeType: TIntegerTypeSet;
Zero: Boolean;
begin
SetAddress := Ptr (DSeg, Value.Word);
First16 := SetAddress^;
SizeType := [it32Bytes];
Zero := True;
For N := 1 to 15 do
begin
Inc (SetAddress);
If SetAddress^ <> 0 then Zero := False;
end;
If Zero then
Case Hi (First16) of
0: SizeType := [itUnsigned];
else SizeType := [itUnsigned,it16Bit];
end;
DataType := SizeType;
end;
This procedure generates code for operation between sets: union (+
), intersection (*
), difference (-
) and relations (<=
, >=
).
Procedure GenerateCodeForSetOperations;
Var CommonSetType: TIntegerTypeSet;
TempExpression: TExpression;
begin
CommonSetType := LeftExpression.DataType + RightExpression.DataType;
If not (it32Bytes in CommonSetType) then
begin
LeftExpression.LoadConstantSmallSetIntoValue;
LeftExpression.ExtendInteger (CommonSetType);
RightExpression.LoadConstantSmallSetIntoValue;
RightExpression.ExtendInteger (CommonSetType);
Case Operation of
Calc_Subtract,
Calc_IsLowerOrEqual: RightExpression.NOT_Integer;
Calc_IsGreaterOrEqual: begin
ExchangeLeftAndRightExpression;
RightExpression.NOT_Integer;
end;
end;
Case Operation of
Calc_Add: Operation := Calc_OR;
Calc_Subtract,
Calc_Multiply: Operation := Calc_AND;
Calc_IsLowerOrEqual: begin
Operation := Calc_AND;
IntegerOperations;
LeftExpression.SetExpressionToBooleanJump (JE);
Exit;
end;
end;
IntegerOperations;
Exit;
end;
Case Operation of
Calc_IsEqual..Calc_IsLowerOrEqual: LeftExpression.ExpandSetToStackFrameAndPushPointer (32);
else LeftExpression.ExpandSetToStackFrameAndPushPointer (0);
end;
RightExpression.ExpandSetToStackFrameAndPushPointer (32);
If Operation = Calc_IsLowerOrEqual then
begin
TempExpression := LeftExpression;
LeftExpression := RightExpression;
RightExpression := TempExpression;
end;
ConvertToPointerAndPushBothExpressions (LeftExpression, RightExpression);
CalculateBothExpressions (LeftExpression, RightExpression);
If Operation = Calc_IsLowerOrEqual then
begin
TempExpression := LeftExpression;
LeftExpression := RightExpression;
RightExpression := TempExpression;
end;
Case Operation of
Calc_Add,
Calc_Subtract,
Calc_Multiply: begin
Case Operation of
Calc_Add: GenerateInstruction_CALL_FAR (SysProc_ZUnion);
Calc_Subtract: GenerateInstruction_CALL_FAR (SysProc_ZDifference);
Calc_Multiply: GenerateInstruction_CALL_FAR (SysProc_ZIntersect);
end;
LeftExpression.EndIntermediateCodeSubroutine;
LeftExpression.UsedRegisters := [urBX, urDX, urCX, urAX];
end;
else begin
Case Operation of
Calc_IsEqual,
Calc_IsNotEqual: GenerateInstruction_CALL_FAR (SysProc_ZEqual);
Calc_IsGreaterOrEqual,
Calc_IsLowerOrEqual: GenerateInstruction_CALL_FAR (SysProc_ZRelation);
end;
LeftExpression.EndIntermediateCodeSubroutine;
LeftExpression.UsedRegisters := [urBX, urDX, urCX, urAX];
Case Operation of
Calc_IsNotEqual: LeftExpression.SetExpressionToBooleanJump (JNE);
else LeftExpression.SetExpressionToBooleanJump (JE);
end;
end;
end;
end;
This procedure loads small set (16 elements or less) into Value.Word
from memory at Value.Pointer
.
Procedure TExpression.LoadConstantSmallSetIntoValue;
begin
If Location = elConstant then Value.LongInt := Word (Ptr (DSeg, Value.Word)^);
end;
This procedure generates code for operator IN
. If set is constant it generates special code to compare ranges of set elements.
Procedure GenerateCodeForOperator_IN (Var ElementExpression, SetExpression: TExpression);
Var ExpressionSet: PSetOfByte;
Index, StartIndex, N, FoundJumpOpCode: Byte;
_8_OrMoreElementRanges: Boolean;
Function SearchForSetElementRange: Boolean;
begin
SearchForSetElementRange := False;
Repeat
If Index in ExpressionSet^ then Break;
Inc (Index);
If Index = 0 then Exit;
until False;
StartIndex := Index;
Repeat
If not (Index in ExpressionSet^) then Break;
Inc (Index);
until Index = 0;
SearchForSetElementRange := True;
end;
begin
CheckElementAndSetCompatibility (ElementExpression, SetExpression);
ElementExpression.CheckOrdinalRange (Ptr (SystemUnitSegment, Byte_TypeOffset));
ExpressionSet := PSetOfByte (Ptr (DSeg, SetExpression.Value.Word));
If SetExpression.Location <> elConstant then
begin
OperationWithSet (ElementExpression, SetExpression, $01, TEST_Memory_AL, TEST_Memory_ImmediateByte);
Exit;
end;
If ElementExpression.Location = elConstant then
begin
ElementExpression.SetConstantBooleanExpression (ElementExpression.Value.Byte in ExpressionSet^);
Exit;
end;
Index := 0; { Set is known at compile time }
If not SearchForSetElementRange then
begin
ElementExpression.SetConstantBooleanExpression (False);
Exit;
end;
If Index or StartIndex = 0 then
begin
ElementExpression.SetConstantBooleanExpression (True);
Exit;
end;
_8_OrMoreElementRanges := True;
For N := 1 to 8 do
If (Index = 0) or (not SearchForSetElementRange) then
begin
_8_OrMoreElementRanges := False;
Break;
end;
If _8_OrMoreElementRanges then
begin
SetExpression.ExpandSetToStackFrameAndPushPointer (32);
OperationWithSet (ElementExpression, SetExpression, $01, TEST_Memory_AL, TEST_Memory_ImmediateByte);
Exit;
end;
ElementExpression.Calculate;
ElementExpression.LoadExpressionToRegisters (urAX);
With ElementExpression do
begin
Location := elBoolean;
DataType := [];
Value.ShortCircuitJumps := 0;
end;
Index := 0;
FoundJumpOpCode := 0;
While SearchForSetElementRange do
begin
Repeat
If FoundJumpOpCode <> 0 then GenerateCodeForNearJump (ElementExpression.Value.LastJumpToTrue, FoundJumpOpCode);
Dec (Index);
If Index <> StartIndex then
begin
If StartIndex <> 0 then
begin
GenerateInstruction_TwoBytes (CMP_AL_Immediate, StartIndex);
FoundJumpOpCode := JNB;
If Index = $FF then Break;
GenerateCodeForNearJump (ElementExpression.Value.LastJumpToFalse, JB);
end;
GenerateInstruction_TwoBytes (CMP_AL_Immediate, Index);
FoundJumpOpCode := JBE;
Break;
end;
GenerateInstruction_TwoBytes (CMP_AL_Immediate, StartIndex);
FoundJumpOpCode := JE;
until True;
Inc (Index);
If Index = 0 then Break;
end;
With ElementExpression do
begin
LocationData.JumpIfTrueOpCode := FoundJumpOpCode;
ElementExpression.EndIntermediateCodeSubroutine;
TypeDefPtr := Ptr (SystemUnitSegment, Boolean_TypeOffset);
end;
end;
This procedure checks compatibility between element type and set element type and reports error if they don’t match.
Procedure CheckElementAndSetCompatibility (Var ElementExpression, SetExpression: TExpression);
begin
If ElementExpression.TypeDefPtr^.BaseType < btInteger then Error (OperandTypesDoNotMatchOperator);
If SetExpression.TypeDefPtr^.BaseType <> btSet then Error (OperandTypesDoNotMatchOperator);
If PSetTypeDefinition (SetExpression.TypeDefPtr)^.BaseSetTypeOffset.TypeOffset <> 0 then
begin
If PointerFromOffsets (POrdinalTypeDefinition (ElementExpression.TypeDefPtr)^.OrdinalType) <>
PointerFromOffsets (POrdinalTypeDefinition (
PointerFromOffsets (PSetTypeDefinition (SetExpression.TypeDefPtr)^.BaseSetTypeOffset))^.OrdinalType) then
Error (TypeMismatch);
end;
end;
This procedure generates code with general non-constant set operation in memory.
Procedure OperationWithSet (Var ElementExpression, SetExpression: TExpression; Mask, TestMemoryRegisterOpCode: Byte; TestMemoryImmediateOpCode: Word);
Var SetSize: Byte;
SetElementLowerLimit_div_8: Byte;
MaskWord: WordRec;
Function RegisterSetSizeInBits: Byte;
begin
If it16Bit in SetExpression.DataType then RegisterSetSizeInBits := 16 else RegisterSetSizeInBits := 8;
end;
Procedure GetSetSizeInMemory;
begin
If SetExpression.Location = elMemory then
SetSize :=
PSetTypeDefinition (SetExpression.TypeDefPtr)^.GetSetSizeAndLowestElementDataOffset (SetElementLowerLimit_div_8)
else begin
PopPointerAndConvertToMemory (SetExpression);
SetSize := 32;
SetElementLowerLimit_div_8 := 0;
end;
end;
begin
TestSetInForElementMask := Mask;
TestMemoryWithRegisterOpCode := TestMemoryRegisterOpCode;
TestMemoryWithImmediateOpCode := TestMemoryImmediateOpCode;
LastJumpToFalse := 0;
Case ElementExpression.Location of
elConstant: begin
Case SetExpression.Location of
elRegister: begin
If ElementExpression.Value.Byte >= RegisterSetSizeInBits then
begin
ElementExpression.SetConstantBooleanExpression (False);
Exit;
end;
SetExpression.Calculate;
Case it16Bit in SetExpression.DataType of
True: begin
GenerateInstruction_Byte (TEST_AX_Immediate);
GenerateInstruction_Word ($0001 shl ElementExpression.Value.Byte);
end;
else begin
GenerateInstruction_Byte (TEST_AL_Immediate);
GenerateInstruction_Byte ($01 shl ElementExpression.Value.Byte)
end;
end;
end;
else begin
GetSetSizeInMemory;
If (ElementExpression.Value.Byte shr 3 < SetElementLowerLimit_div_8) or
(ElementExpression.Value.Byte shr 3 - SetElementLowerLimit_div_8 >= SetSize) then
begin
ElementExpression.SetConstantBooleanExpression (False);
Exit;
end;
Inc (SetExpression.Value.Word, ElementExpression.Value.Byte shr 3 - SetElementLowerLimit_div_8);
SetExpression.Calculate;
SetExpression.GenerateInstructionWithExpressionInMemOrReg (
Lo (TestMemoryWithImmediateOpCode), Hi (TestMemoryWithImmediateOpCode));
MaskWord.Word := TestSetInForElementMask shl (ElementExpression.Value.Byte and $07);
GenerateInstruction_Byte (MaskWord.ByteL or MaskWord.ByteH);
end;
end;
end;
else begin
Case it32Bytes in SetExpression.DataType of
True: begin
GetSetSizeInMemory;
ElementExpression.Calculate;
ElementExpression.LoadExpressionToRegisters (urAX);
GenerateInstruction_TwoBytes (MOV_AH_Immediate, TestSetInForElementMask);
GenerateInstruction_Byte (MOV_DX_Immediate);
GenerateInstruction_TwoBytes (SetSize, SetElementLowerLimit_div_8);
GenerateInstruction_CALL_FAR (SysProc_ZBitMask);
With ElementExpression do
begin
DataType := [it32Bit, it16Bit];
UsedRegisters := [urBX, urDX, urCX, urAX];
end;
end;
else begin
ElementExpression.Calculate;
ElementExpression.LoadExpressionToRegisters (urCX);
GenerateInstruction_Word (CMP_CL_Immediate);
GenerateInstruction_Byte (RegisterSetSizeInBits);
GenerateCodeForNearJump (LastJumpToFalse, JNB);
If it16Bit in SetExpression.DataType then
begin
GenerateInstruction_Byte (MOV_AX_Immediate);
GenerateInstruction_Word (Integer (ShortInt (TestSetInForElementMask)));
GenerateInstruction_Word (ROL_AX_CL);
end else begin
GenerateInstruction_TwoBytes (MOV_AL_Immediate, TestSetInForElementMask);
GenerateInstruction_Word (ROL_AL_CL);
end;
With ElementExpression do
begin
DataType := SetExpression.DataType;
Include (UsedRegisters, urAX);
LocationData.Flags := [];
end
end;
end;
ElementExpression.Save (SetExpression.UsedRegisters);
SetExpression.Calculate;
ElementExpression.PopToRegisters ([urAX]);
With SetExpression do
If it32Bytes in DataType then
begin
DataType := [itUnsigned];
Case ofsDI in LocationData.Flags of
True: GenerateInstruction_Word (ADD_DI_Register or (rDX or ElementExpression.LocationData.Register) shl 8);
else begin
Include (SetExpression.LocationData.Flags, ofsDI);
GenerateInstruction_Word (MOV_DI_Register or
(rDX or ElementExpression.LocationData.Register) shl 8);
end;
end;
ES_DI_PointerDestroyed;
end;
SetExpression.GenerateInstruction_8_16_bit (TestMemoryWithRegisterOpCode,
ElementExpression.LocationData.Register shl 3);
end;
end;
With ElementExpression do
begin
ElementExpression.EndIntermediateCodeSubroutine;
ElementExpression.SetExpressionToBooleanJump (JNE);
Value.LastJumpToFalse := LastJumpToFalse;
UsedRegisters := UsedRegisters + SetExpression.UsedRegisters;
end;
end;