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
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~
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
© 2016 - 2025 Red Hat, Inc.