It now covers pretty much all the new features, as well as addressing the errata from the release notes and some other miscellaneous issues. The early chapters still need to be updated to refer to a hard disk installation, rather than being based on running it from floppies (which is no longer supported). I'm sure more proofreading and editing would also be beneficial.
This is the ORCA/C 2.0 manual from Opus ][, re-saved in the modern Microsoft Word format and adjusted to fix some formatting issues. In particular, the embedded images needed to be converted to formats that current versions of Word support. The result is very close to the original version, although the pagination winds up slightly different in some places.
When the lint check for undefined variables was enabled, a "lint: unused variable: @struct" would be produced for any function returning a struct or union, due to the special static variable that is created to hold the return value. That spurious lint message is now suppressed.
This prohibits initializers in "extern" declarations within a function, and in the parameter declarations of non-prototyped functions.
Here are examples illustrating these cases:
int main(void) {
extern int i = 50;
}
void f(i)
int i = 60;
{
}
We now recognize cases where the same value needs to be pushed for several consecutive words, so it is more efficient to load it into a register and push that rather than just using PEA instructions.
This covers code like the following:
int main(void) {
auto int a[20];
static int *p = &a[5];
}
Previously, this would compile without error, and then either give a linker error or be linked to refer to the global symbol "a" (if there was one).
Any stack-allocated array must be < 32KB, so we can use the same approach as in the small memory model to compute indexes for it (which is considerably more efficient than the large-memory-model code).
The code generator never generates this code sequence (and did not do so even prior to the last commit), so having a peephole optimization for it is pointless.
The new code is smaller and (in the common case where the subtraction does not overflow) faster. It takes advantage of the fact that in overflow cases the carry flag always gets set to the opposite of the sign bit of the result.
This will change a "jump if true" to "jump if false" (or vice versa) and logically negate the condition in certain cases where that generates better code.
An assembly peephole optimization for certain "branch to branch" instructions is also added. (Certain conditionals could generate these.)
The hash algorithm has been modified to include a rotate at each step. This should improve the quality of hashes and reduce the number of collisions. However, probably the more important change for performance is to do the modulo computation by repeated subtraction rather than by calling a slow library function.
This could occur because when FindSymbol was called to look for symbols in all spaces, it would find a tag in an inner scope before a typedef in an outer scope. The processing order has been changed to look for regular symbols (including typedefs) in any scope, and only look for tags if no regular symbol is found.
Here is an example illustrating the problem:
typedef int T;
int main(void) {
struct T;
T x;
}
This occurred due to looking for the symbol in all namespaces rather than only variable space.
Here is an example affected by this:
int X;
int main(void) {
struct X {int i;};
static int *i = &X;
}
If an identifier is used as a typedef in an outer scope but then declared as something else in an inner scope (e.g. a variable name or tag), and that same identifier is the next token after the end of the inner scope, it would not be recognized properly as a typedef name leading to spurious errors.\
Here is an example that triggered this:
typedef char Type;
void f(int Type);
Type t;
Here is another one:
int main(void) {
typedef int S;
if (1)
(struct S {int a;} *)0;
S x;
}
The old approach would call GenerateCode twice for each parameter expression. Now, it is only called once. This is faster, and also avoids some oddities with error handling. With the previous approach, expressionType would not be set if there was an error in the expression, which could lead to additional spurious errors. Also, a lint message treated as a warning could appear twice.
This could happen in some cases where one subexpression of a larger expression was a constant. One effect of this was to cause spurious "lint: implicit conversion changes value of constant" messages in certain cases (when that lint check was enabled). It may also have caused certain errors to be missed in other situations.