From b16210a50bd11fb7586ec7db903a4423d6bab7fd Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Sun, 29 Aug 2021 21:10:20 -0500 Subject: [PATCH] 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 #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 int main(void) { int y; for (unsigned i = 0; i < 100; i++) { y = x*2 + 7; } } --- CCommon.pas | 5 +++- Expression.pas | 26 ++++++++++---------- Header.pas | 18 +++++++++++--- Parser.pas | 64 +++++++++++++++++++++++++++++--------------------- Symbol.Print | 6 ++++- Symbol.pas | 57 ++++++++++++++++++++++---------------------- cc.notes | 2 ++ 7 files changed, 105 insertions(+), 73 deletions(-) diff --git a/CCommon.pas b/CCommon.pas index 3f98ec0..674bd73 100644 --- a/CCommon.pas +++ b/CCommon.pas @@ -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} diff --git a/Expression.pas b/Expression.pas index 474c722..775ff7a 100644 --- a/Expression.pas +++ b/Expression.pas @@ -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; diff --git a/Header.pas b/Header.pas index 676a46a..554ef30 100644 --- a/Header.pas +++ b/Header.pas @@ -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 diff --git a/Parser.pas b/Parser.pas index 83c31f9..0767eac 100644 --- a/Parser.pas +++ b/Parser.pas @@ -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; diff --git a/Symbol.Print b/Symbol.Print index 3ca0cc9..fa4ae2e 100644 --- a/Symbol.Print +++ b/Symbol.Print @@ -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 diff --git a/Symbol.pas b/Symbol.pas index bcc1ea1..811ad4f 100644 --- a/Symbol.pas +++ b/Symbol.pas @@ -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} diff --git a/cc.notes b/cc.notes index 29c9dbf..9051197 100644 --- a/cc.notes +++ b/cc.notes @@ -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.