1 %************************************************************************
3 \section[Driver-asm-fiddling]{Fiddling with assembler files (m68k)}
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/;
50 next if /^#(NO_)?APP/;
52 if ( /^\s+/ ) { # most common case first -- a simple line!
53 # duplicated from the bottom
57 } elsif ( /^_(ret_|djn_)/ ) {
62 } elsif ( /^_vtbl_([A-Za-z0-9_]+):$/ ) {
64 $chkcat[$i] = 'vector';
69 } elsif ( /^_([A-Za-z0-9_]+)DirectReturn:$/ ) {
71 $chkcat[$i] = 'direct';
76 } elsif ( /^_[A-Za-z0-9_]+_upd:$/ ) {
81 } elsif ( /^LC(\d+):$/ ) {
83 $chkcat[$i] = 'string';
86 } elsif ( /^___stg_split_marker(\d+):$/ ) {
88 $chkcat[$i] = 'splitmarker';
91 } elsif ( /^_([A-Za-z0-9_]+)_info:$/ ) {
94 $chkcat[$i] = 'infotbl';
99 } elsif ( /^_([A-Za-z0-9_]+)_entry:$/ ) {
101 $chkcat[$i] = 'slow';
106 } elsif ( /^_([A-Za-z0-9_]+)_fast\d+:$/ ) {
108 $chkcat[$i] = 'fast';
113 } elsif ( /^_([A-Za-z0-9_]+)_closure:$/ ) {
115 $chkcat[$i] = 'closure';
118 $closurechk{$1} = $i;
120 } elsif ( /^_ghc.*c_ID:/ ) {
122 $chkcat[$i] = 'consist';
124 } elsif ( /^(___gnu_compiled_c|gcc2_compiled\.):/ ) {
127 } elsif ( /^_ErrorIO_call_count:/ # HACK!!!!
128 || /^_[A-Za-z0-9_]+\.\d+:$/
129 || /^_.*_CAT:/ # PROF: _entryname_CAT
130 || /^_CC_.*_struct:/ # PROF: _CC_ccident_struct
131 || /^_.*_done:/ # PROF: _module_done
132 || /^__module_registered:/ # PROF: _module_registered
135 $chkcat[$i] = 'data';
138 } elsif ( /^_[A-Za-z0-9_]/ ) {
141 print STDERR "Funny global thing?: $_"
142 unless $KNOWN_FUNNY_THING{$thing}
143 || /^__(PRIn|PRStart).*:/ # pointer reversal GC routines
144 || /^_CC_.*:/ # PROF: _CC_ccident
145 || /^__reg.*:/; # PROF: __reg<module>
147 $chkcat[$i] = 'misc';
150 } else { # simple line (duplicated at the top)
155 $numchks = $#chk + 1;
157 # the division into chunks is imperfect;
158 # we throw some things over the fence into the next
161 # also, there are things we would like to know
162 # about the whole module before we start spitting
165 # NB: we start meddling at chunk 1, not chunk 0
167 for ($i = 1; $i < $numchks; $i++) {
168 $c = $chk[$i]; # convenience copy
170 # print STDERR "\nCHK $i (BEFORE) (",$chkcat[$i],"):\n", $c;
172 # toss all prologue stuff;
173 # be slightly paranoid to make sure there's
174 # nothing surprising in there
175 if ( $c =~ /--- BEGIN ---/ ) {
176 if (($p, $r) = split(/--- BEGIN ---/, $c)) {
177 $p =~ s/^\tlink a6,#-?\d.*\n//;
178 $p =~ s/^\tmovel d2,sp\@-\n//;
179 $p =~ s/^\tmovel d5,sp\@-\n//; # SMmark.* only?
180 $p =~ s/^\tmoveml \#0x[0-9a-f]+,sp\@-\n//; # SMmark.* only?
181 die "Prologue junk?: $p\n" if $p =~ /^\t[^\.]/;
183 # glue together what's left
188 # toss all epilogue stuff; again, paranoidly
189 if ( $c =~ /--- END ---/ ) {
190 if (($r, $e) = split(/--- END ---/, $c)) {
191 $e =~ s/^\tunlk a6\n//;
193 die "Epilogue junk?: $e\n" if $e =~ /^\t[^\.]/;
195 # glue together what's left
200 # toss all calls to __DISCARD__
201 $c =~ s/^\tjbsr ___DISCARD__\n//g;
203 # toss stack adjustment after DoSparks
204 $c =~ s/^(\tjbsr _DoSparks\n)\taddqw #8,sp/$1/g;
206 # pin a funny end-thing on (for easier matching):
207 $c .= 'FUNNY#END#THING';
209 # pick some end-things and move them to the next chunk
211 while ( $c =~ /^\s*(\.align\s+\d+\n|\.proc\s+\d+\n|\.const\n|\.cstring\n|\.globl\s+\S+\n|\.text\n|\.data\n|\.even\n|\.stab[^n].*\n)FUNNY#END#THING/ ) {
214 if ( $to_move =~ /\.(globl|proc|stab)/ && $i < ($numchks - 1) ) {
215 $chk[$i + 1] = $to_move . $chk[$i + 1];
216 # otherwise they're tossed
219 $c =~ s/^.*\nFUNNY#END#THING/FUNNY#END#THING/;
222 $c =~ s/FUNNY#END#THING//;
223 $chk[$i] = $c; # update w/ convenience copy
226 # print out all the literal strings first
227 for ($i = 0; $i < $numchks; $i++) {
228 if ( $chkcat[$i] eq 'string' ) {
229 print OUTASM "\.text\n\t\.even\n";
230 print OUTASM $chk[$i];
232 $chkcat[$i] = 'DONE ALREADY';
236 for ($i = 0; $i < $numchks; $i++) {
237 # print STDERR "$i: cat $chkcat[$i], symb $chksymb[$i]\n";
239 next if $chkcat[$i] eq 'DONE ALREADY';
241 if ( $chkcat[$i] eq 'misc' ) {
242 print OUTASM "\.text\n\t\.even\n";
243 print OUTASM $chk[$i];
245 } elsif ( $chkcat[$i] eq 'data' ) {
246 print OUTASM "\.data\n\t\.even\n";
247 print OUTASM $chk[$i];
249 } elsif ( $chkcat[$i] eq 'consist' ) {
250 if ( $chk[$i] =~ /\.ascii.*\)(hsc|cc) (.*)\\11"\n\t\.ascii\s+"(.*)\\0"/ ) {
251 local($consist) = "$1.$2.$3";
253 $consist =~ s/\//./g;
255 $consist =~ s/[^A-Za-z0-9_.]/ZZ/g; # ToDo: properly?
256 print OUTASM "\.text\n$consist:\n";
258 print STDERR "Couldn't grok consistency: ", $chk[$i];
261 } elsif ( $chkcat[$i] eq 'splitmarker' ) {
262 # we can just re-constitute this one...
263 print OUTASM "___stg_split_marker",$chksymb[$i],":\n";
265 } elsif ( $chkcat[$i] eq 'closure'
266 || $chkcat[$i] eq 'infotbl'
267 || $chkcat[$i] eq 'slow'
268 || $chkcat[$i] eq 'fast' ) { # do them in that order
269 $symb = $chksymb[$i];
272 if ( defined($closurechk{$symb}) ) {
273 print OUTASM "\.data\n\t\.even\n";
274 print OUTASM $chk[$closurechk{$symb}];
275 $chkcat[$closurechk{$symb}] = 'DONE ALREADY';
279 if ( defined($infochk{$symb}) ) {
281 print OUTASM "\.text\n\t\.even\n";
282 print OUTASM &rev_tbl($symb, $chk[$infochk{$symb}], 1);
283 # entry code will be put here!
285 $chkcat[$infochk{$symb}] = 'DONE ALREADY';
289 if ( defined($slowchk{$symb}) ) {
291 # teach it to drop through to the fast entry point:
292 $c = $chk[$slowchk{$symb}];
293 $c =~ s/^\tjmp _${symb}_fast\d+.*\n\tnop\n//;
294 $c =~ s/^\tjmp _${symb}_fast\d+.*\n//;
296 print STDERR "still has jump to fast entry point:\n$c"
297 if $c =~ /_${symb}_fast/;
299 print OUTASM "\.text\n\t\.even\n";
301 $chkcat[$slowchk{$symb}] = 'DONE ALREADY';
305 if ( defined($fastchk{$symb}) ) {
306 print OUTASM "\.text\n\t\.even\n";
307 print OUTASM $chk[$fastchk{$symb}];
308 $chkcat[$fastchk{$symb}] = 'DONE ALREADY';
311 } elsif ( $chkcat[$i] eq 'vector'
312 || $chkcat[$i] eq 'direct' ) { # do them in that order
313 $symb = $chksymb[$i];
316 if ( defined($vectorchk{$symb}) ) {
317 print OUTASM "\.text\n\t\.even\n";
318 print OUTASM &rev_tbl($symb, $chk[$vectorchk{$symb}], 0);
319 # direct return code will be put here!
320 $chkcat[$vectorchk{$symb}] = 'DONE ALREADY';
324 if ( defined($directchk{$symb}) ) {
325 print OUTASM "\.text\n\t\.even\n";
326 print OUTASM $chk[$directchk{$symb}];
327 $chkcat[$directchk{$symb}] = 'DONE ALREADY';
331 &tidy_up_and_die(1,"$Pgm: unknown chkcat (ghc-asm m68k)\n$chkcat[$i]\n$chk[$i]\n");
336 close(OUTASM) || &tidy_up_and_die(1,"Failed writing to $out_asmf\n");
337 close(INASM) || &tidy_up_and_die(1,"Failed reading from $in_asmf\n");
342 sub init_FUNNY_THINGS {
343 %KNOWN_FUNNY_THING = (
344 '_CheckHeapCode:', 1,
345 '_CommonUnderflow:', 1,
347 '_EnterNodeCode:', 1,
348 '_ErrorIO_call_count:', 1,
349 '_ErrorIO_innards:', 1,
359 '_PrimUnderflow:', 1,
360 '_StackUnderflowEnterNode:', 1,
362 '_UnderflowVect0:', 1,
363 '_UnderflowVect1:', 1,
364 '_UnderflowVect2:', 1,
365 '_UnderflowVect3:', 1,
366 '_UnderflowVect4:', 1,
367 '_UnderflowVect5:', 1,
368 '_UnderflowVect6:', 1,
369 '_UnderflowVect7:', 1,
372 '_WorldStateToken:', 1,
373 '__Enter_Internal:', 1,
374 '__PRMarking_MarkNextAStack:', 1,
375 '__PRMarking_MarkNextBStack:', 1,
376 '__PRMarking_MarkNextCAF:', 1,
377 '__PRMarking_MarkNextGA:', 1,
378 '__PRMarking_MarkNextRoot:', 1,
379 '__PRMarking_MarkNextSpark:', 1,
380 '__Scavenge_Forward_Ref:', 1,
381 '___std_entry_error__:', 1,
382 '__startMarkWorld:', 1,
384 '_startCcRegisteringWorld:', 1,
385 '_startEnterFloat:', 1,
386 '_startEnterInt:', 1,
387 '_startPerformIO:', 1,
388 '_startStgWorld:', 1,
394 The following table reversal is used for both info tables and return
395 vectors. In both cases, we remove the first entry from the table,
396 reverse the table, put the label at the end, and paste some code
397 (that which is normally referred to by the first entry in the table)
398 right after the table itself. (The code pasting is done elsewhere.)
402 local($symb, $tbl, $discard1) = @_;
408 local(@lines) = split(/\n/, $tbl);
411 for ($i = 0; $i <= $#lines && $lines[$i] !~ /^\t\.long\s+/; $i++) {
412 $label .= $lines[$i] . "\n",
413 next if $lines[$i] =~ /^[A-Za-z0-9_]+_info:$/
414 || $lines[$i] =~ /^\.globl/
415 || $lines[$i] =~ /^_vtbl_\S+:$/;
417 $before .= $lines[$i] . "\n"; # otherwise...
420 for ( ; $i <= $#lines && $lines[$i] =~ /^\t\.long\s+/; $i++) {
421 push(@words, $lines[$i]);
423 # now throw away the first word (entry code):
424 shift(@words) if $discard1;
426 for (; $i <= $#lines; $i++) {
427 $after .= $lines[$i] . "\n";
430 $tbl = $before . join("\n", (reverse @words)) . "\n" . $label . $after;
432 # print STDERR "before=$before\n";
433 # print STDERR "label=$label\n";
434 # print STDERR "words=",(reverse @words),"\n";
435 # print STDERR "after=$after\n";
441 %************************************************************************
443 \subsection[Driver-asm-info]{Collect interesting (static) info from an assembler file}
445 %************************************************************************
447 How many times is each asm instruction used?
450 %AsmInsn = (); # init
452 sub dump_asm_insn_counts {
455 open(INASM, "< $asmf") || &tidy_up_and_die(1,"$Pgm: failed to open `$asmf' (to read)\n");
457 if ( /^\t([a-z][a-z0-9]+)\b/ ) {
461 close(INASM) || &tidy_up_and_die(1,"Failed reading from $asmf\n");
463 # OK, now print what we collected (to stderr)
464 foreach $i (sort (keys %AsmInsn)) {
465 print STDERR "INSN:: $i\t",$AsmInsn{$i},"\n";
469 sub dump_asm_globals_info {
472 # make "require"r happy...