Skip to content
VLSI Mentor

Perl Part II · Module 16 · Perl in Semiconductor Engineering

Misconception: "Perl Is Completely Dead"

This is the most repeated claim about Perl and the least useful, because it is not a statement anybody can act on. Dead where? Dead for whom? If it means nobody starts new projects in Perl, that is largely true and this track agrees with it. If it means the Perl in your flow has stopped running, that is a factual claim about your flow and you can check it tonight. This chapter does not argue with the slogan. It ships a tool that counts how many Perl processes your regression actually starts, separates the scripts that run from the ones nothing calls any more, and tells you which single script would take the whole night down — because that number, for your team, settles the question in a way no industry opinion can.

Foundation12 min readPerlPart IIEDA Flows

Part II · Module 16 · Chapter 16.6 · Misconception: "Perl Is Completely Dead"

1. What the Slogan Is Doing

"Perl is dead" compresses three different claims into one phrase, and they have three different truth values:

"Nobody starts new projects in Perl." Largely true, and Chapter 16.4 agreed with it on measurement. This track does not recommend writing a new regression framework in Perl.

"Perl is no longer being developed." False and easy to check — the version on this machine is 5.34.1, and newer releases exist.

"The Perl in your flow has stopped mattering." This is the one that gets acted on, and it is the one nobody checks. It is also the only one that is a question about your flow rather than about the world.

The useful move is to stop arguing and count. A number from your own tree is a fact; a number about the industry is somebody's estimate, and Chapter 16.1 explained why this track does not offer one.

2. Count What Your Flow Runs

Azvya Education Pvt. Ltd.VLSI Mentor
census.pl
use strict;
use warnings;
use File::Find;
use File::Temp qw(tempdir);
use File::Spec;
use File::Path qw(make_path);

# ---- a SAMPLE flow, built here (not taken from a real project).
#      Point the census at your own tree instead: census.pl FLOWDIR TESTS
my $root = File::Spec->catdir(tempdir(CLEANUP => 1), "flow");
make_path(File::Spec->catdir($root, $_)) for qw(bin lists);
my %file = (
  "bin/run_regress" => <<'E',
#!/usr/bin/perl
system("$ENV{FLOW}/bin/mk_filelist.pl > rtl/files.f");
open my $l, "<", "lists/nightly.list" or die;
while (my $t = <$l>) { chomp $t; next unless $t =~ /\S/;
  system("vcs -f rtl/files.f $t");
  system("$ENV{FLOW}/bin/parse_sim_log", "$t.log");
}
system("$ENV{FLOW}/bin/summarise");
E
  "bin/mk_filelist.pl" => "#!/usr/bin/perl\nprint \"./alu.v\\n\";\n",
  "bin/parse_sim_log"  => "#!/usr/bin/perl\nexit 0;\n",
  "bin/summarise"      => "#!/usr/bin/perl\nexit 0;\n",
  "bin/old_unused.pl"  => "#!/usr/bin/perl\n# nothing calls this any more\n",
  "lists/nightly.list" => "test_a\ntest_b\ntest_c\ntest_d\n",
);
for my $rel (sort keys %file) {
    my $p = File::Spec->catfile($root, split m{/}, $rel);
    open my $w, ">", $p or die "cannot write: $!\n";
    print $w $file{$rel};
    close $w;
}
open my $lf, "<", File::Spec->catfile($root, "lists", "nightly.list") or die $!;
my $tests = grep { /\S/ } <$lf>;
close $lf;

# ---- 1. every Perl file, by extension OR shebang (the 16.3 finding)
my @perl;
find(sub {
    return unless -f $_;
    (my $rel = $File::Find::name) =~ s{^\Q$root\E/?}{};
    if (/\.(?:pl|pm|t)$/) { push @perl, $rel; return }
    open my $fh, "<", $_ or return;
    my $first = <$fh> // "";
    close $fh;
    push @perl, $rel
        if $first =~ m{^\#!\s*\S*/perl\b} || $first =~ m{^\#!\s*\S*/env\s+perl\b};
}, $root);

# ---- 2. who calls whom, and is the call INSIDE a loop body?
my %invoked;
for my $f (@perl) {
    open my $fh, "<", File::Spec->catfile($root, split m{/}, $f) or next;
    # Track brace depth and remember the depth each loop body opened at, so a
    # call AFTER the closing brace is "once". A sticky in-loop flag gets this
    # wrong and silently multiplies the total.
    my ($depth, @loop) = (0);
    while (my $l = <$fh>) {
        my $opens_loop = $l =~ /\b(?:while|for|foreach)\b/ ? 1 : 0;
        for my $ch (split //, $l) {
            if ($ch eq "{") { $depth++; push @loop, $depth if $opens_loop; $opens_loop = 0 }
            if ($ch eq "}") { pop @loop while @loop && $loop[-1] > $depth - 1; $depth-- }
        }
        while ($l =~ m{(?:system|exec)\s*\(?\s*["']?[^"'\s]*?([\w.]+)(?:["'\s,)])}g) {
            my $callee = $1;
            next unless grep { m{(?:^|/)\Q$callee\E$} } @perl;
            $invoked{$callee} = @loop ? "per-test" : "once";
        }
    }
    close $fh;
}

