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