Record volatile and restrict qualifiers in types.

These are needed to correctly distinguish pointer types in _Generic. They should also be used for type compatibility checks in other contexts, but currently are not.

This also fixes a couple small problems related to type qualifiers:
*restrict was not allowed to appear after * in type-names
*volatile status was not properly recorded in sym files

Here is an example of using _Generic to distinguish pointer types based on the qualifiers of the pointed-to type:

#include <stdio.h>

#define f(e) _Generic((e),\
        int * restrict *: 1,\
        int * volatile const *: 2,\
        int **: 3,\
        default: 0)

#define g(e) _Generic((e),\
        int *: 1,\
        const int *: 2,\
        volatile int *: 3,\
        default: 0)

int main(void) {
        int * restrict * p1;
        int * volatile const * p2;
        int * const * p3;

        // should print "1 2 0 1"
        printf("%i %i %i %i\n", f(p1), f(p2), f(p3), f((int * restrict *)0));

        int *q1;
        const int *q2;
        volatile int *q3;
        const volatile int *q4;

        // should print "1 2 3 0"
        printf("%i %i %i %i\n", g(q1), g(q2), g(q3), g(q4));
}

Here is an example of a problem resulting from volatile not being recorded in sym files (if a sym file was present, the read of x was lifted out of the loop):

#pragma optimize -1
static volatile int x;
#include <stdio.h>
int main(void) {
        int y;
        for (unsigned i = 0; i < 100; i++) {
                y = x*2 + 7;
        }
}
This commit is contained in:
Stephen Heumann 2021-08-29 21:10:20 -05:00
parent 586e3f9146
commit b16210a50b
7 changed files with 105 additions and 73 deletions

View File