# ---- 3. processes per night
my $total = 0;
printf "%-22s %-12s %s\n", "PERL FILE", "CALLED", "RUNS PER NIGHT";
for my $f (sort @perl) {
    (my $base = $f) =~ s{.*/}{};
    my $how  = $invoked{$base};
    my $runs = !defined $how ? 0 : $how eq "per-test" ? $tests : 1;
    if ($f =~ /run_regress$/) { $how = "entry point"; $runs = 1 }
    $total += $runs;
    printf "%-22s %-12s %s\n", $f, ($how // "NOT CALLED"), $runs;
}
printf "\n  %d Perl file(s); about %d Perl process(es) per night at %d tests\n",
    scalar @perl, $total, $tests;
printf "  at 2000 tests the same flow would start about %d\n",
    $total - $tests + 2000;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
PERL FILE              CALLED       RUNS PER NIGHT
bin/mk_filelist.pl     once         1
bin/old_unused.pl      NOT CALLED   0
bin/parse_sim_log      per-test     4
bin/run_regress        entry point  1
bin/summarise          once         1

  5 Perl file(s); about 7 Perl process(es) per night at 4 tests
  at 2000 tests the same flow would start about 2003

Five Perl files, and the census separates them into three very different kinds.

3. Reading the Census

One file nothing calls. old_unused.pl runs zero times. This is the part of the footprint the slogan is right about — every mature bin directory has some, and deleting it costs nothing but a git rm. Find these first; they make the real number smaller and the remaining decisions clearer.

Three files that run once a night. The entry point, the filelist generator, the summariser. Replacing one of these is a contained job: it runs once, its inputs and outputs are files you can diff, and a bad night is one bad night.

One file that runs once per test. parse_sim_log runs 4 times on this sample list — and that is the number that changes everything, because it scales with the regression. At 2000 tests the same flow starts about 2003 Perl processes a night, essentially all of them this one script.

That single row is worth three conclusions:

It is the one that is not dead. Whatever is true of the industry, a script executing two thousand times a night is load-bearing.

It is where interpreter startup becomes real. Chapter 16.4 measured a 3x-to-5x startup advantage and said it only matters per-invocation at scale. This is that case, and it is the only place in the flow where that measurement should affect a decision.

It is the most dangerous thing to rewrite and the most valuable thing to get right. Every test's verdict passes through it. Chapter 16.2's analysis belongs on this file before anything else.

4. What "Dead" Actually Looks Like

The chapter would be one-sided without this. There are places where the slogan is simply correct, and recognising them is as useful as finding the load-bearing script.

Scripts nothing calls. Measured above: one of five. Delete them.

Scripts whose tool no longer exists. A parser for a report format from a tool the team stopped licensing years ago. It still runs, it still passes its own checks, and it has had no input since 2019.

Scripts replaced but not removed. A Python version was written, the Makefile was switched over, and the Perl original was left in place "just in case". The census shows this one clearly: zero runs, and a near-identical filename.

Scripts that only one person ever ran. Not part of the flow at all — somebody's personal helper that ended up committed. These are not infrastructure and should not be counted as such.

So the honest summary of the footprint, after a census, is usually: a lot of files, a few processes, and one or two scripts that matter enormously. That shape is much more actionable than either "Perl is dead" or "we have a hundred Perl scripts".

And it suggests the order of work, which is the opposite of what people usually do:

  1. Delete what nothing calls. Free, and it shrinks the problem.
  2. Document the per-test script. Chapter 16.2's analyser output, in a comment, changing no behaviour.
  3. Put a test harness around it before touching it.
  4. Leave the once-a-night scripts alone until there is a reason beyond language preference.

5. What To Remember

  • "Perl is dead" is three claims in one. Only one of them — "the Perl in your flow stopped mattering" — is about your flow, and only that one is checkable.
  • A number from your own tree is a fact; an industry number is an estimate. This track does not offer one.
  • Count processes per night, not files. The file count is nearly meaningless.
  • Measured on the sample flow: 5 files, 1 never called, 3 once a night, 1 per test, about 7 processes at 4 tests.
  • The per-test script dominates at scale — about 2003 processes at 2000 tests, essentially all of them one script.
  • That one script is the load-bearing one, is where startup cost genuinely matters, and is the one to document first.
  • My first census was wrong: a sticky in-loop flag counted a call after the loop as per-test and reported 10 instead of 7. Track brace depth.
  • The census is static and can undercount — a call through a variable is invisible, Makefile calls need Chapter 16.3's scanner, and loop detection is crude.
  • Confirm with a log, not the source. Record every invocation for one night and compare.
  • Four kinds of genuinely dead script: nothing calls it, its tool is gone, it was replaced but not removed, or only one person ever ran it.
  • The usual true shape is: many files, few processes, one or two that matter enormously.
  • Work in this order: delete the dead, document the per-test script, test it, and leave the once-a-night scripts alone.

Chapter 16.7 takes the next misconception, and it is the one that explains the census's shape. Migrations do not fail; they stall — and they stall in a pattern that puts exactly the wrong scripts at the back of the queue.

Where this fits

Part of the Perl curriculum.