[[project @ 1998-05-19 16:47:43 by reid] reid**19980519164743 Added first draft of coding style doc ] { addfile ./docs/coding-style.html hunk ./docs/coding-style.html 1 + + +
++ /* minimum alignment of unsigned int */ + #define ALIGNMENT_UNSIGNED_INT 4 + + /* minimum alignment of long */ + #define ALIGNMENT_LONG 4 + + /* minimum alignment of float */ + #define ALIGNMENT_FLOAT 4 + + /* minimum alignment of double */ + #define ALIGNMENT_DOUBLE 4 ++ +
+Use PK_FLT(addr), PK_DBL(addr) to read StgFloat and +StgDouble values from the stack/heap, and ASSIGN_FLT(val,addr)/ +ASSIGN_DBL(val,addr) to assign StgFloat/StgDouble values to heap/stack +locations. These macros take care of alignment restrictions. + +
+Heap/Stack locations are StgWord aligned; the alignment requirements +of an StgDouble may be more than that of StgWord, but we don't pad +misaligned StgDoubles (doing so would be too much hassle). + +
+Doing a direct assignment/read of an StgDouble to/from a mis-aligned +location may not work, so we use the ASSIGN_DBL(,)/PK_DBL() macro, +which goes via a temporary. + +
+Problem: if the architecture allows mis-aligned accesses, but prefers +aligned accesses, these macros just add an extra level of indirection. +We need to distinguish between an architecture that allows mis-aligned +accesses and one that just imposes a performance penalty (this is most +of them). Perhaps have PREFERRED_ALIGNMENT and REQUIRED_ALIGMENT +configurations? + +
+ #ifdef solaris_HOST_OS + // do something solaris specific + #endif ++ +Instead, add an appropriate test to the configure.in script and use +the result of that test instead. + +
+ #ifdef HAVE_BSD_H + // use a BSD library + #endif ++ +The problem is that things change from one version of an OS to another +- things get added, things get deleted, things get broken, some things +are optional extras. Using "feature tests" instead of "system tests" +makes things a lot less brittle. Things also tend to get documented +better. + +
+The ideas in this section are mostly aimed at this issue: + +
+typedef enum + { i_INTERNAL_ERROR /* Instruction 0 raises an internal error */ + , i_PANIC /* irrefutable pattern match failed! */ + , i_ERROR /* user level error */ + + ... ++
+In particular: +
+ #define add(x,y) ((x)+(y)) ++instead of +
+ #define add(x,y) x+y ++ +
+ #define ASSIGN_CC_ID(ccID) \ + { \ + ccID = CC_ID; \ + CC_ID++; \ + } ++ +but it's better to use the "do { ... } while (0)" trick instead: + +
+ #define ASSIGN_CC_ID(ccID) \ + do { \ + ccID = CC_ID; \ + CC_ID++; \ + } while(0) ++ +The following explanation comes from +The Usenet C programming FAQ +
+10.4: What's the best way to write a multi-statement macro? + +A: The usual goal is to write a macro that can be invoked as if it + were a statement consisting of a single function call. This + means that the "caller" will be supplying the final semicolon, + so the macro body should not. The macro body cannot therefore + be a simple brace-enclosed compound statement, because syntax + errors would result if it were invoked (apparently as a single + statement, but with a resultant extra semicolon) as the if + branch of an if/else statement with an explicit else clause. + + The traditional solution, therefore, is to use + + #define MACRO(arg1, arg2) do { \ + /* declarations */ \ + stmt1; \ + stmt2; \ + /* ... */ \ + } while(0) /* (no trailing ; ) */ + + When the caller appends a semicolon, this expansion becomes a + single statement regardless of context. (An optimizing compiler + will remove any "dead" tests or branches on the constant + condition 0, although lint may complain.) + + If all of the statements in the intended macro are simple + expressions, with no declarations or loops, another technique is + to write a single, parenthesized expression using one or more + comma operators. (For an example, see the first DEBUG() macro + in question 10.26.) This technique also allows a value to be + "returned." + + References: H&S Sec. 3.3.2 p. 45; CT&P Sec. 6.3 pp. 82-3. ++ +
+ #define doNothing() do { } while (0) ++
+ // you can use this as an l-value or an l-value + #define PROF_INFO(cl) (((StgClosure*)(cl))->header.profInfo) + + // polymorphic case + // but note that min(min(1,2),3) does 3 comparisions instead of 2!! + #define min(x,y) (((x)<=(y)) ? (x) : (y)) ++ +
+ When a function is both inline and `static', if all calls to the +function are integrated into the caller, and the function's address is +never used, then the function's own assembler code is never referenced. +In this case, GNU CC does not actually output assembler code for the +function, unless you specify the option `-fkeep-inline-functions'. +Some calls cannot be integrated for various reasons (in particular, +calls that precede the function's definition cannot be integrated, and +neither can recursive calls within the definition). If there is a +nonintegrated call, then the function is compiled to assembler code as +usual. The function must also be compiled as usual if the program +refers to its address, because that can't be inlined. + + When an inline function is not `static', then the compiler must +assume that there may be calls from other source files; since a global +symbol can be defined only once in any program, the function must not +be defined in the other source files, so the calls therein cannot be +integrated. Therefore, a non-`static' inline function is always +compiled on its own in the usual fashion. + + If you specify both `inline' and `extern' in the function +definition, then the definition is used only for inlining. In no case +is the function compiled on its own, not even if you refer to its +address explicitly. Such an address becomes an external reference, as +if you had only declared the function, and had not defined it. + + This combination of `inline' and `extern' has almost the effect of a +macro. The way to use it is to put a function definition in a header +file with these keywords, and put another copy of the definition +(lacking `inline' and `extern') in a library file. The definition in +the header file will cause most calls to the function to be inlined. +If any uses of the function remain, they will refer to the single copy +in the library. ++ +
+ typedef enum { /* N.B. Used as indexes into arrays */ + NO_HEAP_PROFILING, + HEAP_BY_CC, + HEAP_BY_MOD, + HEAP_BY_GRP, + HEAP_BY_DESCR, + HEAP_BY_TYPE, + HEAP_BY_TIME + } ProfilingFlags; ++instead of +
+ # define NO_HEAP_PROFILING 0 /* N.B. Used as indexes into arrays */ + # define HEAP_BY_CC 1 + # define HEAP_BY_MOD 2 + # define HEAP_BY_GRP 3 + # define HEAP_BY_DESCR 4 + # define HEAP_BY_TYPE 5 + # define HEAP_BY_TIME 6 ++and +
+ typedef enum { + CCchar = 'C', + MODchar = 'M', + GRPchar = 'G', + DESCRchar = 'D', + TYPEchar = 'Y', + TIMEchar = 'T' + } ProfilingTag; ++instead of +
+ # define CCchar 'C' + # define MODchar 'M' + # define GRPchar 'G' + # define DESCRchar 'D' + # define TYPEchar 'Y' + # define TIMEchar 'T' ++ToDo: at the time of writing, we still use the former. + +
+#define stgCast(ty,e) ((ty)(e)) ++ +
+On which note: +Hugs related pieces of code often start with the line: +
+ /* -*- mode: hugs-c; -*- */ ++which helps Emacs mimic the indentation style used by Mark P Jones +within Hugs. Add this to your .emacs file. +
+ (defun hugs-c-mode () + "C mode with adjusted defaults for use with Hugs (based on linux-c-mode)" + (interactive) + (c-mode) + (setq c-basic-offset 4) + (setq indent-tabs-mode nil) ; don't use tabs to indent + (setq c-recognize-knr-r nil) ; no K&R here - don't pay the price + ; no: (setq tab-width 4) + + (c-set-offset 'knr-argdecl-intro 0) + (c-set-offset 'case-label 0) + (c-set-offset 'statement-case-intro '++) + (c-set-offset 'statement-case-open '+) + ) ++ + +
+If you must reindent or reorganise, don't do anything else in that commit +and give advance warning that you're about to do it in case anyone else +is changing that file. +