[project @ 1996-01-10 12:38:57 by partain]
[ghc-hetmet.git] / ghc / driver / ghc-asm-hppa.lprl
1 %************************************************************************
2 %*                                                                      *
3 \section[Driver-asm-fiddling]{Fiddling with assembler files (HP-PA)}
4 %*                                                                      *
5 %************************************************************************
6
7 Tasks:
8 \begin{itemize}
9 \item
10 Utterly stomp out C functions' prologues and epilogues; i.e., the
11 stuff to do with the C stack.
12 \item
13 Any other required tidying up.
14 \end{itemize}
15
16 HP specific notes:
17 \begin{itemize}
18 \item
19 The HP linker is very picky about symbols being in the appropriate
20 space (code vs. data).  When we mangle the threaded code to put the
21 info tables just prior to the code, they wind up in code space
22 rather than data space.  This means that references to *_info from
23 un-mangled parts of the RTS (e.g. unthreaded GC code) get
24 unresolved symbols.  Solution:  mini-mangler for .c files on HP.  I
25 think this should really be triggered in the driver by a new -rts
26 option, so that user code doesn't get mangled inappropriately.
27 \item
28 With reversed tables, jumps are to the _info label rather than to
29 the _entry label.  The _info label is just an address in code
30 space, rather than an entry point with the descriptive blob we
31 talked about yesterday.  As a result, you can't use the call-style
32 JMP_ macro.  However, some JMP_ macros take _info labels as targets
33 and some take code entry points within the RTS.  The latter won't
34 work with the goto-style JMP_ macro.  Sigh.  Solution: Use the goto
35 style JMP_ macro, and mangle some more assembly, changing all
36 "RP'literal" and "LP'literal" references to "R'literal" and
37 "L'literal," so that you get the real address of the code, rather
38 than the descriptive blob.  Also change all ".word P%literal"
39 entries in info tables and vector tables to just ".word literal,"
40 for the same reason.  Advantage: No more ridiculous call sequences.
41 \end{itemize}
42
43 \begin{code}
44 sub mangle_asm {
45     local($in_asmf, $out_asmf) = @_;
46
47     # multi-line regexp matching:
48     local($*) = 1;
49     local($i, $c);
50     &init_FUNNY_THINGS();
51
52     open(INASM, "< $in_asmf")
53         || &tidy_up_and_die(1,"$Pgm: failed to open `$in_asmf' (to read)\n");
54     open(OUTASM,"> $out_asmf")
55         || &tidy_up_and_die(1,"$Pgm: failed to open `$out_asmf' (to write)\n");
56
57     # read whole file, divide into "chunks":
58     #   record some info about what we've found...
59
60     @chk = ();          # contents of the chunk
61     $numchks = 0;       # number of them
62     @chkcat = ();       # what category of thing in each chunk
63     @chksymb = ();      # what symbol(base) is defined in this chunk
64     %slowchk = ();      # ditto, its regular "slow" entry code
65     %fastchk = ();      # ditto, fast entry code
66     %closurechk = ();   # ditto, the (static) closure
67     %infochk = ();      # ditto, normal info tbl
68     %vectorchk = ();    # ditto, return vector table
69     %directchk = ();    # ditto, direct return code
70
71     $i = 0;
72     $chkcat[0] = 'misc';
73
74     while (<INASM>) {
75 #???    next if /^\.stab.*___stg_split_marker/;
76 #???    next if /^\.stab.*ghc.*c_ID/;
77
78         next if /^;/;
79
80         if ( /^\s+/ ) { # most common case first -- a simple line!
81             # duplicated from the bottom
82             $chk[$i] .= $_;
83
84         } elsif ( /^L\$C(\d+)$/ ) {
85             $chk[++$i] .= $_;
86             $chkcat[$i] = 'literal';
87             $chksymb[$i] = $1;
88
89         } elsif ( /^(ret_|djn_)/ ) {
90             $chk[++$i] .= $_;
91             $chkcat[$i] = 'misc';
92             $chksymb[$i] = '';
93
94         } elsif ( /^vtbl_([A-Za-z0-9_]+)$/ ) {
95             $chk[++$i] .= $_;
96             $chkcat[$i] = 'vector';
97             $chksymb[$i] = $1;
98
99             $vectorchk{$1} = $i;
100
101         } elsif ( /^([A-Za-z0-9_]+)DirectReturn$/ ) {
102             $chk[++$i] .= $_;
103             $chkcat[$i] = 'direct';
104             $chksymb[$i] = $1;
105
106             $directchk{$1} = $i;
107
108         } elsif ( /^[A-Za-z0-9_]+_upd$/ ) {
109             $chk[++$i] .= $_;
110             $chkcat[$i] = 'misc';
111             $chksymb[$i] = '';
112
113         } elsif ( /^__stg_split_marker(\d+)$/ ) {
114             $chk[++$i] .= $_;
115             $chkcat[$i] = 'splitmarker';
116             $chksymb[$i] = $1;
117
118         } elsif ( /^([A-Za-z0-9_]+)_info$/ ) {
119             $symb = $1;
120             $chk[++$i] .= $_;
121             $chkcat[$i] = 'infotbl';
122             $chksymb[$i] = $symb;
123
124             $infochk{$symb} = $i;
125
126         } elsif ( /^([A-Za-z0-9_]+)_entry$/ ) {
127             $chk[++$i] .= $_;
128             $chkcat[$i] = 'slow';
129             $chksymb[$i] = $1;
130
131             $slowchk{$1} = $i;
132
133         } elsif ( /^([A-Za-z0-9_]+)_fast\d+$/ ) {
134             $chk[++$i] .= $_;
135             $chkcat[$i] = 'fast';
136             $chksymb[$i] = $1;
137
138             $fastchk{$1} = $i;
139
140         } elsif ( /^([A-Za-z0-9_]+)_closure$/ ) {
141             $chk[++$i] .= $_;
142             $chkcat[$i] = 'closure';
143             $chksymb[$i] = $1;
144
145             $closurechk{$1} = $i;
146
147         } elsif ( /^ghc.*c_ID/ ) {
148             $chk[++$i] .= $_;
149             $chkcat[$i] = 'consist';
150
151         } elsif ( /^(__gnu_compiled_c|gcc2_compiled\.)/ ) {
152             ; # toss it
153
154         } elsif ( /^ErrorIO_call_count/  # HACK!!!!
155                || /^[A-Za-z0-9_]+\.\d+$/
156                || /^.*_CAT/                     # PROF: _entryname_CAT
157                || /^CC_.*_struct/               # PROF: _CC_ccident_struct
158                || /^.*_done/                    # PROF: _module_done
159                || /^_module_registered/         # PROF: _module_registered
160                ) {
161             $chk[++$i] .= $_;
162             $chkcat[$i] = 'data';
163             $chksymb[$i] = '';
164
165         } elsif ( /^([A-Za-z0-9_]+)\s+\.comm/ ) {
166             $chk[++$i] .= $_;
167             $chkcat[$i] = 'bss';
168             $chksymb[$i] = $1;
169
170         } elsif ( /^[A-Za-z0-9_]/ && ! /^L\$\d+$/) {
171             local($thing);
172             chop($thing = $_);
173             print STDERR "Funny global thing?: $_"
174                 unless $KNOWN_FUNNY_THING{$thing}
175                     || /^_(PRIn|PRStart)/       # pointer reversal GC routines
176                     || /^CC_.*/                 # PROF: _CC_ccident
177                     || /^_reg.*/;               # PROF: _reg<module>
178             $chk[++$i] .= $_;
179             $chkcat[$i] = 'misc';
180             $chksymb[$i] = '';
181
182         } else { # simple line (duplicated at the top)
183             $chk[$i] .= $_;
184         }
185     }
186     $numchks = $#chk + 1;
187
188 #    print STDERR "\nCLOSURES:\n";
189 #    foreach $s (sort (keys %closurechk)) {
190 #       print STDERR "$s:\t\t",$closurechk{$s},"\n";
191 #    }
192 #    print STDERR "\nNORMAL INFOS:\n";
193 #    foreach $s (sort (keys %infochk)) {
194 #       print STDERR "$s:\t\t",$infochk{$s},"\n";
195 #    }
196 #    print STDERR "SLOWS:\n";
197 #    foreach $s (sort (keys %slowchk)) {
198 #       print STDERR "$s:\t\t",$slowchk{$s},"\n";
199 #    }
200 #    print STDERR "\nFASTS:\n";
201 #    foreach $s (sort (keys %fastchk)) {
202 #       print STDERR "$s:\t\t",$fastchk{$s},"\n";
203 #    }
204
205     # the division into chunks is imperfect;
206     # we throw some things over the fence into the next
207     # chunk.
208     #
209     # also, there are things we would like to know
210     # about the whole module before we start spitting
211     # output.
212
213     # NB: we start meddling at chunk 1, not chunk 0
214
215     for ($i = 1; $i < $numchks; $i++) {
216         $c = $chk[$i]; # convenience copy
217
218 #       print STDERR "\nCHK $i (BEFORE):\n", $c;
219
220         # toss all prologue stuff
221         $c =~ s/^\s+\.ENTRY[^\0]*--- BEGIN ---/\t.ENTRY/;
222
223         # Lie about our .CALLINFO
224         $c =~ s/^\s+\.CALLINFO.*$/\t.CALLINFO NO_CALLS,NO_UNWIND/;
225
226         # Get rid of P'
227
228         $c =~ s/LP'/L'/g;
229         $c =~ s/RP'/R'/g;
230
231 #       print STDERR "\nCHK $i (STEP 1):\n", $c;
232
233         # toss all epilogue stuff
234         $c =~ s/^\s+--- END ---[^\0]*\.EXIT/\t.EXIT/;
235
236 #       print STDERR "\nCHK $i (STEP 2):\n", $c;
237
238         # Sorry; we moved the _info stuff to the code segment.
239         $c =~ s/_info,DATA/_info,CODE/g;
240
241         # pin a funny end-thing on (for easier matching):
242         $c .= 'FUNNY#END#THING';
243
244         # pick some end-things and move them to the next chunk
245
246 #       print STDERR "\nCHK $i (STEP 3):\n", $c;
247         while ($c =~ /^(\s+\.(IMPORT|EXPORT|PARAM).*\n)FUNNY#END#THING/
248              || $c =~ /^(\s+\.align\s+\d+\n)FUNNY#END#THING/
249              || $c =~ /^(\s+\.(SPACE|SUBSPA)\s+\S+\n)FUNNY#END#THING/
250              ||  $c =~ /^(\s*\n)FUNNY#END#THING/ ) {
251             $to_move = $1;
252
253             if ( $i < ($numchks - 1) && ($to_move =~ /^\s+\.(IMPORT|EXPORT)/ 
254                 || ($to_move =~ /align/ && $chkcat[$i+1] eq 'literal')) ) {
255                 $chk[$i + 1] = $to_move . $chk[$i + 1];
256                 # otherwise they're tossed
257             }
258             $c =~ s/^.*\nFUNNY#END#THING/FUNNY#END#THING/;
259         }
260 #       print STDERR "\nCHK $i (STEP 4):\n", $c;
261
262         $c =~ s/FUNNY#END#THING//;
263         $chk[$i] = $c; # update w/ convenience copy
264     }
265
266     # print out the header stuff first
267
268     print OUTASM $chk[0];
269
270     # print out all the literals second
271     
272     for ($i = 1; $i < $numchks; $i++) {
273         if ( $chkcat[$i] eq 'literal' ) {
274             print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$LIT\$\n";
275             print OUTASM $chk[$i];
276             print OUTASM "; end literal\n"; # for the splitter
277
278             $chkcat[$i] = 'DONE ALREADY';
279         }
280     }
281
282     # print out all the bss third
283     
284     for ($i = 1; $i < $numchks; $i++) {
285         if ( $chkcat[$i] eq 'bss' ) {
286             print OUTASM "\t.SPACE \$PRIVATE\$\n\t.SUBSPA \$BSS\$\n\t.align 4\n";
287             print OUTASM $chk[$i];
288             
289             $chkcat[$i] = 'DONE ALREADY';
290         }
291     }
292
293     for ($i = 1; $i < $numchks; $i++) {
294 #       print STDERR "$i: cat $chkcat[$i], symb $chksymb[$i]\n";
295
296         next if $chkcat[$i] eq 'DONE ALREADY';
297
298         if ( $chkcat[$i] eq 'misc' ) {
299             print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$CODE\$\n\t\.align 4\n";
300             print OUTASM $chk[$i];
301
302         } elsif ( $chkcat[$i] eq 'data' ) {
303             print OUTASM "\t.SPACE \$PRIVATE\$\n\t.SUBSPA \$DATA\$\n\t\.align 4\n";
304             print OUTASM $chk[$i];
305
306         } elsif ( $chkcat[$i] eq 'consist' ) {
307             if ( $chk[$i] =~ /\.STRING.*\)(hsc|cc) (.*)\\x09(.*)\\x00/ ) {
308                 local($consist) = "$1.$2.$3";
309                 $consist =~ s/,/./g;
310                 $consist =~ s/\//./g;
311                 $consist =~ s/-/_/g;
312                 $consist =~ s/[^A-Za-z0-9_.]/ZZ/g; # ToDo: properly?
313                 print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$LIT\$\n$consist\n";
314             } else {
315                 print STDERR "Couldn't grok consistency: ", $chk[$i];
316             }
317
318         } elsif ( $chkcat[$i] eq 'splitmarker' ) {
319             # we can just re-constitute this one...
320             # ignore the final split marker, to save an empty object module
321             # Use _three_ underscores so that ghc-split doesn't get overly complicated
322             print OUTASM "___stg_split_marker$chksymb[$i]\n";
323
324         } elsif ( $chkcat[$i] eq 'closure'
325                || $chkcat[$i] eq 'infotbl'
326                || $chkcat[$i] eq 'slow'
327                || $chkcat[$i] eq 'fast' ) { # do them in that order
328             $symb = $chksymb[$i];
329
330             # CLOSURE
331             if ( defined($closurechk{$symb}) ) {
332                 print OUTASM "\t.SPACE \$PRIVATE\$\n\t.SUBSPA \$DATA\$\n\t\.align 4\n";
333                 print OUTASM $chk[$closurechk{$symb}];
334                 $chkcat[$closurechk{$symb}] = 'DONE ALREADY';
335             }
336
337             # INFO TABLE
338             if ( defined($infochk{$symb}) ) {
339                 print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$CODE\$\n\t\.align 4\n";
340                 print OUTASM &rev_tbl($symb, $chk[$infochk{$symb}], 1);
341                 # entry code will be put here!
342 #               if ( $chk[$infochk{$symb}] =~ /\.word\s+([A-Za-z0-9_]+_entry)$/
343 #                 && $1 ne "_${symb}_entry" ) {
344 #                   print STDERR "!!! entry point???\n",$chk[$infochk{$symb}];
345 #               }
346
347                 $chkcat[$infochk{$symb}] = 'DONE ALREADY';
348             }
349
350             # STD ENTRY POINT
351             if ( defined($slowchk{$symb}) ) {
352
353                 # teach it to drop through to the fast entry point:
354                 $c = $chk[$slowchk{$symb}];
355                 if ( defined($fastchk{$symb}) ) {
356                     $c =~ s/^\s+ldil.*\n\s+ldo.*\n\s+bv.*\n(.*\n)?\s+\.EXIT/$1\t.EXIT/;
357                 }
358
359                 print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$CODE\$\n\t\.align 4\n";
360                 print OUTASM $c;
361                 $chkcat[$slowchk{$symb}] = 'DONE ALREADY';
362             }
363             
364             # FAST ENTRY POINT
365             if ( defined($fastchk{$symb}) ) {
366                 $c = $chk[$fastchk{$symb}];
367                 if ( ! defined($slowchk{$symb}) ) {
368                     print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$CODE\$\n\t\.align 4\n";
369                 }
370                 print OUTASM $c;
371                 $chkcat[$fastchk{$symb}] = 'DONE ALREADY';
372             }
373
374         } elsif ( $chkcat[$i] eq 'vector'
375                || $chkcat[$i] eq 'direct' ) { # do them in that order
376             $symb = $chksymb[$i];
377
378             # VECTOR TABLE
379             if ( defined($vectorchk{$symb}) ) {
380                 print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$CODE\$\n\t\.align 4\n";
381                 print OUTASM &rev_tbl($symb, $chk[$vectorchk{$symb}], 0);
382                 # direct return code will be put here!
383                 $chkcat[$vectorchk{$symb}] = 'DONE ALREADY';
384             }
385
386             # DIRECT RETURN
387             if ( defined($directchk{$symb}) ) {
388                 print OUTASM "\t.SPACE \$TEXT\$\n\t.SUBSPA \$CODE\$\n\t\.align 4\n";
389                 print OUTASM $chk[$directchk{$symb}];
390                 $chkcat[$directchk{$symb}] = 'DONE ALREADY';
391             }
392         } else {
393             &tidy_up_and_die(1,"$Pgm: unknown chkcat (ghc-asm hppa)\n$chkcat[$i]\n$chk[$i]\n");
394         }
395     }
396
397     # finished:
398     close(OUTASM) || &tidy_up_and_die(1,"Failed writing to $out_asmf\n");
399     close(INASM)  || &tidy_up_and_die(1,"Failed reading from $in_asmf\n");
400 }
401 \end{code}
402
403 The HP is a major nuisance.  The threaded code mangler moved info tables
404 from data space to code space, but unthreaded code in the RTS still has
405 references to info tables in data space.  Since the HP linker is very precise
406 about where symbols live, we need to patch the references in the unthreaded
407 RTS as well.
408
409 \begin{code}
410
411 sub mini_mangle_asm {
412     local($in_asmf, $out_asmf) = @_;
413
414     open(INASM, "< $in_asmf")
415         || &tidy_up_and_die(1,"$Pgm: failed to open `$in_asmf' (to read)\n");
416     open(OUTASM,"> $out_asmf")
417         || &tidy_up_and_die(1,"$Pgm: failed to open `$out_asmf' (to write)\n");
418
419     while (<INASM>) {
420         s/_info,DATA/_info,CODE/;   # Move _info references to code space
421         s/P%_PR/_PR/;
422         print OUTASM;
423     }
424
425     # finished:
426     close(OUTASM) || &tidy_up_and_die(1,"Failed writing to $out_asmf\n");
427     close(INASM)  || &tidy_up_and_die(1,"Failed reading from $in_asmf\n");
428 }
429
430 \end{code}
431
432 \begin{code}
433 sub init_FUNNY_THINGS {
434     %KNOWN_FUNNY_THING = (
435         'CheckHeapCode', 1,
436         'CommonUnderflow', 1,
437         'Continue', 1,
438         'EnterNodeCode', 1,
439         'ErrorIO_call_count', 1,
440         'ErrorIO_innards', 1,
441         'IndUpdRetDir', 1,
442         'IndUpdRetV0', 1,
443         'IndUpdRetV1', 1,
444         'IndUpdRetV2', 1,
445         'IndUpdRetV3', 1,
446         'IndUpdRetV4', 1,
447         'IndUpdRetV5', 1,
448         'IndUpdRetV6', 1,
449         'IndUpdRetV7', 1,
450         'PrimUnderflow', 1,
451         'StackUnderflowEnterNode', 1,
452         'StdErrorCode', 1,
453         'UnderflowVect0', 1,
454         'UnderflowVect1', 1,
455         'UnderflowVect2', 1,
456         'UnderflowVect3', 1,
457         'UnderflowVect4', 1,
458         'UnderflowVect5', 1,
459         'UnderflowVect6', 1,
460         'UnderflowVect7', 1,
461         'UpdErr', 1,
462         'UpdatePAP', 1,
463         'WorldStateToken', 1,
464         '_Enter_Internal', 1,
465         '_PRMarking_MarkNextAStack', 1,
466         '_PRMarking_MarkNextBStack', 1,
467         '_PRMarking_MarkNextCAF', 1,
468         '_PRMarking_MarkNextGA', 1,
469         '_PRMarking_MarkNextRoot', 1,
470         '_PRMarking_MarkNextSpark', 1,
471         '_Scavenge_Forward_Ref', 1,
472         '__std_entry_error__', 1,
473         '_startMarkWorld', 1,
474         'resumeThread', 1,
475         'startCcRegisteringWorld', 1,
476         'startEnterFloat', 1,
477         'startEnterInt', 1,
478         'startPerformIO', 1,
479         'startStgWorld', 1,
480         'stopPerformIO', 1
481     );
482 }
483 \end{code}
484
485 The following table reversal is used for both info tables and return
486 vectors.  In both cases, we remove the first entry from the table,
487 reverse the table, put the label at the end, and paste some code
488 (that which is normally referred to by the first entry in the table)
489 right after the table itself.  (The code pasting is done elsewhere.)
490
491 \begin{code}
492 sub rev_tbl {
493     local($symb, $tbl, $discard1) = @_;
494
495     local($before) = '';
496     local($label) = '';
497     local(@imports) = ();
498     local(@words) = ();
499     local($after) = '';
500     local(@lines) = split(/\n/, $tbl);
501     local($i);
502
503     for ($i = 0; $i <= $#lines && $lines[$i] !~ /^\s+\.word\s+/; $i++) {
504         $label .= $lines[$i] . "\n",
505             next if $lines[$i] =~ /^[A-Za-z0-9_]+$/
506                  || $lines[$i] =~ /^\s+\.EXPORT/;
507
508         $before .= $lines[$i] . "\n"; # otherwise...
509     }
510
511     for ( ; $i <= $#lines && $lines[$i] =~ /^\s+\.(word|IMPORT)/; $i++) {
512         if ($lines[$i] =~ /^\s+\.IMPORT/) {
513             push(@imports, $lines[$i]);
514         } else {
515             # We don't use HP's ``function pointers''
516             # We just use labels in code space, like normal people
517             $lines[$i] =~ s/P%//;
518             push(@words, $lines[$i]);
519         }
520     }
521     # now throw away the first word (entry code):
522     if ($discard1) {
523         shift(@words);
524     }
525
526     for (; $i <= $#lines; $i++) {
527         $after .= $lines[$i] . "\n";
528     }
529
530     $tbl = $before . join("\n", @imports) . "\n" .
531            join("\n", (reverse @words)) . "\n" . $label . $after;
532
533 #    print STDERR "before=$before\n";
534 #    print STDERR "label=$label\n";
535 #    print STDERR "words=",(reverse @words),"\n";
536 #    print STDERR "after=$after\n";
537
538     $tbl;
539 }
540 \end{code}
541
542 %************************************************************************
543 %*                                                                      *
544 \subsection[Driver-asm-info]{Collect interesting (static) info from an assembler file}
545 %*                                                                      *
546 %************************************************************************
547
548 How many times is each asm instruction used?
549
550 \begin{code}
551 %AsmInsn = (); # init
552
553 sub dump_asm_insn_counts {
554     local($asmf) = @_;
555
556     open(INASM, "< $asmf")  || &tidy_up_and_die(1,"$Pgm: failed to open `$asmf' (to read)\n");
557     while (<INASM>) {
558         if ( /^\t([a-z][a-z0-9]+)\b/ ) {
559             $AsmInsn{$1} ++;
560         }
561     }
562     close(INASM) || &tidy_up_and_die(1,"Failed reading from $asmf\n");
563
564     # OK, now print what we collected (to stderr)
565     foreach $i (sort (keys %AsmInsn)) {
566         print STDERR "INSN:: $i\t",$AsmInsn{$i},"\n";
567     }
568 }
569
570 sub dump_asm_globals_info {
571 }
572
573 # make "require"r happy...
574 1;
575 \end{code}