[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RISU PATCH v3 03/18] risugen_x86_asm: add module
From: |
Jan Bobek |
Subject: |
[Qemu-devel] [RISU PATCH v3 03/18] risugen_x86_asm: add module |
Date: |
Thu, 11 Jul 2019 18:32:45 -0400 |
The module risugen_x86_asm.pm exports named register constants and
asm_insn_* family of functions, which greatly simplify emission of x86
instructions.
Signed-off-by: Jan Bobek <address@hidden>
---
risugen_x86_asm.pm | 918 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 918 insertions(+)
create mode 100644 risugen_x86_asm.pm
diff --git a/risugen_x86_asm.pm b/risugen_x86_asm.pm
new file mode 100644
index 0000000..642f18b
--- /dev/null
+++ b/risugen_x86_asm.pm
@@ -0,0 +1,918 @@
+#!/usr/bin/perl -w
+###############################################################################
+# Copyright (c) 2019 Jan Bobek
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Jan Bobek - initial implementation
+###############################################################################
+
+# risugen_x86_asm -- risugen_x86's helper module for x86 assembly
+package risugen_x86_asm;
+
+use strict;
+use warnings;
+
+use risugen_common;
+
+our @ISA = qw(Exporter);
+our @EXPORT = qw(
+ asm_insn asm_insn_ud1 asm_insn_xor asm_insn_sahf asm_insn_lea64
+ asm_insn_call asm_insn_jmp asm_insn_pop asm_insn_mov
+ asm_insn_mov64 asm_insn_movq asm_insn_add asm_insn_add64
+ asm_insn_and asm_insn_and64 asm_insn_neg asm_insn_neg64
+ asm_insn_xchg asm_insn_xchg64 asm_insn_movaps asm_insn_vmovaps
+ asm_insn_movdqu asm_insn_vmovdqu
+ REG_RAX REG_RCX REG_RDX REG_RBX REG_RSP REG_RBP REG_RSI REG_RDI
+ REG_R8 REG_R9 REG_R10 REG_R11 REG_R12 REG_R13 REG_R14 REG_R15
+ );
+
+use constant {
+ VEX_L_128 => 0,
+ VEX_L_256 => 1,
+
+ VEX_P_NONE => 0b00,
+ VEX_P_DATA16 => 0b01,
+ VEX_P_REP => 0b10,
+ VEX_P_REPNE => 0b11,
+
+ VEX_M_0F => 0b00001,
+ VEX_M_0F38 => 0b00010,
+ VEX_M_0F3A => 0b00011,
+
+ VEX_V_UNUSED => 0b1111,
+
+ REG_RAX => 0,
+ REG_RCX => 1,
+ REG_RDX => 2,
+ REG_RBX => 3,
+ REG_RSP => 4,
+ REG_RBP => 5,
+ REG_RSI => 6,
+ REG_RDI => 7,
+ REG_R8 => 8,
+ REG_R9 => 9,
+ REG_R10 => 10,
+ REG_R11 => 11,
+ REG_R12 => 12,
+ REG_R13 => 13,
+ REG_R14 => 14,
+ REG_R15 => 15,
+
+ MOD_INDIRECT => 0b00,
+ MOD_INDIRECT_DISP8 => 0b01,
+ MOD_INDIRECT_DISP32 => 0b10,
+ MOD_DIRECT => 0b11,
+};
+
+sub write_insn_repne(%)
+{
+ insnv(value => 0xF2, width => 8);
+}
+
+sub write_insn_rep(%)
+{
+ insnv(value => 0xF3, width => 8);
+}
+
+sub write_insn_data16(%)
+{
+ insnv(value => 0x66, width => 8);
+}
+
+sub write_insn_rex(%)
+{
+ my (%args) = @_;
+
+ my $rex = 0x40;
+ $rex |= (defined $args{w} && $args{w}) << 3;
+ $rex |= (defined $args{r} && $args{r}) << 2;
+ $rex |= (defined $args{x} && $args{x}) << 1;
+ $rex |= (defined $args{b} && $args{b}) << 0;
+ insnv(value => $rex, width => 8);
+}
+
+sub write_insn_vex(%)
+{
+ my (%args) = @_;
+
+ $args{r} = 1 unless defined $args{r};
+ $args{x} = 1 unless defined $args{x};
+ $args{b} = 1 unless defined $args{b};
+ $args{w} = 0 unless defined $args{w};
+ $args{m} = VEX_M_0F unless defined $args{m};
+ $args{v} = VEX_V_UNUSED unless defined $args{v};
+ $args{p} = VEX_P_NONE unless defined $args{p};
+
+ # The Intel manual implies that 2-byte VEX prefix is equivalent to
+ # VEX.X = 1, VEX.B = 1, VEX.W = 0 and VEX.M = VEX_M_0F.
+ if ($args{x} && $args{b} && !$args{w} && $args{m} == VEX_M_0F) {
+ # We can use the 2-byte VEX prefix
+ my $vex = 0xC5 << 8;
+ $vex |= ($args{r} & 0b1) << 7;
+ $vex |= ($args{v} & 0b1111) << 3;
+ $vex |= ($args{l} & 0b1) << 2;
+ $vex |= ($args{p} & 0b11) << 0;
+ insnv(value => $vex, width => 16);
+ } else {
+ # We have to use the 3-byte VEX prefix
+ my $vex = 0xC4 << 16;
+ $vex |= ($args{r} & 0b1) << 15;
+ $vex |= ($args{x} & 0b1) << 14;
+ $vex |= ($args{b} & 0b1) << 13;
+ $vex |= ($args{m} & 0b11111) << 8;
+ $vex |= ($args{w} & 0b1) << 7;
+ $vex |= ($args{v} & 0b1111) << 3;
+ $vex |= ($args{l} & 0b1) << 2;
+ $vex |= ($args{p} & 0b11) << 0;
+ insnv(value => $vex, width => 24);
+ }
+}
+
+sub write_insn_modrm(%)
+{
+ my (%args) = @_;
+
+ my $modrm = 0;
+ $modrm |= ($args{mod} & 0b11) << 6;
+ $modrm |= ($args{reg} & 0b111) << 3;
+ $modrm |= ($args{rm} & 0b111) << 0;
+ insnv(value => $modrm, width => 8);
+}
+
+sub write_insn_sib(%)
+{
+ my (%args) = @_;
+
+ my $sib = 0;
+ $sib |= ($args{ss} & 0b11) << 6;
+ $sib |= ($args{index} & 0b111) << 3;
+ $sib |= ($args{base} & 0b111) << 0;
+ insnv(value => $sib, width => 8);
+}
+
+sub write_insn(%)
+{
+ my (%insn) = @_;
+
+ my @tokens;
+ push @tokens, "EVEX" if defined $insn{evex};
+ push @tokens, "VEX" if defined $insn{vex};
+ push @tokens, "REP" if defined $insn{rep};
+ push @tokens, "REPNE" if defined $insn{repne};
+ push @tokens, "DATA16" if defined $insn{data16};
+ push @tokens, "REX" if defined $insn{rex};
+ push @tokens, "OP" if defined $insn{opcode};
+ push @tokens, "MODRM" if defined $insn{modrm};
+ push @tokens, "SIB" if defined $insn{sib};
+ push @tokens, "DISP" if defined $insn{disp};
+ push @tokens, "IMM" if defined $insn{imm};
+ push @tokens, "END";
+
+ # (EVEX | VEX | ((REP | REPNE)? DATA16? REX?)) OP (MODRM SIB? DISP?)? IMM?
END
+ my $token = shift @tokens;
+
+ if ($token eq "EVEX") {
+ $token = shift @tokens;
+ write_insn_evex(%{$insn{evex}});
+ } elsif ($token eq "VEX") {
+ $token = shift @tokens;
+ write_insn_vex(%{$insn{vex}});
+ } else {
+ if ($token eq "REP") {
+ $token = shift @tokens;
+ write_insn_rep(%{$insn{rep}});
+ } elsif ($token eq "REPNE") {
+ $token = shift @tokens;
+ write_insn_repne(%{$insn{repne}});
+ }
+ if ($token eq "DATA16") {
+ $token = shift @tokens;
+ write_insn_data16(%{$insn{data16}});
+ }
+ if ($token eq "REX") {
+ $token = shift @tokens;
+ write_insn_rex(%{$insn{rex}});
+ }
+ }
+
+ die "Unexpected instruction tokens where OP expected: $token @tokens\n"
+ unless $token eq "OP";
+
+ $token = shift @tokens;
+ insnv(%{$insn{opcode}});
+
+ if ($token eq "MODRM") {
+ $token = shift @tokens;
+ write_insn_modrm(%{$insn{modrm}});
+
+ if ($token eq "SIB") {
+ $token = shift @tokens;
+ write_insn_sib(%{$insn{sib}});
+ }
+ if ($token eq "DISP") {
+ $token = shift @tokens;
+ insnv(%{$insn{disp}}, bigendian => 0);
+ }
+ }
+ if ($token eq "IMM") {
+ $token = shift @tokens;
+ insnv(%{$insn{imm}}, bigendian => 0);
+ }
+
+ die "Unexpected junk tokens at the end of instruction: $token @tokens\n"
+ unless $token eq "END";
+}
+
+sub asm_insn_vex_rxb($)
+{
+ my ($insn) = @_;
+ my $have_rex = defined $insn->{rex};
+
+ my @tokens;
+ push @tokens, "VEX.R" if defined $insn->{vex}{r};
+ push @tokens, "REX.R" if $have_rex && defined $insn->{rex}{r};
+ push @tokens, "VEX.X" if defined $insn->{vex}{x};
+ push @tokens, "REX.X" if $have_rex && defined $insn->{rex}{x};
+ push @tokens, "VEX.B" if defined $insn->{vex}{b};
+ push @tokens, "REX.B" if $have_rex && defined $insn->{rex}{b};
+ push @tokens, "END";
+
+ # (VEX.R | REX.R)? (VEX.X | REX.X)? (VEX.B | REX.B)? END
+ my $token = shift @tokens;
+
+ if ($token eq "VEX.R") {
+ $token = shift @tokens;
+ } elsif ($token eq "REX.R") {
+ $token = shift @tokens;
+ $insn->{vex}{r} = !$insn->{rex}{r};
+ delete $insn->{rex}{r};
+ }
+ if ($token eq "VEX.X") {
+ $token = shift @tokens;
+ } elsif ($token eq "REX.X") {
+ $token = shift @tokens;
+ $insn->{vex}{x} = !$insn->{rex}{x};
+ delete $insn->{rex}{x};
+ }
+ if ($token eq "VEX.B") {
+ $token = shift @tokens;
+ } elsif ($token eq "REX.B") {
+ $token = shift @tokens;
+ $insn->{vex}{b} = !$insn->{rex}{b};
+ delete $insn->{rex}{b};
+ }
+
+ die "unexpected junk at the end of VEX.RXB tokens: $token @tokens\n"
+ unless $token eq "END";
+
+ if ($have_rex) {
+ die "REX not empty"
+ unless !%{$insn->{rex}};
+
+ delete $insn->{rex};
+ }
+}
+
+sub asm_insn_vex_p($)
+{
+ my ($insn) = @_;
+
+ my @tokens;
+ push @tokens, "VEX.P" if defined $insn->{vex}{p};
+ push @tokens, "DATA16" if defined $insn->{data16};
+ push @tokens, "REP" if defined $insn->{rep};
+ push @tokens, "REPNE" if defined $insn->{repne};
+ push @tokens, "END";
+
+ # (VEX.P | DATA16 | REP | REPNE)? END
+ my $token = shift @tokens;
+
+ if ($token eq "VEX.P") {
+ $token = shift @tokens;
+ my $vex_p = $insn->{vex}{p};
+ delete $insn->{vex}{p};
+
+ $insn->{vex}{p} = VEX_P_DATA16 if $vex_p == 0x66;
+ $insn->{vex}{p} = VEX_P_REPNE if $vex_p == 0xF2;
+ $insn->{vex}{p} = VEX_P_REP if $vex_p == 0xF3;
+
+ die "invalid value of VEX.P=$vex_p\n"
+ unless defined $insn->{vex}{p};
+ } elsif ($token eq "DATA16") {
+ $token = shift @tokens;
+ $insn->{vex}{p} = VEX_P_DATA16;
+ delete $insn->{data16};
+ } elsif ($token eq "REP") {
+ $token = shift @tokens;
+ $insn->{vex}{p} = VEX_P_REP;
+ delete $insn->{rep};
+ } elsif ($token eq "REPNE") {
+ $token = shift @tokens;
+ $insn->{vex}{p} = VEX_P_REPNE;
+ delete $insn->{repne};
+ }
+
+ die "unexpected junk at the end of VEX.P tokens: $token @tokens\n"
+ unless $token eq "END";
+}
+
+sub asm_insn_vex_m($)
+{
+ my ($insn) = @_;
+ my $opcvalue = $insn->{opcode}{value};
+ my $opcwidth = $insn->{opcode}{width};
+
+ my @tokens;
+ push @tokens, "VEX.M" if defined $insn->{vex}{m};
+ push @tokens, "0F" if $opcwidth >= 16 && (($opcvalue >> ($opcwidth -
8)) & 0xFF) == 0x0F;
+ push @tokens, "38" if $opcwidth >= 24 && (($opcvalue >> ($opcwidth -
16)) & 0xFF) == 0x38;
+ push @tokens, "3A" if $opcwidth >= 24 && (($opcvalue >> ($opcwidth -
16)) & 0xFF) == 0x3A;
+ push @tokens, "END";
+
+ # (VEX.M | 0F (38 | 3A)?) END
+ my $token = shift @tokens;
+
+ if ($token eq "VEX.M") {
+ $token = shift @tokens;
+ my $vex_m = $insn->{vex}{m};
+ delete $insn->{vex}{m};
+
+ $insn->{vex}{m} = VEX_M_0F if $vex_m == 0x0F;
+ $insn->{vex}{m} = VEX_M_0F38 if $vex_m == 0x0F38;
+ $insn->{vex}{m} = VEX_M_0F3A if $vex_m == 0x0F3A;
+
+ die "invalid value of VEX.M=$vex_m\n"
+ unless defined $insn->{vex}{m};
+ } elsif ($token eq "0F") {
+ $token = shift @tokens;
+
+ if ($token eq "38" || $token eq "3A") {
+ $token = shift @tokens;
+
+ $insn->{vex}{m} = VEX_M_0F38 if $token eq "38";
+ $insn->{vex}{m} = VEX_M_0F3A if $token eq "3A";
+ $insn->{opcode}{value} &= (1 << ($opcwidth - 16)) - 1;
+ $insn->{opcode}{width} -= 16;
+ } else {
+ $insn->{vex}{m} = VEX_M_0F;
+ $insn->{opcode}{value} &= (1 << ($opcwidth - 8)) - 1;
+ $insn->{opcode}{width} -= 8;
+ }
+ } else {
+ die "unexpected vex token where VEX.M or 0F expected: $token
@tokens\n";
+ }
+
+ die "unexpected junk at the end of VEX.M tokens: $token @tokens\n"
+ unless $token eq "END";
+}
+
+sub asm_insn_vex_l($)
+{
+ my ($insn) = @_;
+ my $vex_l = $insn->{vex}{l};
+ delete $insn->{vex}{l};
+
+ $insn->{vex}{l} = 0 if $vex_l == 0;
+ $insn->{vex}{l} = VEX_L_128 if $vex_l == 128;
+ $insn->{vex}{l} = VEX_L_256 if $vex_l == 256;
+
+ die "invalid value of VEX.L=$vex_l\n"
+ unless defined $insn->{vex}{l};
+}
+
+sub asm_insn_vex_v($)
+{
+ my ($insn) = @_;
+
+ $insn->{vex}{v} ^= 0b1111 if defined $insn->{vex}{v};
+}
+
+sub asm_insn_vex($)
+{
+ my ($insn) = @_;
+
+ asm_insn_vex_rxb($insn);
+ asm_insn_vex_p($insn);
+ asm_insn_vex_m($insn);
+ asm_insn_vex_l($insn);
+ asm_insn_vex_v($insn);
+}
+
+sub asm_insn_modrm_rex($)
+{
+ my ($insn) = @_;
+
+ asm_insn_val($insn->{modrm}, 'disp');
+
+ my @tokens;
+ push @tokens, "REG" if defined $insn->{modrm}{reg};
+ push @tokens, "REG2" if defined $insn->{modrm}{reg2};
+ push @tokens, "BASE" if defined $insn->{modrm}{base};
+ push @tokens, "DISP" if defined $insn->{modrm}{disp};
+ push @tokens, "INDEX" if defined $insn->{modrm}{index};
+ push @tokens, "VINDEX" if defined $insn->{modrm}{vindex};
+ push @tokens, "END";
+
+ # REG (REG2 | (BASE DISP? | DISP) (INDEX | VINDEX)?) END
+ my $token = shift @tokens;
+
+ die "unexpected modrm tokens where REG expected: $token @tokens\n"
+ unless $token eq "REG";
+
+ $token = shift @tokens;
+ my $reg = $insn->{modrm}{reg};
+
+ $insn->{rex}{r} = 1 if $reg & 0b1000;
+ $insn->{modrm}{reg} = $reg & 0b111;
+
+ if ($token eq "REG2") {
+ $token = shift @tokens;
+ my $reg2 = $insn->{modrm}{reg2};
+ delete $insn->{modrm}{reg2};
+
+ $insn->{rex}{b} = 1 if $reg2 & 0b1000;
+ $insn->{modrm}{mod} = MOD_DIRECT;
+ $insn->{modrm}{rm} = $reg2 & 0b111;
+ } else {
+ if ($token eq "BASE") {
+ $token = shift @tokens;
+ my $base = $insn->{modrm}{base};
+ delete $insn->{modrm}{base};
+
+ $insn->{rex}{b} = 1 if $base & 0b1000;
+ $insn->{modrm}{rm} = $base & 0b111;
+
+ if ($token eq "DISP") {
+ $token = shift @tokens;
+ my $disp = $insn->{modrm}{disp};
+ delete $insn->{modrm}{disp};
+
+ die "displacement too large: $disp->{width}\n"
+ unless $disp->{width} <= 32;
+
+ if ($disp->{width} <= 8) {
+ $insn->{modrm}{mod} = MOD_INDIRECT_DISP8;
+ $insn->{disp}{width} = 8;
+ } else {
+ $insn->{modrm}{mod} = MOD_INDIRECT_DISP32;
+ $insn->{disp}{width} = 32;
+ }
+
+ $insn->{disp}{value} = $disp->{value};
+ } elsif (($base & 0b111) == REG_RBP) {
+ # Must use explicit displacement for RBP/R13-based
+ # addressing
+ $insn->{modrm}{mod} = MOD_INDIRECT_DISP8;
+ $insn->{disp}{value} = 0;
+ $insn->{disp}{width} = 8;
+ } else {
+ $insn->{modrm}{mod} = MOD_INDIRECT;
+ }
+ } elsif ($token eq "DISP") {
+ $token = shift @tokens;
+ my $disp = $insn->{modrm}{disp};
+ delete $insn->{modrm}{disp};
+
+ die "displacement too large: $disp->{width}\n"
+ unless $disp->{width} <= 32;
+
+ # Displacement only
+ $insn->{modrm}{mod} = MOD_INDIRECT;
+ $insn->{modrm}{rm} = REG_RBP;
+ $insn->{disp}{value} = $disp->{value};
+ $insn->{disp}{width} = 32;
+ } else {
+ die "DISP or BASE expected: $token @tokens\n";
+ }
+
+ if ($token eq "INDEX" || $token eq "VINDEX") {
+ $insn->{modrm}{ss} = 0 unless defined $insn->{modrm}{ss};
+
+ my $index;
+ if ($token eq "VINDEX") {
+ $index = $insn->{modrm}{vindex};
+ delete $insn->{modrm}{vindex};
+ } else {
+ $index = $insn->{modrm}{index};
+ delete $insn->{modrm}{index};
+
+ # RSP cannot be encoded as index register.
+ die "cannot encode RSP as index register\n"
+ if $index == REG_RSP;
+ }
+
+ $token = shift @tokens;
+ my $ss = $insn->{modrm}{ss};
+ delete $insn->{modrm}{ss};
+
+ $insn->{rex}{x} = 1 if $index & 0b1000;
+ $insn->{sib}{ss} = $ss;
+ $insn->{sib}{index} = $index & 0b111;
+ $insn->{sib}{base} = $insn->{modrm}{rm};
+ $insn->{modrm}{rm} = REG_RSP; # SIB
+ } elsif ($insn->{modrm}{rm} == REG_RSP) {
+ # Must use SIB for RSP/R12-based adressing
+ $insn->{sib}{ss} = 0;
+ $insn->{sib}{index} = REG_RSP; # No index
+ $insn->{sib}{base} = REG_RSP;
+ }
+ }
+
+ die "unexpected junk at the end of modrm tokens: $token @tokens\n"
+ unless $token eq "END";
+}
+
+sub asm_insn_val($$)
+{
+ my ($insn, $k) = @_;
+
+ my @tokens;
+ push @tokens, "K" if defined $insn->{$k};
+ push @tokens, "K8" if defined $insn->{$k . "8"};
+ push @tokens, "K16" if defined $insn->{$k . "16"};
+ push @tokens, "K32" if defined $insn->{$k . "32"};
+ push @tokens, "K64" if defined $insn->{$k . "64"};
+ push @tokens, "END";
+
+ # (K | K8 | K16 | K32 | K64)? END
+ my $token = shift @tokens;
+
+ if ($token eq "K") {
+ $token = shift @tokens;
+ } elsif ($token eq "K8") {
+ $token = shift @tokens;
+ my $value = $insn->{$k . "8"};
+ delete $insn->{$k . "8"};
+
+ $insn->{$k}{value} = $value;
+ $insn->{$k}{width} = 8;
+ } elsif ($token eq "K16") {
+ $token = shift @tokens;
+ my $value = $insn->{$k . "16"};
+ delete $insn->{$k . "16"};
+
+ $insn->{$k}{value} = $value;
+ $insn->{$k}{width} = 16;
+ } elsif ($token eq "K32") {
+ $token = shift @tokens;
+ my $value = $insn->{$k . "32"};
+ delete $insn->{$k . "32"};
+
+ $insn->{$k}{value} = $value;
+ $insn->{$k}{width} = 32;
+ } elsif ($token eq "K64") {
+ $token = shift @tokens;
+ my $value = $insn->{$k . "64"};
+ delete $insn->{$k . "64"};
+
+ $insn->{$k}{value} = $value;
+ $insn->{$k}{width} = 64;
+ }
+
+ die "unexpected junk at the end of value tokens: $token @tokens\n"
+ unless $token eq "END";
+}
+
+sub asm_insn(%)
+{
+ my (%insn) = @_;
+
+ asm_insn_val(\%insn, 'opcode');
+ asm_insn_val(\%insn, 'imm');
+ asm_insn_modrm_rex(\%insn) if defined $insn{modrm};
+ asm_insn_vex(\%insn) if defined $insn{vex};
+ write_insn(%insn);
+}
+
+sub asm_insn_ud1(%)
+{
+ my (%modrm) = @_;
+ asm_insn(opcode16 => 0x0FB9, modrm => \%modrm);
+}
+
+sub asm_insn_xor(%)
+{
+ my (%modrm) = @_;
+
+ my %insn = ();
+ $insn{opcode8} = 0x33;
+ $insn{modrm} = \%modrm;
+ asm_insn(%insn);
+}
+
+sub asm_insn_sahf()
+{
+ my %insn = ();
+ $insn{opcode8} = 0x9E;
+ asm_insn(%insn);
+}
+
+sub asm_insn_lea64(%)
+{
+ my (%modrm) = @_;
+
+ my %insn = ();
+ $insn{rex}{w} = 1;
+ $insn{opcode8} = 0x8D;
+ $insn{modrm} = \%modrm;
+ asm_insn(%insn);
+}
+
+sub asm_insn_call(%)
+{
+ my (%insn) = @_;
+ asm_insn_val(\%insn, 'imm');
+
+ die "imm too large: $insn{imm}{width}"
+ unless $insn{imm}{width} <= 32;
+
+ $insn{opcode8} = 0xE8;
+ $insn{imm}{width} = 32;
+ asm_insn(%insn);
+}
+
+sub asm_insn_jmp(%)
+{
+ my (%insn) = @_;
+ asm_insn_val(\%insn, 'imm');
+
+ die "imm too large: $insn{imm}{width}"
+ unless $insn{imm}{width} <= 32;
+
+ $insn{opcode8} = 0xE9;
+ $insn{imm}{width} = 32;
+ asm_insn(%insn);
+}
+
+sub asm_insn_pop(%)
+{
+ my (%args) = @_;
+
+ my %insn = ();
+ $insn{rex}{b} = 1 if $args{reg} & 0b1000;
+ $insn{opcode8} = 0x58 | ($args{reg} & 0b111);
+ asm_insn(%insn);
+}
+
+sub asm_insn_mov_(%)
+{
+ my (%args) = @_;
+ my $is_wide64 = $args{w}; delete $args{w};
+ asm_insn_val(\%args, 'imm');
+
+ if (!defined $args{imm}) {
+ # Regular MOV reg, r/m. The W flag differentiates between
+ # 32-bit and 64-bit registers.
+ my %insn = ();
+ $insn{rex}{w} = 1 if $is_wide64;
+ $insn{opcode8} = 0x8B;
+ $insn{modrm} = \%args;
+ asm_insn(%insn);
+ } elsif ($is_wide64
+ && $args{imm}{width} <= 32
+ && $args{imm}{value} < 0) {
+ # Move signed immediate to 64-bit register. This is the right
+ # time to use sign-extending move to save space; no point in
+ # using this opcode for 32-bit registers or for non-negative
+ # values, since both of these cases are better handled by
+ # 0xB8, which is shorter.
+ $args{imm}{width} = 32;
+
+ my %insn = ();
+ $insn{rex}{w} = 1;
+ $insn{opcode8} = 0xC7;
+ $insn{modrm}{reg} = 0;
+ $insn{modrm}{reg2} = $args{reg};
+ $insn{imm} = $args{imm};
+ asm_insn(%insn);
+ } elsif ($args{imm}{width} <= (!$is_wide64 ? 32 : 64)) {
+ # Move immediate to 32/64-bit register. Note that this opcode
+ # is zero-extending, since the upper part of the destination
+ # register is automatically zeroed when moving a 32-bit
+ # immediate on x86_64.
+ $args{imm}{width} = ($args{imm}{width} <= 32 ? 32 : 64);
+
+ my %insn = ();
+ $insn{rex}{w} = 1 if $args{imm}{width} > 32;
+ $insn{rex}{b} = 1 if $args{reg} & 0b1000;
+ $insn{opcode8} = 0xB8 | ($args{reg} & 0b111);
+ $insn{imm} = $args{imm};
+ asm_insn(%insn);
+ } else {
+ die "imm too large: $args{imm}{width}";
+ }
+}
+
+sub asm_insn_mov(%)
+{
+ my (%args) = @_;
+ asm_insn_mov_(%args, w => 0);
+}
+
+sub asm_insn_mov64(%)
+{
+ my (%args) = @_;
+ asm_insn_mov_(%args, w => 1);
+}
+
+# Currently only the MOVQ mm, mm/m64 form is supported (and assumed).
+sub asm_insn_movq(%)
+{
+ my (%modrm) = @_;
+
+ my %insn = ();
+ $insn{opcode16} = 0x0F6F;
+ $insn{modrm} = \%modrm;
+ asm_insn(%insn);
+}
+
+sub asm_insn_add_(%)
+{
+ my (%args) = @_;
+ my $is_wide64 = $args{w}; delete $args{w};
+ asm_insn_val(\%args, 'imm');
+
+ if (!defined $args{imm}) {
+ # Regular ADD r/m, reg. The W flag differentiates between
+ # 32-bit and 64-bit registers.
+ my %insn = ();
+ $insn{rex}{w} = 1 if $is_wide64;
+ $insn{opcode8} = 0x01;
+ $insn{modrm} = \%args;
+ asm_insn(%insn);
+ } elsif ($args{imm}{width} <= 8) {
+ # ADD r/m, imm8 with sign-extension.
+ my %insn = ();
+ $insn{rex}{w} = 1 if $is_wide64;
+ $insn{opcode8} = 0x83;
+ $insn{modrm} = \%args;
+ $insn{modrm}{reg} = 0;
+ $insn{imm} = $args{imm}; delete $args{imm};
+ $insn{imm}{width} = 8;
+ asm_insn(%insn);
+ } else {
+ die "imm too large: $args{imm}{width}\n";
+ }
+}
+
+sub asm_insn_add(%)
+{
+ my (%args) = @_;
+ asm_insn_add_(%args, w => 0);
+}
+
+sub asm_insn_add64(%)
+{
+ my (%args) = @_;
+ asm_insn_add_(%args, w => 1);
+}
+
+sub asm_insn_and_(%)
+{
+ my (%args) = @_;
+ my $is_wide64 = $args{w}; delete $args{w};
+ asm_insn_val(\%args, 'imm');
+
+ if (!defined $args{imm}) {
+ # Regular AND r/m, reg. The W flag differentiates between
+ # 32-bit and 64-bit registers.
+ my %insn = ();
+ $insn{rex}{w} = 1 if $is_wide64;
+ $insn{opcode8} = 0x21;
+ $insn{modrm} = \%args;
+ asm_insn(%insn);
+ } elsif ($args{imm}{width} <= 8) {
+ # AND r/m, imm8 with sign-extension.
+ my %insn = ();
+ $insn{rex}{w} = 1 if $is_wide64;
+ $insn{opcode8} = 0x83;
+ $insn{modrm} = \%args;
+ $insn{modrm}{reg} = 4;
+ $insn{imm} = $args{imm}; delete $args{imm};
+ $insn{imm}{width} = 8;
+ asm_insn(%insn);
+ } else {
+ die "imm too large: $args{imm}{width}\n";
+ }
+}
+
+sub asm_insn_and(%)
+{
+ my (%args) = @_;
+ asm_insn_and_(%args, w => 0);
+}
+
+sub asm_insn_and64(%)
+{
+ my (%args) = @_;
+ asm_insn_and_(%args, w => 1);
+}
+
+sub asm_insn_neg_(%)
+{
+ my (%args) = @_;
+
+ my %insn = ();
+ $insn{rex}{w} = 1 if $args{w}; delete $args{w};
+ $insn{opcode8} = 0xF7;
+ $insn{modrm} = \%args;
+ $insn{modrm}{reg} = 3;
+ asm_insn(%insn);
+}
+
+sub asm_insn_neg(%)
+{
+ my (%modrm) = @_;
+ asm_insn_neg_(%modrm, w => 0);
+}
+
+sub asm_insn_neg64(%)
+{
+ my (%modrm) = @_;
+ asm_insn_neg_(%modrm, w => 1);
+}
+
+sub asm_insn_xchg_(%)
+{
+ my (%args) = @_;
+
+ if (defined $args{reg2} &&
+ ($args{reg} == REG_RAX || $args{reg2} == REG_RAX)) {
+ # We can use the short form, yay!
+ my $reg = ($args{reg} == REG_RAX ? $args{reg2} : $args{reg});
+
+ my %insn = ();
+ $insn{rex}{w} = 1 if $args{w}; delete $args{w};
+ $insn{rex}{b} = 1 if $reg & 0b1000;
+ $insn{opcode8} = 0x90 | ($reg & 0b111);
+ asm_insn(%insn);
+ } else {
+ my %insn = ();
+ $insn{rex}{w} = 1 if $args{w}; delete $args{w};
+ $insn{opcode8} = 0x87;
+ $insn{modrm} = \%args;
+ asm_insn(%insn);
+ }
+}
+
+sub asm_insn_xchg(%)
+{
+ my (%modrm) = @_;
+ asm_insn_xchg_(%modrm, w => 0);
+}
+
+sub asm_insn_xchg64(%)
+{
+ my (%modrm) = @_;
+ asm_insn_xchg_(%modrm, w => 1);
+}
+
+sub asm_insn_movaps(%)
+{
+ my (%modrm) = @_;
+
+ my %insn = ();
+ $insn{opcode16} = 0x0F28;
+ $insn{modrm} = \%modrm;
+ asm_insn(%insn);
+}
+
+sub asm_insn_vmovaps(%)
+{
+ my (%args) = @_;
+
+ my %insn = ();
+ $insn{vex}{l} = $args{l}; delete $args{l};
+ $insn{vex}{m} = 0x0F;
+ $insn{opcode8} = 0x28;
+ $insn{modrm} = \%args;
+ asm_insn(%insn);
+}
+
+sub asm_insn_movdqu(%)
+{
+ my (%modrm) = @_;
+
+ my %insn = ();
+ $insn{rep} = {};
+ $insn{opcode16} = 0x0F6F;
+ $insn{modrm} = \%modrm;
+ asm_insn(%insn);
+}
+
+sub asm_insn_vmovdqu(%)
+{
+ my (%args) = @_;
+
+ my %insn = ();
+ $insn{rep} = {};
+ $insn{vex}{l} = $args{l}; delete $args{l};
+ $insn{vex}{m} = 0x0F;
+ $insn{opcode8} = 0x6F;
+ $insn{modrm} = \%args;
+ asm_insn(%insn);
+}
--
2.20.1
- Re: [Qemu-devel] [RISU PATCH v3 01/18] risugen_common: add helper functions insnv, randint, (continued)
- [Qemu-devel] [RISU PATCH v3 07/18] risugen: allow all byte-aligned instructions, Jan Bobek, 2019/07/11
- [Qemu-devel] [RISU PATCH v3 02/18] risugen_common: split eval_with_fields into extract_fields and eval_block, Jan Bobek, 2019/07/11
- [Qemu-devel] [RISU PATCH v3 04/18] risugen_x86_constraints: add module, Jan Bobek, 2019/07/11
- [Qemu-devel] [RISU PATCH v3 15/18] x86.risu: add SSE4.1 and SSE4.2 instructions, Jan Bobek, 2019/07/11
- [Qemu-devel] [RISU PATCH v3 03/18] risugen_x86_asm: add module,
Jan Bobek <=
- [Qemu-devel] [RISU PATCH v3 14/18] x86.risu: add SSSE3 instructions, Jan Bobek, 2019/07/11
- [Qemu-devel] [RISU PATCH v3 12/18] x86.risu: add SSE2 instructions, Jan Bobek, 2019/07/11
- [Qemu-devel] [RISU PATCH v3 17/18] x86.risu: add AVX instructions, Jan Bobek, 2019/07/11