+
+# Generate the content of B::Op_private
+
+sub print_B_Op_private {
+ my $fh = shift;
+
+ my $header = <<'EOF';
+@=head1 NAME
+@
+@B::Op_private - OP op_private flag definitions
+@
+@=head1 SYNOPSIS
+@
+@ use B::Op_private;
+@
+@ # flag details for bit 7 of OP_AELEM's op_private:
+@ my $name = $B::Op_private::bits{aelem}{7}; # OPpLVAL_INTRO
+@ my $value = $B::Op_private::defines{$name}; # 128
+@ my $label = $B::Op_private::labels{$name}; # LVINTRO
+@
+@ # the bit field at bits 5..6 of OP_AELEM's op_private:
+@ my $bf = $B::Op_private::bits{aelem}{6};
+@ my $mask = $bf->{bitmask}; # etc
+@
+@=head1 DESCRIPTION
+@
+@This module provides four global hashes:
+@
+@ %B::Op_private::bits
+@ %B::Op_private::defines
+@ %B::Op_private::labels
+@ %B::Op_private::ops_using
+@
+@which contain information about the per-op meanings of the bits in the
+@op_private field.
+@
+@=head2 C<%bits>
+@
+@This is indexed by op name and then bit number (0..7). For single bit flags,
+@it returns the name of the define (if any) for that bit:
+@
+@ $B::Op_private::bits{aelem}{7} eq 'OPpLVAL_INTRO';
+@
+@For bit fields, it returns a hash ref containing details about the field.
+@The same reference will be returned for all bit positions that make
+@up the bit field; so for example these both return the same hash ref:
+@
+@ $bitfield = $B::Op_private::bits{aelem}{5};
+@ $bitfield = $B::Op_private::bits{aelem}{6};
+@
+@The general format of this hash ref is
+@
+@ {
+@ # The bit range and mask; these are always present.
+@ bitmin => 5,
+@ bitmax => 6,
+@ bitmask => 0x60,
+@
+@ # (The remaining keys are optional)
+@
+@ # The names of any defines that were requested:
+@ mask_def => 'OPpFOO_MASK',
+@ baseshift_def => 'OPpFOO_SHIFT',
+@ bitcount_def => 'OPpFOO_BITS',
+@
+@ # If present, Concise etc will display the value with a 'FOO='
+@ # prefix. If it equals '-', then Concise will treat the bit
+@ # field as raw bits and not try to interpret it.
+@ label => 'FOO',
+@
+@ # If present, specifies the names of some defines and the
+@ # display labels that are used to assign meaning to particu-
+@ # lar integer values within the bit field; e.g. 3 is dis-
+@ # played as 'C'.
+@ enum => [ qw(
+@ 1 OPpFOO_A A
+@ 2 OPpFOO_B B
+@ 3 OPpFOO_C C
+@ )],
+@
+@ };
+@
+@
+@=head2 C<%defines>
+@
+@This gives the value of every C<OPp> define, e.g.
+@
+@ $B::Op_private::defines{OPpLVAL_INTRO} == 128;
+@
+@=head2 C<%labels>
+@
+@This gives the short display label for each define, as used by C<B::Concise>
+@and C<perl -Dx>, e.g.
+@
+@ $B::Op_private::labels{OPpLVAL_INTRO} eq 'LVINTRO';
+@
+@If the label equals '-', then Concise will treat the bit as a raw bit and
+@not try to display it symbolically.
+@
+@=head2 C<%ops_using>
+@
+@For each define, this gives a reference to an array of op names that use
+@the flag.
+@
+@ @ops_using_lvintro = @{ $B::Op_private::ops_using{OPp_LVAL_INTRO} };
+@
+@=cut
+
+package B::Op_private;
+
+our %bits;
+
+EOF
+ # remove podcheck.t-defeating leading char
+ $header =~ s/^\@//gm;
+ print $fh $header;
+ my $v = (::perl_version())[3];
+ print $fh qq{\nour \$VERSION = "$v";\n\n};
+
+ my %ops_using;
+
+ # for each flag/bit combination, find the ops which use it
+ my %combos;
+ for my $op (sort keys %FLAGS) {
+ my $entry = $FLAGS{$op};
+ for my $bit (0..7) {
+ my $e = $entry->{$bit};
+ next unless defined $e;
+ next if ref $e; # bit field, not flag
+ push @{$combos{$e}{$bit}}, $op;
+ push @{$ops_using{$e}}, $op;
+ }
+ }
+
+ # dump flags used by multiple ops
+ for my $flag (sort keys %combos) {
+ for my $bit (sort keys %{$combos{$flag}}) {
+ my $ops = $combos{$flag}{$bit};
+ next unless @$ops > 1;
+ my @o = sort @$ops;
+ print $fh "\$bits{\$_}{$bit} = '$flag' for qw(@o);\n";
+ }
+ }
+
+ # dump bit field definitions
+
+ my %bitfield_ix;
+ {
+ my %bitfields;
+ # stringified-ref to ref mapping
+ $bitfields{$_} = $_ for values %BITFIELDS;
+ my $ix = -1;
+ my $s = "\nmy \@bf = (\n";
+ for my $bitfield_key (sort keys %BITFIELDS) {
+ my $bitfield = $BITFIELDS{$bitfield_key};
+ $ix++;
+ $bitfield_ix{$bitfield} = $ix;
+
+ $s .= " {\n";
+ for (qw(label mask_def baseshift_def bitcount_def)) {
+ next unless defined $bitfield->{$_};
+ $s .= sprintf " %-9s => '%s',\n",
+ $_, $bitfield->{$_};
+ }
+ for (qw(bitmin bitmax bitmask)) {
+ croak "panic" unless defined $bitfield->{$_};
+ $s .= sprintf " %-9s => %d,\n",
+ $_, $bitfield->{$_};
+ }
+ if (defined $bitfield->{enum}) {
+ $s .= " enum => [\n";
+ my @enum = @{$bitfield->{enum}};
+ while (@enum) {
+ my $i = shift @enum;
+ my $name = shift @enum;
+ my $label = shift @enum;
+ $s .= sprintf " %d, %-10s, %s,\n",
+ $i, "'$name'", "'$label'";
+ }
+ $s .= " ],\n";
+ }
+ $s .= " },\n";
+
+ }
+ $s .= ");\n";
+ print $fh "$s\n";
+ }
+
+ # dump bitfields and remaining labels
+
+ for my $op (sort keys %FLAGS) {
+ my @indices;
+ my @vals;
+ my $entry = $FLAGS{$op};
+ my $bit;
+
+ for ($bit = 7; $bit >= 0; $bit--) {
+ next unless defined $entry->{$bit};
+ my $e = $entry->{$bit};
+ if (ref $e) {
+ my $ix = $bitfield_ix{$e};
+ for (reverse $e->{bitmin}..$e->{bitmax}) {
+ push @indices, $_;
+ push @vals, "\$bf[$ix]";
+ }
+ $bit = $e->{bitmin};
+ }
+ else {
+ next if @{$combos{$e}{$bit}} > 1; # already output
+ push @indices, $bit;
+ push @vals, "'$e'";
+ }
+ }
+ if (@indices) {
+ my $s = '';
+ $s = '@{' if @indices > 1;
+ $s .= "\$bits{$op}";
+ $s .= '}' if @indices > 1;
+ $s .= '{' . join(',', @indices) . '} = ';
+ $s .= '(' if @indices > 1;
+ $s .= join ', ', @vals;
+ $s .= ')' if @indices > 1;
+ print $fh "$s;\n";
+ }
+ }
+
+ # populate %defines and %labels
+
+ print $fh "\n\nour %defines = (\n";
+ printf $fh " %-23s => %3d,\n", $_ , $DEFINES{$_} for sort keys %DEFINES;
+ print $fh ");\n\nour %labels = (\n";
+ printf $fh " %-23s => '%s',\n", $_ , $LABELS{$_} for sort keys %LABELS;
+ print $fh ");\n";
+
+ # %ops_using
+ print $fh "\n\nour %ops_using = (\n";
+ # Save memory by using the same array wherever possible.
+ my %flag_by_op_list;
+ my $pending = '';
+ for my $flag (sort keys %ops_using) {
+ my $op_list = $ops_using{$flag} = "@{$ops_using{$flag}}";
+ if (!exists $flag_by_op_list{$op_list}) {
+ $flag_by_op_list{$op_list} = $flag;
+ printf $fh " %-23s => %s,\n", $flag , "[qw($op_list)]"
+ }
+ else {
+ $pending .= "\$ops_using{$flag} = "
+ . "\$ops_using{$flag_by_op_list{$op_list}};\n";
+ }
+ }
+ print $fh ");\n\n$pending";
+
+}
+
+
+
+# output the contents of the assorted PL_op_private_*[] tables
+
+sub print_PL_op_private_tables {
+ my $fh = shift;
+
+ my $PL_op_private_labels = '';
+ my $PL_op_private_valid = '';
+ my $PL_op_private_bitdef_ix = '';
+ my $PL_op_private_bitdefs = '';
+ my $PL_op_private_bitfields = '';
+
+ my %label_ix;
+ my %bitfield_ix;
+
+ # generate $PL_op_private_labels
+
+ {
+ my %labs;
+ $labs{$_} = 1 for values %LABELS; # de-duplicate labels
+ # add in bit field labels
+ for (values %BITFIELDS) {
+ next unless defined $_->{label};
+ $labs{$_->{label}} = 1;
+ }
+
+ my $labels = '';
+ for my $lab (sort keys %labs) {
+ $label_ix{$lab} = length $labels;
+ $labels .= "$lab\0";
+ $PL_op_private_labels .=
+ " "
+ . join(',', map("'$_'", split //, $lab))
+ . ",'\\0',\n";
+ }
+ }
+
+
+ # generate PL_op_private_bitfields
+
+ {
+ my %bitfields;
+ # stringified-ref to ref mapping
+ $bitfields{$_} = $_ for values %BITFIELDS;
+
+ my $ix = 0;
+ for my $bitfield_key (sort keys %BITFIELDS) {
+ my $bf = $BITFIELDS{$bitfield_key};
+ $bitfield_ix{$bf} = $ix;
+
+ my @b;
+ push @b, $bf->{bitmin},
+ defined $bf->{label} ? $label_ix{$bf->{label}} : -1;
+ my $enum = $bf->{enum};
+ if (defined $enum) {
+ my @enum = @$enum;
+ while (@enum) {
+ my $i = shift @enum;
+ my $name = shift @enum;
+ my $label = shift @enum;
+ push @b, $i, $label_ix{$label};
+ }
+ }
+ push @b, -1; # terminate enum list
+
+ $PL_op_private_bitfields .= " " . join(', ', @b) .",\n";
+ $ix += @b;
+ }
+ }
+
+
+ # generate PL_op_private_bitdefs, PL_op_private_bitdef_ix
+
+ {
+ my $bitdef_count = 0;
+
+ my %not_seen = %FLAGS;
+ my @seen_bitdefs;
+ my %seen_bitdefs;
+
+ my $opnum = -1;
+ for my $op (sort { $opnum{$a} <=> $opnum{$b} } keys %opnum) {
+ $opnum++;
+ die "panic: opnum misorder: opnum=$opnum opnum{op}=$opnum{$op}"
+ unless $opnum == $opnum{$op};
+ delete $not_seen{$op};
+
+ my @bitdefs;
+ my $entry = $FLAGS{$op};
+ my $bit;
+ my $index;
+
+ for ($bit = 7; $bit >= 0; $bit--) {
+ my $e = $entry->{$bit};
+ next unless defined $e;
+
+ my $ix;
+ if (ref $e) {
+ $ix = $bitfield_ix{$e};
+ die "panic: \$bit =\= $e->{bitmax}"
+ unless $bit == $e->{bitmax};
+
+ push @bitdefs, ( ($ix << 5) | ($bit << 2) | 2 );
+ $bit = $e->{bitmin};
+ }
+ else {
+ $ix = $label_ix{$LABELS{$e}};
+ die "panic: no label ix for '$e'" unless defined $ix;
+ push @bitdefs, ( ($ix << 5) | ($bit << 2));
+ }
+ if ($ix > 2047) {
+ die "Too many labels or bitfields (ix=$ix): "
+ . "maybe the type of PL_op_private_bitdefs needs "
+ . "expanding from U16 to U32???";
+ }
+ }
+ if (@bitdefs) {
+ $bitdefs[-1] |= 1; # stop bit
+ my $key = join(', ', map(sprintf("0x%04x", $_), @bitdefs));
+ if (!$seen_bitdefs{$key}) {
+ $index = $bitdef_count;
+ $bitdef_count += @bitdefs;
+ push @seen_bitdefs,
+ $seen_bitdefs{$key} = [$index, $key];
+ }
+ else {
+ $index = $seen_bitdefs{$key}[0];
+ }
+ push @{$seen_bitdefs{$key}}, $op;
+ }
+ else {
+ $index = -1;
+ }
+ $PL_op_private_bitdef_ix .= sprintf " %4d, /* %s */\n", $index, $op;
+ }
+ if (%not_seen) {
+ die "panic: unprocessed ops: ". join(',', keys %not_seen);
+ }
+ for (@seen_bitdefs) {
+ local $" = ", ";
+ $PL_op_private_bitdefs .= " $$_[1], /* @$_[2..$#$_] */\n";
+ }
+ }
+
+
+ # generate PL_op_private_valid
+
+ for my $op (@ops) {
+ my $last;
+ my @flags;
+ for my $bit (0..7) {
+ next unless exists $FLAGS{$op};
+ my $entry = $FLAGS{$op}{$bit};
+ next unless defined $entry;
+ if (ref $entry) {
+ # skip later entries for the same bit field
+ next if defined $last and $last == $entry;
+ $last = $entry;
+ push @flags,
+ defined $entry->{mask_def}
+ ? $entry->{mask_def}
+ : $entry->{bitmask};
+ }
+ else {
+ push @flags, $entry;
+ }
+ }
+
+ # all bets are off
+ @flags = '0xff' if $op eq 'null' or $op eq 'custom';
+
+ $PL_op_private_valid .= sprintf " /* %-10s */ (%s),\n", uc($op),
+ @flags ? join('|', @flags): '0';
+ }
+
+ print $fh <<EOF;
+START_EXTERN_C
+
+#ifndef PERL_GLOBAL_STRUCT_INIT
+
+# ifndef DOINIT
+
+/* data about the flags in op_private */
+
+EXTCONST I16 PL_op_private_bitdef_ix[];
+EXTCONST U16 PL_op_private_bitdefs[];
+EXTCONST char PL_op_private_labels[];
+EXTCONST I16 PL_op_private_bitfields[];
+EXTCONST U8 PL_op_private_valid[];
+
+# else
+
+
+/* PL_op_private_labels[]: the short descriptions of private flags.
+ * All labels are concatenated into a single char array
+ * (separated by \\0's) for compactness.