diff mbox series

[RISU,RFC,v2,01/14] risugen_common: add insnv, randint_constr, rand_fill

Message ID 20190701043536.26019-2-jan.bobek@gmail.com (mailing list archive)
State New, archived
Headers show
Series Support for generating x86 MMX/SSE/AVX test images | expand

Commit Message

Jan Bobek July 1, 2019, 4:35 a.m. UTC
Add three common utility functions:

- insnv allows emitting variable-length instructions in little-endian
  or big-endian byte order; it subsumes functionality of former
  insn16() and insn32() functions.

- randint_constr allows generating random integers according to
  several constraints passed as arguments.

- rand_fill uses randint_constr to fill a given hash with
  (optionally constrained) random values.

Signed-off-by: Jan Bobek <jan.bobek@gmail.com>
---
 risugen_common.pm | 107 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 101 insertions(+), 6 deletions(-)

Comments

Richard Henderson July 3, 2019, 3:22 p.m. UTC | #1
On 7/1/19 6:35 AM, Jan Bobek wrote:
> +    while ($bitcur < $bitend) {
> +        my $format;
> +        my $bitlen;
> +
> +        if ($bitcur + 64 <= $bitend) {
> +            $format = "Q";
> +            $bitlen = 64;
> +        } elsif ($bitcur + 32 <= $bitend) {
> +            $format = "L";
> +            $bitlen = 32;
> +        } elsif ($bitcur + 16 <= $bitend) {
> +            $format = "S";
> +            $bitlen = 16;
> +        } else {
> +            $format = "C";
> +            $bitlen = 8;
> +        }
> +
> +        $format .= ($args{bigendian} ? ">" : "<") if $bitlen > 8;

It now occurs to me to wonder if it's worth simplifying this function to always
emit bytes, and thus take care of all of the endianness ourselves, since we're
doing it anyway for larger/odd-sized hunks.

Otherwise,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~
Jan Bobek July 10, 2019, 5:48 p.m. UTC | #2
Hi Richard,

sorry for replying so late. I read your comments last week; as I
mentioned in our weekly update email, I ended up adding/removing quite
a lot since v2, so I wasn't 100% sure how much of it will remain
relevant.

Anyways,

On 7/3/19 11:22 AM, Richard Henderson wrote:
> On 7/1/19 6:35 AM, Jan Bobek wrote:
>> +    while ($bitcur < $bitend) {
>> +        my $format;
>> +        my $bitlen;
>> +
>> +        if ($bitcur + 64 <= $bitend) {
>> +            $format = "Q";
>> +            $bitlen = 64;
>> +        } elsif ($bitcur + 32 <= $bitend) {
>> +            $format = "L";
>> +            $bitlen = 32;
>> +        } elsif ($bitcur + 16 <= $bitend) {
>> +            $format = "S";
>> +            $bitlen = 16;
>> +        } else {
>> +            $format = "C";
>> +            $bitlen = 8;
>> +        }
>> +
>> +        $format .= ($args{bigendian} ? ">" : "<") if $bitlen > 8;
> 
> It now occurs to me to wonder if it's worth simplifying this function to always
> emit bytes, and thus take care of all of the endianness ourselves, since we're
> doing it anyway for larger/odd-sized hunks.

Good point. *facepalm*

I will include this change in v3.

-Jan
diff mbox series

Patch

diff --git a/risugen_common.pm b/risugen_common.pm
index 71ee996..c5d861e 100644
--- a/risugen_common.pm
+++ b/risugen_common.pm
@@ -23,7 +23,8 @@  BEGIN {
     require Exporter;
 
     our @ISA = qw(Exporter);
-    our @EXPORT = qw(open_bin close_bin set_endian insn32 insn16 $bytecount
+    our @EXPORT = qw(open_bin close_bin set_endian insn32 insn16
+                   $bytecount insnv randint_constr rand_fill
                    progress_start progress_update progress_end
                    eval_with_fields is_pow_of_2 sextract ctz
                    dump_insn_details);
@@ -37,7 +38,7 @@  my $bigendian = 0;
 # (default is little endian, 0).
 sub set_endian
 {
-    $bigendian = @_;
+    ($bigendian) = @_;
 }
 
 sub open_bin
@@ -52,18 +53,112 @@  sub close_bin
     close(BIN) or die "can't close output file: $!";
 }
 
+sub insnv(%)
+{
+    my (%args) = @_;
+
+    # Default to big-endian order, so that the instruction bytes are
+    # emitted in the same order as they are written in the
+    # configuration file.
+    $args{bigendian} = 1 unless defined $args{bigendian};
+
+    my $bitcur = 0;
+    my $bitend = 8 * $args{len};
+    while ($bitcur < $bitend) {
+        my $format;
+        my $bitlen;
+
+        if ($bitcur + 64 <= $bitend) {
+            $format = "Q";
+            $bitlen = 64;
+        } elsif ($bitcur + 32 <= $bitend) {
+            $format = "L";
+            $bitlen = 32;
+        } elsif ($bitcur + 16 <= $bitend) {
+            $format = "S";
+            $bitlen = 16;
+        } else {
+            $format = "C";
+            $bitlen = 8;
+        }
+
+        $format .= ($args{bigendian} ? ">" : "<") if $bitlen > 8;
+
+        my $bitmask = (1 << $bitlen) - 1;
+        my $value = $args{value} >> ($args{bigendian}
+                                     ? $bitend - $bitcur - $bitlen
+                                     : $bitcur);
+
+        print BIN pack($format, $value & $bitmask);
+        $bytecount += $bitlen / 8;
+
+        $bitcur += $bitlen;
+    }
+}
+
 sub insn32($)
 {
     my ($insn) = @_;
-    print BIN pack($bigendian ? "N" : "V", $insn);
-    $bytecount += 4;
+    insnv(value => $insn, len => 4, bigendian => $bigendian);
 }
 
 sub insn16($)
 {
     my ($insn) = @_;
-    print BIN pack($bigendian ? "n" : "v", $insn);
-    $bytecount += 2;
+    insnv(value => $insn, len => 2, bigendian => $bigendian);
+}
+
+sub randint_constr(%)
+{
+    my (%args) = @_;
+    my $bitlen = $args{bitlen};
+    my $halfrange = 1 << ($bitlen - 1);
+
+    while (1) {
+        my $value = int(rand(2 * $halfrange));
+        $value -= $halfrange if defined $args{signed} && $args{signed};
+        $value &= ~$args{fixedbitmask} if defined $args{fixedbitmask};
+        $value |= $args{fixedbits} if defined $args{fixedbits};
+
+        if (defined $args{constraint}) {
+            # The idea is: if the most significant bit of
+            # $args{constraint} is zero, $args{constraint} is the
+            # value we want to return; if the most significant bit is
+            # one, ~$args{constraint} (its bit inversion) is the value
+            # we want to *avoid*, so we try again.
+
+            if (!($args{constraint} >> 63)) {
+                $value = $args{constraint};
+            } elsif ($value == ~$args{constraint}) {
+                next;
+            }
+        }
+
+        return $value;
+    }
+}
+
+sub rand_fill($$)
+{
+    my ($target, $constraints) = @_;
+
+    for (keys %{$target}) {
+        my %args = (bitlen => $target->{$_}{bitlen});
+
+        $args{fixedbits} = $target->{$_}{fixedbits}
+            if defined $target->{$_}{fixedbits};
+        $args{fixedbitmask} = $target->{$_}{fixedbitmask}
+            if defined $target->{$_}{fixedbitmask};
+        $args{signed} = $target->{$_}{signed}
+            if defined $target->{$_}{signed};
+
+        $args{constraint} = $constraints->{$_}
+            if defined $constraints->{$_};
+
+        $target->{$_} = randint_constr(%args);
+    }
+
+    return $target;
 }
 
 # Progress bar implementation