1 %************************************************************************
3 \section[Driver-asm-fiddling]{Fiddling with assembler files (alpha)}
5 %************************************************************************
10 Utterly stomp out C functions' prologues and epilogues; i.e., the
11 stuff to do with the C stack.
13 Any other required tidying up.
18 local($in_asmf, $out_asmf) = @_;
20 # multi-line regexp matching:
25 open(INASM, "< $in_asmf")
26 || &tidy_up_and_die(1,"$Pgm: failed to open `$in_asmf' (to read)\n");
27 open(OUTASM,"> $out_asmf")
28 || &tidy_up_and_die(1,"$Pgm: failed to open `$out_asmf' (to write)\n");
30 # read whole file, divide into "chunks":
31 # record some info about what we've found...
33 @chk = (); # contents of the chunk
34 $numchks = 0; # number of them
35 @chkcat = (); # what category of thing in each chunk
36 @chksymb = (); # what symbol(base) is defined in this chunk
37 %slowchk = (); # ditto, its regular "slow" entry code
38 %fastchk = (); # ditto, fast entry code
39 %closurechk = (); # ditto, the (static) closure
40 %infochk = (); # given a symbol base, say what chunk its info tbl is in
41 %vectorchk = (); # ditto, return vector table
42 %directchk = (); # ditto, direct return code
48 #??? next if /^\.stab.*___stg_split_marker/;
49 #??? next if /^\.stab.*ghc.*c_ID/;
53 if ( /^\s+/ ) { # most common case first -- a simple line!
54 # duplicated from the bottom
57 } elsif ( /\.\.ng:$/ ) { # Local labels not to be confused with new chunks
60 } elsif ( /^(ret_|djn_)/ ) {
65 } elsif ( /^vtbl_([A-Za-z0-9_]+):$/ ) {
67 $chkcat[$i] = 'vector';
72 } elsif ( /^([A-Za-z0-9_]+)DirectReturn:$/ ) {
74 $chkcat[$i] = 'direct';
79 } elsif ( /^[A-Za-z0-9_]+_upd:$/ ) {
84 } elsif ( /^\$C(\d+):$/ ) {
86 $chkcat[$i] = 'string';
89 } elsif ( /^__stg_split_marker(\d+):$/ ) {
91 $chkcat[$i] = 'splitmarker';
94 } elsif ( /^([A-Za-z0-9_]+)_info:$/ ) {
97 $chkcat[$i] = 'infotbl';
100 $infochk{$symb} = $i;
102 } elsif ( /^([A-Za-z0-9_]+)_entry:$/ ) {
104 $chkcat[$i] = 'slow';
109 } elsif ( /^([A-Za-z0-9_]+)_fast\d+:$/ ) {
111 $chkcat[$i] = 'fast';
116 } elsif ( /^([A-Za-z0-9_]+)_closure:$/ ) {
118 $chkcat[$i] = 'closure';
121 $closurechk{$1} = $i;
123 } elsif ( /^ghc.*c_ID:/ ) {
125 $chkcat[$i] = 'consist';
127 } elsif ( /^(__gnu_compiled_c|gcc2_compiled\.):/ ) {
130 } elsif ( /^ErrorIO_call_count:/ # HACK!!!!
131 || /^[A-Za-z0-9_]+\.\d+:$/
132 || /^.*_CAT:/ # PROF: _entryname_CAT
133 || /^CC_.*_struct:/ # PROF: _CC_ccident_struct
134 || /^.*_done:/ # PROF: _module_done
135 || /^_module_registered:/ # PROF: _module_registered
138 $chkcat[$i] = 'data';
141 } elsif ( /^[A-Za-z0-9_]/ ) {
144 print STDERR "Funny global thing?: $_"
145 unless $KNOWN_FUNNY_THING{$thing}
146 || /^_(PRIn|PRStart).*:/ # pointer reversal GC routines
147 || /^CC_.*:/ # PROF: _CC_ccident
148 || /^_reg.*:/; # PROF: _reg<module>
150 $chkcat[$i] = 'misc';
153 } else { # simple line (duplicated at the top)
157 $numchks = $#chk + 1;
159 # print STDERR "\nCLOSURES:\n";
160 # foreach $s (sort (keys %closurechk)) {
161 # print STDERR "$s:\t\t",$closurechk{$s},"\n";
163 # print STDERR "\nINFOS:\n";
164 # foreach $s (sort (keys %infochk)) {
165 # print STDERR "$s:\t\t",$infochk{$s},"\n";
167 # print STDERR "SLOWS:\n";
168 # foreach $s (sort (keys %slowchk)) {
169 # print STDERR "$s:\t\t",$slowchk{$s},"\n";
171 # print STDERR "\nFASTS:\n";
172 # foreach $s (sort (keys %fastchk)) {
173 # print STDERR "$s:\t\t",$fastchk{$s},"\n";
176 # the division into chunks is imperfect;
177 # we throw some things over the fence into the next
180 # also, there are things we would like to know
181 # about the whole module before we start spitting
184 # NB: we start meddling at chunk 1, not chunk 0
186 # the first ".rdata" is quite magical; as of GCC 2.7.x, it
187 # spits a ".quad 0" in after the v first ".rdata"; we
188 # detect this special case (tossing the ".quad 0")!
189 $magic_rdata_seen = 0;
191 for ($i = 1; $i < $numchks; $i++) {
192 $c = $chk[$i]; # convenience copy
194 # print STDERR "\nCHK $i (BEFORE):\n", $c;
196 # pin a funny end-thing on (for easier matching):
197 $c .= 'FUNNY#END#THING';
199 if ( ! $magic_rdata_seen && $c =~ /^\s*\.rdata\n\t\.quad 0\n\t\.align \d\n/ ) {
200 $c =~ s/^\s*\.rdata\n\t\.quad 0\n\t\.align (\d)\n/\.rdata\n\t\.align $1\n/;
201 $magic_rdata_seen = 1;
204 # pick some end-things and move them to the next chunk
206 while ( $c =~ /^(\s*\.align\s+\d+\n)FUNNY#END#THING/
207 || $c =~ /^(\s*\.(globl|ent)\s+\S+\n)FUNNY#END#THING/
208 || $c =~ /^(\s*\#.*\n)FUNNY#END#THING/
209 || $c =~ /^(\s*\.(file|loc)\s+\S+\s+\S+\n)FUNNY#END#THING/
210 || $c =~ /^(\s*\.text\n|\s*\.r?data\n)FUNNY#END#THING/ ) {
213 if ( $to_move =~ /^\s*(\#|\.(file|globl|ent|loc))/ && $i < ($numchks - 1) ) {
214 $chk[$i + 1] = $to_move . $chk[$i + 1];
215 # otherwise they're tossed
218 $c =~ s/^.*\nFUNNY#END#THING/FUNNY#END#THING/;
221 if ($c =~ /^\t\.ent\s+(\S+)/) {
223 # toss all prologue stuff, except for loading gp, and the ..ng address
224 if (($p, $r) = split(/^\t\.prologue/, $c)) {
225 # print STDERR "$ent: prologue:\n$p\nrest:\n$r\n";
226 if (($keep, $junk) = split(/\.\.ng:/, $p)) {
227 $c = $keep . "..ng:\n";
229 print STDERR "malformed code block ($ent)?\n"
232 $c .= "\t.frame \$30,0,\$26,0\n\t.prologue" . $r;
235 $c =~ s/FUNNY#END#THING//;
236 $chk[$i] = $c; # update w/ convenience copy
238 # print STDERR "\nCHK $i (AFTER):\n", $c;
241 # print out the header stuff first
243 $chk[0] =~ s/^(\t\.file.*)"(ghc\d+\.c)"/\1"$ifile_root.hc"/;
244 print OUTASM $chk[0];
246 # print out all the literal strings second
247 for ($i = 1; $i < $numchks; $i++) {
248 if ( $chkcat[$i] eq 'string' ) {
249 print OUTASM "\.rdata\n\t\.align 3\n";
250 print OUTASM $chk[$i];
252 $chkcat[$i] = 'DONE ALREADY';
256 for ($i = 1; $i < $numchks; $i++) {
257 # print STDERR "$i: cat $chkcat[$i], symb $chksymb[$i]\n";
259 next if $chkcat[$i] eq 'DONE ALREADY';
261 if ( $chkcat[$i] eq 'misc' ) {
262 print OUTASM "\.text\n\t\.align 3\n";
263 print OUTASM $chk[$i];
265 } elsif ( $chkcat[$i] eq 'data' ) {
266 print OUTASM "\.data\n\t\.align 3\n";
267 print OUTASM $chk[$i];
269 } elsif ( $chkcat[$i] eq 'consist' ) {
270 if ( $chk[$i] =~ /\.ascii.*\)(hsc|cc) (.*)\\11"\n\t\.ascii\s+"(.*)\\0"/ ) {
271 local($consist) = "$1.$2.$3";
273 $consist =~ s/\//./g;
275 $consist =~ s/[^A-Za-z0-9_.]/ZZ/g; # ToDo: properly?
276 print OUTASM "\.text\n$consist:\n";
278 print STDERR "Couldn't grok consistency: ", $chk[$i];
281 } elsif ( $chkcat[$i] eq 'splitmarker' ) {
282 # we can just re-constitute this one...
283 # ignore the final split marker, to save an empty object module
284 # Use _three_ underscores so that ghc-split doesn't get overly complicated
285 print OUTASM "___stg_split_marker",$chksymb[$i],":\n";
287 } elsif ( $chkcat[$i] eq 'closure'
288 || $chkcat[$i] eq 'infotbl'
289 || $chkcat[$i] eq 'slow'
290 || $chkcat[$i] eq 'fast' ) { # do them in that order
291 $symb = $chksymb[$i];
294 if ( defined($closurechk{$symb}) ) {
295 print OUTASM "\.data\n\t\.align 3\n";
296 print OUTASM $chk[$closurechk{$symb}];
297 $chkcat[$closurechk{$symb}] = 'DONE ALREADY';
301 if ( defined($infochk{$symb}) ) {
303 print OUTASM "\.text\n\t\.align 3\n";
304 print OUTASM &rev_tbl($symb, $chk[$infochk{$symb}], 1);
305 # entry code will be put here!
307 $chkcat[$infochk{$symb}] = 'DONE ALREADY';
311 if ( defined($slowchk{$symb}) ) {
313 # teach it to drop through to the fast entry point:
314 $c = $chk[$slowchk{$symb}];
315 if ( defined($fastchk{$symb}) ) {
316 $c =~ s/^\tjmp \$31,\(\$27\),0\n\t\.align 4\n\t\.end/\t.align 4\n\t.end/;
319 print OUTASM "\.text\n\t\.align 3\n";
321 $chkcat[$slowchk{$symb}] = 'DONE ALREADY';
325 if ( defined($fastchk{$symb}) ) {
326 $c = $chk[$fastchk{$symb}];
327 if ( ! defined($slowchk{$symb}) ) {
328 print OUTASM "\.text\n\t\.align 3\n";
331 $chkcat[$fastchk{$symb}] = 'DONE ALREADY';
334 } elsif ( $chkcat[$i] eq 'vector'
335 || $chkcat[$i] eq 'direct' ) { # do them in that order
336 $symb = $chksymb[$i];
339 if ( defined($vectorchk{$symb}) ) {
340 print OUTASM "\.text\n\t\.align 3\n";
341 print OUTASM &rev_tbl($symb, $chk[$vectorchk{$symb}], 0);
342 # direct return code will be put here!
343 $chkcat[$vectorchk{$symb}] = 'DONE ALREADY';
347 if ( defined($directchk{$symb}) ) {
348 print OUTASM "\.text\n\t\.align 3\n";
349 print OUTASM $chk[$directchk{$symb}];
350 $chkcat[$directchk{$symb}] = 'DONE ALREADY';
352 # The commented nop is for the splitter, to ensure
353 # that no module ends with a label as the very last
354 # thing. (The linker will adjust the label to point
355 # to the first code word of the next module linked in,
356 # even if alignment constraints cause the label to move!)
358 print OUTASM "\t# nop\n";
361 &tidy_up_and_die(1,"$Pgm: unknown chkcat (ghc-asm alpha)\n$chkcat[$i]\n$chk[$i]\n");
366 close(OUTASM) || &tidy_up_and_die(1,"Failed writing to $out_asmf\n");
367 close(INASM) || &tidy_up_and_die(1,"Failed reading from $in_asmf\n");
372 sub init_FUNNY_THINGS {
373 %KNOWN_FUNNY_THING = (
375 'CommonUnderflow:', 1,
378 'ErrorIO_call_count:', 1,
379 'ErrorIO_innards:', 1,
390 'StackUnderflowEnterNode:', 1,
392 'UnderflowVect0:', 1,
393 'UnderflowVect1:', 1,
394 'UnderflowVect2:', 1,
395 'UnderflowVect3:', 1,
396 'UnderflowVect4:', 1,
397 'UnderflowVect5:', 1,
398 'UnderflowVect6:', 1,
399 'UnderflowVect7:', 1,
402 'WorldStateToken:', 1,
403 '_Enter_Internal:', 1,
404 '_PRMarking_MarkNextAStack:', 1,
405 '_PRMarking_MarkNextBStack:', 1,
406 '_PRMarking_MarkNextCAF:', 1,
407 '_PRMarking_MarkNextGA:', 1,
408 '_PRMarking_MarkNextRoot:', 1,
409 '_PRMarking_MarkNextSpark:', 1,
410 '_Scavenge_Forward_Ref:', 1,
411 '__std_entry_error__:', 1,
412 '_startMarkWorld:', 1,
414 'startCcRegisteringWorld:', 1,
415 'startEnterFloat:', 1,
417 'startPerformIO:', 1,
424 The following table reversal is used for both info tables and return
425 vectors. In both cases, we remove the first entry from the table,
426 reverse the table, put the label at the end, and paste some code
427 (that which is normally referred to by the first entry in the table)
428 right after the table itself. (The code pasting is done elsewhere.)
432 local($symb, $tbl, $discard1) = @_;
438 local(@lines) = split(/\n/, $tbl);
441 for ($i = 0; $i <= $#lines && $lines[$i] !~ /^\t\.quad\s+/; $i++) {
442 $label .= $lines[$i] . "\n",
443 next if $lines[$i] =~ /^[A-Za-z0-9_]+:$/
444 || $lines[$i] =~ /^\t\.globl/;
446 $before .= $lines[$i] . "\n"; # otherwise...
449 for ( ; $i <= $#lines && $lines[$i] =~ /^\t\.quad\s+/; $i++) {
450 push(@words, $lines[$i]);
452 # now throw away the first word (entry code):
453 shift(@words) if $discard1;
455 for (; $i <= $#lines; $i++) {
456 $after .= $lines[$i] . "\n";
459 # If we have anonymous text (not part of a procedure), the linker
460 # may complain about missing exception information. Bleh.
461 if ($label =~ /^([A-Za-z0-9_]+):$/) {
462 $before = "\t.ent $1\n" . $before;
463 $after .= "\t.end $1\n";
466 $tbl = $before . join("\n", (reverse @words)) . "\n" . $label . $after;
468 # print STDERR "before=$before\n";
469 # print STDERR "label=$label\n";
470 # print STDERR "words=",(reverse @words),"\n";
471 # print STDERR "after=$after\n";
477 %************************************************************************
479 \subsection[Driver-asm-info]{Collect interesting (static) info from an assembler file}
481 %************************************************************************
483 How many times is each asm instruction used?
486 %AsmInsn = (); # init
488 sub dump_asm_insn_counts {
491 open(INASM, "< $asmf") || &tidy_up_and_die(1,"$Pgm: failed to open `$asmf' (to read)\n");
493 if ( /^\t([a-z][a-z0-9]+)\b/ ) {
497 close(INASM) || &tidy_up_and_die(1,"Failed reading from $asmf\n");
499 # OK, now print what we collected (to stderr)
500 foreach $i (sort (keys %AsmInsn)) {
501 print STDERR "INSN:: $i\t",$AsmInsn{$i},"\n";
505 sub dump_asm_globals_info {
508 # make "require"r happy...