@ -274,11 +274,14 @@ type
parameterType: typePtr;
end;
typeQualifierEnum = (tqConst, tqVolatile, tqRestrict);
typeQualifierSet = set of typeQualifierEnum;
typeKind = (scalarType,arrayType,pointerType,functionType,enumType,
enumConst,structType,unionType,definedType);
typeRecord = record {type}
size: longint; {size of the type in bytes}
isConstant: boolean; {is the type a constant?}
qualifiers: typeQualifierSet; {type qualifiers}
saveDisp: longint; {disp in symbol file}
case kind: typeKind of {NOTE: aType,pType and fType must overlap}
scalarType : (baseType: baseTypeEnum; {our internal type representation}

View File

@ -575,7 +575,7 @@ var
begin {AssignmentConversion}
kind1 := t1^.kind;
kind2 := t2^.kind;
if t1^.isConstant then
if tqConst in t1^.qualifiers then
if genCode then
if checkConst then
Error(93);
@ -947,7 +947,7 @@ var
fnPtr := pointer(GCalloc(sizeof(typeRecord)));
{fnPtr^.size := 0;}
{fnPtr^.saveDisp := 0;}
{fnPtr^.isConstant := false;}
{fnPtr^.qualifiers := [];}
fnPtr^.kind := functionType;
fnPtr^.fType := intPtr;
{fnPtr^.varargs := false;}
@ -1888,14 +1888,14 @@ var
if expressionType^.kind = functionType then begin
controllingType.size := cgLongSize;
controllingType.saveDisp := 0;
controllingType.isConstant := false;
controllingType.qualifiers := [];
controllingType.kind := pointerType;
controllingType.pType := expressionType;
end {if}
else if expressionType^.kind in [structType,unionType] then begin
controllingType.size := expressionType^.size;
controllingType.saveDisp := 0;
controllingType.isConstant := false;
controllingType.qualifiers := [];
controllingType.kind := definedType;
controllingType.dType := expressionType;
end {else if}
@ -1903,7 +1903,7 @@ var
controllingType := expressionType^;
if controllingType.kind = arrayType then
controllingType.kind := pointerType;
controllingType.isConstant := false;
controllingType.qualifiers := [];
typesSeen := nil;
resultExpr := nil;
@ -2778,7 +2778,7 @@ var
eType := pointer(Malloc(sizeof(typeRecord)));
eType^.size := cgLongSize;
eType^.saveDisp := 0;
eType^.isConstant := false;
eType^.qualifiers := [];
eType^.kind := pointerType;
eType^.pType := iType;
expressionType := eType;
@ -2813,7 +2813,7 @@ var
eType := pointer(Malloc(sizeof(typeRecord)));
eType^.size := cgLongSize;
eType^.saveDisp := 0;
eType^.isConstant := false;
eType^.qualifiers := [];
eType^.kind := pointerType;
eType^.pType := expressionType;
expressionType := eType;
@ -2830,7 +2830,7 @@ var
eType := pointer(Malloc(sizeof(typeRecord)));
eType^.size := cgLongSize;
eType^.saveDisp := 0;
eType^.isConstant := false;
eType^.qualifiers := [];
eType^.kind := pointerType;
eType^.pType := expressionType;
expressionType := eType;
@ -2943,7 +2943,7 @@ var
with tree^.id^ do begin
{check for ++ or -- of a constant}
if iType^.isConstant then
if tqConst in iType^.qualifiers then
Error(93);
{do an efficient ++ or -- on a named location}
@ -3560,6 +3560,8 @@ case tree^.token.kind of
GenerateCode(tree^.right);
AssignmentConversion(lType, expressionType, lastWasConst,
lastConst, true, true);
while lType^.kind = definedType do
lType := lType^.dType;
case lType^.kind of
scalarType:
if lisBitField then
@ -3623,7 +3625,7 @@ case tree^.token.kind of
else
Gen1t(pc_ind, 0, lType^.baseType);
end; {else}
if lType^.isConstant then
if tqConst in lType^.qualifiers then
Error(93);
if doingScalar
and (ltype^.kind = arrayType) and (id^.storage = parameter) then
@ -4082,7 +4084,7 @@ case tree^.token.kind of
tType := pointer(Malloc(sizeof(typeRecord)));
tType^.size := cgLongSize;
tType^.saveDisp := 0;
tType^.isConstant := false;
tType^.qualifiers := [];
tType^.kind := pointerType;
tType^.pType := expressionType^.aType;
expressionType := tType;
@ -4138,7 +4140,7 @@ case tree^.token.kind of
tType := pointer(Malloc(sizeof(typeRecord)));
tType^.size := cgLongSize;
tType^.saveDisp := 0;
tType^.isConstant := false;
tType^.qualifiers := [];
tType^.kind := pointerType;
tType^.pType := expressionType^.aType;
expressionType := tType;

View File

@ -18,7 +18,7 @@ uses CCommon, MM, Scanner, Symbol, CGI;
{$segment 'SCANNER'}
const
symFileVersion = 14; {version number of .sym file format}
symFileVersion = 15; {version number of .sym file format}
var
inhibitHeader: boolean; {should .sym includes be blocked?}
@ -1016,7 +1016,9 @@ procedure EndInclude {chPtr: ptr};
WriteByte(0);
tp^.saveDisp := GetMark;
WriteLong(tp^.size);
WriteByte(ord(tp^.isConstant));
WriteByte(ord(tqConst in tp^.qualifiers)
| (ord(tqVolatile in tp^.qualifiers) << 1)
| (ord(tqRestrict in tp^.qualifiers) << 2));
WriteByte(ord(tp^.kind));
case tp^.kind of
scalarType: begin
@ -1636,7 +1638,17 @@ var
tdisp^.tPtr := tp;
tp^.size := ReadLong;
tp^.saveDisp := 0;
tp^.isConstant := boolean(ReadByte);
val := ReadByte;
if odd(val) then
tp^.qualifiers := [tqConst]
else
tp^.qualifiers := [];
if odd(val >> 1) then begin
tp^.qualifiers := tp^.qualifiers + [tqVolatile];
volatile := true;
end; {if}
if odd(val >> 2) then
tp^.qualifiers := tp^.qualifiers + [tqRestrict];
tp^.kind := typeKind(ReadByte);
case tp^.kind of
scalarType: begin

View File

@ -1237,7 +1237,7 @@ type
pointerListPtr = ^pointerList; {for stacking pointer types}
pointerList = record
next: pointerListPtr;
isConstant: boolean;
qualifiers: typeQualifierSet;
end;
var
@ -1315,7 +1315,7 @@ var
{tPtr2^.size := 0;}
{tPtr2^.saveDisp := 0;}
tPtr2^.kind := definedType;
{tPtr^.isConstant := false;}
{tPtr^.qualifiers := [];}
tPtr2^.dType := tPtr;
end {if}
else
@ -1342,14 +1342,16 @@ var
new(cp);
cp^.next := cpList;
cpList := cp;
cp^.isConstant := false;
cp^.qualifiers := [];
while token.kind in [_Alignassy..whilesy] do begin
if token.kind = constsy then
cpList^.isConstant := true
else if token.kind = volatilesy then
cpList^.qualifiers := cpList^.qualifiers + [tqConst]
else if token.kind = volatilesy then begin
cpList^.qualifiers := cpList^.qualifiers + [tqVolatile];
volatile := true
else if token.kind = restrictsy then
{always allowed for now (not recorded as part of the type)}
end {else if}
else if token.kind = restrictsy then {always allowed for now}
cpList^.qualifiers := cpList^.qualifiers + [tqRestrict]
else
Error(9);
NextToken;
@ -1403,7 +1405,7 @@ var
{tPtr2^.size := 0;}
{tPtr2^.saveDisp := 0;}
tPtr2^.kind := functionType;
{tPtr2^.isConstant := false;}
{tPtr2^.qualifiers := [];}
{tPtr2^.varargs := false;}
{tPtr2^.prototyped := false;}
{tPtr2^.overrideKR := false;}
@ -1548,7 +1550,7 @@ var
tPtr2 := pointer(Calloc(sizeof(typeRecord)));
{tPtr2^.size := 0;}
{tPtr2^.saveDisp := 0;}
{tPtr2^.isConstant := false;}
{tPtr2^.qualifiers := [];}
tPtr2^.kind := arrayType;
{tPtr2^.elements := 0;}
new(ttPtr);
@ -1573,7 +1575,7 @@ var
tPtr2 := pointer(Malloc(sizeof(typeRecord)));
tPtr2^.size := cgPointerSize;
tPtr2^.saveDisp := 0;
tPtr2^.isConstant := cpList^.isConstant;
tPtr2^.qualifiers := cpList^.qualifiers;
tPtr2^.kind := pointerType;
new(ttPtr);
ttPtr^.next := typeStack;
@ -2689,7 +2691,7 @@ var
typeSpecifiers: tokenSet; {set of tokens specifying the type}
typeDone: boolean; {no more type specifiers can be accepted}
isConstant: boolean; {did we find a constsy?}
typeQualifiers: typeQualifierSet; {set of type qualifiers found}
myIsForwardDeclared: boolean; {value of isForwardDeclared to generate}
mySkipDeclarator: boolean; {value of skipDeclarator to generate}
@ -2929,9 +2931,9 @@ myTypeSpec := nil;
myIsForwardDeclared := false; {not doing a forward reference (yet)}
mySkipDeclarator := false; {declarations are required (so far)}
myDeclarationModifiers := [];
typeQualifiers := [];
typeSpecifiers := [];
typeDone := false;
isConstant := false;
isLongLong := false;
while token.kind in allowedTokens do begin
case token.kind of
@ -2981,18 +2983,20 @@ while token.kind in allowedTokens do begin
{type qualifiers}
constsy: begin
myDeclarationModifiers := myDeclarationModifiers + [token.kind];
isConstant := true;
typeQualifiers := typeQualifiers + [tqConst];
NextToken;
end;
volatilesy: begin
myDeclarationModifiers := myDeclarationModifiers + [token.kind];
typeQualifiers := typeQualifiers + [tqVolatile];
volatile := true;
NextToken;
end;
restrictsy: begin
myDeclarationModifiers := myDeclarationModifiers + [token.kind];
typeQualifiers := typeQualifiers + [tqRestrict];
if typeDone or (typeSpecifiers <> []) then
if (myTypeSpec^.kind <> pointerType)
or (myTypeSpec^.pType^.kind = functionType) then
@ -3063,7 +3067,7 @@ while token.kind in allowedTokens do begin
tPtr := pointer(Malloc(sizeof(typeRecord)));
tPtr^.size := cgWordSize;
tPtr^.saveDisp := 0;
tPtr^.isConstant := false;
tPtr^.qualifiers := [];
tPtr^.kind := enumType;
variable :=
NewSymbol(ttoken.name, tPtr, storageClass, tagSpace, defined);
@ -3077,7 +3081,7 @@ while token.kind in allowedTokens do begin
tPtr := pointer(Malloc(sizeof(typeRecord)));
tPtr^.size := cgWordSize;
tPtr^.saveDisp := 0;
tPtr^.isConstant := false;
tPtr^.qualifiers := [];
tPtr^.kind := enumConst;
if token.kind = ident then begin
variable :=
@ -3157,7 +3161,7 @@ while token.kind in allowedTokens do begin
structTypePtr := pointer(Calloc(sizeof(typeRecord)));
{structTypePtr^.size := 0;}
{structTypePtr^.saveDisp := 0;}
{structTypePtr^.isConstant := false;}
{structTypePtr^.qualifiers := [];}
structTypePtr^.kind := tkind;
{structTypePtr^.fieldList := nil;}
{structTypePtr^.sName := nil;}
@ -3189,7 +3193,7 @@ while token.kind in allowedTokens do begin
structTypePtr := pointer(Calloc(sizeof(typeRecord)));
{structTypePtr^.size := 0;}
{structTypePtr^.saveDisp := 0;}
{structTypePtr^.isConstant := false;}
{structTypePtr^.qualifiers := [];}
structTypePtr^.kind := tkind;
{structTypePtr^.fieldList := nil;}
{structTypePtr^.sName := nil;}
@ -3260,7 +3264,7 @@ if typeSpec = nil then begin
if (lint & lintC99Syntax) <> 0 then
Error(151);
end; {if}
if isconstant then begin {handle a constant type}
if typeQualifiers <> [] then begin {handle a qualified type}
new(tPtr);
if typeSpec^.kind in [structType,unionType] then begin
with tPtr^ do begin
@ -3268,11 +3272,13 @@ if isconstant then begin {handle a constant type}
kind := definedType;
dType := typeSpec;
saveDisp := 0;
qualifiers := typeQualifiers;
end; {with}
end {if}
else
else begin
tPtr^ := typeSpec^;
tPtr^.isConstant := true;
tPtr^.qualifiers := tPtr^.qualifiers + typeQualifiers;
end; {else}
typeSpec := tPtr;
end; {if}
end; {DeclarationSpecifiers}
@ -3969,7 +3975,7 @@ var
tp := pointer(Calloc(sizeof(typeRecord)));
{tp^.size := 0;}
{tp^.saveDisp := 0;}
{tp^.isConstant := false;}
{tp^.qualifiers := [];}
tp^.kind := functionType;
{tp^.varargs := false;}
{tp^.prototyped := false;}
@ -4004,13 +4010,17 @@ var
tp := pointer(Malloc(sizeof(typeRecord)));
tp^.size := cgLongSize;
tp^.saveDisp := 0;
tp^.isConstant := false;
tp^.qualifiers := [];
tp^.kind := pointerType;
while token.kind in [constsy,volatilesy] do begin
while token.kind in [constsy,volatilesy,restrictsy] do begin
if token.kind = constsy then
tp^.isConstant := true
else {if token.kind = volatilesy then}
tp^.qualifiers := tp^.qualifiers + [tqConst]
else if token.kind = volatilesy then begin
tp^.qualifiers := tp^.qualifiers + [tqVolatile];
volatile := true;
end {else}
else {if token.kind = restrictsy then}
tp^.qualifiers := tp^.qualifiers + [tqRestrict];
NextToken;
end; {while}
AbstractDeclarator;
@ -4053,7 +4063,7 @@ var
tp := pointer(Calloc(sizeof(typeRecord)));
{tp^.size := 0;}
{tp.saveDisp := 0;}
{tp^.isConstant := false;}
{tp^.qualifiers := [];}
tp^.kind := functionType;
{tp^.varargs := false;}
{tp^.prototyped := false;}
@ -4493,7 +4503,7 @@ len := ord(functionName^[0]) + 1;
tp := pointer(GCalloc(sizeof(typeRecord)));
tp^.size := len;
tp^.saveDisp := 0;
tp^.isConstant := false;
tp^.qualifiers := [];
tp^.kind := arrayType;
tp^.aType := constCharPtr;
tp^.elements := len;

View File

@ -33,8 +33,12 @@ procedure PrintOneSymbol {ip: identPtr};
begin {PrintType}
with tp^ do begin
write(' ', size:1, ' byte ');
if isConstant then
if tqConst in qualifiers then
write('constant ');
if tqVolatile in qualifiers then
write('volatile ');
if tqRestrict in qualifiers then
write('restricted ');
case kind of
scalarType : writeln('scalar');
arrayType : begin

View File

@ -474,9 +474,8 @@ if t1 = t2 then begin {shortcut}
goto 1;
end; {if}
StrictCompTypes := false; {assume the types are not compatible}
if t1^.isConstant <> t2^.isConstant then {qualifiers must be the same}
if t1^.qualifiers <> t2^.qualifiers then {qualifiers must be the same}
goto 1;
{TODO: Check other qualifiers (currently not recorded)}
while t1^.kind = definedType do {scan past type definitions}
t1 := t1^.dType;
while t2^.kind = definedType do
@ -521,13 +520,13 @@ case kind1 of
if p1^.parameterType = p2^.parameterType then
{these parameters are compatible}
else begin
tp1.isConstant := false;
tp2.isConstant := false;
tp1.qualifiers := [];
tp2.qualifiers := [];
if tp1.kind = arrayType then
tp1.kind := pointerType
else if tp1.kind = functionType then begin
tp1.size := cgLongSize;
tp1.isConstant := false;
tp1.qualifiers := [];
tp1.saveDisp := 0;
tp1.kind := pointerType;
tp1.pType := p1^.parameterType;
@ -536,7 +535,7 @@ case kind1 of
tp2.kind := pointerType
else if tp2.kind = functionType then begin
tp2.size := cgLongSize;
tp2.isConstant := false;
tp2.qualifiers := [];
tp2.saveDisp := 0;
tp2.kind := pointerType;
tp2.pType := p2^.parameterType;
@ -1368,7 +1367,7 @@ new(sCharPtr); {signed char}
with sCharPtr^ do begin
size := cgByteSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgByte;
cType := ctSChar;
@ -1377,7 +1376,7 @@ new(charPtr); {char}
with charPtr^ do begin
size := cgByteSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgUByte;
cType := ctChar;
@ -1386,7 +1385,7 @@ new(uCharPtr); {unsigned char}
with uCharPtr^ do begin
size := cgByteSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgUByte;
cType := ctUChar;
@ -1395,7 +1394,7 @@ new(shortPtr); {short}
with shortPtr^ do begin
size := cgWordSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgWord;
cType := ctShort;
@ -1404,7 +1403,7 @@ new(uShortPtr); {unsigned short}
with uShortPtr^ do begin
size := cgWordSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgUWord;
cType := ctUShort;
@ -1413,7 +1412,7 @@ new(intPtr); {int}
with intPtr^ do begin
size := cgWordSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgWord;
cType := ctInt;
@ -1422,7 +1421,7 @@ new(uIntPtr); {unsigned int}
with uIntPtr^ do begin
size := cgWordSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgUWord;
cType := ctUInt;
@ -1431,7 +1430,7 @@ new(int32Ptr); {int (32-bit)}
with int32Ptr^ do begin
size := cgLongSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgLong;
cType := ctInt32;
@ -1440,7 +1439,7 @@ new(uInt32Ptr); {unsigned int (32-bit)}
with uInt32Ptr^ do begin
size := cgLongSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgULong;
cType := ctUInt32;
@ -1449,7 +1448,7 @@ new(longPtr); {long}
with longPtr^ do begin
size := cgLongSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgLong;
cType := ctLong;
@ -1458,7 +1457,7 @@ new(uLongPtr); {unsigned long}
with uLongPtr^ do begin
size := cgLongSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgULong;
cType := ctULong;
@ -1467,7 +1466,7 @@ new(longLongPtr); {long long}
with longLongPtr^ do begin
size := cgQuadSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgQuad;
cType := ctLongLong;
@ -1476,7 +1475,7 @@ new(uLongLongPtr); {unsigned long long}
with uLongLongPtr^ do begin
size := cgQuadSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgUQuad;
cType := ctULongLong;
@ -1485,7 +1484,7 @@ new(floatPtr); {real}
with floatPtr^ do begin
size := cgRealSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgReal;
cType := ctFloat;
@ -1494,7 +1493,7 @@ new(doublePtr); {double}
with doublePtr^ do begin
size := cgDoubleSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgDouble;
cType := ctDouble;
@ -1503,7 +1502,7 @@ new(compPtr); {comp}
with compPtr^ do begin
size := cgCompSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgComp;
cType := ctComp;
@ -1512,7 +1511,7 @@ new(extendedPtr); {extended, aka long double}
with extendedPtr^ do begin
size := cgExtendedSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgExtended;
cType := ctLongDouble;
@ -1521,7 +1520,7 @@ new(boolPtr); {_Bool}
with boolPtr^ do begin
size := cgWordSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgWord;
cType := ctBool;
@ -1530,7 +1529,7 @@ new(stringTypePtr); {string constant type}
with stringTypePtr^ do begin
size := 0;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := arrayType;
aType := charPtr;
elements := 1;
@ -1539,7 +1538,7 @@ new(voidPtr); {void}
with voidPtr^ do begin
size := 0;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := scalarType;
baseType := cgVoid;
cType := ctVoid;
@ -1548,7 +1547,7 @@ new(voidPtrPtr); {typeless pointer}
with voidPtrPtr^ do begin
size := 4;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := pointerType;
pType := voidPtr;
end; {with}
@ -1556,7 +1555,7 @@ new(defaultStruct); {default structure}
with defaultStruct^ do begin {(for structures with errors)}
size := cgWordSize;
saveDisp := 0;
isConstant := false;
qualifiers := [];
kind := structType;
sName := nil;
new(fieldList);
@ -1572,7 +1571,7 @@ with defaultStruct^ do begin {(for structures with errors)}
end; {with}
new(constCharPtr); {const char}
constCharPtr^ := charPtr^;
constCharPtr^.isConstant := true;
constCharPtr^.qualifiers := [tqConst];
end; {InitSymbol}

View File

@ -1204,6 +1204,8 @@ int foo(int[42]);
159. The CLOCKS_PER_SEC and CLK_TCK macros were hard-coded as 60, but they should actually be 50 if the system is in 50Hz video mode. This could throw off timing code that used these macros in conjunction with clock(). The macros now expand to code that will detect the video mode and give the appropriate value.
160. The volatile type qualifier was omitted from declarations recorded in a .sym file. This could lead to volatile accesses being optimized away.
-- Bugs from C 2.1.0 that have been fixed -----------------------------------
1. In some situations, fread() reread the first 1K or so of the file.