[Qemu-devel] [RISU RFC PATCH v1 1/7] risugen_common: add insnv, randint_constr, rand_fill

Jan Bobek posted 7 patches 6 years, 4 months ago
[Qemu-devel] [RISU RFC PATCH v1 1/7] risugen_common: add insnv, randint_constr, rand_fill
Posted by Jan Bobek 6 years, 4 months ago
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 | 101 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 95 insertions(+), 6 deletions(-)

diff --git a/risugen_common.pm b/risugen_common.pm
index 71ee996..98b9170 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,106 @@ 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};
+
+    while (0 < $args{len}) {
+        my $format;
+        my $len;
+
+        if ($args{len} >= 8) {
+            $format = "Q";
+            $len = 8;
+        } elsif ($args{len} >= 4) {
+            $format = "L";
+            $len = 4;
+        } elsif ($args{len} >= 2) {
+            $format = "S";
+            $len = 2;
+        } else {
+            $format = "C";
+            $len = 1;
+        }
+
+        $format .= ($args{bigendian} ? ">" : "<") if $len > 1;
+
+        my $bitlen = 8 * $len;
+        my $bitmask = (1 << $bitlen) - 1;
+        my $value = ($args{bigendian}
+                     ? ($args{value} >> (8 * $args{len} - $bitlen))
+                     : $args{value});
+
+        print BIN pack($format, $value & $bitmask);
+        $bytecount += $len;
+
+        $args{len} -= $len;
+        $args{value} >>= $bitlen unless $args{bigendian};
+    }
+}
+
 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}) {
+            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
-- 
2.20.1


Re: [Qemu-devel] [RISU RFC PATCH v1 1/7] risugen_common: add insnv, randint_constr, rand_fill
Posted by Richard Henderson 6 years, 4 months ago
On 6/19/19 7:04 AM, Jan Bobek wrote:
> +        my $value = ($args{bigendian}
> +                     ? ($args{value} >> (8 * $args{len} - $bitlen))
> +                     : $args{value});
...
> +        $args{value} >>= $bitlen unless $args{bigendian};

I think this could be clearer without modifying $args.
I mis-read these the first time around.

Perhaps

    my $bitpos = 0;
    my $bitend = 8 * $args{len};
    while ($bitpos < $bitend) {
        ...
        my $value = $args{value} >> ($args{bigendian}
                                     ? $bitend - $bitpos - $bitlen
                                     : $bitpos);
        ...
        $bitpos += $bitlen;
    }

> +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}) {
> +            if (!($args{constraint} >> 63)) {
> +                $value = $args{constraint};
> +            } elsif ($value == ~$args{constraint}) {
> +                next;
> +            }
> +        }

I don't understand what you're doing here with {constraint}.
Some additional commentary would help.


r~

Re: [Qemu-devel] [RISU RFC PATCH v1 1/7] risugen_common: add insnv, randint_constr, rand_fill
Posted by Jan Bobek 6 years, 4 months ago
On 6/27/19 4:53 AM, Richard Henderson wrote:
> On 6/19/19 7:04 AM, Jan Bobek wrote:
>> +        my $value = ($args{bigendian}
>> +                     ? ($args{value} >> (8 * $args{len} - $bitlen))
>> +                     : $args{value});
> ...
>> +        $args{value} >>= $bitlen unless $args{bigendian};
> 
> I think this could be clearer without modifying $args.
> I mis-read these the first time around.
> 
> Perhaps
> 
>     my $bitpos = 0;
>     my $bitend = 8 * $args{len};
>     while ($bitpos < $bitend) {
>         ...
>         my $value = $args{value} >> ($args{bigendian}
>                                      ? $bitend - $bitpos - $bitlen
>                                      : $bitpos);
>         ...
>         $bitpos += $bitlen;
>     }

Looks good, I'll change it.

>> +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}) {
>> +            if (!($args{constraint} >> 63)) {
>> +                $value = $args{constraint};
>> +            } elsif ($value == ~$args{constraint}) {
>> +                next;
>> +            }
>> +        }
> 
> I don't understand what you're doing here with {constraint}.
> Some additional commentary would help.

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. This is used to to
implement constraints on fields such as

MOVLPS          SSE     00001111 0001001 d !emit { modrm(mod => ~MOD_DIRECT); mem(size => 8); }
MOVLHPS         SSE     00001111 00010110  !emit { modrm(mod => MOD_DIRECT); }

The bitshift by 63 assumes 64-bit integers, but that assumption is
present in other places, too. I couldn't think of a better way to do
it: comparing it to zero doesn't work because the value is unsigned.
I will include a comment explaining this in v2, unless you have
other suggestions.

-Jan