From: Paul Kimpel on 1 Aug 2010 19:09 On 7/21/2010 7:05 PM, Louis Krupp wrote: > On 7/21/2010 5:12 PM, Ex-FE wrote: >> Hi, Since I haven't used Fortran since 1967-68 on an IBM 1620, I >> decided to read the Fortran77 Manual. Changed Fortran77 program >> to: >> >> Notice compiler now allocates 1 array for r and d as result of >> equivalance statement and generates appropriate code for single >> precision r and double precision code for d using the same memory >> offset (3,0002). >> >> PROGRAM Test implicit none real r(2) double precision d(1) >> equivalence (r, d) r(1) = 4.0 Debug Programdump(all) d(1) = 5.0 >> Debug Programdump(all) r(1) = 3.1 Debug Programdump(all) d(1) = >> 3.1 Debug Programdump(all) STOP END >> >> >> ***** LOCAL AND GLOBAL VARIABLES FOR TEST ***** REAL ARRAY R >> REL.ADDR.= (3,0002) >> >> From first Program Dump r(1) = 4.0 Note: Tag 0 indicating Single >> Precision 0024 (03,0002) C 200000 00248B Desc: Present-mom, >> ASD=012459, Length=2 0(0000) 0 000000 000004 0 000000 000000 >> >> From second Program Dump d(1) = 5.0 Note: Tag 2 indicating Double >> Precision 0024 (03,0002) C 200000 00248B Desc: Present-mom, >> ASD=012459, Length=2 0(0000) 2 000000 000005 2 000000 000000 >> >> From third Program Dump r(1) = 3.1 Note: Tag 0 indicting single >> precision normalized 0024 (03,0002) C 200000 00248B Desc: >> Present-mom, ASD=012459, Length=2 0(0000) 0 263199 99999A 2 000000 >> 000000 > > So the second word still has tag 2. According to the standard, it > sounds like r(2) should be undefined, and referencing r(2) at this > point on this system could cause a more dramatic failure than the > standard writers ever dreamed of. (The same would happen if nothing > had been stored to either d or r, since as I recall both words had > their tag initialized to 2.) > > Louis On 7/21/2010 9:23 PM, Mike Hore wrote: > On 22/07/10 11:35 AM, Louis Krupp wrote: > >> ... >>> From third Program Dump r(1) = 3.1 Note: Tag 0 indicting single >>> precision normalized 0024 (03,0002) C 200000 00248B Desc: >>> Present-mom, ASD=012459, Length=2 0(0000) 0 263199 99999A 2 >>> 000000 000000 >> >> So the second word still has tag 2. According to the standard, it >> sounds like r(2) should be undefined, and referencing r(2) at this >> point on this system could cause a more dramatic failure than the >> standard writers ever dreamed of. > > My guess would be a bounds violation error -- the hardware on seeing > the tag 2 would try to fetch the next word, which would be outside > the array. If r had been declared bigger, then on my reading of the > Level Epsilon manual under aFOP <fetch operand value>, the next word > would be fetched, and its tag of zero wouldn't give an error since > the manual just says the tag must be in {0, 2}, and the result is > simply set to tag 2 regardless. So basically you'd get garbage in the > lower half of your DP operand. > > But I don't have an MCP machine here, so I can't check for sure. > > Cheers, Mike. > > --------------------------------------------------------------- Mike > Hore mike_horeREM(a)OVE.invalid.aapt.net.au > --------------------------------------------------------------- Like Mike, my guess would have been that attempting to access r(2) [containing the second half of a DP operand] would have produced a bounds error, but no, there's a twist to the way that MCP architectures access memory that I didn't appreciate until now. I amended Ex-FE's FORTRAN77 program slightly to investigate access to r(2): PROGRAM Test implicit none real r(2), x -- modified to add "x" double precision d(1) equivalence (r, d) r(1) = 4.0 Debug Programdump(all) d(1) = 5.0 Debug Programdump(arrays) r(1) = 3.1 Debug Programdump(arrays) d(1) = 3.1d0 -- modified for DP value Debug Programdump(arrays) x = r(2) -- added Debug Programdump(arrays) -- added x = r(3) -- added STOP END I also changed the parameter for all but the first call on Programdump from "all" to "arrays", simply to cut down on the volume of debug output. To understand what is happening to the contents of the r and d arrays, we need to look at the code that the compiler is generating. I'll try to explain this in some detail for those that are not familiar with the MCP architecture and instruction set. The following was generated on an LX100 using the MCP 10.1 F77 compiler targeting a Level Delta processor. I believe the Level Epsilon code would the be same for this case. 00000120 r(1) = 4.0 0004:0001:5 ZERO % B0 0004:0002:0 NAMC (3,002) % 7002 0004:0002:2 INDX % A6 0004:0002:3 LT8 4 % B204 0004:0002:5 STOD % B8 The first executable statement is pretty straightforward. ZERO pushes a literal zero value onto the stack. The triplet along the left margin is the code address, e.g., segment 4, word 1, syllable (byte) 5. There are six bytes to a 48-bit word. I'll refer to the word at the top of stack as TOS, the word below that as TOS-1, etc. NAMC (Name Call) pushes an address in the form of a Stuffed Indirect Reference Word (SIRW) onto the stack. The SIRW is essentially a fancy base-plus-offset address, in this case referencing the stack frame (activation record) at lexicographic level 3, plus an offset of two words. FORTRAN programs would generally have their global/COMMON data allocated at lex level 2 and the locals for each subroutine at level 3. The program mainline is compiled as a subroutine that is called by the global initialization code. The address referenced by the address couple (lex level 3, offset 2) is that of a data descriptor (DD) that points to the r/d array. The memory area for this array is outside the stack frame, and is allocated automatically by the system on first reference. INDX indexes the array by applying the offset [zero for r(1)] at TOS-1 to the address of the array's DD at TOS, popping both operands and pushing an indexed DD (IDD) back onto the stack. In the process of indexing the array, the hardware checked that the offset was within the valid bounds of the array. The IDD is another form of address, but instead of addressing the array as a whole, addresses a specific element of the array. An important feature of the architecture to mention here is that both indexed and unindexed DDs have an element size associated with them. This is a three-bit field in the DD word, with valid values 0=single-precision word, 1=double-precision word, 2=hex (4-bit) digit, and 4=byte. The element size affects how the hardware translates an index offset to a memory address. Back to the code -- at this point we have an IDD at TOS pointing to r(1). The LT8 instruction pushes a literal integer value of 4 onto the stack. STOD (Store Destructive) stores the integer value at TOS in the location specified by the address (the IDD in this case) at TOS-1, then pops both operands from the stack (hence "destructive"). Note that the compiler blithely generated an integer literal and stored it in a floating-point array. As Glen Hermannsfeldt pointed out on a related thread, MCP machines treat integers as a special case of floating point numbers, normalized to the right and with an exponent of zero. Unlike many floating point formats, the scaling point in the MCP format is after the low-order bit of the mantissa. The hardware arithmetic and operators (including the arithmetic compare ops) are equally blithe about mixed integer and floating-point operands. As Louis Krupp pointed out, Glen is not correct about 1 and 1.0 having the same bitwise representation. Integer 1 is represented as 000000000001 (hex), whereas 1.0 would have a normalized representation of 261000000000 (hex), or 68719476736 x 8^-12 [in other words, 1 x 8^12 x 8^-12]. I discuss the FP format a little later on. 00000140 Debug Programdump(all) 0004:0003:0 NAMC (3,005) % 7005 0004:0003:2 EVAL % AC 0004:0003:3 DLET % B5 This is sort of a weird way to call the Programdump intrinsic, but it has to do with the way the debugging facilities for F77 work. The NAMC pushes an address to a tag-7 Program Control Word (PCW), which is a code address. PCWs are used primarily as entry point addresses for subroutines and as operands for dynamic branch instructions. EVAL traverses a reference chain, returning the operand at the end of that chain. If a PCW is encountered along that chain, however, an accidental entry (or thunk) is generated by the hardware, and the value returned by the thunk-ed procedure is used to continue the chain. In this case the thunk-ed procedure ends up calling Programdump. The F77 debug mechanism does it this way because debugging is enabled by a run-time option. Based on this option, the initialization code of the program will either put a PCW at stack location (3,5), or put a zero word. If debugging is not enabled, the target of the EVAL will be the zero operand, no PCW is encountered, no thunk is generated, and no dump takes place. Whatever value is returned by the EVAL operator is simply popped by the DLET (Delete TOS) operator, so the whole purpose of the EVAL is to optionally generate a side-effect -- the dump. This is pretty far off the topic of FORTRAN EQUIVALENCE, but this thread started on the subject of tagged data access, so I wanted to point out this behavior as just one more reason why tags are good. 002A (03,0003) 0 000000 000000 0029 (03,0002) C 800000 001A70 Desc: Present-mom, ASD=00D384, Length=2 0(0000) 0 000000 000004 0 000000 000000 Back to the subject of FORTRAN EQUIVALENCE. Above is a snippet of the program's stack from the first Programdump. The first token is the hex offset from the base of the stack (MCP stacks are a contiguous area of memory and grow towards higher memory addresses). The address couple in parentheses shows the lex level of the activation record and the offset from the base of the activation record. Next is the stack word itself, formatted as one hex digit for the 4-bit tag and two groups of six hex digits for the 48 data bits. (3,3) is the location for variable x; (3,2) is the location of the DD for array r/d. Below that DD the contents of the array are formatted with the word offset in decimal and (hex), followed by tags and data bits for each word. You can see that r(1) has a value of 4. The MCP initializes all arrays to zero when they are allocated, so r(2) has it's initial value of zero. This is just what we would expect to see. 00000160 d(1) = 5.0 0004:0003:4 ZERO % B0 0004:0003:5 NAMC (3,002) % 7002 0004:0004:1 INDX % A6 0004:0004:2 SEDW % 95A3 0004:0004:4 LT8 5 % B205 0004:0005:0 XTND % CE 0004:0005:1 STOD % B8 The next statement stores a value of five in d(1), which overlays r(1) and r(2). This is similar to the code for r(1)=4.0 except for the addition of the SEDW and XTND operators. SEDW (Set Element Size to Double Words) modifies the IDD that was generated by the INDX operator for d(1), changing its element size field from 0 to 1. This causes memory accesses through that descriptor to consider the data to be double precision. Other MCP languages, particularly COBOL, get the same effect by allocating multiple descriptors for the same area, each with a different element size. The XTND operator simply takes the SP integer value 5 at TOS, pushes a second word of zero onto the stack, and sets the tags of both words to 2, indicating a double-precision value. The STOD operator is sensitive to the element size of the IDD and the tag of the word at TOS, and thus stores the DP result into two virtually-contiguous words of memory. 00000180 Debug Programdump(arrays) 0004:0005:2 NAMC (3,006) % 7006 0004:0005:4 EVAL % AC 0004:0005:5 DLET % B5 002A (03,0003) 0 000000 000000 0029 (03,0002) C 800000 001A70 Desc: Present-mom, ASD=00D384, Length=2 0(0000) 2 000000 000005 2 000000 000000 The second Programdump shows the DP value of five in the r/d array at (3,2). Note again that the scale point is after the low-order bit of the first word. 00000200 r(1) = 3.1 0004:0006:0 ZERO % B0 0004:0006:1 NAMC (3,002) % 7002 0004:0006:3 INDX % A6 0004:0006:4 LT48 26319999999A % BEFF26319999999A 0004:0008:0 STOD % B8 The next statement overwrites r(1) with the FP value 3.1. Mike Hore mentioned that the FP format is a bit weird... well, it's at least different. The FP word format, along with the idea of integers being a subset of FP values, were carried over intact from the Burroughs B5500: bit 47 (high order): not used (was the "flag" bit on B5500) bit 46: mantissa sign (0=positive) bit 45: exponent sign (0=positive) bits 44-39: exponent, base 8 bits 38-0: mantissa, with scale point to the right Therefore, that hex value of 26319999999A is simply 213030377882 x 8^-12, or approximately 3.1. 00000220 Debug Programdump(arrays) 0004:0008:1 NAMC (3,007) % 7007 0004:0008:3 EVAL % AC 0004:0008:4 DLET % B5 002A (03,0003) 0 000000 000000 0029 (03,0002) C 800000 001A70 Desc: Present-mom, ASD=00D384, Length=2 0(0000) 0 263199 99999A 2 000000 000000 The dump clearly shows just r(1) being overwritten, with r(2) [the second word of d(1)] remaining untouched. Up to this point, the results are the same as those reported by Ex-FE. 00000240 d(1) = 3.1d0 0004:0008:5 ZERO % B0 0004:0009:0 NAMC (3,002) % 7002 0004:0009:2 INDX % A6 0004:0009:3 SEDW % 95A3 0004:0009:5 LT48 263199999999 % BE263199999999 0004:000B:0 LT48 004CCCCCCCCD % BEFFFFFFFFFF004CCCCCCCCD 0004:000D:0 JOIN % 9542 0004:000D:2 STOD % B8 Now for something a little different. 3.1 is a SP value, so when it is coerced to DP, the compiler just emits code to extend the SP value with a second word of zero. By specifying the constant as 3.1d0, the compiler generates a DP literal value. It does this by pushing two SP words onto the stack and using the JOIN operator to convert them to a single DP operand. Note again the use of SEDW to fiddle with the element size of the IDD for r/d. 00000260 Debug Programdump(arrays) 0004:000D:3 NAMC (3,008) % 7008 0004:000D:5 EVAL % AC 0004:000E:0 DLET % B5 002A (03,0003) 0 000000 000000 0029 (03,0002) C 800000 001A70 Desc: Present-mom, ASD=00D384, Length=2 0(0000) 2 263199 999999 2 004CCC CCCCCD The corresponding dump shows the two words of r/d as a DP value. In the MCP architecture's representation of DP values, the first word has exactly the same format as a SP value (which allows coercion from SP to DP simply by appending a zero word). The second word of the DP value has this format: bits 47-39: high-order extension to the exponent bits 38-0: low-order extension to the mantissa Therefore, a DP value has a 15-bit exponent (base 8) and a 78-bit mantissa, with the scale point midway between the two 39-bit halves. 00000270 x = r(2) 0004:000E:1 ONE % B1 0004:000E:2 NAMC (3,002) % 7002 0004:000E:4 NXLV % AD 0004:000E:5 NAMC (3,003) % 7003 0004:000F:1 STOD % B8 Now for the critical test. This statement fetches the second word of r (which has a double-precision tag) and stores it in the SP variable x. The NXLV (Index and Load Value) operator indexes the DD for r and pushes the word at the resulting memory location. The second NAMC and the STOD simply store that value in the location for x. As I mentioned in the beginning, I was in Mike Hore's camp -- I would have thought that this would produce a bounds violation fault, as the hardware found r(2) to have a DP tag value, and consequently tried to fetch the second word, which would have been outside the bounds of the array. Surprise, surprise, it completed without generating a fault. 00000272 Debug Programdump(arrays) 0004:000F:2 NAMC (3,009) % 7009 0004:000F:4 EVAL % AC 0004:000F:5 DLET % B5 002A (03,0003) 0 004CCC CCCCCD Op: Dec: 329853488333 0029 (03,0002) C 800000 001A70 Desc: Present-mom, ASD=00D384, Length=2 0(0000) 2 263199 999999 2 004CCC CCCCCD The corresponding dump shows that x now contains a copy of the value at r(2), but with the tag 2 replaced by a tag 0. How? Why? For the answer, it turns out that one need only RTFM, although in the case of the Architecture Reference Manual, that is not normally a trivial task. Mike was on the right track with the "aFOP" common action (under Read Evaluation Operators in Section 5, Operator Set), but you have to look at the entire text. Quote: For most clients, aFOP generates an error interrupt if the referenced word does not have a tag in {0,2}. The exceptions are value-call and LOAD, for which a non-operand may be a chained reference or a target, respectively. If the reference is an address couple or SIRW, or the reference is an IndexedSingleDD or IndexedCharDD and the client is LOAD, the non-operand value is returned to the client operator. [Here's the important part -- note that "referenced word" is singular] If the reference is an IRW and the referenced word has a tag of 0 or the reference is an IndexedSingleDD or IndexedCharDD and the referenced word has a tag in {0,2}, the referenced word is fetched and the tag set to zero (if necessary) to form an operand value. The extension is not modified. If the reference is an IRW and the fetched word has a tag of 2, its successor word (at the next higher memory address) is fetched. That word must have a tag in {0, 2}; the referenced word and its successor are joined as the first and second words of a double precision operand (with tag 2) to form an operand value. [This part explains why access to array d fetches two words] If the reference is an IndexedDoubleDD, the referenced word and its successor are fetched; each must have a tag in {0,2}. The two words are joined as the first and second words of a double precision operand (with tag 2) to form an operand value. A split across a page boundary can occur if an IndexedDoubleDD is so constructed that its index refers to the last word of a page. End quote. In order words, if the memory reference is in the form of an SIRW (which addresses a word in a stack), the hardware is sensitive to the tag of the first word that is fetched. If that tag is 2, the hardware automatically fetches the second word, which must have a tag in {0,2}. If, however, the memory reference is in the form of an IDD, it is the element size field of the IDD that determines whether one or two words are fetched. The tags on the words in memory are ignored, as long as they are in {0,2}, but the tags of the words pushed onto the stack will be forced to 2. The element size in the descriptor at (3,2) is 0, making it an "IndexedSingleDD", so only one word is fetched, and its tag is forced to zero in the process of pushing it onto the stack. 00000274 x = r(3) 0004:0010:0 LT8 2 % B202 0004:0010:2 NAMC (3,002) % 7002 0004:0010:4 NXLV % AD 0004:0010:5 NAMC (3,003) % 7003 0004:0011:1 STOD % B8 Just to prove the bounds checking was in fact working, I added the statement above to index r outside of its declared bounds. Sure enough, the program died on the NXLV operator with an Invalid Index fault. I found this exercise quite interesting. The thing that was new to me was the difference between the way that memory is accessed through an SIRW compared to an IDD. I went back to the B6700 reference manual to see if it had always been this way, but cannot find any discussion in that manual about how NXLV works that is specific enough to tell. For those who would wish to join in the delights of reading the Architecture Support Reference Manual, here you can find the Level Epsilon edition: http://public.support.unisys.com/c71/docs/libra780-1.0/pdf/68787530-005.pdf and here its precursor, the Level Delta edition: http://public.support.unisys.com/aseries/docs/ClearPath-MCP-10.1/PDF/68882141-001.pdf -- Paul
From: glen herrmannsfeldt on 1 Aug 2010 22:23 In comp.lang.fortran Paul Kimpel <paul.kimpel(a)digm.com> wrote: (snip) > Note that the compiler blithely generated an integer literal and stored > it in a floating-point array. As Glen Hermannsfeldt pointed out on a > related thread, MCP machines treat integers as a special case of > floating point numbers, normalized to the right and with an exponent of > zero. Unlike many floating point formats, the scaling point in the MCP > format is after the low-order bit of the mantissa. The hardware > arithmetic and operators (including the arithmetic compare ops) are > equally blithe about mixed integer and floating-point operands. > As Louis Krupp pointed out, Glen is not correct about 1 and 1.0 having > the same bitwise representation. Integer 1 is represented as > 000000000001 (hex), whereas 1.0 would have a normalized representation > of 261000000000 (hex), or 68719476736 x 8^-12 [in other words, 1 x 8^12 > x 8^-12]. I discuss the FP format a little later on. (snip) Yes, so the question, then, is when is the normalized result given. I believe that there are some machines that give an unnormalized result with zero exponent in the cases where no bits are lost, otherwise the normalized result. From Blauuw and Brooks "Computer Architecture Concepts and Evolution" description of the B5500, and also some of the differences in later machines: (for floating point addition and subtraction) "When the exponents of the two single-precision operands are equal, then this exponent becomes the preferred exponent for an unnormalized result; otherwise the result is normalized." (for floating point multiplication) "Multiply attempts, like Add, to obtain an integer result from integer operands. The condition applied in multiplication is that the exponents of both operands be 0. Division does not attempt to preserve integer values; it always normalizes. Divide Integer and Remainder, however, attemtps to give an integer quotient and remainder, respectively." In compiling constants, the compiler could generate either the normalized or unnormalized form for 1.0. I believe that description also applies to the B6700, though I am not positive about that. Given that, the results of EQUIVALENCE between INTEGER and REAL could be very interesting, as often the same value will be valid both ways. -- glen
From: Paul Kimpel on 2 Aug 2010 12:11 On 8/1/2010 7:23 PM, glen herrmannsfeldt wrote: > In comp.lang.fortran Paul Kimpel<paul.kimpel(a)digm.com> wrote: > (snip) > >> Note that the compiler blithely generated an integer literal and stored >> it in a floating-point array. As Glen Hermannsfeldt pointed out on a >> related thread, MCP machines treat integers as a special case of >> floating point numbers, normalized to the right and with an exponent of >> zero. Unlike many floating point formats, the scaling point in the MCP >> format is after the low-order bit of the mantissa. The hardware >> arithmetic and operators (including the arithmetic compare ops) are >> equally blithe about mixed integer and floating-point operands. > >> As Louis Krupp pointed out, Glen is not correct about 1 and 1.0 having >> the same bitwise representation. Integer 1 is represented as >> 000000000001 (hex), whereas 1.0 would have a normalized representation >> of 261000000000 (hex), or 68719476736 x 8^-12 [in other words, 1 x 8^12 >> x 8^-12]. I discuss the FP format a little later on. > > (snip) > > Yes, so the question, then, is when is the normalized result given. > > I believe that there are some machines that give an unnormalized > result with zero exponent in the cases where no bits are lost, > otherwise the normalized result. > > From Blauuw and Brooks "Computer Architecture Concepts and Evolution" > description of the B5500, and also some of the differences in > later machines: > > (for floating point addition and subtraction) > "When the exponents of the two single-precision operands > are equal, then this exponent becomes the preferred exponent > for an unnormalized result; otherwise the result is normalized." > > (for floating point multiplication) > "Multiply attempts, like Add, to obtain an integer result from > integer operands. The condition applied in multiplication is > that the exponents of both operands be 0. Division does not > attempt to preserve integer values; it always normalizes. > Divide Integer and Remainder, however, attemtps to give an > integer quotient and remainder, respectively." > > In compiling constants, the compiler could generate either > the normalized or unnormalized form for 1.0. > > I believe that description also applies to the B6700, though > I am not positive about that. > > Given that, the results of EQUIVALENCE between INTEGER and REAL > could be very interesting, as often the same value will be > valid both ways. > > -- glen Interesting question, especially since with the MCP architecture there are two forms of normalized result -- normalized as integer and normalized as floating point. To investigate this, I wrote the following FORTRAN77 program and compiled and ran it on an LX100 under MCP 10.1. This is an implementation of the MCP architecture emulated on Intel under Windows, but I believe the FP operations try to be faithful to the way that the "hard" processors work: Program Norm implicit none real i1, i2, i3, i4 real r1, r2 real add1, sub1, mul1, div1 real add2, sub2, mul2, div2 real add3, sub3, mul3, div3 real add4, sub4, mul4, div4 C Case 1 -- Integer OP Integer i1 = 4 i2 = 25 add1 = i1 + i2 sub1 = i1 - i2 mul1 = i1 * i2 div1 = i1 / i2 C Case 2 -- Integer OP Real r1 = 25.25 add2 = i1 + r1 sub2 = i1 - r1 mul2 = i1 * r1 div2 = i1 / r1 C Case 3 -- Real OP Real r2 = 3.75 add3 = r1 + r2 sub3 = r1 - r2 mul3 = r1 * r2 div3 = r1 / r2 C Case 4 -- Integer OP Integer producing FP result i3 = 400000000000 i4 = 500000000000 add4 = i3 + i4 sub4 = -i3 - i4 mul4 = i3 * i4 div4 = i3 / i4 Debug Programdump stop end Here is the code that is generated by the compiler. Note that there are only one set of arithmetic operators, ADD, SUBT, MULT, DIVD, for integer, single-precision FP and double-precision FP computations. DIVD always produces a FP result. There are also IDIV and RDIV ops for integer and remainder divide, and MULX which always produces a double-precision multiplication result. VALC (Value Call) fetches the operand at the location identified by the address couple and pushes a copy onto the stack. CHSN (Change Sign) simply complements bit 46, the mantissa sign bit, in the TOS word. 00000340 i1 = 4 0004:0001:5 LT8 4 % B204 0004:0002:1 NAMC (3,002) % 7002 0004:0002:3 STOD % B8 00000360 i2 = 25 0004:0002:4 LT8 25 (19) % B219 0004:0003:0 NAMC (3,003) % 7003 0004:0003:2 STOD % B8 00000400 add1 = i1 + i2 0004:0003:3 VALC (3,002) % 3002 0004:0003:5 VALC (3,003) % 3003 0004:0004:1 ADD % 80 0004:0004:2 NAMC (3,008) % 7008 0004:0004:4 STOD % B8 00000420 sub1 = i1 - i2 0004:0004:5 VALC (3,002) % 3002 0004:0005:1 VALC (3,003) % 3003 0004:0005:3 SUBT % 81 0004:0005:4 NAMC (3,009) % 7009 0004:0006:0 STOD % B8 00000440 mul1 = i1 * i2 0004:0006:1 VALC (3,002) % 3002 0004:0006:3 VALC (3,003) % 3003 0004:0006:5 MULT % 82 0004:0007:0 NAMC (3,00A) % 700A 0004:0007:2 STOD % B8 00000460 div1 = i1 / i2 0004:0007:3 VALC (3,002) % 3002 0004:0007:5 VALC (3,003) % 3003 0004:0008:1 DIVD % 83 0004:0008:2 NAMC (3,00B) % 700B 0004:0008:4 STOD % B8 00000480 00000500C Case 2 -- Integer OP Real 00000540 r1 = 25.25 0004:0008:5 LT48 25B280000000 % BE25B280000000 0004:000A:0 NAMC (3,006) % 7006 0004:000A:2 STOD % B8 00000580 add2 = i1 + r1 0004:000A:3 VALC (3,002) % 3002 0004:000A:5 VALC (3,006) % 3006 0004:000B:1 ADD % 80 0004:000B:2 NAMC (3,00C) % 700C 0004:000B:4 STOD % B8 00000600 sub2 = i1 - r1 0004:000B:5 VALC (3,002) % 3002 0004:000C:1 VALC (3,006) % 3006 0004:000C:3 SUBT % 81 0004:000C:4 NAMC (3,00D) % 700D 0004:000D:0 STOD % B8 00000620 mul2 = i1 * r1 0004:000D:1 VALC (3,002) % 3002 0004:000D:3 VALC (3,006) % 3006 0004:000D:5 MULT % 82 0004:000E:0 NAMC (3,00E) % 700E 0004:000E:2 STOD % B8 00000640 div2 = i1 / r1 0004:000E:3 VALC (3,002) % 3002 0004:000E:5 VALC (3,006) % 3006 0004:000F:1 DIVD % 83 0004:000F:2 NAMC (3,00F) % 700F 0004:000F:4 STOD % B8 00000660 00000680C Case 3 -- Real OP Real 00000720 r2 = 3.75 0004:000F:5 LT48 263C00000000 % BE263C00000000 0004:0011:0 NAMC (3,007) % 7007 0004:0011:2 STOD % B8 00000760 add3 = r1 + r2 0004:0011:3 VALC (3,006) % 3006 0004:0011:5 VALC (3,007) % 3007 0004:0012:1 ADD % 80 0004:0012:2 NAMC (3,010) % 7010 0004:0012:4 STOD % B8 00000780 sub3 = r1 - r2 0004:0012:5 VALC (3,006) % 3006 0004:0013:1 VALC (3,007) % 3007 0004:0013:3 SUBT % 81 0004:0013:4 NAMC (3,011) % 7011 0004:0014:0 STOD % B8 00000800 mul3 = r1 * r2 0004:0014:1 VALC (3,006) % 3006 0004:0014:3 VALC (3,007) % 3007 0004:0014:5 MULT % 82 0004:0015:0 NAMC (3,012) % 7012 0004:0015:2 STOD % B8 00000820 div3 = r1 / r2 0004:0015:3 VALC (3,006) % 3006 0004:0015:5 VALC (3,007) % 3007 0004:0016:1 DIVD % 83 0004:0016:2 NAMC (3,013) % 7013 0004:0016:4 STOD % B8 00000840 00000860C Case 4 -- Integer OP Integer producing FP result 00000900 i3 = 400000000000 0004:0016:5 LT48 005D21DBA000 % BE005D21DBA000 0004:0018:0 NAMC (3,004) % 7004 0004:0018:2 STOD % B8 00000920 i4 = 500000000000 0004:0018:3 LT48 00746A528800 % BEFFFF00746A528800 0004:001A:0 NAMC (3,005) % 7005 0004:001A:2 STOD % B8 00000960 add4 = i3 + i4 0004:001A:3 VALC (3,004) % 3004 0004:001A:5 VALC (3,005) % 3005 0004:001B:1 ADD % 80 0004:001B:2 NAMC (3,014) % 7014 0004:001B:4 STOD % B8 00000980 sub4 = -i3 - i4 0004:001B:5 VALC (3,004) % 3004 0004:001C:1 CHSN % 8E 0004:001C:2 VALC (3,005) % 3005 0004:001C:4 SUBT % 81 0004:001C:5 NAMC (3,015) % 7015 0004:001D:1 STOD % B8 00001000 mul4 = i3 * i4 0004:001D:2 VALC (3,004) % 3004 0004:001D:4 VALC (3,005) % 3005 0004:001E:0 MULT % 82 0004:001E:1 NAMC (3,016) % 7016 0004:001E:3 STOD % B8 00001020 div4 = i3 / i4 0004:001E:4 VALC (3,004) % 3004 0004:001F:0 VALC (3,005) % 3005 0004:001F:2 DIVD % 83 0004:001F:3 NAMC (3,017) % 7017 0004:001F:5 STOD % B8 00190000 00190100 Debug Programdump 0004:0020:0 NAMC (3,019) % 7019 0004:0020:2 EVAL % AC 0004:0020:3 DLET % B5 00190300 stop 0004:0020:4 EXIT % A3 Here is the section of the resulting Programdump that shows the values of the variables. I have annotated this with the variable names to the right of the hex word format. 003E (03,0017) 0 26E666 666666 DIV4 Op: Dec: .79999999999927240424 003D (03,0016) 0 06D4B4 0B1F85 MUL4 Op: Dec: 1.9999999999990583+23 003C (03,0015) 0 409A31 85C500 SUB4 Op: Dec: -9.0E+11 003B (03,0014) 0 009A31 85C500 ADD3 Op: Dec: 9.0E+11 003A (03,0013) 0 266BBB BBBBBC DIV3 Op: Dec: 6.7333333333372138441 0039 (03,0012) 0 2517AC 000000 MUL3 Op: Dec: 94.6875 0038 (03,0011) 0 25AB00 000000 SUB3 Op: Dec: 21.5 0037 (03,0010) 0 25BA00 000000 ADD3 Op: Dec: 29.0 0036 (03,000F) 0 269446 F86563 DIV2 Op: Dec: .15841584158442856278 0035 (03,000E) 0 251940 000000 MUL2 Op: Dec: 101.0 0034 (03,000D) 0 65AA80 000000 SUB2 Op: Dec: -21.25 0033 (03,000C) 0 25BA80 000000 ADD2 Op: Dec: 29.25 0032 (03,000B) 0 26947A E147AE DIV1 Op: Dec: .15999999999985448085 0031 (03,000A) 0 000000 000064 MUL1 Op: Dec: 100 0030 (03,0009) 0 400000 000015 SUB1 Op: Dec: -21 002F (03,0008) 0 000000 00001D ADD1 Op: Dec: 29 002E (03,0007) 0 263C00 000000 R2 Op: Dec: 3.75 002D (03,0006) 0 25B280 000000 R1 Op: Dec: 25.25 002C (03,0005) 0 00746A 528800 I4 Op: Dec: 500000000000 002B (03,0004) 0 005D21 DBA000 I3 Op: Dec: 400000000000 002A (03,0003) 0 000000 000019 I2 Op: Dec: 25 0029 (03,0002) 0 000000 000004 I1 Op: Dec: 4 Note that the results for Case 1 (where both operands were normalized as integers) are all normalized as integers, except for the divide result, as DIVD always produces a FP result. The results for Cases 2 and 3 (where at least one of the operands was normalized as floating point) all generate results normalized as floating point. For Case 4, the operands are normalized as integers, but they are large enough in magnitude that the results for ADD, SUBT, and MULT will exceed the size of the 39-bit integer mantissa. The operators automatically generated normalized floating point results in this case. The thing I could not figure out how to do in FORTRAN77 was generate un-normalized FP operands, so I switched to Extended Algol and wrote this program: begin integer i1; real r1, r2, r3, add1, sub1, mul1, div1, add2, sub2, mul2, div2, add3, sub3, mul3, div3; % Case 1 -- Integer OP Unnormalized FP i1:= 25; r1:= 4"230000100000"; % should represent 4.0 add1:= i1 + r1; sub1:= i1 - r1; mul1:= i1 * r1; div1:= i1 / r1; % Case 2 -- Unnormalized FP OP Unnormalized FP r2:= 4"208000000070"; % should represent 14.0 add2:= r1 + r2; sub2:= r1 - r2; mul2:= r1 * r2; div2:= r1 / r2; % Case 3 -- Unnorm FP OP Unnorm FP, same exp r3:= 4"208000000030"; % should represent 6.0 add3:= r2 + r3; sub3:= r2 - r3; mul3:= r2 * r3; div3:= r2 / r3; programdump; end. The code generated for the arithmetic expressions looks just like that for FORTRAN77 above. Here is what the variables in the stack look like at the time of the Programdump call: 0027 (02,0011) 0 262555 555555 DIV3 Dec: 2.3333333333284826949 0026 (02,0010) 0 251500 000000 MUL3 Dec: 84.0 0025 (02,000F) 0 259000 000000 SUB3 Dec: 8.0 0024 (02,000E) 0 25A800 000000 ADD3 Dec: 20.0 0023 (02,000D) 0 26A492 492492 DIV2 Dec: .28571428571376600303 0022 (02,000C) 0 25F000 000000 MUL2 Dec: 56.0 0021 (02,000B) 0 659400 000000 SUB2 Dec: -10.0 0020 (02,000A) 0 25A400 000000 ADD2 Dec: 18.0 001F (02,0009) 0 266400 000000 DIV1 Dec: 6.25 001E (02,0008) 0 251900 000000 MUL1 Dec: 100.0 001D (02,0007) 0 25AA00 000000 SUB1 Dec: 21.0 001C (02,0006) 0 25BA00 000000 ADD1 Dec: 29.0 001B (02,0005) 0 208000 000030 R3 Dec: 6.0 001A (02,0004) 0 208000 000070 R2 Dec: 14.0 0019 (02,0003) 0 230000 100000 R1 Dec: 4.0 0018 (02,0002) 0 000000 000019 I1 Dec: 25 Note that all of the results are properly normalized floating-point values, even through r1 and r2 are un-normalized operands. This is what I would have expected to happen, but it's nice to see that it's actually the case. Case 3 above tests Blauuw & Brooks statement that for addition and subtractions, operands with the same exponents can produce an un-normalized result. That may have been true for the B5500, but as can be seen, even though R2 and R3 are un-normalized and have the same exponent value, results based on those operands are properly normalized by the current architecture. What I conclude from this exercise (for single-precision values, at least), is that for addition, subtraction, and multiplication, if the two operands are both normalized as integers, and the result can be expressed as an integer, it will be. Otherwise, the result will be a normalized floating-point value. The issue with equivalencing INTEGER and REAL in FORTRAN on this architecture is not with fetching data and doing computations, but with storing the results. The computations will work regardless of how the source operands are typed or represented bitwise. Before storing into a location typed as integer, however, the compilers emit an integerization operator (NTGR or NTIA) to normalize the result to integer format. If the magnitude of the result exceeds 2^39-1, an integer overflow fault is generated. Note that even if both operands are typed as integers and represented bitwise as integers, it's still possible with large enough values to generate a floating point result that exceeds the capacity of the normalized integer format. That situation will not be detected until the integerization occurs just before the store, however. That also means that the intermediate values of some computations on purely integer values could exceed the integer capacity, but if the final result is within the integer capacity, you will still get an integer result, albeit possibly with some loss of precision. -- Paul
From: Louis Krupp on 2 Aug 2010 15:48 On 8/2/2010 10:11 AM, Paul Kimpel wrote: <snip> > Case 3 above tests Blauuw & Brooks statement that for addition and > subtractions, operands with the same exponents can produce an > un-normalized result. That may have been true for the B5500, but as can > be seen, even though R2 and R3 are un-normalized and have the same > exponent value, results based on those operands are properly normalized > by the current architecture. > > What I conclude from this exercise (for single-precision values, at > least), is that for addition, subtraction, and multiplication, if the > two operands are both normalized as integers, and the result can be > expressed as an integer, it will be. Otherwise, the result will be a > normalized floating-point value. Interesting. If someone had asked me, I would have said that the system normalizes floating point values "when it feels like it," but it sounds like there's a method to the madness. FWIW, the B6500 Processor Functional Flow Charts (to the extent to which that document reflects anything that was actually implemented) reinforces Blauuw and Brooks and shows the ADD operator testing to see if the exponent fields of the operands were equal and, if they were, just adding the mantissas, checking for overflow and calling it good. Thanks very much for investigating all of this. Louis
From: ralf.schaa on 2 Aug 2010 21:14
On Jul 15, 6:16 am, nos...(a)see.signature (Richard Maine) wrote: > Jan Gerrit Kootstra <jan.ger...(a)kootstra.org.uk> wrote: > > > This not a side effect. > > > This is what is called an operation on a global variable, for it is > > defined outside the function. > > You appear to have a very narrow definition of "side effect". I can only > guess that you must be restricting your definition to changes in the > values of arguments. Most definitions include changes in any variables > that are known outside of the function. That includes changes to global > variables; indeed, that is often cited as one of the classic examples of > a side effect. > > The Fortran standard doesn't actually define "side effect" as a term. > That's more of an informal description that we tend to use as shorthand > in discussing it. However, if one wants to use such a term in describing > the limitations on Fortran functions, one needs to use a definition that > goes along with those restrictions. > > > It is a totally legitimate result. > > See my quote to the contrary from the standard (not the quote about > arguments, but the quote about "affect or be affected by"). Note that > although the quote does use the term "effect", it does not use "side > effect". As long as there is an effect, it is prohibitted, regardless of > whether you want to call it "side" or not. > > > Rewriting your code with the statement in the main body would lead to this: > > [elided] > > That rewrite assumes an interpretation that the standard does not > specify. The standard goes out of its way to be sure not to specify > that. If, for example, the two invocations of the function were done in > parallel, you would not necessarily get that result. Allowance of such > things as parallel invocation is part of why the standard is specified > the way it is. > > -- > Richard Maine | Good judgment comes from experience; > email: last name at domain . net | experience comes from bad judgment. > domain: summertriangle | -- Mark Twain I am confused after reading the thread - if there is an illegal statement, is it that global variable n is modified in the function? Or is it rather in the statement f(3) + f(3) ? If the first case is illegal than how can one define or change global variables? uhhh -R. |