whatcanGOwrong

This commit is contained in:
2024-09-19 21:38:24 -04:00
commit d0ae4d841d
17908 changed files with 4096831 additions and 0 deletions
@@ -0,0 +1,26 @@
# Contributing to Go
Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help!
## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
## Contributing code
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches.
Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file.
@@ -0,0 +1,27 @@
Copyright (c) 2015 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
@@ -0,0 +1,15 @@
# arch
[![Go Reference](https://pkg.go.dev/badge/golang.org/x/arch.svg)](https://pkg.go.dev/golang.org/x/arch)
This repository holds machine architecture information used by the Go toolchain.
The parts needed in the main Go repository are copied in.
## Report Issues / Send Patches
This repository uses Gerrit for code changes. To learn how to submit changes to
this repository, see https://golang.org/doc/contribute.html.
The main issue tracker for the arch repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/arch:" in the
subject line, so it is easy to find.
@@ -0,0 +1,458 @@
# arm instruction description version 0.01.
# https://golang.org/x/arch/arm
#
# This file contains a block of comment lines, each beginning with #,
# followed by entries in CSV format. All the # comments are at the top
# of the file, so a reader can skip past the comments and hand the
# rest of the file to a standard CSV reader.
#
# Each line in the CSV section contains 5 fields:
#
# mask value mnemonic encoding tags
#
# The mnemonic and encoding correspond to the notation used in the
# ARM Architecture Reference Manual.
#
# A particular instruction word w matches a line if w&mask == value.
# The mask and value are computed from a combination of the encodings
# and the additional pseudocode restrictions given in the manual
# for each instruction.
#
# Multiple lines may match a particular instruction word.
# The tags specify which line takes priority in these cases,
# as a partial order. Eventually the plan is that a tag of the form
# 'name:x' marks a line with the name x, and a tag of the form
# 'except:x' says that the line on which the tag appears is of lower
# priority than the ones marked with name x.
# For now, however, a tag of the form 'SEE XXX' is taken from the
# manual and is like 'except:x' but without a rigorous resolution for
# the x. So far we've been able to just sort instructions into two classes:
# those with SEE and those without.
#
# The tag 'pseudo' indicates that this instruction is an alternate name
# for another encoding and should be ignored during disassembly.
#
# This file was generated by a program reading the PDF version of
# the manual, but it was then hand edited to make corrections.
# The eventual plan is for the generator to write the
# file directly, using the PDF and a list of modifications, but
# no hand editing. Then, when a new version of the manual comes out,
# updating the list should be as easy as downloading the new PDF
# and invoking the generator. However, we are not there yet.
#
"0x0fe00000","0x02a00000","ADC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|0|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00a00010","ADC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00a00000","ADC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fe00000","0x02800000","ADD{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|0|0|S|Rn:4|Rd:4|imm12:12","SEE ADR SEE ADD (SP plus immediate) SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00800010","ADD{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00800000","ADD{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions SEE ADD (SP plus register)"
"0x0fef0000","0x028d0000","ADD{S}<c> <Rd>,SP,#<const>","cond:4|0|0|1|0|1|0|0|S|1|1|0|1|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fef0010","0x008d0000","ADD{S}<c> <Rd>,SP,<Rm>{,<shift>}","cond:4|0|0|0|0|1|0|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fff0000","0x028f0000","ADR<c> <Rd>,<label+12>","cond:4|0|0|1|0|1|0|0|0|1|1|1|1|Rd:4|imm12:12","pseudo"
"0x0fff0000","0x024f0000","ADR<c> <Rd>,<label-12>","cond:4|0|0|1|0|0|1|0|0|1|1|1|1|Rd:4|imm12:12","pseudo"
"0x0fe00000","0x02000000","AND{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|0|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00000010","AND{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00000000","AND{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fef0070","0x01a00040","ASR{S}<c> <Rd>,<Rm>,#<imm5_32>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|0|0|Rm:4",""
"0x0fef00f0","0x01a00050","ASR{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|0|1|Rn:4",""
"0x0f000000","0x0a000000","B<c> <label24>","cond:4|1|0|1|0|imm24:24",""
"0x0fe0007f","0x07c0001f","BFC<c> <Rd>,#<lsb>,#<width>","cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|1|1|1|1",""
"0x0fe00070","0x07c00010","BFI<c> <Rd>,<Rn>,#<lsb>,#<width>","cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|Rn:4","SEE BFC"
"0x0fe00000","0x03c00000","BIC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|1|1|1|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x01c00010","BIC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x01c00000","BIC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0ff000f0","0x01200070","BKPT<c> #<imm12+4>","cond:4|0|0|0|1|0|0|1|0|imm12:12|0|1|1|1|imm4:4",""
"0x0f000000","0x0b000000","BL<c> <label24>","cond:4|1|0|1|1|imm24:24",""
"0xfe000000","0xfa000000","BLX <label24H>","1|1|1|1|1|0|1|H|imm24:24",""
"0x0ffffff0","0x012fff30","BLX<c> <Rm>","cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0ffffff0","0x012fff10","BX<c> <Rm>","cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ffffff0","0x012fff20","BXJ<c> <Rm>","cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4",""
"0xffffffff","0xf57ff01f","CLREX","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1)",""
"0x0fff0ff0","0x016f0f10","CLZ<c> <Rd>,<Rm>","cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff0f000","0x03700000","CMN<c> <Rn>,#<const>","cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
"0x0ff0f090","0x01700010","CMN<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
"0x0ff0f010","0x01700000","CMN<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
"0x0ff0f000","0x03500000","CMP<c> <Rn>,#<const>","cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
"0x0ff0f090","0x01500010","CMP<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
"0x0ff0f010","0x01500000","CMP<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
"0x0ffffff0","0x0320f0f0","DBG<c> #<option>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|1|1|1|1|option:4",""
"0xfffffff0","0xf57ff050","DMB #<option>","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|0|1|option:4",""
"0xfffffff0","0xf57ff040","DSB #<option>","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|0|0|option:4",""
"0x0fe00000","0x02200000","EOR{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|0|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00200010","EOR{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00200000","EOR{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0xfffffff0","0xf57ff060","ISB #<option>","1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|1|0|option:4",""
"0x0fd00000","0x08900000","LDM<c> <Rn>{!},<registers>","cond:4|1|0|0|0|1|0|W|1|Rn:4|register_list:16","SEE POP"
"0x0fd00000","0x08100000","LDMDA<c> <Rn>{!},<registers>","cond:4|1|0|0|0|0|0|W|1|Rn:4|register_list:16",""
"0x0fd00000","0x09100000","LDMDB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|0|0|W|1|Rn:4|register_list:16",""
"0x0fd00000","0x09900000","LDMIB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|1|0|W|1|Rn:4|register_list:16",""
"0x0f7f0000","0x051f0000","LDR<c> <Rt>,<label+/-12>","cond:4|0|1|0|(1)|U|0|(0)|1|1|1|1|1|Rt:4|imm12:12",""
"0x0e500010","0x06100000","LDR<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|0|W|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE LDRT"
"0x0e500000","0x04100000","LDR<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|0|W|1|Rn:4|Rt:4|imm12:12","SEE LDR (literal) SEE LDRT SEE POP"
"0x0f7f0000","0x055f0000","LDRB<c> <Rt>,<label+/-12>","cond:4|0|1|0|(1)|U|1|(0)|1|1|1|1|1|Rt:4|imm12:12",""
"0x0e500010","0x06500000","LDRB<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|1|W|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE LDRBT"
"0x0e500000","0x04500000","LDRB<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|1|W|1|Rn:4|Rt:4|imm12:12","SEE LDRB (literal) SEE LDRBT"
"0x0f700000","0x04700000","LDRBT<c> <Rt>,[<Rn>],#+/-<imm12>","cond:4|0|1|0|0|U|1|1|1|Rn:4|Rt:4|imm12:12",""
"0x0f700010","0x06700000","LDRBT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|1|1|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
"0x0f7f10f0","0x014f00d0","LDRD<c> <Rt1>,<Rt2>,<label+/-4+4>","cond:4|0|0|0|(1)|U|1|(0)|0|1|1|1|1|Rt:4|imm4H:4|1|1|0|1|imm4L:4","pseudo"
"0x0e500ff0","0x000000d0","LDRD<c> <Rt1>,<Rt2>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|(0)|(0)|(0)|(0)|1|1|0|1|Rm:4",""
"0x0e5000f0","0x004000d0","LDRD<c> <Rt1>,<Rt2>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4","SEE LDRD (literal)"
"0x0ff00fff","0x01900f9f","LDREX<c> <Rt>,[<Rn>]","cond:4|0|0|0|1|1|0|0|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
"0x0ff00fff","0x01d00f9f","LDREXB<c> <Rt>, [<Rn>]","cond:4|0|0|0|1|1|1|0|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
"0x0ff00fff","0x01b00f9f","LDREXD<c> <Rt1>,<Rt2>,[<Rn>]","cond:4|0|0|0|1|1|0|1|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
"0x0ff00fff","0x01f00f9f","LDREXH<c> <Rt>, [<Rn>]","cond:4|0|0|0|1|1|1|1|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)",""
"0x0f7f00f0","0x015f00b0","LDRH<c> <Rt>,<label+/-4+4>","cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|0|1|1|imm4L:4","pseudo"
"0x0e500ff0","0x001000b0","LDRH<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4","SEE LDRHT"
"0x0e5000f0","0x005000b0","LDRH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4","SEE LDRH literal SEE LDRHT"
"0x0f7000f0","0x007000b0","LDRHT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4",""
"0x0f700ff0","0x003000b0","LDRHT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4",""
"0x0f7f00f0","0x015f00d0","LDRSB<c> <Rt>,<label+/-4+4>","cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|1|0|1|imm4L:4","pseudo"
"0x0e500ff0","0x001000d0","LDRSB<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|1|0|1|Rm:4","SEE LDRSBT"
"0x0e5000f0","0x005000d0","LDRSB<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4","SEE LDRSB literal SEE LDRSBT"
"0x0f7000f0","0x007000d0","LDRSBT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4",""
"0x0f700ff0","0x003000d0","LDRSBT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|1|0|1|Rm:4",""
"0x0f7f00f0","0x015f00f0","LDRSH<c> <Rt>,<label+/-4+4>","cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|1|1|1|imm4L:4","pseudo"
"0x0e500ff0","0x001000f0","LDRSH<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|1|1|1|Rm:4","SEE LDRSHT"
"0x0e5000f0","0x005000f0","LDRSH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4","SEE LDRSH literal SEE LDRSHT"
"0x0f7000f0","0x007000f0","LDRSHT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4",""
"0x0f700ff0","0x003000f0","LDRSHT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|1|1|1|Rm:4",""
"0x0f700000","0x04300000","LDRT<c> <Rt>, [<Rn>] {,#+/-<imm12>}","cond:4|0|1|0|0|U|0|1|1|Rn:4|Rt:4|imm12:12",""
"0x0f700010","0x06300000","LDRT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|0|1|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
"0x0fef0070","0x01a00000","LSL{S}<c> <Rd>,<Rm>,#<imm5_nz>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|0|0|0|Rm:4","SEE MOV register"
"0x0fef00f0","0x01a00010","LSL{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|0|0|1|Rn:4",""
"0x0fef0070","0x01a00020","LSR{S}<c> <Rd>,<Rm>,#<imm5_32>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|0|1|0|Rm:4",""
"0x0fef00f0","0x01a00030","LSR{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|0|1|1|Rn:4",""
"0x0fe000f0","0x00200090","MLA{S}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|0|0|0|1|S|Rd:4|Ra:4|Rm:4|1|0|0|1|Rn:4",""
"0x0ff000f0","0x00600090","MLS<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|0|0|1|1|0|Rd:4|Ra:4|Rm:4|1|0|0|1|Rn:4",""
"0x0ff00000","0x03400000","MOVT<c> <Rd>,#<imm12+4>","cond:4|0|0|1|1|0|1|0|0|imm4:4|Rd:4|imm12:12",""
"0x0ff00000","0x03000000","MOVW<c> <Rd>,#<imm12+4>","cond:4|0|0|1|1|0|0|0|0|imm4:4|Rd:4|imm12:12",""
"0x0fef0000","0x03a00000","MOV{S}<c> <Rd>,#<const>","cond:4|0|0|1|1|1|0|1|S|0|0|0|0|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fef0ff0","0x01a00000","MOV{S}<c> <Rd>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|0|0|0|0|0|0|0|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fff0fff","0x010f0000","MRS<c> <Rd>,APSR","cond:4|0|0|0|1|0|0|0|0|(1)|(1)|(1)|(1)|Rd:4|(0)|(0)|(0)|(0)|0|0|0|0|(0)|(0)|(0)|(0)",""
"0x0ffffff0","0x012cf000","MSR<c> APSR,<Rn>","cond:4|0|0|0|1|0|0|1|0|1|1|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|Rn:4",""
"0x0ffff000","0x032cf000","MSR<c> APSR,#<const>","cond:4|0|0|1|1|0|0|1|0|1|1|0|0|(1)|(1)|(1)|(1)|imm12:12",""
"0x0fe0f0f0","0x00000090","MUL{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|0|0|0|0|S|Rd:4|(0)|(0)|(0)|(0)|Rm:4|1|0|0|1|Rn:4",""
"0x0fef0000","0x03e00000","MVN{S}<c> <Rd>,#<const>","cond:4|0|0|1|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fef0090","0x01e00010","MVN{S}<c> <Rd>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fef0010","0x01e00000","MVN{S}<c> <Rd>,<Rm>{,<shift>}","cond:4|0|0|0|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fffffff","0x0320f000","NOP<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|0|0",""
"0x0fe00000","0x03800000","ORR{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|1|1|0|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x01800010","ORR{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x01800000","ORR{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0ff00030","0x06800010","PKH<BT,TB><c> <Rd>,<Rn>,<Rm>{,LSL #<imm5>}","cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|imm5:5|tb|0|1|Rm:4",""
"0xff7ff000","0xf55ff000","PLD <label+/-12>","1|1|1|1|0|1|0|1|U|(1)|0|1|1|1|1|1|(1)|(1)|(1)|(1)|imm12:12",""
"0xff30f000","0xf510f000","PLD{W} [<Rn>,#+/-<imm12>]","1|1|1|1|0|1|0|1|U|R|0|1|Rn:4|(1)|(1)|(1)|(1)|imm12:12","SEE PLD (literal)"
"0xff30f010","0xf710f000","PLD{W} [<Rn>,+/-<Rm>{, <shift>}]","1|1|1|1|0|1|1|1|U|R|0|1|Rn:4|(1)|(1)|(1)|(1)|imm5:5|type:2|0|Rm:4",""
"0xff70f000","0xf450f000","PLI [<Rn>,#+/-<imm12>]","1|1|1|1|0|1|0|0|U|1|0|1|Rn:4|(1)|(1)|(1)|(1)|imm12:12",""
"0xff70f010","0xf650f000","PLI [<Rn>,+/-<Rm>{, <shift>}]","1|1|1|1|0|1|1|0|U|1|0|1|Rn:4|(1)|(1)|(1)|(1)|imm5:5|type:2|0|Rm:4",""
"0x0fff0000","0x08bd0000","POP<c> <registers2>","cond:4|1|0|0|0|1|0|1|1|1|1|0|1|register_list:16",""
"0x0fff0fff","0x049d0004","POP<c> <registers1>","cond:4|0|1|0|0|1|0|0|1|1|1|0|1|Rt:4|0|0|0|0|0|0|0|0|0|1|0|0",""
"0x0fff0000","0x092d0000","PUSH<c> <registers2>","cond:4|1|0|0|1|0|0|1|0|1|1|0|1|register_list:16",""
"0x0fff0fff","0x052d0004","PUSH<c> <registers1>","cond:4|0|1|0|1|0|0|1|0|1|1|0|1|Rt:4|0|0|0|0|0|0|0|0|0|1|0|0",""
"0x0ff00ff0","0x06200f10","QADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff00ff0","0x06200f90","QADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
"0x0ff00ff0","0x01000050","QADD<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|0|0|0|Rn:4|Rd:4|(0)|(0)|(0)|(0)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06200f30","QASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0ff00ff0","0x01400050","QDADD<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|1|0|0|Rn:4|Rd:4|(0)|(0)|(0)|(0)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x01600050","QDSUB<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|1|1|0|Rn:4|Rd:4|0|0|0|0|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06200f50","QSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06200f70","QSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
"0x0ff00ff0","0x06200ff0","QSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
"0x0ff00ff0","0x01200050","QSUB<c> <Rd>,<Rm>,<Rn>","cond:4|0|0|0|1|0|0|1|0|Rn:4|Rd:4|0|0|0|0|0|1|0|1|Rm:4",""
"0x0fff0ff0","0x06ff0f30","RBIT<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|1|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0fff0ff0","0x06bf0fb0","REV16<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|0|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4",""
"0x0fff0ff0","0x06bf0f30","REV<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|0|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0fff0ff0","0x06ff0fb0","REVSH<c> <Rd>,<Rm>","cond:4|0|1|1|0|1|1|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4",""
"0x0fef0070","0x01a00060","ROR{S}<c> <Rd>,<Rm>,#<imm5>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|1|0|Rm:4","SEE RRX"
"0x0fef00f0","0x01a00070","ROR{S}<c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|1|1|Rn:4",""
"0x0fef0ff0","0x01a00060","RRX{S}<c> <Rd>,<Rm>","cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|0|0|0|0|0|1|1|0|Rm:4",""
"0x0fe00000","0x02600000","RSB{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|1|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00600010","RSB{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|1|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00600000","RSB{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|1|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fe00000","0x02e00000","RSC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|1|1|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00e00010","RSC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|1|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00e00000","RSC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|1|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0ff00ff0","0x06100f10","SADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff00ff0","0x06100f90","SADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
"0x0ff00ff0","0x06100f30","SASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0fe00000","0x02c00000","SBC{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|1|1|0|S|Rn:4|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00c00010","SBC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00c00000","SBC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0fe00070","0x07a00050","SBFX<c> <Rd>,<Rn>,#<lsb>,#<widthm1>","cond:4|0|1|1|1|1|0|1|widthm1:5|Rd:4|lsb:5|1|0|1|Rn:4",""
"0x0ff0f0f0","0x0710f010","SDIV<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|0|1|Rd:4|(1)|(1)|(1)|(1)|Rm:4|0|0|0|1|Rn:4",""
"0x0ff00ff0","0x06800fb0","SEL<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4",""
"0xfffffdff","0xf1010000","SETEND <endian_specifier>","1|1|1|1|0|0|0|1|0|0|0|0|0|0|0|1|0|0|0|0|0|0|E|(0)|(0)|(0)|(0)|(0)|(0)|(0)|(0)|(0)",""
"0x0fffffff","0x0320f004","SEV<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|1|0|0",""
"0x0ff00ff0","0x06300f10","SHADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff00ff0","0x06300f90","SHADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
"0x0ff00ff0","0x06300f30","SHASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0ff00ff0","0x06300f50","SHSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06300f70","SHSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
"0x0ff00ff0","0x06300ff0","SHSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
"0x0ff00090","0x01000080","SMLA<x><y><c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|1|0|0|0|0|Rd:4|Ra:4|Rm:4|1|M|N|0|Rn:4",""
"0x0ff000d0","0x07000010","SMLAD{X}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|0|0|0|Rd:4|Ra:4|Rm:4|0|0|M|1|Rn:4","SEE SMUAD"
"0x0ff00090","0x01400080","SMLAL<x><y><c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|1|M|N|0|Rn:4",""
"0x0ff000d0","0x07400010","SMLALD{X}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|1|1|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|0|0|M|1|Rn:4",""
"0x0fe000f0","0x00e00090","SMLAL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|1|1|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
"0x0ff000b0","0x01200080","SMLAW<y><c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|0|0|1|0|0|1|0|Rd:4|Ra:4|Rm:4|1|M|0|0|Rn:4",""
"0x0ff000d0","0x07000050","SMLSD{X}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|0|0|0|Rd:4|Ra:4|Rm:4|0|1|M|1|Rn:4","SEE SMUSD"
"0x0ff000d0","0x07400050","SMLSLD{X}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|1|1|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|0|1|M|1|Rn:4",""
"0x0ff000d0","0x07500010","SMMLA{R}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|1|0|1|Rd:4|Ra:4|Rm:4|0|0|R|1|Rn:4","SEE SMMUL"
"0x0ff000d0","0x075000d0","SMMLS{R}<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|0|1|0|1|Rd:4|Ra:4|Rm:4|1|1|R|1|Rn:4",""
"0x0ff0f0d0","0x0750f010","SMMUL{R}<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|1|0|1|Rd:4|1|1|1|1|Rm:4|0|0|R|1|Rn:4",""
"0x0ff0f0d0","0x0700f010","SMUAD{X}<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|0|0|Rd:4|1|1|1|1|Rm:4|0|0|M|1|Rn:4",""
"0x0ff0f090","0x01600080","SMUL<x><y><c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|0|1|1|0|Rd:4|0|0|0|0|Rm:4|1|M|N|0|Rn:4",""
"0x0fe000f0","0x00c00090","SMULL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|1|0|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
"0x0ff0f0b0","0x012000a0","SMULW<y><c> <Rd>,<Rn>,<Rm>","cond:4|0|0|0|1|0|0|1|0|Rd:4|0|0|0|0|Rm:4|1|M|1|0|Rn:4",""
"0x0ff0f0d0","0x0700f050","SMUSD{X}<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|0|0|Rd:4|1|1|1|1|Rm:4|0|1|M|1|Rn:4",""
"0x0ff00ff0","0x06a00f30","SSAT16<c> <Rd>,#<sat_imm4m1>,<Rn>","cond:4|0|1|1|0|1|0|1|0|sat_imm:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rn:4",""
"0x0fe00030","0x06a00010","SSAT<c> <Rd>,#<sat_imm5m1>,<Rn>{,<shift>}","cond:4|0|1|1|0|1|0|1|sat_imm:5|Rd:4|imm5:5|sh|0|1|Rn:4",""
"0x0ff00ff0","0x06100f50","SSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06100f70","SSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
"0x0ff00ff0","0x06100ff0","SSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
"0x0fd00000","0x08800000","STM<c> <Rn>{!},<registers>","cond:4|1|0|0|0|1|0|W|0|Rn:4|register_list:16",""
"0x0fd00000","0x08000000","STMDA<c> <Rn>{!},<registers>","cond:4|1|0|0|0|0|0|W|0|Rn:4|register_list:16",""
"0x0fd00000","0x09000000","STMDB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|0|0|W|0|Rn:4|register_list:16","SEE PUSH"
"0x0fd00000","0x09800000","STMIB<c> <Rn>{!},<registers>","cond:4|1|0|0|1|1|0|W|0|Rn:4|register_list:16",""
"0x0e500018","0x06000000","STR<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|0|W|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE STRT"
"0x0e500000","0x04000000","STR<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|0|W|0|Rn:4|Rt:4|imm12:12","SEE STRT SEE PUSH"
"0x0e500010","0x06400000","STRB<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!}","cond:4|0|1|1|P|U|1|W|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4","SEE STRBT"
"0x0e500000","0x04400000","STRB<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!}","cond:4|0|1|0|P|U|1|W|0|Rn:4|Rt:4|imm12:12","SEE STRBT"
"0x0f700000","0x04600000","STRBT<c> <Rt>,[<Rn>],#+/-<imm12>","cond:4|0|1|0|0|U|1|1|0|Rn:4|Rt:4|imm12:12",""
"0x0f700010","0x06600000","STRBT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|1|1|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
"0x0e500ff0","0x000000f0","STRD<c> <Rt1>,<Rt2>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|(0)|(0)|(0)|(0)|1|1|1|1|Rm:4",""
"0x0e5000f0","0x004000f0","STRD<c> <Rt1>,<Rt2>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4",""
"0x0ff00ff0","0x01800f90","STREX<c> <Rd>,<Rt>,[<Rn>]","cond:4|0|0|0|1|1|0|0|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
"0x0ff00ff0","0x01c00f90","STREXB<c> <Rd>,<Rt>,[<Rn>]","cond:4|0|0|0|1|1|1|0|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
"0x0ff00ff0","0x01a00f90","STREXD<c> <Rd>,<Rt1>,<Rt2>,[<Rn>]","cond:4|0|0|0|1|1|0|1|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
"0x0ff00ff0","0x01e00f90","STREXH<c> <Rd>,<Rt>,[<Rn>]","cond:4|0|0|0|1|1|1|1|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4",""
"0x0e500ff0","0x000000b0","STRH<c> <Rt>,[<Rn>,+/-<Rm>]{!}","cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4","SEE STRHT"
"0x0e5000f0","0x004000b0","STRH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!}","cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4","SEE STRHT"
"0x0f7000f0","0x006000b0","STRHT<c> <Rt>, [<Rn>] {,#+/-<imm8>}","cond:4|0|0|0|0|U|1|1|0|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4",""
"0x0f700ff0","0x002000b0","STRHT<c> <Rt>, [<Rn>], +/-<Rm>","cond:4|0|0|0|0|U|0|1|0|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4",""
"0x0f700000","0x04200000","STRT<c> <Rt>, [<Rn>] {,#+/-<imm12>}","cond:4|0|1|0|0|U|0|1|0|Rn:4|Rt:4|imm12:12",""
"0x0f700010","0x06200000","STRT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>}","cond:4|0|1|1|0|U|0|1|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4",""
"0x0fe00000","0x02400000","SUB{S}<c> <Rd>,<Rn>,#<const>","cond:4|0|0|1|0|0|1|0|S|Rn:4|Rd:4|imm12:12","SEE ADR SEE SUB (SP minus immediate) SEE SUBS PC, LR and related instructions"
"0x0fe00090","0x00400010","SUB{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|0|0|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4",""
"0x0fe00010","0x00400000","SUB{S}<c> <Rd>,<Rn>,<Rm>{,<shift>}","cond:4|0|0|0|0|0|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions SEE SUB (SP minus register)"
"0x0fef0000","0x024d0000","SUB{S}<c> <Rd>,SP,#<const>","cond:4|0|0|1|0|0|1|0|S|1|1|0|1|Rd:4|imm12:12","SEE SUBS PC, LR and related instructions"
"0x0fef0010","0x004d0000","SUB{S}<c> <Rd>,SP,<Rm>{,<shift>}","cond:4|0|0|0|0|0|1|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4","SEE SUBS PC, LR and related instructions"
"0x0f000000","0x0f000000","SVC<c> #<imm24>","cond:4|1|1|1|1|imm24:24",""
"0x0fb00ff0","0x01000090","SWP{B}<c> <Rt>,<Rm>,[<Rn>]","cond:4|0|0|0|1|0|B|0|0|Rn:4|Rt:4|0|0|0|0|1|0|0|1|Rm:4",""
"0x0ff003f0","0x06800070","SXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE SXTB16"
"0x0ff003f0","0x06a00070","SXTAB<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE SXTB"
"0x0ff003f0","0x06b00070","SXTAH<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|1|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE SXTH"
"0x0fff03f0","0x068f0070","SXTB16<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|0|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
"0x0fff03f0","0x06af0070","SXTB<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
"0x0fff03f0","0x06bf0070","SXTH<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|0|1|1|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
"0x0ff0f000","0x03300000","TEQ<c> <Rn>,#<const>","cond:4|0|0|1|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
"0x0ff0f090","0x01300010","TEQ<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
"0x0ff0f010","0x01300000","TEQ<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
"0x0ff0f000","0x03100000","TST<c> <Rn>,#<const>","cond:4|0|0|1|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12",""
"0x0ff0f090","0x01100010","TST<c> <Rn>,<Rm>,<type> <Rs>","cond:4|0|0|0|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4",""
"0x0ff0f010","0x01100000","TST<c> <Rn>,<Rm>{,<shift>}","cond:4|0|0|0|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4",""
"0x0ff00ff0","0x06500f10","UADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff00ff0","0x06500f90","UADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
"0x0ff00ff0","0x06500f30","UASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0fe00070","0x07e00050","UBFX<c> <Rd>,<Rn>,#<lsb>,#<widthm1>","cond:4|0|1|1|1|1|1|1|widthm1:5|Rd:4|lsb:5|1|0|1|Rn:4",""
"0x0ff0f0f0","0x0730f010","UDIV<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|0|0|1|1|Rd:4|(1)|(1)|(1)|(1)|Rm:4|0|0|0|1|Rn:4",""
"0x0ff00ff0","0x06700f10","UHADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff00ff0","0x06700f90","UHADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
"0x0ff00ff0","0x06700f30","UHASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0ff00ff0","0x06700f50","UHSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06700f70","UHSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
"0x0ff00ff0","0x06700ff0","UHSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
"0x0ff000f0","0x00400090","UMAAL<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|0|1|0|0|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
"0x0fe000f0","0x00a00090","UMLAL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|0|1|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
"0x0fe000f0","0x00800090","UMULL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm>","cond:4|0|0|0|0|1|0|0|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4",""
"0x0ff00ff0","0x06600f10","UQADD16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4",""
"0x0ff00ff0","0x06600f90","UQADD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4",""
"0x0ff00ff0","0x06600f30","UQASX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4",""
"0x0ff00ff0","0x06600f50","UQSAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06600f70","UQSUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
"0x0ff00ff0","0x06600ff0","UQSUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
"0x0ff0f0f0","0x0780f010","USAD8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|1|1|0|0|0|Rd:4|1|1|1|1|Rm:4|0|0|0|1|Rn:4",""
"0x0ff000f0","0x07800010","USADA8<c> <Rd>,<Rn>,<Rm>,<Ra>","cond:4|0|1|1|1|1|0|0|0|Rd:4|Ra:4|Rm:4|0|0|0|1|Rn:4","SEE USAD8"
"0x0ff00ff0","0x06e00f30","USAT16<c> <Rd>,#<sat_imm4>,<Rn>","cond:4|0|1|1|0|1|1|1|0|sat_imm:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rn:4",""
"0x0fe00030","0x06e00010","USAT<c> <Rd>,#<sat_imm5>,<Rn>{,<shift>}","cond:4|0|1|1|0|1|1|1|sat_imm:5|Rd:4|imm5:5|sh|0|1|Rn:4",""
"0x0ff00ff0","0x06500f50","USAX<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4",""
"0x0ff00ff0","0x06500f70","USUB16<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4",""
"0x0ff00ff0","0x06500ff0","USUB8<c> <Rd>,<Rn>,<Rm>","cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4",""
"0x0ff003f0","0x06c00070","UXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|0|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE UXTB16"
"0x0ff003f0","0x06e00070","UXTAB<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE UXTB"
"0x0ff003f0","0x06f00070","UXTAH<c> <Rd>,<Rn>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|1|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4","SEE UXTH"
"0x0fff03f0","0x06cf0070","UXTB16<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|0|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
"0x0fff03f0","0x06ef0070","UXTB<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
"0x0fff03f0","0x06ff0070","UXTH<c> <Rd>,<Rm>{,<rotation>}","cond:4|0|1|1|0|1|1|1|1|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4",""
"0xff800f10","0xf3000110","V<BIF,BIT,BSL> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|op:2|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4","SEE VEOR"
"0xffb00c10","0xf3b00800","V<TBL,TBX>.8 <Dd>, <list_len>, <Dm>","1|1|1|1|0|0|1|1|1|D|1|1|Vn:4|Vd:4|1|0|len:2|N|op|M|0|Vm:4",""
"0xfe800a50","0xf2800040","V<MLA,MLS>.<dt> <Qd>, <Qn>, <Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|0|op|0|F|N|1|M|0|Vm:4","SEE “Related encodings”"
"0xfe800f00","0xf2000600","V<MAX,MIN>.<dt> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|0|N|Q|M|op|Vm:4",""
"0xfe800f10","0xf2000900","V<MLA,MLS>.<dt> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|op|0|D|size:2|Vn:4|Vd:4|1|0|0|1|N|Q|M|0|Vm:4",""
"0xff800f10","0xf2000d10","V<MLA,MLS>.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|op|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|1|Vm:4",""
"0xff800f10","0xf2000f00","V<MAX,MIN>.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|op|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|0|Vm:4",""
"0xff800f10","0xf3000e10","VAC<GE,GT>.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|op|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|1|Vm:4",""
"0x0fb00e10","0x0e000a00","V<MLA,MLS><c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|0|0|Vn:4|Vd:4|1|0|1|sz|N|op|M|0|Vm:4","vfp"
"0xfe801d50","0xf2800800","V<MLA,MLS>L.<dt> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|0|op|0|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xfe801b50","0xf2800240","V<MLA,MLS>L.<dt> <Qd>, <Dn>, <Dm[x]>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|op|1|0|N|1|M|0|Vm:4","SEE “Related encodings”"
"0xfe800f10","0xf2000710","VABA.<dt_Usize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|1|N|Q|M|1|Vm:4",""
"0xfe801f50","0xf2800500","VABAL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|1|0|1|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xfe800f10","0xf2000700","VABD.<dt_Usize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|1|N|Q|M|0|Vm:4",""
"0xffa00f10","0xf3200d00","VABD.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|1|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
"0xfe801f50","0xf2800700","VABDL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|1|1|1|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xffb30b90","0xf3b10300","VABS.<dt_Fsize> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|1|0|Q|M|0|Vm:4",""
"0x0fbf0ed0","0x0eb00ac0","VABS<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|0|Vd:4|1|0|1|sz|1|1|M|0|Vm:4","vfp"
"0xff800f10","0xf2000800","VADD.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|0|Vm:4",""
"0xffa00f10","0xf2000d00","VADD.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
"0x0fb00e50","0x0e300a00","VADD<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|1|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4","vfp"
"0xff810f51","0xf2800400","VADDHN.<dt_Isize> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|1|0|0|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xfe801e50","0xf2800000","VADDL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|0|0|op|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xffb00f10","0xf2000110","VAND <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
"0xffb00f10","0xf2100110","VBIC <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|1|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
"0xfeb800b0","0xf2800030","VBIC.<dt_simd> <Qd>, #<imm_simd1>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|1|1|imm4:4","SEE “Related encodings”"
"0xffb30b90","0xf3b10100","VCEQ.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|1|0|Q|M|0|Vm:4",""
"0xff800f10","0xf3000810","VCEQ.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|1|Vm:4",""
"0xffa00f10","0xf2000e00","VCEQ.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4",""
"0xffb30b90","0xf3b10080","VCGE.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|0|1|Q|M|0|Vm:4",""
"0xfe800f10","0xf2000310","VCGE.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|1|N|Q|M|1|Vm:4",""
"0xffa00f10","0xf3000e00","VCGE.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4",""
"0xffb30b90","0xf3b10000","VCGT.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|0|0|Q|M|0|Vm:4",""
"0xfe800f10","0xf2000300","VCGT.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|1|N|Q|M|0|Vm:4",""
"0xffa00f10","0xf3200e00","VCGT.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|1|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4",""
"0xffb30b90","0xf3b10180","VCLE.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|1|1|Q|M|0|Vm:4",""
"0xffb30f90","0xf3b00400","VCLS.<dt_Isize> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|0|0|Q|M|0|Vm:4",""
"0xffb30b90","0xf3b10200","VCLT.<dt_Fsize> <Qd>, <Qm>, #0","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|0|0|Q|M|0|Vm:4",""
"0xffb30f90","0xf3b00480","VCLZ.<dt_Isize> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|0|1|Q|M|0|Vm:4",""
"0x0fbf0e7f","0x0eb50a40","VCMP{E}<c>.F<32,64> <Sd,Dd>, #0.0","cond:4|1|1|1|0|1|D|1|1|0|1|0|1|Vd:4|1|0|1|sz|E|1|0|0|(0)|(0)|(0)|(0)","vfp"
"0x0fbf0e50","0x0eb40a40","VCMP{E}<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|1|0|0|Vd:4|1|0|1|sz|E|1|M|0|Vm:4","vfp"
"0xffbf0f90","0xf3b00500","VCNT.8 <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|1|0|Q|M|0|Vm:4",""
"0xffbf0e10","0xf3bb0600","VCVT.<Td>.<Tm_1> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|1|op:2|Q|M|0|Vm:4",""
"0xfea00e90","0xf2a00e10","VCVT.<Td>.<Tm_2> <Qd>, <Qm>, #<fbits>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|1|1|op|0|Q|M|1|Vm:4","SEE “Related encodings”"
"0x0fbe0e50","0x0eba0a40","VCVT<c>.F<32,64>.FX<S,U><16,32> <Sd,Dd>, <Sd,Dd>, #<fbits>","cond:4|1|1|1|0|1|D|1|1|1|0|1|U|Vd:4|1|0|1|sz|sx|1|i|0|imm4:4","vfp"
"0x0fbe0e50","0x0ebe0a40","VCVT<c>.FX<S,U><16,32>.F<32,64> <Sd,Dd>, <Sd,Dd>, #<fbits>","cond:4|1|1|1|0|1|D|1|1|1|1|1|U|Vd:4|1|0|1|sz|sx|1|i|0|imm4:4","vfp"
"0xffbf0ed0","0xf3b60600","VCVT.F32.F16 <Qd>, <Dm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|1|1|op|0|0|M|0|Vm:4",""
"0x0fbf0ed0","0x0eb70ac0","VCVT<c>.<F64.F32,F32.F64> <Dd,Sd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|1|1|1|Vd:4|1|0|1|sz|1|1|M|0|Vm:4","vfp"
"0x0fbe0f50","0x0eb20a40","VCVT<B,T><c>.<F32.F16,F16.F32> <Sd>, <Sm>","cond:4|1|1|1|0|1|D|1|1|0|0|1|op|Vd:4|1|0|1|0|T|1|M|0|Vm:4","vfp"
"0x0fbf0e50","0x0eb80a40","VCVT<c>.F<32,64>.<U,S>32 <Sd,Dd>, <Sm>","cond:4|1|1|1|0|1|D|1|1|1|0|0|0|Vd:4|1|0|1|sz|op|1|M|0|Vm:4","vfp"
"0x0fbe0e50","0x0ebc0a40","VCVT<R,><c>.<U,S>32.F<32,64> <Sd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|1|1|0|signed|Vd:4|1|0|1|sz|op|1|M|0|Vm:4","vfp"
"0x0fb00e50","0x0e800a00","VDIV<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|1|D|0|0|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4","vfp"
"0xffb00f90","0xf3b00c00","VDUP.<size_x> <Qd>, <Dm[size_x]>","1|1|1|1|0|0|1|1|1|D|1|1|imm4:4|Vd:4|1|1|0|0|0|Q|M|0|Vm:4",""
"0x0f900f5f","0x0e800b10","VDUP<c>.<size_be> <Qd>, <Rt>","cond:4|1|1|1|0|1|b|Q|0|Vd:4|Rt:4|1|0|1|1|D|0|e|1|(0)|(0)|(0)|(0)",""
"0xffb00f10","0xf3000110","VEOR <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|0|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
"0xffb00010","0xf2b00000","VEXT.8 <Qd>, <Qn>, <Qm>, #<imm4>","1|1|1|1|0|0|1|0|1|D|1|1|Vn:4|Vd:4|imm4:4|N|Q|M|0|Vm:4",""
"0xfe800d10","0xf2000000","VH<ADD,SUB> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|op|0|N|Q|M|0|Vm:4",""
"0xffb00200","0xf4200200","VLD1.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00f00","0xf4a00c00","VLD1.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|0|0|size:2|T|a|Rm:4",""
"0xffb00300","0xf4a00000","VLD1.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|0|0|index_align:4|Rm:4","SEE VLD1 (single element to all lanes)"
"0xffb00400","0xf4200000","VLD2.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00f00","0xf4a00d00","VLD2.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|0|1|size:2|T|a|Rm:4",""
"0xffb00300","0xf4a00100","VLD2.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|0|1|index_align:4|Rm:4","SEE VLD2 (single 2-element structure to all lanes)"
"0xffb00f10","0xf4a00e00","VLD3.<size> <list3t>, [<Rn>]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|1|0|size:2|T|a|Rm:4",""
"0xffb00310","0xf4a00200","VLD3.<size> <list3>, [<Rn>]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|1|0|index_align:4|Rm:4","SEE VLD3 (single 3-element structure to all lanes)"
"0xffb00e20","0xf4200400","VLD3.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00f00","0xf4a00f00","VLD4.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|1|1|size:2|T|a|Rm:4",""
"0xffb00e00","0xf4200000","VLD4.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00300","0xf4a00300","VLD4.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|1|1|index_align:4|Rm:4","SEE VLD4 (single 4-element structure to all lanes)"
"0x0f300e00","0x0d100a00","VLDR<c> <Sd,Dd>, [<Rn>{,#+/-<imm8>}]","cond:4|1|1|0|1|U|D|0|1|Rn:4|Vd:4|1|0|1|sz|imm8:8","vfp"
"0x0fe00fd0","0x0c400b10","VMOV<c> <Dm>, <Rt>, <Rt2>","cond:4|1|1|0|0|0|1|0|op|Rt2:4|Rt:4|1|0|1|1|0|0|M|1|Vm:4",""
"0xffb00f10","0xf2200110","VMOV <Qd>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|0|Vm:4|Vd:4|0|0|0|1|M|Q|M|1|Vm:4","SEE VORR (register)"
"0x0fe00fd0","0x0c400a10","VMOV<c> <Sm>, <Sm1>, <Rt>, <Rt2>","cond:4|1|1|0|0|0|1|0|op|Rt2:4|Rt:4|1|0|1|0|0|0|M|1|Vm:4",""
"0x0ff00f7f","0x0e000a10","VMOV<c> <Sn>, <Rt>","cond:4|1|1|1|0|0|0|0|0|Vn:4|Rt:4|1|0|1|0|N|0|0|1|0|0|0|0","vfp"
"0x0ff00f7f","0x0e100a10","VMOV<c> <Rt>, <Sn>","cond:4|1|1|1|0|0|0|0|1|Vn:4|Rt:4|1|0|1|0|N|0|0|1|0|0|0|0","vfp"
"0xfeb80090","0xf2800010","VMOV.<dt_simd> <Qd>, #<imm_simd>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|op|1|imm4:4","SEE VORR (immediate) SEE “Related encodings”"
"0x0fd00f7f","0x0e100b10","VMOV<c>.32 <Rt>, <Dn[x]>","cond:4|1|1|1|0|0|0|opc1|1|Vn:4|Rt:4|1|0|1|1|N|0|0|1|0|0|0|0","vfp"
"0x0fd00f7f","0x0e000b10","VMOV<c>.32 <Dd[x]>, <Rt>","cond:4|1|1|1|0|0|0|opc1|0|Vd:4|Rt:4|1|0|1|1|D|0|0|1|0|0|0|0","vfp"
"0x0fb00ef0","0x0eb00a00","VMOV<c>.F<32,64> <Sd,Dd>, #<imm_vfp>","cond:4|1|1|1|0|1|D|1|1|imm4H:4|Vd:4|1|0|1|sz|0|0|0|0|imm4L:4","vfp"
"0x0fbf0ed0","0x0eb00a40","VMOV<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|0|Vd:4|1|0|1|sz|0|1|M|0|Vm:4","vfp"
"0xfe871fd0","0xf2800a10","VMOVL.<dt> <Qd>, <Dm>","1|1|1|1|0|0|1|U|1|D|imm3:3|0|0|0|Vd:4|1|0|1|0|0|0|M|1|Vm:4","SEE “Related encodings” SEE VSHLL"
"0xffb30fd1","0xf3b20200","VMOVN.<dt> <Dd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|0|0|0|M|0|Vm:4",""
"0x0fff0fff","0x0ef10a10","VMRS<c> <Rt_nzcv>, FPSCR","cond:4|1|1|1|0|1|1|1|1|0|0|0|1|Rt:4|1|0|1|0|0|0|0|1|0|0|0|0","vfp"
"0x0fff0fff","0x0ee10a10","VMSR<c> FPSCR, <Rt>","cond:4|1|1|1|0|1|1|1|0|0|0|0|1|Rt:4|1|0|1|0|0|0|0|1|0|0|0|0","vfp"
"0xfe800e50","0xf2800840","VMUL.<dt> <Qd>, <Qn>, <Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|0|0|F|N|1|M|0|Vm:4","SEE “Related encodings”"
"0xfe800f10","0xf2000910","VMUL.<dt> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|op|0|D|size:2|Vn:4|Vd:4|1|0|0|1|N|Q|M|1|Vm:4",""
"0xffa00f10","0xf3000d10","VMUL.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|1|Vm:4",""
"0x0fb00e50","0x0e200a00","VMUL<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|0|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4","vfp"
"0xfe801d50","0xf2800c00","VMULL.<dt> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|1|op|0|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xfe801f50","0xf2800a40","VMULL.<dt> <Qd>, <Dn>, <Dm[x]>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|0|1|0|N|1|M|0|Vm:4","SEE “Related encodings”"
"0xffbf0f90","0xf3b00580","VMVN <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|1|1|Q|M|0|Vm:4",""
"0xfeb800b0","0xf2800030","VMVN.<dt> <Qd>, #<imm3>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|1|1|imm4:4","SEE “Related encodings”"
"0xffb30b90","0xf3b10380","VNEG.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|1|1|Q|M|0|Vm:4",""
"0x0fbf0ed0","0x0eb10a40","VNEG<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|1|Vd:4|1|0|1|sz|0|1|M|0|Vm:4","vfp"
"0x0fb00e10","0x0e100a00","VN<MLS,MLA><c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|0|1|Vn:4|Vd:4|1|0|1|sz|N|op|M|0|Vm:4","vfp"
"0x0fb00e50","0x0e200a40","VNMUL<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|0|Vn:4|Vd:4|1|0|1|sz|N|1|M|0|Vm:4","vfp"
"0xffb00f10","0xf2300110","VORN <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|1|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4",""
"0xffb00f10","0xf2200110","VORR <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4","SEE VMOV (register)"
"0xfeb800b0","0xf2800010","VORR.<dt> <Qd>, #<imm3>","1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|0|1|imm4:4","SEE VMOV (immediate)"
"0xfe800f00","0xf2000a00","VP<MIN,MAX>.<dt> <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|1|0|1|0|N|Q|M|op|Vm:4",""
"0xff800f10","0xf3000f00","VP<MIN,MAX>.F32 <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|1|0|D|op|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|0|Vm:4",""
"0xffb30f10","0xf3b00600","VPADAL.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|0|op|Q|M|0|Vm:4",""
"0xff800f10","0xf2000b10","VPADD.<dt> <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|1|Vm:4",""
"0xffa00f10","0xf3000d00","VPADD.F32 <Dd>, <Dn>, <Dm>","1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
"0xffb30f10","0xf3b00200","VPADDL.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|0|1|0|op|Q|M|0|Vm:4",""
"0x0fbf0f00","0x0cbd0a00","VPOP <vlist32>","cond:4|1|1|0|0|1|D|1|1|1|1|0|1|Vd:4|1|0|1|0|imm8:8",""
"0x0fbf0f00","0x0cbd0b00","VPOP <vlist64>","cond:4|1|1|0|0|1|D|1|1|1|1|0|1|Vd:4|1|0|1|1|imm8:8",""
"0x0fbf0f00","0x0d2d0a00","VPUSH<c> <vlist32>","cond:4|1|1|0|1|0|D|1|0|1|1|0|1|Vd:4|1|0|1|0|imm8:8",""
"0x0fbf0f00","0x0d2d0b00","VPUSH<c> <vlist64>","cond:4|1|1|0|1|0|D|1|0|1|1|0|1|Vd:4|1|0|1|1|imm8:8",""
"0xffb30f90","0xf3b00700","VQABS.<dt> <Qd>,<Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|1|0|Q|M|0|Vm:4",""
"0xfe800f10","0xf2000010","VQADD.<dt> <Qd>,<Qn>,<Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|0|0|N|Q|M|1|Vm:4",""
"0xff801d50","0xf2800900","VQD<MLAL,MLSL>.<dt> <Qd>,<Dn>,<Dm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|0|op|1|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xff801b50","0xf2800340","VQD<MLAL,MLSL>.<dt> <Qd>,<Dn>,<Dm[x]>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|op|1|1|N|1|M|0|Vm:4","SEE “Related encodings”"
"0xfe800f50","0xf2800c40","VQDMULH.<dt> <Qd>,<Qn>,<Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|1|0|0|N|1|M|0|Vm:4",""
"0xff800f10","0xf2000b00","VQDMULH.<dt> <Qd>,<Qn>,<Qm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|0|Vm:4",""
"0xff801f50","0xf2800d00","VQDMULL.<dt> <Qd>,<Dn>,<Dm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|1|0|1|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xff801f50","0xf2800b40","VQDMULL.<dt> <Qd>,<Dn>,<Dm[x]>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|0|1|1|N|1|M|0|Vm:4","SEE “Related encodings”"
"0xffb30f11","0xf3b20200","VQMOV{U}N.<type><size> <Dd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|0|op:2|M|0|Vm:4","SEE VMOVN"
"0xffb30f90","0xf3b00780","VQNEG.<dt> <Qd>,<Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|1|1|Q|M|0|Vm:4",""
"0xfe800f50","0xf2800d40","VQRDMULH.<dt> <Qd>,<Qn>,<Dm[x]>","1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|1|0|1|N|1|M|0|Vm:4",""
"0xff800f10","0xf3000b00","VQRDMULH.<dt> <Qd>,<Qn>,<Qm>","1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|0|Vm:4",""
"0xfe800f10","0xf2000510","VQRSHL.<type><size> <Qd>,<Qm>,<Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|1|N|Q|M|1|Vm:4",""
"0xfe800ed1","0xf2800850","VQRSHR{U}N.<type><size> <Dd>,<Qm>,#<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|0|op|0|1|M|1|Vm:4","SEE “Related encodings” SEE VRSHRN"
"0xfe800f10","0xf2000410","VQSHL.<type><size> <Qd>,<Qm>,<Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|0|N|Q|M|1|Vm:4",""
"0xfe800e90","0xf2800690","VQSHL{U}.<type><size> <Qd>,<Qm>,#<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|1|1|op|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xfe800ed1","0xf2800810","VQSHR{U}N.<type><size> <Dd>,<Qm>,#<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|0|op|0|0|M|1|Vm:4","SEE “Related encodings” SEE VSHRN"
"0xfe800f10","0xf2000210","VQSUB.<type><size> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|0|N|Q|M|1|Vm:4",""
"0xff810f51","0xf3800400","VRADDHN.<dt> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|1|D|size:2|Vn:4|Vd:4|0|1|0|0|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xffbf0e90","0xf3bb0400","VRECPE.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|0|F|0|Q|M|0|Vm:4",""
"0xffa00f10","0xf2000f10","VRECPS.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|1|Vm:4",""
"0xffb30e10","0xf3b00000","VREV<n>.<size> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|0|0|op:2|Q|M|0|Vm:4",""
"0xfe800f10","0xf2000100","VRHADD <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|0|1|N|Q|M|0|Vm:4",""
"0xfe800f10","0xf2000500","VRSHL.<type><size> <Qd>, <Qm>, <Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|1|N|Q|M|0|Vm:4",""
"0xfe800f90","0xf2800290","VRSHR.<type><size> <Qd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|1|0|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xff800fd1","0xf2800850","VRSHRN.I<size> <Dd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|1|0|0|0|0|1|M|1|Vm:4","SEE “Related encodings”"
"0xffbf0e90","0xf3bb0480","VRSQRTE.<dt> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|0|F|1|Q|M|0|Vm:4",""
"0xffa00f10","0xf2200f10","VRSQRTS.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|1|Vm:4",""
"0xfe800f90","0xf2800390","VRSRA.<type><size> <Qd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|1|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xff810f51","0xf3800600","VRSUBHN.<dt> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|1|D|size:2|Vn:4|Vd:4|0|1|1|0|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xff800f90","0xf2800590","VSHL.I<size> <Qd>, <Qm>, #<imm6>","1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|0|1|0|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xfe800f10","0xf2000400","VSHL.I<size> <Qd>, <Qm>, <Qn>","1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|0|N|Q|M|0|Vm:4",""
"0xffb31fd0","0xf3b20300","VSHLL.<type><size> <Qd>, <Dm>, #<immsize>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|1|0|0|M|0|Vm:4",""
"0xfe801fd0","0xf2800a10","VSHLL.<type><size> <Qd>, <Dm>, #<imm6>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|1|0|0|0|M|1|Vm:4","SEE “Related encodings” SEE VMOVL"
"0xfe800f90","0xf2800090","VSHR.<type><size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|0|0|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xff800fd1","0xf2800810","VSHRN.I<size_vsn> <Dd>, <Qm>, #<imm_vsn>","1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|1|0|0|0|0|0|M|1|Vm:4","SEE “Related encodings”"
"0xff800f90","0xf3800590","VSLI.<size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|1|1|D|imm6:6|Vd:4|0|1|0|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0x0fbf0ed0","0x0eb10ac0","VSQRT<c>.F<32,64> <Sd,Dd>, <Sm,Dm>","cond:4|1|1|1|0|1|D|1|1|0|0|0|1|Vd:4|1|0|1|sz|1|1|M|0|Vm:4","vfp"
"0xfe800f90","0xf2800190","VSRA.<type_U><size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|0|1|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xff800f90","0xf3800490","VSRI.<size_vs> <Qd>, <Qm>, #<imm_vs>","1|1|1|1|0|0|1|1|1|D|imm6:6|Vd:4|0|1|0|0|L|Q|M|1|Vm:4","SEE “Related encodings”"
"0xffb00200","0xf4000200","VST1.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00300","0xf4800000","VST1.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|0|0|index_align:4|Rm:4",""
"0xffb00400","0xf4000000","VST2.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00300","0xf4800100","VST2.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|0|1|index_align:4|Rm:4",""
"0xffb00300","0xf4800200","VST3.<size> <list4>, [<Rn>]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|1|0|index_align:4|Rm:4",""
"0xffb00e20","0xf4000400","VST3.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00e00","0xf4000000","VST4.<size> <list4>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4","SEE “Related encodings”"
"0xffb00300","0xf4800300","VST4.<size> <list1>, [<Rn>{@<align>}]{!}","1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|1|1|index_align:4|Rm:4",""
"0x0f300e00","0x0d000a00","VSTR<c> <Sd,Dd>, [<Rn>{,#+/-<imm8>}]","cond:4|1|1|0|1|U|D|0|0|Rn:4|Vd:4|1|0|1|sz|imm8:8","vfp"
"0xff800f10","0xf3000800","VSUB.<dt_Isize> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|0|Vm:4",""
"0xffa00f10","0xf2200d00","VSUB.F32 <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|1|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4",""
"0x0fb00e50","0x0e300a40","VSUB<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm>","cond:4|1|1|1|0|0|D|1|1|Vn:4|Vd:4|1|0|1|sz|N|1|M|0|Vm:4","vfp"
"0xff810f51","0xf2800600","VSUBHN.<dt_Isize> <Dd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|1|1|0|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xfe801e50","0xf2800200","VSUBL.<dt_Usize> <Qd>, <Dn>, <Dm>","1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|0|1|op|N|0|M|0|Vm:4","SEE “Related encodings”"
"0xffbf0f90","0xf3b20000","VSWP <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|0|0|Q|M|0|Vm:4",""
"0xffb30f90","0xf3b20080","VTRN.<size_n> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|0|1|Q|M|0|Vm:4",""
"0xff800f10","0xf2000810","VTST.<size_n> <Qd>, <Qn>, <Qm>","1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|1|Vm:4",""
"0xffb30f90","0xf3b20100","VUZP.<size_n> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|1|0|Q|M|0|Vm:4",""
"0xffb30f90","0xf3b20180","VZIP.<size_n> <Qd>, <Qm>","1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|1|1|Q|M|0|Vm:4",""
"0x0fffffff","0x0320f002","WFE<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|1|0",""
"0x0fffffff","0x0320f003","WFI<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|1|1",""
"0x0fffffff","0x0320f001","YIELD<c>","cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|0|1",""
"0xffffffff","0xf7fabcfd","UNDEF","1|1|1|1|0|1|1|1|1|1|1|1|1|0|1|0|1|0|1|1|1|1|0|0|1|1|1|1|1|1|0|1",""
1 # arm instruction description version 0.01.
2 # https://golang.org/x/arch/arm
3 #
4 # This file contains a block of comment lines each beginning with #
5 # followed by entries in CSV format. All the # comments are at the top
6 # of the file so a reader can skip past the comments and hand the
7 # rest of the file to a standard CSV reader.
8 #
9 # Each line in the CSV section contains 5 fields:
10 #
11 # mask value mnemonic encoding tags
12 #
13 # The mnemonic and encoding correspond to the notation used in the
14 # ARM Architecture Reference Manual.
15 #
16 # A particular instruction word w matches a line if w&mask == value.
17 # The mask and value are computed from a combination of the encodings
18 # and the additional pseudocode restrictions given in the manual
19 # for each instruction.
20 #
21 # Multiple lines may match a particular instruction word.
22 # The tags specify which line takes priority in these cases
23 # as a partial order. Eventually the plan is that a tag of the form
24 # 'name:x' marks a line with the name x and a tag of the form
25 # 'except:x' says that the line on which the tag appears is of lower
26 # priority than the ones marked with name x.
27 # For now however a tag of the form 'SEE XXX' is taken from the
28 # manual and is like 'except:x' but without a rigorous resolution for
29 # the x. So far we've been able to just sort instructions into two classes:
30 # those with SEE and those without.
31 #
32 # The tag 'pseudo' indicates that this instruction is an alternate name
33 # for another encoding and should be ignored during disassembly.
34 #
35 # This file was generated by a program reading the PDF version of
36 # the manual but it was then hand edited to make corrections.
37 # The eventual plan is for the generator to write the
38 # file directly using the PDF and a list of modifications but
39 # no hand editing. Then when a new version of the manual comes out
40 # updating the list should be as easy as downloading the new PDF
41 # and invoking the generator. However we are not there yet.
42 #
43 0x0fe00000 0x02a00000 ADC{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|1|0|1|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
44 0x0fe00090 0x00a00010 ADC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
45 0x0fe00010 0x00a00000 ADC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
46 0x0fe00000 0x02800000 ADD{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|1|0|0|S|Rn:4|Rd:4|imm12:12 SEE ADR SEE ADD (SP plus immediate) SEE SUBS PC, LR and related instructions
47 0x0fe00090 0x00800010 ADD{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
48 0x0fe00010 0x00800000 ADD{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions SEE ADD (SP plus register)
49 0x0fef0000 0x028d0000 ADD{S}<c> <Rd>,SP,#<const> cond:4|0|0|1|0|1|0|0|S|1|1|0|1|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
50 0x0fef0010 0x008d0000 ADD{S}<c> <Rd>,SP,<Rm>{,<shift>} cond:4|0|0|0|0|1|0|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
51 0x0fff0000 0x028f0000 ADR<c> <Rd>,<label+12> cond:4|0|0|1|0|1|0|0|0|1|1|1|1|Rd:4|imm12:12 pseudo
52 0x0fff0000 0x024f0000 ADR<c> <Rd>,<label-12> cond:4|0|0|1|0|0|1|0|0|1|1|1|1|Rd:4|imm12:12 pseudo
53 0x0fe00000 0x02000000 AND{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|0|0|0|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
54 0x0fe00090 0x00000010 AND{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
55 0x0fe00010 0x00000000 AND{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
56 0x0fef0070 0x01a00040 ASR{S}<c> <Rd>,<Rm>,#<imm5_32> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|0|0|Rm:4
57 0x0fef00f0 0x01a00050 ASR{S}<c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|0|1|Rn:4
58 0x0f000000 0x0a000000 B<c> <label24> cond:4|1|0|1|0|imm24:24
59 0x0fe0007f 0x07c0001f BFC<c> <Rd>,#<lsb>,#<width> cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|1|1|1|1
60 0x0fe00070 0x07c00010 BFI<c> <Rd>,<Rn>,#<lsb>,#<width> cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|Rn:4 SEE BFC
61 0x0fe00000 0x03c00000 BIC{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|1|1|1|0|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
62 0x0fe00090 0x01c00010 BIC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
63 0x0fe00010 0x01c00000 BIC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
64 0x0ff000f0 0x01200070 BKPT<c> #<imm12+4> cond:4|0|0|0|1|0|0|1|0|imm12:12|0|1|1|1|imm4:4
65 0x0f000000 0x0b000000 BL<c> <label24> cond:4|1|0|1|1|imm24:24
66 0xfe000000 0xfa000000 BLX <label24H> 1|1|1|1|1|0|1|H|imm24:24
67 0x0ffffff0 0x012fff30 BLX<c> <Rm> cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
68 0x0ffffff0 0x012fff10 BX<c> <Rm> cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
69 0x0ffffff0 0x012fff20 BXJ<c> <Rm> cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4
70 0xffffffff 0xf57ff01f CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1)
71 0x0fff0ff0 0x016f0f10 CLZ<c> <Rd>,<Rm> cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
72 0x0ff0f000 0x03700000 CMN<c> <Rn>,#<const> cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12
73 0x0ff0f090 0x01700010 CMN<c> <Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4
74 0x0ff0f010 0x01700000 CMN<c> <Rn>,<Rm>{,<shift>} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4
75 0x0ff0f000 0x03500000 CMP<c> <Rn>,#<const> cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12
76 0x0ff0f090 0x01500010 CMP<c> <Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4
77 0x0ff0f010 0x01500000 CMP<c> <Rn>,<Rm>{,<shift>} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4
78 0x0ffffff0 0x0320f0f0 DBG<c> #<option> cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|1|1|1|1|option:4
79 0xfffffff0 0xf57ff050 DMB #<option> 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|0|1|option:4
80 0xfffffff0 0xf57ff040 DSB #<option> 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|0|0|option:4
81 0x0fe00000 0x02200000 EOR{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|0|0|1|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
82 0x0fe00090 0x00200010 EOR{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|0|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
83 0x0fe00010 0x00200000 EOR{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|0|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
84 0xfffffff0 0xf57ff060 ISB #<option> 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|1|1|0|option:4
85 0x0fd00000 0x08900000 LDM<c> <Rn>{!},<registers> cond:4|1|0|0|0|1|0|W|1|Rn:4|register_list:16 SEE POP
86 0x0fd00000 0x08100000 LDMDA<c> <Rn>{!},<registers> cond:4|1|0|0|0|0|0|W|1|Rn:4|register_list:16
87 0x0fd00000 0x09100000 LDMDB<c> <Rn>{!},<registers> cond:4|1|0|0|1|0|0|W|1|Rn:4|register_list:16
88 0x0fd00000 0x09900000 LDMIB<c> <Rn>{!},<registers> cond:4|1|0|0|1|1|0|W|1|Rn:4|register_list:16
89 0x0f7f0000 0x051f0000 LDR<c> <Rt>,<label+/-12> cond:4|0|1|0|(1)|U|0|(0)|1|1|1|1|1|Rt:4|imm12:12
90 0x0e500010 0x06100000 LDR<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!} cond:4|0|1|1|P|U|0|W|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4 SEE LDRT
91 0x0e500000 0x04100000 LDR<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!} cond:4|0|1|0|P|U|0|W|1|Rn:4|Rt:4|imm12:12 SEE LDR (literal) SEE LDRT SEE POP
92 0x0f7f0000 0x055f0000 LDRB<c> <Rt>,<label+/-12> cond:4|0|1|0|(1)|U|1|(0)|1|1|1|1|1|Rt:4|imm12:12
93 0x0e500010 0x06500000 LDRB<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!} cond:4|0|1|1|P|U|1|W|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4 SEE LDRBT
94 0x0e500000 0x04500000 LDRB<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!} cond:4|0|1|0|P|U|1|W|1|Rn:4|Rt:4|imm12:12 SEE LDRB (literal) SEE LDRBT
95 0x0f700000 0x04700000 LDRBT<c> <Rt>,[<Rn>],#+/-<imm12> cond:4|0|1|0|0|U|1|1|1|Rn:4|Rt:4|imm12:12
96 0x0f700010 0x06700000 LDRBT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>} cond:4|0|1|1|0|U|1|1|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4
97 0x0f7f10f0 0x014f00d0 LDRD<c> <Rt1>,<Rt2>,<label+/-4+4> cond:4|0|0|0|(1)|U|1|(0)|0|1|1|1|1|Rt:4|imm4H:4|1|1|0|1|imm4L:4 pseudo
98 0x0e500ff0 0x000000d0 LDRD<c> <Rt1>,<Rt2>,[<Rn>,+/-<Rm>]{!} cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|(0)|(0)|(0)|(0)|1|1|0|1|Rm:4
99 0x0e5000f0 0x004000d0 LDRD<c> <Rt1>,<Rt2>,[<Rn>{,#+/-<imm8>}]{!} cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4 SEE LDRD (literal)
100 0x0ff00fff 0x01900f9f LDREX<c> <Rt>,[<Rn>] cond:4|0|0|0|1|1|0|0|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)
101 0x0ff00fff 0x01d00f9f LDREXB<c> <Rt>, [<Rn>] cond:4|0|0|0|1|1|1|0|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)
102 0x0ff00fff 0x01b00f9f LDREXD<c> <Rt1>,<Rt2>,[<Rn>] cond:4|0|0|0|1|1|0|1|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)
103 0x0ff00fff 0x01f00f9f LDREXH<c> <Rt>, [<Rn>] cond:4|0|0|0|1|1|1|1|1|Rn:4|Rt:4|(1)|(1)|(1)|(1)|1|0|0|1|(1)|(1)|(1)|(1)
104 0x0f7f00f0 0x015f00b0 LDRH<c> <Rt>,<label+/-4+4> cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|0|1|1|imm4L:4 pseudo
105 0x0e500ff0 0x001000b0 LDRH<c> <Rt>,[<Rn>,+/-<Rm>]{!} cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4 SEE LDRHT
106 0x0e5000f0 0x005000b0 LDRH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!} cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4 SEE LDRH literal SEE LDRHT
107 0x0f7000f0 0x007000b0 LDRHT<c> <Rt>, [<Rn>] {,#+/-<imm8>} cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4
108 0x0f700ff0 0x003000b0 LDRHT<c> <Rt>, [<Rn>], +/-<Rm> cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4
109 0x0f7f00f0 0x015f00d0 LDRSB<c> <Rt>,<label+/-4+4> cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|1|0|1|imm4L:4 pseudo
110 0x0e500ff0 0x001000d0 LDRSB<c> <Rt>,[<Rn>,+/-<Rm>]{!} cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|1|0|1|Rm:4 SEE LDRSBT
111 0x0e5000f0 0x005000d0 LDRSB<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!} cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4 SEE LDRSB literal SEE LDRSBT
112 0x0f7000f0 0x007000d0 LDRSBT<c> <Rt>, [<Rn>] {,#+/-<imm8>} cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|1|0|1|imm4L:4
113 0x0f700ff0 0x003000d0 LDRSBT<c> <Rt>, [<Rn>], +/-<Rm> cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|1|0|1|Rm:4
114 0x0f7f00f0 0x015f00f0 LDRSH<c> <Rt>,<label+/-4+4> cond:4|0|0|0|1|U|1|0|1|1|1|1|1|Rt:4|imm4H:4|1|1|1|1|imm4L:4 pseudo
115 0x0e500ff0 0x001000f0 LDRSH<c> <Rt>,[<Rn>,+/-<Rm>]{!} cond:4|0|0|0|P|U|0|W|1|Rn:4|Rt:4|0|0|0|0|1|1|1|1|Rm:4 SEE LDRSHT
116 0x0e5000f0 0x005000f0 LDRSH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!} cond:4|0|0|0|P|U|1|W|1|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4 SEE LDRSH literal SEE LDRSHT
117 0x0f7000f0 0x007000f0 LDRSHT<c> <Rt>, [<Rn>] {,#+/-<imm8>} cond:4|0|0|0|0|U|1|1|1|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4
118 0x0f700ff0 0x003000f0 LDRSHT<c> <Rt>, [<Rn>], +/-<Rm> cond:4|0|0|0|0|U|0|1|1|Rn:4|Rt:4|0|0|0|0|1|1|1|1|Rm:4
119 0x0f700000 0x04300000 LDRT<c> <Rt>, [<Rn>] {,#+/-<imm12>} cond:4|0|1|0|0|U|0|1|1|Rn:4|Rt:4|imm12:12
120 0x0f700010 0x06300000 LDRT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>} cond:4|0|1|1|0|U|0|1|1|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4
121 0x0fef0070 0x01a00000 LSL{S}<c> <Rd>,<Rm>,#<imm5_nz> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|0|0|0|Rm:4 SEE MOV register
122 0x0fef00f0 0x01a00010 LSL{S}<c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|0|0|1|Rn:4
123 0x0fef0070 0x01a00020 LSR{S}<c> <Rd>,<Rm>,#<imm5_32> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|0|1|0|Rm:4
124 0x0fef00f0 0x01a00030 LSR{S}<c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|0|1|1|Rn:4
125 0x0fe000f0 0x00200090 MLA{S}<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|0|0|0|0|0|1|S|Rd:4|Ra:4|Rm:4|1|0|0|1|Rn:4
126 0x0ff000f0 0x00600090 MLS<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|0|0|0|0|1|1|0|Rd:4|Ra:4|Rm:4|1|0|0|1|Rn:4
127 0x0ff00000 0x03400000 MOVT<c> <Rd>,#<imm12+4> cond:4|0|0|1|1|0|1|0|0|imm4:4|Rd:4|imm12:12
128 0x0ff00000 0x03000000 MOVW<c> <Rd>,#<imm12+4> cond:4|0|0|1|1|0|0|0|0|imm4:4|Rd:4|imm12:12
129 0x0fef0000 0x03a00000 MOV{S}<c> <Rd>,#<const> cond:4|0|0|1|1|1|0|1|S|0|0|0|0|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
130 0x0fef0ff0 0x01a00000 MOV{S}<c> <Rd>,<Rm> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|0|0|0|0|0|0|0|0|Rm:4 SEE SUBS PC, LR and related instructions
131 0x0fff0fff 0x010f0000 MRS<c> <Rd>,APSR cond:4|0|0|0|1|0|0|0|0|(1)|(1)|(1)|(1)|Rd:4|(0)|(0)|(0)|(0)|0|0|0|0|(0)|(0)|(0)|(0)
132 0x0ffffff0 0x012cf000 MSR<c> APSR,<Rn> cond:4|0|0|0|1|0|0|1|0|1|1|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|Rn:4
133 0x0ffff000 0x032cf000 MSR<c> APSR,#<const> cond:4|0|0|1|1|0|0|1|0|1|1|0|0|(1)|(1)|(1)|(1)|imm12:12
134 0x0fe0f0f0 0x00000090 MUL{S}<c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|0|0|0|0|S|Rd:4|(0)|(0)|(0)|(0)|Rm:4|1|0|0|1|Rn:4
135 0x0fef0000 0x03e00000 MVN{S}<c> <Rd>,#<const> cond:4|0|0|1|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
136 0x0fef0090 0x01e00010 MVN{S}<c> <Rd>,<Rm>,<type> <Rs> cond:4|0|0|0|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|Rs:4|0|type:2|1|Rm:4
137 0x0fef0010 0x01e00000 MVN{S}<c> <Rd>,<Rm>{,<shift>} cond:4|0|0|0|1|1|1|1|S|(0)|(0)|(0)|(0)|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
138 0x0fffffff 0x0320f000 NOP<c> cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|0|0
139 0x0fe00000 0x03800000 ORR{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|1|1|0|0|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
140 0x0fe00090 0x01800010 ORR{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|1|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
141 0x0fe00010 0x01800000 ORR{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|1|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
142 0x0ff00030 0x06800010 PKH<BT,TB><c> <Rd>,<Rn>,<Rm>{,LSL #<imm5>} cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|imm5:5|tb|0|1|Rm:4
143 0xff7ff000 0xf55ff000 PLD <label+/-12> 1|1|1|1|0|1|0|1|U|(1)|0|1|1|1|1|1|(1)|(1)|(1)|(1)|imm12:12
144 0xff30f000 0xf510f000 PLD{W} [<Rn>,#+/-<imm12>] 1|1|1|1|0|1|0|1|U|R|0|1|Rn:4|(1)|(1)|(1)|(1)|imm12:12 SEE PLD (literal)
145 0xff30f010 0xf710f000 PLD{W} [<Rn>,+/-<Rm>{, <shift>}] 1|1|1|1|0|1|1|1|U|R|0|1|Rn:4|(1)|(1)|(1)|(1)|imm5:5|type:2|0|Rm:4
146 0xff70f000 0xf450f000 PLI [<Rn>,#+/-<imm12>] 1|1|1|1|0|1|0|0|U|1|0|1|Rn:4|(1)|(1)|(1)|(1)|imm12:12
147 0xff70f010 0xf650f000 PLI [<Rn>,+/-<Rm>{, <shift>}] 1|1|1|1|0|1|1|0|U|1|0|1|Rn:4|(1)|(1)|(1)|(1)|imm5:5|type:2|0|Rm:4
148 0x0fff0000 0x08bd0000 POP<c> <registers2> cond:4|1|0|0|0|1|0|1|1|1|1|0|1|register_list:16
149 0x0fff0fff 0x049d0004 POP<c> <registers1> cond:4|0|1|0|0|1|0|0|1|1|1|0|1|Rt:4|0|0|0|0|0|0|0|0|0|1|0|0
150 0x0fff0000 0x092d0000 PUSH<c> <registers2> cond:4|1|0|0|1|0|0|1|0|1|1|0|1|register_list:16
151 0x0fff0fff 0x052d0004 PUSH<c> <registers1> cond:4|0|1|0|1|0|0|1|0|1|1|0|1|Rt:4|0|0|0|0|0|0|0|0|0|1|0|0
152 0x0ff00ff0 0x06200f10 QADD16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
153 0x0ff00ff0 0x06200f90 QADD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4
154 0x0ff00ff0 0x01000050 QADD<c> <Rd>,<Rm>,<Rn> cond:4|0|0|0|1|0|0|0|0|Rn:4|Rd:4|(0)|(0)|(0)|(0)|0|1|0|1|Rm:4
155 0x0ff00ff0 0x06200f30 QASX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
156 0x0ff00ff0 0x01400050 QDADD<c> <Rd>,<Rm>,<Rn> cond:4|0|0|0|1|0|1|0|0|Rn:4|Rd:4|(0)|(0)|(0)|(0)|0|1|0|1|Rm:4
157 0x0ff00ff0 0x01600050 QDSUB<c> <Rd>,<Rm>,<Rn> cond:4|0|0|0|1|0|1|1|0|Rn:4|Rd:4|0|0|0|0|0|1|0|1|Rm:4
158 0x0ff00ff0 0x06200f50 QSAX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4
159 0x0ff00ff0 0x06200f70 QSUB16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4
160 0x0ff00ff0 0x06200ff0 QSUB8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4
161 0x0ff00ff0 0x01200050 QSUB<c> <Rd>,<Rm>,<Rn> cond:4|0|0|0|1|0|0|1|0|Rn:4|Rd:4|0|0|0|0|0|1|0|1|Rm:4
162 0x0fff0ff0 0x06ff0f30 RBIT<c> <Rd>,<Rm> cond:4|0|1|1|0|1|1|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
163 0x0fff0ff0 0x06bf0fb0 REV16<c> <Rd>,<Rm> cond:4|0|1|1|0|1|0|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4
164 0x0fff0ff0 0x06bf0f30 REV<c> <Rd>,<Rm> cond:4|0|1|1|0|1|0|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
165 0x0fff0ff0 0x06ff0fb0 REVSH<c> <Rd>,<Rm> cond:4|0|1|1|0|1|1|1|1|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4
166 0x0fef0070 0x01a00060 ROR{S}<c> <Rd>,<Rm>,#<imm5> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|1|0|Rm:4 SEE RRX
167 0x0fef00f0 0x01a00070 ROR{S}<c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|1|1|Rn:4
168 0x0fef0ff0 0x01a00060 RRX{S}<c> <Rd>,<Rm> cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|0|0|0|0|0|1|1|0|Rm:4
169 0x0fe00000 0x02600000 RSB{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|0|1|1|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
170 0x0fe00090 0x00600010 RSB{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|0|1|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
171 0x0fe00010 0x00600000 RSB{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|0|1|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
172 0x0fe00000 0x02e00000 RSC{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|1|1|1|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
173 0x0fe00090 0x00e00010 RSC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|1|1|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
174 0x0fe00010 0x00e00000 RSC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|1|1|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
175 0x0ff00ff0 0x06100f10 SADD16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
176 0x0ff00ff0 0x06100f90 SADD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4
177 0x0ff00ff0 0x06100f30 SASX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
178 0x0fe00000 0x02c00000 SBC{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|1|1|0|S|Rn:4|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
179 0x0fe00090 0x00c00010 SBC{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
180 0x0fe00010 0x00c00000 SBC{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
181 0x0fe00070 0x07a00050 SBFX<c> <Rd>,<Rn>,#<lsb>,#<widthm1> cond:4|0|1|1|1|1|0|1|widthm1:5|Rd:4|lsb:5|1|0|1|Rn:4
182 0x0ff0f0f0 0x0710f010 SDIV<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|1|0|0|0|1|Rd:4|(1)|(1)|(1)|(1)|Rm:4|0|0|0|1|Rn:4
183 0x0ff00ff0 0x06800fb0 SEL<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|1|1|Rm:4
184 0xfffffdff 0xf1010000 SETEND <endian_specifier> 1|1|1|1|0|0|0|1|0|0|0|0|0|0|0|1|0|0|0|0|0|0|E|(0)|(0)|(0)|(0)|(0)|(0)|(0)|(0)|(0)
185 0x0fffffff 0x0320f004 SEV<c> cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|1|0|0
186 0x0ff00ff0 0x06300f10 SHADD16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
187 0x0ff00ff0 0x06300f90 SHADD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4
188 0x0ff00ff0 0x06300f30 SHASX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
189 0x0ff00ff0 0x06300f50 SHSAX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4
190 0x0ff00ff0 0x06300f70 SHSUB16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4
191 0x0ff00ff0 0x06300ff0 SHSUB8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4
192 0x0ff00090 0x01000080 SMLA<x><y><c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|0|0|1|0|0|0|0|Rd:4|Ra:4|Rm:4|1|M|N|0|Rn:4
193 0x0ff000d0 0x07000010 SMLAD{X}<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|1|1|1|0|0|0|0|Rd:4|Ra:4|Rm:4|0|0|M|1|Rn:4 SEE SMUAD
194 0x0ff00090 0x01400080 SMLAL<x><y><c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|0|0|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|1|M|N|0|Rn:4
195 0x0ff000d0 0x07400010 SMLALD{X}<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|1|1|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|0|0|M|1|Rn:4
196 0x0fe000f0 0x00e00090 SMLAL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|0|0|0|1|1|1|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4
197 0x0ff000b0 0x01200080 SMLAW<y><c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|0|0|1|0|0|1|0|Rd:4|Ra:4|Rm:4|1|M|0|0|Rn:4
198 0x0ff000d0 0x07000050 SMLSD{X}<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|1|1|1|0|0|0|0|Rd:4|Ra:4|Rm:4|0|1|M|1|Rn:4 SEE SMUSD
199 0x0ff000d0 0x07400050 SMLSLD{X}<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|1|1|1|0|1|0|0|RdHi:4|RdLo:4|Rm:4|0|1|M|1|Rn:4
200 0x0ff000d0 0x07500010 SMMLA{R}<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|1|1|1|0|1|0|1|Rd:4|Ra:4|Rm:4|0|0|R|1|Rn:4 SEE SMMUL
201 0x0ff000d0 0x075000d0 SMMLS{R}<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|1|1|1|0|1|0|1|Rd:4|Ra:4|Rm:4|1|1|R|1|Rn:4
202 0x0ff0f0d0 0x0750f010 SMMUL{R}<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|1|0|1|0|1|Rd:4|1|1|1|1|Rm:4|0|0|R|1|Rn:4
203 0x0ff0f0d0 0x0700f010 SMUAD{X}<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|1|0|0|0|0|Rd:4|1|1|1|1|Rm:4|0|0|M|1|Rn:4
204 0x0ff0f090 0x01600080 SMUL<x><y><c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|1|0|1|1|0|Rd:4|0|0|0|0|Rm:4|1|M|N|0|Rn:4
205 0x0fe000f0 0x00c00090 SMULL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|0|0|0|1|1|0|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4
206 0x0ff0f0b0 0x012000a0 SMULW<y><c> <Rd>,<Rn>,<Rm> cond:4|0|0|0|1|0|0|1|0|Rd:4|0|0|0|0|Rm:4|1|M|1|0|Rn:4
207 0x0ff0f0d0 0x0700f050 SMUSD{X}<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|1|0|0|0|0|Rd:4|1|1|1|1|Rm:4|0|1|M|1|Rn:4
208 0x0ff00ff0 0x06a00f30 SSAT16<c> <Rd>,#<sat_imm4m1>,<Rn> cond:4|0|1|1|0|1|0|1|0|sat_imm:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rn:4
209 0x0fe00030 0x06a00010 SSAT<c> <Rd>,#<sat_imm5m1>,<Rn>{,<shift>} cond:4|0|1|1|0|1|0|1|sat_imm:5|Rd:4|imm5:5|sh|0|1|Rn:4
210 0x0ff00ff0 0x06100f50 SSAX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4
211 0x0ff00ff0 0x06100f70 SSUB16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4
212 0x0ff00ff0 0x06100ff0 SSUB8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|0|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4
213 0x0fd00000 0x08800000 STM<c> <Rn>{!},<registers> cond:4|1|0|0|0|1|0|W|0|Rn:4|register_list:16
214 0x0fd00000 0x08000000 STMDA<c> <Rn>{!},<registers> cond:4|1|0|0|0|0|0|W|0|Rn:4|register_list:16
215 0x0fd00000 0x09000000 STMDB<c> <Rn>{!},<registers> cond:4|1|0|0|1|0|0|W|0|Rn:4|register_list:16 SEE PUSH
216 0x0fd00000 0x09800000 STMIB<c> <Rn>{!},<registers> cond:4|1|0|0|1|1|0|W|0|Rn:4|register_list:16
217 0x0e500018 0x06000000 STR<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!} cond:4|0|1|1|P|U|0|W|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4 SEE STRT
218 0x0e500000 0x04000000 STR<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!} cond:4|0|1|0|P|U|0|W|0|Rn:4|Rt:4|imm12:12 SEE STRT SEE PUSH
219 0x0e500010 0x06400000 STRB<c> <Rt>,[<Rn>,+/-<Rm>{, <shift>}]{!} cond:4|0|1|1|P|U|1|W|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4 SEE STRBT
220 0x0e500000 0x04400000 STRB<c> <Rt>,[<Rn>{,#+/-<imm12>}]{!} cond:4|0|1|0|P|U|1|W|0|Rn:4|Rt:4|imm12:12 SEE STRBT
221 0x0f700000 0x04600000 STRBT<c> <Rt>,[<Rn>],#+/-<imm12> cond:4|0|1|0|0|U|1|1|0|Rn:4|Rt:4|imm12:12
222 0x0f700010 0x06600000 STRBT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>} cond:4|0|1|1|0|U|1|1|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4
223 0x0e500ff0 0x000000f0 STRD<c> <Rt1>,<Rt2>,[<Rn>,+/-<Rm>]{!} cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|(0)|(0)|(0)|(0)|1|1|1|1|Rm:4
224 0x0e5000f0 0x004000f0 STRD<c> <Rt1>,<Rt2>,[<Rn>{,#+/-<imm8>}]{!} cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|1|1|1|imm4L:4
225 0x0ff00ff0 0x01800f90 STREX<c> <Rd>,<Rt>,[<Rn>] cond:4|0|0|0|1|1|0|0|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4
226 0x0ff00ff0 0x01c00f90 STREXB<c> <Rd>,<Rt>,[<Rn>] cond:4|0|0|0|1|1|1|0|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4
227 0x0ff00ff0 0x01a00f90 STREXD<c> <Rd>,<Rt1>,<Rt2>,[<Rn>] cond:4|0|0|0|1|1|0|1|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4
228 0x0ff00ff0 0x01e00f90 STREXH<c> <Rd>,<Rt>,[<Rn>] cond:4|0|0|0|1|1|1|1|0|Rn:4|Rd:4|1|1|1|1|1|0|0|1|Rt:4
229 0x0e500ff0 0x000000b0 STRH<c> <Rt>,[<Rn>,+/-<Rm>]{!} cond:4|0|0|0|P|U|0|W|0|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4 SEE STRHT
230 0x0e5000f0 0x004000b0 STRH<c> <Rt>,[<Rn>{,#+/-<imm8>}]{!} cond:4|0|0|0|P|U|1|W|0|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4 SEE STRHT
231 0x0f7000f0 0x006000b0 STRHT<c> <Rt>, [<Rn>] {,#+/-<imm8>} cond:4|0|0|0|0|U|1|1|0|Rn:4|Rt:4|imm4H:4|1|0|1|1|imm4L:4
232 0x0f700ff0 0x002000b0 STRHT<c> <Rt>, [<Rn>], +/-<Rm> cond:4|0|0|0|0|U|0|1|0|Rn:4|Rt:4|0|0|0|0|1|0|1|1|Rm:4
233 0x0f700000 0x04200000 STRT<c> <Rt>, [<Rn>] {,#+/-<imm12>} cond:4|0|1|0|0|U|0|1|0|Rn:4|Rt:4|imm12:12
234 0x0f700010 0x06200000 STRT<c> <Rt>,[<Rn>],+/-<Rm>{, <shift>} cond:4|0|1|1|0|U|0|1|0|Rn:4|Rt:4|imm5:5|type:2|0|Rm:4
235 0x0fe00000 0x02400000 SUB{S}<c> <Rd>,<Rn>,#<const> cond:4|0|0|1|0|0|1|0|S|Rn:4|Rd:4|imm12:12 SEE ADR SEE SUB (SP minus immediate) SEE SUBS PC, LR and related instructions
236 0x0fe00090 0x00400010 SUB{S}<c> <Rd>,<Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|0|0|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4
237 0x0fe00010 0x00400000 SUB{S}<c> <Rd>,<Rn>,<Rm>{,<shift>} cond:4|0|0|0|0|0|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions SEE SUB (SP minus register)
238 0x0fef0000 0x024d0000 SUB{S}<c> <Rd>,SP,#<const> cond:4|0|0|1|0|0|1|0|S|1|1|0|1|Rd:4|imm12:12 SEE SUBS PC, LR and related instructions
239 0x0fef0010 0x004d0000 SUB{S}<c> <Rd>,SP,<Rm>{,<shift>} cond:4|0|0|0|0|0|1|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4 SEE SUBS PC, LR and related instructions
240 0x0f000000 0x0f000000 SVC<c> #<imm24> cond:4|1|1|1|1|imm24:24
241 0x0fb00ff0 0x01000090 SWP{B}<c> <Rt>,<Rm>,[<Rn>] cond:4|0|0|0|1|0|B|0|0|Rn:4|Rt:4|0|0|0|0|1|0|0|1|Rm:4
242 0x0ff003f0 0x06800070 SXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|0|0|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4 SEE SXTB16
243 0x0ff003f0 0x06a00070 SXTAB<c> <Rd>,<Rn>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|0|1|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4 SEE SXTB
244 0x0ff003f0 0x06b00070 SXTAH<c> <Rd>,<Rn>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|0|1|1|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4 SEE SXTH
245 0x0fff03f0 0x068f0070 SXTB16<c> <Rd>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|0|0|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4
246 0x0fff03f0 0x06af0070 SXTB<c> <Rd>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|0|1|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4
247 0x0fff03f0 0x06bf0070 SXTH<c> <Rd>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|0|1|1|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4
248 0x0ff0f000 0x03300000 TEQ<c> <Rn>,#<const> cond:4|0|0|1|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12
249 0x0ff0f090 0x01300010 TEQ<c> <Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4
250 0x0ff0f010 0x01300000 TEQ<c> <Rn>,<Rm>{,<shift>} cond:4|0|0|0|1|0|0|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4
251 0x0ff0f000 0x03100000 TST<c> <Rn>,#<const> cond:4|0|0|1|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12
252 0x0ff0f090 0x01100010 TST<c> <Rn>,<Rm>,<type> <Rs> cond:4|0|0|0|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4
253 0x0ff0f010 0x01100000 TST<c> <Rn>,<Rm>{,<shift>} cond:4|0|0|0|1|0|0|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4
254 0x0ff00ff0 0x06500f10 UADD16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
255 0x0ff00ff0 0x06500f90 UADD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4
256 0x0ff00ff0 0x06500f30 UASX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
257 0x0fe00070 0x07e00050 UBFX<c> <Rd>,<Rn>,#<lsb>,#<widthm1> cond:4|0|1|1|1|1|1|1|widthm1:5|Rd:4|lsb:5|1|0|1|Rn:4
258 0x0ff0f0f0 0x0730f010 UDIV<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|1|0|0|1|1|Rd:4|(1)|(1)|(1)|(1)|Rm:4|0|0|0|1|Rn:4
259 0x0ff00ff0 0x06700f10 UHADD16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
260 0x0ff00ff0 0x06700f90 UHADD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4
261 0x0ff00ff0 0x06700f30 UHASX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
262 0x0ff00ff0 0x06700f50 UHSAX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4
263 0x0ff00ff0 0x06700f70 UHSUB16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4
264 0x0ff00ff0 0x06700ff0 UHSUB8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4
265 0x0ff000f0 0x00400090 UMAAL<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|0|0|0|0|1|0|0|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4
266 0x0fe000f0 0x00a00090 UMLAL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|0|0|0|1|0|1|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4
267 0x0fe000f0 0x00800090 UMULL{S}<c> <RdLo>,<RdHi>,<Rn>,<Rm> cond:4|0|0|0|0|1|0|0|S|RdHi:4|RdLo:4|Rm:4|1|0|0|1|Rn:4
268 0x0ff00ff0 0x06600f10 UQADD16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4
269 0x0ff00ff0 0x06600f90 UQADD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|0|0|1|Rm:4
270 0x0ff00ff0 0x06600f30 UQASX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4
271 0x0ff00ff0 0x06600f50 UQSAX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4
272 0x0ff00ff0 0x06600f70 UQSUB16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4
273 0x0ff00ff0 0x06600ff0 UQSUB8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|1|0|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4
274 0x0ff0f0f0 0x0780f010 USAD8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|1|1|0|0|0|Rd:4|1|1|1|1|Rm:4|0|0|0|1|Rn:4
275 0x0ff000f0 0x07800010 USADA8<c> <Rd>,<Rn>,<Rm>,<Ra> cond:4|0|1|1|1|1|0|0|0|Rd:4|Ra:4|Rm:4|0|0|0|1|Rn:4 SEE USAD8
276 0x0ff00ff0 0x06e00f30 USAT16<c> <Rd>,#<sat_imm4>,<Rn> cond:4|0|1|1|0|1|1|1|0|sat_imm:4|Rd:4|(1)|(1)|(1)|(1)|0|0|1|1|Rn:4
277 0x0fe00030 0x06e00010 USAT<c> <Rd>,#<sat_imm5>,<Rn>{,<shift>} cond:4|0|1|1|0|1|1|1|sat_imm:5|Rd:4|imm5:5|sh|0|1|Rn:4
278 0x0ff00ff0 0x06500f50 USAX<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|0|1|Rm:4
279 0x0ff00ff0 0x06500f70 USUB16<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|0|1|1|1|Rm:4
280 0x0ff00ff0 0x06500ff0 USUB8<c> <Rd>,<Rn>,<Rm> cond:4|0|1|1|0|0|1|0|1|Rn:4|Rd:4|(1)|(1)|(1)|(1)|1|1|1|1|Rm:4
281 0x0ff003f0 0x06c00070 UXTAB16<c> <Rd>,<Rn>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|1|0|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4 SEE UXTB16
282 0x0ff003f0 0x06e00070 UXTAB<c> <Rd>,<Rn>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|1|1|0|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4 SEE UXTB
283 0x0ff003f0 0x06f00070 UXTAH<c> <Rd>,<Rn>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|1|1|1|Rn:4|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4 SEE UXTH
284 0x0fff03f0 0x06cf0070 UXTB16<c> <Rd>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|1|0|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4
285 0x0fff03f0 0x06ef0070 UXTB<c> <Rd>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|1|1|0|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4
286 0x0fff03f0 0x06ff0070 UXTH<c> <Rd>,<Rm>{,<rotation>} cond:4|0|1|1|0|1|1|1|1|1|1|1|1|Rd:4|rotate:2|0|0|0|1|1|1|Rm:4
287 0xff800f10 0xf3000110 V<BIF,BIT,BSL> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|op:2|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4 SEE VEOR
288 0xffb00c10 0xf3b00800 V<TBL,TBX>.8 <Dd>, <list_len>, <Dm> 1|1|1|1|0|0|1|1|1|D|1|1|Vn:4|Vd:4|1|0|len:2|N|op|M|0|Vm:4
289 0xfe800a50 0xf2800040 V<MLA,MLS>.<dt> <Qd>, <Qn>, <Dm[x]> 1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|0|op|0|F|N|1|M|0|Vm:4 SEE “Related encodings”
290 0xfe800f00 0xf2000600 V<MAX,MIN>.<dt> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|0|N|Q|M|op|Vm:4
291 0xfe800f10 0xf2000900 V<MLA,MLS>.<dt> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|op|0|D|size:2|Vn:4|Vd:4|1|0|0|1|N|Q|M|0|Vm:4
292 0xff800f10 0xf2000d10 V<MLA,MLS>.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|op|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|1|Vm:4
293 0xff800f10 0xf2000f00 V<MAX,MIN>.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|op|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|0|Vm:4
294 0xff800f10 0xf3000e10 VAC<GE,GT>.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|op|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|1|Vm:4
295 0x0fb00e10 0x0e000a00 V<MLA,MLS><c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|0|D|0|0|Vn:4|Vd:4|1|0|1|sz|N|op|M|0|Vm:4 vfp
296 0xfe801d50 0xf2800800 V<MLA,MLS>L.<dt> <Qd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|0|op|0|N|0|M|0|Vm:4 SEE “Related encodings”
297 0xfe801b50 0xf2800240 V<MLA,MLS>L.<dt> <Qd>, <Dn>, <Dm[x]> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|op|1|0|N|1|M|0|Vm:4 SEE “Related encodings”
298 0xfe800f10 0xf2000710 VABA.<dt_Usize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|1|N|Q|M|1|Vm:4
299 0xfe801f50 0xf2800500 VABAL.<dt_Usize> <Qd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|1|0|1|N|0|M|0|Vm:4 SEE “Related encodings”
300 0xfe800f10 0xf2000700 VABD.<dt_Usize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|1|1|N|Q|M|0|Vm:4
301 0xffa00f10 0xf3200d00 VABD.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|1|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4
302 0xfe801f50 0xf2800700 VABDL.<dt_Usize> <Qd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|1|1|1|N|0|M|0|Vm:4 SEE “Related encodings”
303 0xffb30b90 0xf3b10300 VABS.<dt_Fsize> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|1|0|Q|M|0|Vm:4
304 0x0fbf0ed0 0x0eb00ac0 VABS<c>.F<32,64> <Sd,Dd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|0|0|0|0|Vd:4|1|0|1|sz|1|1|M|0|Vm:4 vfp
305 0xff800f10 0xf2000800 VADD.<dt_Isize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|0|Vm:4
306 0xffa00f10 0xf2000d00 VADD.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4
307 0x0fb00e50 0x0e300a00 VADD<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|0|D|1|1|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4 vfp
308 0xff810f51 0xf2800400 VADDHN.<dt_Isize> <Dd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|1|0|0|N|0|M|0|Vm:4 SEE “Related encodings”
309 0xfe801e50 0xf2800000 VADDL.<dt_Usize> <Qd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|0|0|op|N|0|M|0|Vm:4 SEE “Related encodings”
310 0xffb00f10 0xf2000110 VAND <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|0|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4
311 0xffb00f10 0xf2100110 VBIC <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|0|1|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4
312 0xfeb800b0 0xf2800030 VBIC.<dt_simd> <Qd>, #<imm_simd1> 1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|1|1|imm4:4 SEE “Related encodings”
313 0xffb30b90 0xf3b10100 VCEQ.<dt_Fsize> <Qd>, <Qm>, #0 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|1|0|Q|M|0|Vm:4
314 0xff800f10 0xf3000810 VCEQ.<dt_Isize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|1|Vm:4
315 0xffa00f10 0xf2000e00 VCEQ.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4
316 0xffb30b90 0xf3b10080 VCGE.<dt_Fsize> <Qd>, <Qm>, #0 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|0|1|Q|M|0|Vm:4
317 0xfe800f10 0xf2000310 VCGE.<dt_Isize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|1|N|Q|M|1|Vm:4
318 0xffa00f10 0xf3000e00 VCGE.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4
319 0xffb30b90 0xf3b10000 VCGT.<dt_Fsize> <Qd>, <Qm>, #0 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|0|0|Q|M|0|Vm:4
320 0xfe800f10 0xf2000300 VCGT.<dt_Isize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|1|N|Q|M|0|Vm:4
321 0xffa00f10 0xf3200e00 VCGT.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|1|sz|Vn:4|Vd:4|1|1|1|0|N|Q|M|0|Vm:4
322 0xffb30b90 0xf3b10180 VCLE.<dt_Fsize> <Qd>, <Qm>, #0 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|0|1|1|Q|M|0|Vm:4
323 0xffb30f90 0xf3b00400 VCLS.<dt_Isize> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|0|0|Q|M|0|Vm:4
324 0xffb30b90 0xf3b10200 VCLT.<dt_Fsize> <Qd>, <Qm>, #0 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|0|0|Q|M|0|Vm:4
325 0xffb30f90 0xf3b00480 VCLZ.<dt_Isize> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|0|1|Q|M|0|Vm:4
326 0x0fbf0e7f 0x0eb50a40 VCMP{E}<c>.F<32,64> <Sd,Dd>, #0.0 cond:4|1|1|1|0|1|D|1|1|0|1|0|1|Vd:4|1|0|1|sz|E|1|0|0|(0)|(0)|(0)|(0) vfp
327 0x0fbf0e50 0x0eb40a40 VCMP{E}<c>.F<32,64> <Sd,Dd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|0|1|0|0|Vd:4|1|0|1|sz|E|1|M|0|Vm:4 vfp
328 0xffbf0f90 0xf3b00500 VCNT.8 <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|1|0|Q|M|0|Vm:4
329 0xffbf0e10 0xf3bb0600 VCVT.<Td>.<Tm_1> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|1|op:2|Q|M|0|Vm:4
330 0xfea00e90 0xf2a00e10 VCVT.<Td>.<Tm_2> <Qd>, <Qm>, #<fbits> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|1|1|op|0|Q|M|1|Vm:4 SEE “Related encodings”
331 0x0fbe0e50 0x0eba0a40 VCVT<c>.F<32,64>.FX<S,U><16,32> <Sd,Dd>, <Sd,Dd>, #<fbits> cond:4|1|1|1|0|1|D|1|1|1|0|1|U|Vd:4|1|0|1|sz|sx|1|i|0|imm4:4 vfp
332 0x0fbe0e50 0x0ebe0a40 VCVT<c>.FX<S,U><16,32>.F<32,64> <Sd,Dd>, <Sd,Dd>, #<fbits> cond:4|1|1|1|0|1|D|1|1|1|1|1|U|Vd:4|1|0|1|sz|sx|1|i|0|imm4:4 vfp
333 0xffbf0ed0 0xf3b60600 VCVT.F32.F16 <Qd>, <Dm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|1|1|op|0|0|M|0|Vm:4
334 0x0fbf0ed0 0x0eb70ac0 VCVT<c>.<F64.F32,F32.F64> <Dd,Sd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|0|1|1|1|Vd:4|1|0|1|sz|1|1|M|0|Vm:4 vfp
335 0x0fbe0f50 0x0eb20a40 VCVT<B,T><c>.<F32.F16,F16.F32> <Sd>, <Sm> cond:4|1|1|1|0|1|D|1|1|0|0|1|op|Vd:4|1|0|1|0|T|1|M|0|Vm:4 vfp
336 0x0fbf0e50 0x0eb80a40 VCVT<c>.F<32,64>.<U,S>32 <Sd,Dd>, <Sm> cond:4|1|1|1|0|1|D|1|1|1|0|0|0|Vd:4|1|0|1|sz|op|1|M|0|Vm:4 vfp
337 0x0fbe0e50 0x0ebc0a40 VCVT<R,><c>.<U,S>32.F<32,64> <Sd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|1|1|0|signed|Vd:4|1|0|1|sz|op|1|M|0|Vm:4 vfp
338 0x0fb00e50 0x0e800a00 VDIV<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|1|D|0|0|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4 vfp
339 0xffb00f90 0xf3b00c00 VDUP.<size_x> <Qd>, <Dm[size_x]> 1|1|1|1|0|0|1|1|1|D|1|1|imm4:4|Vd:4|1|1|0|0|0|Q|M|0|Vm:4
340 0x0f900f5f 0x0e800b10 VDUP<c>.<size_be> <Qd>, <Rt> cond:4|1|1|1|0|1|b|Q|0|Vd:4|Rt:4|1|0|1|1|D|0|e|1|(0)|(0)|(0)|(0)
341 0xffb00f10 0xf3000110 VEOR <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|0|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4
342 0xffb00010 0xf2b00000 VEXT.8 <Qd>, <Qn>, <Qm>, #<imm4> 1|1|1|1|0|0|1|0|1|D|1|1|Vn:4|Vd:4|imm4:4|N|Q|M|0|Vm:4
343 0xfe800d10 0xf2000000 VH<ADD,SUB> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|op|0|N|Q|M|0|Vm:4
344 0xffb00200 0xf4200200 VLD1.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
345 0xffb00f00 0xf4a00c00 VLD1.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|0|0|size:2|T|a|Rm:4
346 0xffb00300 0xf4a00000 VLD1.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|0|0|index_align:4|Rm:4 SEE VLD1 (single element to all lanes)
347 0xffb00400 0xf4200000 VLD2.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
348 0xffb00f00 0xf4a00d00 VLD2.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|0|1|size:2|T|a|Rm:4
349 0xffb00300 0xf4a00100 VLD2.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|0|1|index_align:4|Rm:4 SEE VLD2 (single 2-element structure to all lanes)
350 0xffb00f10 0xf4a00e00 VLD3.<size> <list3t>, [<Rn>]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|1|0|size:2|T|a|Rm:4
351 0xffb00310 0xf4a00200 VLD3.<size> <list3>, [<Rn>]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|1|0|index_align:4|Rm:4 SEE VLD3 (single 3-element structure to all lanes)
352 0xffb00e20 0xf4200400 VLD3.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
353 0xffb00f00 0xf4a00f00 VLD4.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|1|1|1|1|size:2|T|a|Rm:4
354 0xffb00e00 0xf4200000 VLD4.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|1|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
355 0xffb00300 0xf4a00300 VLD4.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|1|0|Rn:4|Vd:4|size:2|1|1|index_align:4|Rm:4 SEE VLD4 (single 4-element structure to all lanes)
356 0x0f300e00 0x0d100a00 VLDR<c> <Sd,Dd>, [<Rn>{,#+/-<imm8>}] cond:4|1|1|0|1|U|D|0|1|Rn:4|Vd:4|1|0|1|sz|imm8:8 vfp
357 0x0fe00fd0 0x0c400b10 VMOV<c> <Dm>, <Rt>, <Rt2> cond:4|1|1|0|0|0|1|0|op|Rt2:4|Rt:4|1|0|1|1|0|0|M|1|Vm:4
358 0xffb00f10 0xf2200110 VMOV <Qd>, <Qm> 1|1|1|1|0|0|1|0|0|D|1|0|Vm:4|Vd:4|0|0|0|1|M|Q|M|1|Vm:4 SEE VORR (register)
359 0x0fe00fd0 0x0c400a10 VMOV<c> <Sm>, <Sm1>, <Rt>, <Rt2> cond:4|1|1|0|0|0|1|0|op|Rt2:4|Rt:4|1|0|1|0|0|0|M|1|Vm:4
360 0x0ff00f7f 0x0e000a10 VMOV<c> <Sn>, <Rt> cond:4|1|1|1|0|0|0|0|0|Vn:4|Rt:4|1|0|1|0|N|0|0|1|0|0|0|0 vfp
361 0x0ff00f7f 0x0e100a10 VMOV<c> <Rt>, <Sn> cond:4|1|1|1|0|0|0|0|1|Vn:4|Rt:4|1|0|1|0|N|0|0|1|0|0|0|0 vfp
362 0xfeb80090 0xf2800010 VMOV.<dt_simd> <Qd>, #<imm_simd> 1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|op|1|imm4:4 SEE VORR (immediate) SEE “Related encodings”
363 0x0fd00f7f 0x0e100b10 VMOV<c>.32 <Rt>, <Dn[x]> cond:4|1|1|1|0|0|0|opc1|1|Vn:4|Rt:4|1|0|1|1|N|0|0|1|0|0|0|0 vfp
364 0x0fd00f7f 0x0e000b10 VMOV<c>.32 <Dd[x]>, <Rt> cond:4|1|1|1|0|0|0|opc1|0|Vd:4|Rt:4|1|0|1|1|D|0|0|1|0|0|0|0 vfp
365 0x0fb00ef0 0x0eb00a00 VMOV<c>.F<32,64> <Sd,Dd>, #<imm_vfp> cond:4|1|1|1|0|1|D|1|1|imm4H:4|Vd:4|1|0|1|sz|0|0|0|0|imm4L:4 vfp
366 0x0fbf0ed0 0x0eb00a40 VMOV<c>.F<32,64> <Sd,Dd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|0|0|0|0|Vd:4|1|0|1|sz|0|1|M|0|Vm:4 vfp
367 0xfe871fd0 0xf2800a10 VMOVL.<dt> <Qd>, <Dm> 1|1|1|1|0|0|1|U|1|D|imm3:3|0|0|0|Vd:4|1|0|1|0|0|0|M|1|Vm:4 SEE “Related encodings” SEE VSHLL
368 0xffb30fd1 0xf3b20200 VMOVN.<dt> <Dd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|0|0|0|M|0|Vm:4
369 0x0fff0fff 0x0ef10a10 VMRS<c> <Rt_nzcv>, FPSCR cond:4|1|1|1|0|1|1|1|1|0|0|0|1|Rt:4|1|0|1|0|0|0|0|1|0|0|0|0 vfp
370 0x0fff0fff 0x0ee10a10 VMSR<c> FPSCR, <Rt> cond:4|1|1|1|0|1|1|1|0|0|0|0|1|Rt:4|1|0|1|0|0|0|0|1|0|0|0|0 vfp
371 0xfe800e50 0xf2800840 VMUL.<dt> <Qd>, <Qn>, <Dm[x]> 1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|0|0|F|N|1|M|0|Vm:4 SEE “Related encodings”
372 0xfe800f10 0xf2000910 VMUL.<dt> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|op|0|D|size:2|Vn:4|Vd:4|1|0|0|1|N|Q|M|1|Vm:4
373 0xffa00f10 0xf3000d10 VMUL.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|1|Vm:4
374 0x0fb00e50 0x0e200a00 VMUL<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|0|D|1|0|Vn:4|Vd:4|1|0|1|sz|N|0|M|0|Vm:4 vfp
375 0xfe801d50 0xf2800c00 VMULL.<dt> <Qd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|1|op|0|N|0|M|0|Vm:4 SEE “Related encodings”
376 0xfe801f50 0xf2800a40 VMULL.<dt> <Qd>, <Dn>, <Dm[x]> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|1|0|1|0|N|1|M|0|Vm:4 SEE “Related encodings”
377 0xffbf0f90 0xf3b00580 VMVN <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|0|1|1|Q|M|0|Vm:4
378 0xfeb800b0 0xf2800030 VMVN.<dt> <Qd>, #<imm3> 1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|1|1|imm4:4 SEE “Related encodings”
379 0xffb30b90 0xf3b10380 VNEG.<dt> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|1|Vd:4|0|F|1|1|1|Q|M|0|Vm:4
380 0x0fbf0ed0 0x0eb10a40 VNEG<c>.F<32,64> <Sd,Dd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|0|0|0|1|Vd:4|1|0|1|sz|0|1|M|0|Vm:4 vfp
381 0x0fb00e10 0x0e100a00 VN<MLS,MLA><c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|0|D|0|1|Vn:4|Vd:4|1|0|1|sz|N|op|M|0|Vm:4 vfp
382 0x0fb00e50 0x0e200a40 VNMUL<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|0|D|1|0|Vn:4|Vd:4|1|0|1|sz|N|1|M|0|Vm:4 vfp
383 0xffb00f10 0xf2300110 VORN <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|1|1|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4
384 0xffb00f10 0xf2200110 VORR <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|1|0|Vn:4|Vd:4|0|0|0|1|N|Q|M|1|Vm:4 SEE VMOV (register)
385 0xfeb800b0 0xf2800010 VORR.<dt> <Qd>, #<imm3> 1|1|1|1|0|0|1|i|1|D|0|0|0|imm3:3|Vd:4|cmode:4|0|Q|0|1|imm4:4 SEE VMOV (immediate)
386 0xfe800f00 0xf2000a00 VP<MIN,MAX>.<dt> <Dd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|1|0|1|0|N|Q|M|op|Vm:4
387 0xff800f10 0xf3000f00 VP<MIN,MAX>.F32 <Dd>, <Dn>, <Dm> 1|1|1|1|0|0|1|1|0|D|op|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|0|Vm:4
388 0xffb30f10 0xf3b00600 VPADAL.<dt> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|0|op|Q|M|0|Vm:4
389 0xff800f10 0xf2000b10 VPADD.<dt> <Dd>, <Dn>, <Dm> 1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|1|Vm:4
390 0xffa00f10 0xf3000d00 VPADD.F32 <Dd>, <Dn>, <Dm> 1|1|1|1|0|0|1|1|0|D|0|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4
391 0xffb30f10 0xf3b00200 VPADDL.<dt> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|0|1|0|op|Q|M|0|Vm:4
392 0x0fbf0f00 0x0cbd0a00 VPOP <vlist32> cond:4|1|1|0|0|1|D|1|1|1|1|0|1|Vd:4|1|0|1|0|imm8:8
393 0x0fbf0f00 0x0cbd0b00 VPOP <vlist64> cond:4|1|1|0|0|1|D|1|1|1|1|0|1|Vd:4|1|0|1|1|imm8:8
394 0x0fbf0f00 0x0d2d0a00 VPUSH<c> <vlist32> cond:4|1|1|0|1|0|D|1|0|1|1|0|1|Vd:4|1|0|1|0|imm8:8
395 0x0fbf0f00 0x0d2d0b00 VPUSH<c> <vlist64> cond:4|1|1|0|1|0|D|1|0|1|1|0|1|Vd:4|1|0|1|1|imm8:8
396 0xffb30f90 0xf3b00700 VQABS.<dt> <Qd>,<Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|1|0|Q|M|0|Vm:4
397 0xfe800f10 0xf2000010 VQADD.<dt> <Qd>,<Qn>,<Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|0|0|N|Q|M|1|Vm:4
398 0xff801d50 0xf2800900 VQD<MLAL,MLSL>.<dt> <Qd>,<Dn>,<Dm> 1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|0|op|1|N|0|M|0|Vm:4 SEE “Related encodings”
399 0xff801b50 0xf2800340 VQD<MLAL,MLSL>.<dt> <Qd>,<Dn>,<Dm[x]> 1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|op|1|1|N|1|M|0|Vm:4 SEE “Related encodings”
400 0xfe800f50 0xf2800c40 VQDMULH.<dt> <Qd>,<Qn>,<Dm[x]> 1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|1|0|0|N|1|M|0|Vm:4
401 0xff800f10 0xf2000b00 VQDMULH.<dt> <Qd>,<Qn>,<Qm> 1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|0|Vm:4
402 0xff801f50 0xf2800d00 VQDMULL.<dt> <Qd>,<Dn>,<Dm> 1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|1|0|1|N|0|M|0|Vm:4 SEE “Related encodings”
403 0xff801f50 0xf2800b40 VQDMULL.<dt> <Qd>,<Dn>,<Dm[x]> 1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|1|0|1|1|N|1|M|0|Vm:4 SEE “Related encodings”
404 0xffb30f11 0xf3b20200 VQMOV{U}N.<type><size> <Dd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|0|op:2|M|0|Vm:4 SEE VMOVN
405 0xffb30f90 0xf3b00780 VQNEG.<dt> <Qd>,<Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|1|1|1|1|Q|M|0|Vm:4
406 0xfe800f50 0xf2800d40 VQRDMULH.<dt> <Qd>,<Qn>,<Dm[x]> 1|1|1|1|0|0|1|Q|1|D|size:2|Vn:4|Vd:4|1|1|0|1|N|1|M|0|Vm:4
407 0xff800f10 0xf3000b00 VQRDMULH.<dt> <Qd>,<Qn>,<Qm> 1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|1|1|N|Q|M|0|Vm:4
408 0xfe800f10 0xf2000510 VQRSHL.<type><size> <Qd>,<Qm>,<Qn> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|1|N|Q|M|1|Vm:4
409 0xfe800ed1 0xf2800850 VQRSHR{U}N.<type><size> <Dd>,<Qm>,#<imm6> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|0|op|0|1|M|1|Vm:4 SEE “Related encodings” SEE VRSHRN
410 0xfe800f10 0xf2000410 VQSHL.<type><size> <Qd>,<Qm>,<Qn> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|0|N|Q|M|1|Vm:4
411 0xfe800e90 0xf2800690 VQSHL{U}.<type><size> <Qd>,<Qm>,#<imm6> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|1|1|op|L|Q|M|1|Vm:4 SEE “Related encodings”
412 0xfe800ed1 0xf2800810 VQSHR{U}N.<type><size> <Dd>,<Qm>,#<imm6> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|0|op|0|0|M|1|Vm:4 SEE “Related encodings” SEE VSHRN
413 0xfe800f10 0xf2000210 VQSUB.<type><size> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|1|0|N|Q|M|1|Vm:4
414 0xff810f51 0xf3800400 VRADDHN.<dt> <Dd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|1|D|size:2|Vn:4|Vd:4|0|1|0|0|N|0|M|0|Vm:4 SEE “Related encodings”
415 0xffbf0e90 0xf3bb0400 VRECPE.<dt> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|0|F|0|Q|M|0|Vm:4
416 0xffa00f10 0xf2000f10 VRECPS.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|0|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|1|Vm:4
417 0xffb30e10 0xf3b00000 VREV<n>.<size> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|0|0|Vd:4|0|0|0|op:2|Q|M|0|Vm:4
418 0xfe800f10 0xf2000100 VRHADD <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|0|0|1|N|Q|M|0|Vm:4
419 0xfe800f10 0xf2000500 VRSHL.<type><size> <Qd>, <Qm>, <Qn> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|1|N|Q|M|0|Vm:4
420 0xfe800f90 0xf2800290 VRSHR.<type><size> <Qd>, <Qm>, #<imm6> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|1|0|L|Q|M|1|Vm:4 SEE “Related encodings”
421 0xff800fd1 0xf2800850 VRSHRN.I<size> <Dd>, <Qm>, #<imm6> 1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|1|0|0|0|0|1|M|1|Vm:4 SEE “Related encodings”
422 0xffbf0e90 0xf3bb0480 VRSQRTE.<dt> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|1|Vd:4|0|1|0|F|1|Q|M|0|Vm:4
423 0xffa00f10 0xf2200f10 VRSQRTS.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|1|sz|Vn:4|Vd:4|1|1|1|1|N|Q|M|1|Vm:4
424 0xfe800f90 0xf2800390 VRSRA.<type><size> <Qd>, <Qm>, #<imm6> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|1|1|L|Q|M|1|Vm:4 SEE “Related encodings”
425 0xff810f51 0xf3800600 VRSUBHN.<dt> <Dd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|1|D|size:2|Vn:4|Vd:4|0|1|1|0|N|0|M|0|Vm:4 SEE “Related encodings”
426 0xff800f90 0xf2800590 VSHL.I<size> <Qd>, <Qm>, #<imm6> 1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|0|1|0|1|L|Q|M|1|Vm:4 SEE “Related encodings”
427 0xfe800f10 0xf2000400 VSHL.I<size> <Qd>, <Qm>, <Qn> 1|1|1|1|0|0|1|U|0|D|size:2|Vn:4|Vd:4|0|1|0|0|N|Q|M|0|Vm:4
428 0xffb31fd0 0xf3b20300 VSHLL.<type><size> <Qd>, <Dm>, #<immsize> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|1|1|0|0|M|0|Vm:4
429 0xfe801fd0 0xf2800a10 VSHLL.<type><size> <Qd>, <Dm>, #<imm6> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|1|0|1|0|0|0|M|1|Vm:4 SEE “Related encodings” SEE VMOVL
430 0xfe800f90 0xf2800090 VSHR.<type><size_vs> <Qd>, <Qm>, #<imm_vs> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|0|0|L|Q|M|1|Vm:4 SEE “Related encodings”
431 0xff800fd1 0xf2800810 VSHRN.I<size_vsn> <Dd>, <Qm>, #<imm_vsn> 1|1|1|1|0|0|1|0|1|D|imm6:6|Vd:4|1|0|0|0|0|0|M|1|Vm:4 SEE “Related encodings”
432 0xff800f90 0xf3800590 VSLI.<size_vs> <Qd>, <Qm>, #<imm_vs> 1|1|1|1|0|0|1|1|1|D|imm6:6|Vd:4|0|1|0|1|L|Q|M|1|Vm:4 SEE “Related encodings”
433 0x0fbf0ed0 0x0eb10ac0 VSQRT<c>.F<32,64> <Sd,Dd>, <Sm,Dm> cond:4|1|1|1|0|1|D|1|1|0|0|0|1|Vd:4|1|0|1|sz|1|1|M|0|Vm:4 vfp
434 0xfe800f90 0xf2800190 VSRA.<type_U><size_vs> <Qd>, <Qm>, #<imm_vs> 1|1|1|1|0|0|1|U|1|D|imm6:6|Vd:4|0|0|0|1|L|Q|M|1|Vm:4 SEE “Related encodings”
435 0xff800f90 0xf3800490 VSRI.<size_vs> <Qd>, <Qm>, #<imm_vs> 1|1|1|1|0|0|1|1|1|D|imm6:6|Vd:4|0|1|0|0|L|Q|M|1|Vm:4 SEE “Related encodings”
436 0xffb00200 0xf4000200 VST1.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
437 0xffb00300 0xf4800000 VST1.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|0|0|index_align:4|Rm:4
438 0xffb00400 0xf4000000 VST2.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
439 0xffb00300 0xf4800100 VST2.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|0|1|index_align:4|Rm:4
440 0xffb00300 0xf4800200 VST3.<size> <list4>, [<Rn>]{!} 1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|1|0|index_align:4|Rm:4
441 0xffb00e20 0xf4000400 VST3.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
442 0xffb00e00 0xf4000000 VST4.<size> <list4>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|0|D|0|0|Rn:4|Vd:4|type:4|size:2|align:2|Rm:4 SEE “Related encodings”
443 0xffb00300 0xf4800300 VST4.<size> <list1>, [<Rn>{@<align>}]{!} 1|1|1|1|0|1|0|0|1|D|0|0|Rn:4|Vd:4|size:2|1|1|index_align:4|Rm:4
444 0x0f300e00 0x0d000a00 VSTR<c> <Sd,Dd>, [<Rn>{,#+/-<imm8>}] cond:4|1|1|0|1|U|D|0|0|Rn:4|Vd:4|1|0|1|sz|imm8:8 vfp
445 0xff800f10 0xf3000800 VSUB.<dt_Isize> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|1|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|0|Vm:4
446 0xffa00f10 0xf2200d00 VSUB.F32 <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|1|sz|Vn:4|Vd:4|1|1|0|1|N|Q|M|0|Vm:4
447 0x0fb00e50 0x0e300a40 VSUB<c>.F<32,64> <Sd,Dd>, <Sn,Dn>, <Sm,Dm> cond:4|1|1|1|0|0|D|1|1|Vn:4|Vd:4|1|0|1|sz|N|1|M|0|Vm:4 vfp
448 0xff810f51 0xf2800600 VSUBHN.<dt_Isize> <Dd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|1|D|size:2|Vn:4|Vd:4|0|1|1|0|N|0|M|0|Vm:4 SEE “Related encodings”
449 0xfe801e50 0xf2800200 VSUBL.<dt_Usize> <Qd>, <Dn>, <Dm> 1|1|1|1|0|0|1|U|1|D|size:2|Vn:4|Vd:4|0|0|1|op|N|0|M|0|Vm:4 SEE “Related encodings”
450 0xffbf0f90 0xf3b20000 VSWP <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|0|0|Q|M|0|Vm:4
451 0xffb30f90 0xf3b20080 VTRN.<size_n> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|0|1|Q|M|0|Vm:4
452 0xff800f10 0xf2000810 VTST.<size_n> <Qd>, <Qn>, <Qm> 1|1|1|1|0|0|1|0|0|D|size:2|Vn:4|Vd:4|1|0|0|0|N|Q|M|1|Vm:4
453 0xffb30f90 0xf3b20100 VUZP.<size_n> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|1|0|Q|M|0|Vm:4
454 0xffb30f90 0xf3b20180 VZIP.<size_n> <Qd>, <Qm> 1|1|1|1|0|0|1|1|1|D|1|1|size:2|1|0|Vd:4|0|0|0|1|1|Q|M|0|Vm:4
455 0x0fffffff 0x0320f002 WFE<c> cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|1|0
456 0x0fffffff 0x0320f003 WFI<c> cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|1|1
457 0x0fffffff 0x0320f001 YIELD<c> cond:4|0|0|1|1|0|0|1|0|0|0|0|0|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|0|0|0|0|1
458 0xffffffff 0xf7fabcfd UNDEF 1|1|1|1|0|1|1|1|1|1|1|1|1|0|1|0|1|0|1|1|1|1|0|0|1|1|1|1|1|1|0|1
@@ -0,0 +1,2 @@
tables.go: ../armmap/map.go ../arm.csv
go run ../armmap/map.go -fmt=decoder ../arm.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go
@@ -0,0 +1,569 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package armasm
import (
"encoding/binary"
"fmt"
)
// An instFormat describes the format of an instruction encoding.
// An instruction with 32-bit value x matches the format if x&mask == value
// and the condition matches.
// The condition matches if x>>28 == 0xF && value>>28==0xF
// or if x>>28 != 0xF and value>>28 == 0.
// If x matches the format, then the rest of the fields describe how to interpret x.
// The opBits describe bits that should be extracted from x and added to the opcode.
// For example opBits = 0x1234 means that the value
//
// (2 bits at offset 1) followed by (4 bits at offset 3)
//
// should be added to op.
// Finally the args describe how to decode the instruction arguments.
// args is stored as a fixed-size array; if there are fewer than len(args) arguments,
// args[i] == 0 marks the end of the argument list.
type instFormat struct {
mask uint32
value uint32
priority int8
op Op
opBits uint64
args instArgs
}
type instArgs [4]instArg
var (
errMode = fmt.Errorf("unsupported execution mode")
errShort = fmt.Errorf("truncated instruction")
errUnknown = fmt.Errorf("unknown instruction")
)
var decoderCover []bool
// Decode decodes the leading bytes in src as a single instruction.
func Decode(src []byte, mode Mode) (inst Inst, err error) {
if mode != ModeARM {
return Inst{}, errMode
}
if len(src) < 4 {
return Inst{}, errShort
}
if decoderCover == nil {
decoderCover = make([]bool, len(instFormats))
}
x := binary.LittleEndian.Uint32(src)
// The instFormat table contains both conditional and unconditional instructions.
// Considering only the top 4 bits, the conditional instructions use mask=0, value=0,
// while the unconditional instructions use mask=f, value=f.
// Prepare a version of x with the condition cleared to 0 in conditional instructions
// and then assume mask=f during matching.
const condMask = 0xf0000000
xNoCond := x
if x&condMask != condMask {
xNoCond &^= condMask
}
var priority int8
Search:
for i := range instFormats {
f := &instFormats[i]
if xNoCond&(f.mask|condMask) != f.value || f.priority <= priority {
continue
}
delta := uint32(0)
deltaShift := uint(0)
for opBits := f.opBits; opBits != 0; opBits >>= 16 {
n := uint(opBits & 0xFF)
off := uint((opBits >> 8) & 0xFF)
delta |= (x >> off) & (1<<n - 1) << deltaShift
deltaShift += n
}
op := f.op + Op(delta)
// Special case: BKPT encodes with condition but cannot have one.
if op&^15 == BKPT_EQ && op != BKPT {
continue Search
}
var args Args
for j, aop := range f.args {
if aop == 0 {
break
}
arg := decodeArg(aop, x)
if arg == nil { // cannot decode argument
continue Search
}
args[j] = arg
}
decoderCover[i] = true
inst = Inst{
Op: op,
Args: args,
Enc: x,
Len: 4,
}
priority = f.priority
continue Search
}
if inst.Op != 0 {
return inst, nil
}
return Inst{}, errUnknown
}
// An instArg describes the encoding of a single argument.
// In the names used for arguments, _p_ means +, _m_ means -,
// _pm_ means ± (usually keyed by the U bit).
// The _W suffix indicates a general addressing mode based on the P and W bits.
// The _offset and _postindex suffixes force the given addressing mode.
// The rest should be somewhat self-explanatory, at least given
// the decodeArg function.
type instArg uint8
const (
_ instArg = iota
arg_APSR
arg_FPSCR
arg_Dn_half
arg_R1_0
arg_R1_12
arg_R2_0
arg_R2_12
arg_R_0
arg_R_12
arg_R_12_nzcv
arg_R_16
arg_R_16_WB
arg_R_8
arg_R_rotate
arg_R_shift_R
arg_R_shift_imm
arg_SP
arg_Sd
arg_Sd_Dd
arg_Dd_Sd
arg_Sm
arg_Sm_Dm
arg_Sn
arg_Sn_Dn
arg_const
arg_endian
arg_fbits
arg_fp_0
arg_imm24
arg_imm5
arg_imm5_32
arg_imm5_nz
arg_imm_12at8_4at0
arg_imm_4at16_12at0
arg_imm_vfp
arg_label24
arg_label24H
arg_label_m_12
arg_label_p_12
arg_label_pm_12
arg_label_pm_4_4
arg_lsb_width
arg_mem_R
arg_mem_R_pm_R_W
arg_mem_R_pm_R_postindex
arg_mem_R_pm_R_shift_imm_W
arg_mem_R_pm_R_shift_imm_offset
arg_mem_R_pm_R_shift_imm_postindex
arg_mem_R_pm_imm12_W
arg_mem_R_pm_imm12_offset
arg_mem_R_pm_imm12_postindex
arg_mem_R_pm_imm8_W
arg_mem_R_pm_imm8_postindex
arg_mem_R_pm_imm8at0_offset
arg_option
arg_registers
arg_registers1
arg_registers2
arg_satimm4
arg_satimm5
arg_satimm4m1
arg_satimm5m1
arg_widthm1
)
// decodeArg decodes the arg described by aop from the instruction bits x.
// It returns nil if x cannot be decoded according to aop.
func decodeArg(aop instArg, x uint32) Arg {
switch aop {
default:
return nil
case arg_APSR:
return APSR
case arg_FPSCR:
return FPSCR
case arg_R_0:
return Reg(x & (1<<4 - 1))
case arg_R_8:
return Reg((x >> 8) & (1<<4 - 1))
case arg_R_12:
return Reg((x >> 12) & (1<<4 - 1))
case arg_R_16:
return Reg((x >> 16) & (1<<4 - 1))
case arg_R_12_nzcv:
r := Reg((x >> 12) & (1<<4 - 1))
if r == R15 {
return APSR_nzcv
}
return r
case arg_R_16_WB:
mode := AddrLDM
if (x>>21)&1 != 0 {
mode = AddrLDM_WB
}
return Mem{Base: Reg((x >> 16) & (1<<4 - 1)), Mode: mode}
case arg_R_rotate:
Rm := Reg(x & (1<<4 - 1))
typ, count := decodeShift(x)
// ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1.
if typ == RotateRightExt {
return Rm
}
return RegShift{Rm, typ, count}
case arg_R_shift_R:
Rm := Reg(x & (1<<4 - 1))
Rs := Reg((x >> 8) & (1<<4 - 1))
typ := Shift((x >> 5) & (1<<2 - 1))
return RegShiftReg{Rm, typ, Rs}
case arg_R_shift_imm:
Rm := Reg(x & (1<<4 - 1))
typ, count := decodeShift(x)
if typ == ShiftLeft && count == 0 {
return Reg(Rm)
}
return RegShift{Rm, typ, count}
case arg_R1_0:
return Reg((x & (1<<4 - 1)))
case arg_R1_12:
return Reg(((x >> 12) & (1<<4 - 1)))
case arg_R2_0:
return Reg((x & (1<<4 - 1)) | 1)
case arg_R2_12:
return Reg(((x >> 12) & (1<<4 - 1)) | 1)
case arg_SP:
return SP
case arg_Sd_Dd:
v := (x >> 12) & (1<<4 - 1)
vx := (x >> 22) & 1
sz := (x >> 8) & 1
if sz != 0 {
return D0 + Reg(vx<<4+v)
} else {
return S0 + Reg(v<<1+vx)
}
case arg_Dd_Sd:
return decodeArg(arg_Sd_Dd, x^(1<<8))
case arg_Sd:
v := (x >> 12) & (1<<4 - 1)
vx := (x >> 22) & 1
return S0 + Reg(v<<1+vx)
case arg_Sm_Dm:
v := (x >> 0) & (1<<4 - 1)
vx := (x >> 5) & 1
sz := (x >> 8) & 1
if sz != 0 {
return D0 + Reg(vx<<4+v)
} else {
return S0 + Reg(v<<1+vx)
}
case arg_Sm:
v := (x >> 0) & (1<<4 - 1)
vx := (x >> 5) & 1
return S0 + Reg(v<<1+vx)
case arg_Dn_half:
v := (x >> 16) & (1<<4 - 1)
vx := (x >> 7) & 1
return RegX{D0 + Reg(vx<<4+v), int((x >> 21) & 1)}
case arg_Sn_Dn:
v := (x >> 16) & (1<<4 - 1)
vx := (x >> 7) & 1
sz := (x >> 8) & 1
if sz != 0 {
return D0 + Reg(vx<<4+v)
} else {
return S0 + Reg(v<<1+vx)
}
case arg_Sn:
v := (x >> 16) & (1<<4 - 1)
vx := (x >> 7) & 1
return S0 + Reg(v<<1+vx)
case arg_const:
v := x & (1<<8 - 1)
rot := (x >> 8) & (1<<4 - 1) * 2
if rot > 0 && v&3 == 0 {
// could rotate less
return ImmAlt{uint8(v), uint8(rot)}
}
if rot >= 24 && ((v<<(32-rot))&0xFF)>>(32-rot) == v {
// could wrap around to rot==0.
return ImmAlt{uint8(v), uint8(rot)}
}
return Imm(v>>rot | v<<(32-rot))
case arg_endian:
return Endian((x >> 9) & 1)
case arg_fbits:
return Imm((16 << ((x >> 7) & 1)) - ((x&(1<<4-1))<<1 | (x>>5)&1))
case arg_fp_0:
return Imm(0)
case arg_imm24:
return Imm(x & (1<<24 - 1))
case arg_imm5:
return Imm((x >> 7) & (1<<5 - 1))
case arg_imm5_32:
x = (x >> 7) & (1<<5 - 1)
if x == 0 {
x = 32
}
return Imm(x)
case arg_imm5_nz:
x = (x >> 7) & (1<<5 - 1)
if x == 0 {
return nil
}
return Imm(x)
case arg_imm_4at16_12at0:
return Imm((x>>16)&(1<<4-1)<<12 | x&(1<<12-1))
case arg_imm_12at8_4at0:
return Imm((x>>8)&(1<<12-1)<<4 | x&(1<<4-1))
case arg_imm_vfp:
x = (x>>16)&(1<<4-1)<<4 | x&(1<<4-1)
return Imm(x)
case arg_label24:
imm := (x & (1<<24 - 1)) << 2
return PCRel(int32(imm<<6) >> 6)
case arg_label24H:
h := (x >> 24) & 1
imm := (x&(1<<24-1))<<2 | h<<1
return PCRel(int32(imm<<6) >> 6)
case arg_label_m_12:
d := int32(x & (1<<12 - 1))
return Mem{Base: PC, Mode: AddrOffset, Offset: int16(-d)}
case arg_label_p_12:
d := int32(x & (1<<12 - 1))
return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)}
case arg_label_pm_12:
d := int32(x & (1<<12 - 1))
u := (x >> 23) & 1
if u == 0 {
d = -d
}
return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)}
case arg_label_pm_4_4:
d := int32((x>>8)&(1<<4-1)<<4 | x&(1<<4-1))
u := (x >> 23) & 1
if u == 0 {
d = -d
}
return PCRel(d)
case arg_lsb_width:
lsb := (x >> 7) & (1<<5 - 1)
msb := (x >> 16) & (1<<5 - 1)
if msb < lsb || msb >= 32 {
return nil
}
return Imm(msb + 1 - lsb)
case arg_mem_R:
Rn := Reg((x >> 16) & (1<<4 - 1))
return Mem{Base: Rn, Mode: AddrOffset}
case arg_mem_R_pm_R_postindex:
// Treat [<Rn>],+/-<Rm> like [<Rn>,+/-<Rm>{,<shift>}]{!}
// by forcing shift bits to <<0 and P=0, W=0 (postindex=true).
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5|1<<24|1<<21))
case arg_mem_R_pm_R_W:
// Treat [<Rn>,+/-<Rm>]{!} like [<Rn>,+/-<Rm>{,<shift>}]{!}
// by forcing shift bits to <<0.
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5))
case arg_mem_R_pm_R_shift_imm_offset:
// Treat [<Rn>],+/-<Rm>{,<shift>} like [<Rn>,+/-<Rm>{,<shift>}]{!}
// by forcing P=1, W=0 (index=false, wback=false).
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<21)|1<<24)
case arg_mem_R_pm_R_shift_imm_postindex:
// Treat [<Rn>],+/-<Rm>{,<shift>} like [<Rn>,+/-<Rm>{,<shift>}]{!}
// by forcing P=0, W=0 (postindex=true).
return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<24|1<<21))
case arg_mem_R_pm_R_shift_imm_W:
Rn := Reg((x >> 16) & (1<<4 - 1))
Rm := Reg(x & (1<<4 - 1))
typ, count := decodeShift(x)
u := (x >> 23) & 1
w := (x >> 21) & 1
p := (x >> 24) & 1
if p == 0 && w == 1 {
return nil
}
sign := int8(+1)
if u == 0 {
sign = -1
}
mode := AddrMode(uint8(p<<1) | uint8(w^1))
return Mem{Base: Rn, Mode: mode, Sign: sign, Index: Rm, Shift: typ, Count: count}
case arg_mem_R_pm_imm12_offset:
// Treat [<Rn>,#+/-<imm12>] like [<Rn>{,#+/-<imm12>}]{!}
// by forcing P=1, W=0 (index=false, wback=false).
return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<21)|1<<24)
case arg_mem_R_pm_imm12_postindex:
// Treat [<Rn>],#+/-<imm12> like [<Rn>{,#+/-<imm12>}]{!}
// by forcing P=0, W=0 (postindex=true).
return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<24|1<<21))
case arg_mem_R_pm_imm12_W:
Rn := Reg((x >> 16) & (1<<4 - 1))
u := (x >> 23) & 1
w := (x >> 21) & 1
p := (x >> 24) & 1
if p == 0 && w == 1 {
return nil
}
sign := int8(+1)
if u == 0 {
sign = -1
}
imm := int16(x & (1<<12 - 1))
mode := AddrMode(uint8(p<<1) | uint8(w^1))
return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm}
case arg_mem_R_pm_imm8_postindex:
// Treat [<Rn>],#+/-<imm8> like [<Rn>{,#+/-<imm8>}]{!}
// by forcing P=0, W=0 (postindex=true).
return decodeArg(arg_mem_R_pm_imm8_W, x&^(1<<24|1<<21))
case arg_mem_R_pm_imm8_W:
Rn := Reg((x >> 16) & (1<<4 - 1))
u := (x >> 23) & 1
w := (x >> 21) & 1
p := (x >> 24) & 1
if p == 0 && w == 1 {
return nil
}
sign := int8(+1)
if u == 0 {
sign = -1
}
imm := int16((x>>8)&(1<<4-1)<<4 | x&(1<<4-1))
mode := AddrMode(uint8(p<<1) | uint8(w^1))
return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm}
case arg_mem_R_pm_imm8at0_offset:
Rn := Reg((x >> 16) & (1<<4 - 1))
u := (x >> 23) & 1
sign := int8(+1)
if u == 0 {
sign = -1
}
imm := int16(x&(1<<8-1)) << 2
return Mem{Base: Rn, Mode: AddrOffset, Offset: int16(sign) * imm}
case arg_option:
return Imm(x & (1<<4 - 1))
case arg_registers:
return RegList(x & (1<<16 - 1))
case arg_registers2:
x &= 1<<16 - 1
n := 0
for i := 0; i < 16; i++ {
if x>>uint(i)&1 != 0 {
n++
}
}
if n < 2 {
return nil
}
return RegList(x)
case arg_registers1:
Rt := (x >> 12) & (1<<4 - 1)
return RegList(1 << Rt)
case arg_satimm4:
return Imm((x >> 16) & (1<<4 - 1))
case arg_satimm5:
return Imm((x >> 16) & (1<<5 - 1))
case arg_satimm4m1:
return Imm((x>>16)&(1<<4-1) + 1)
case arg_satimm5m1:
return Imm((x>>16)&(1<<5-1) + 1)
case arg_widthm1:
return Imm((x>>16)&(1<<5-1) + 1)
}
}
// decodeShift decodes the shift-by-immediate encoded in x.
func decodeShift(x uint32) (Shift, uint8) {
count := (x >> 7) & (1<<5 - 1)
typ := Shift((x >> 5) & (1<<2 - 1))
switch typ {
case ShiftRight, ShiftRightSigned:
if count == 0 {
count = 32
}
case RotateRight:
if count == 0 {
typ = RotateRightExt
count = 1
}
}
return typ, uint8(count)
}
@@ -0,0 +1,69 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package armasm
import (
"encoding/hex"
"io/ioutil"
"strconv"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
all := string(data)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 4)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f[0])
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f[0])
}
size := i / 2
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f[0], err)
continue
}
mode, err := strconv.Atoi(f[1])
if err != nil {
t.Errorf("invalid mode %q in: %s", f[1], line)
continue
}
syntax, asm := f[2], f[3]
inst, err := Decode(code, Mode(mode))
var out string
if err != nil {
out = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst)
case "plan9": // [sic]
out = GoSyntax(inst, 0, nil, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
}
if out != asm || inst.Len != size {
t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size)
}
}
}
@@ -0,0 +1,615 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
// Copied and simplified from ../../x86/x86asm/ext_test.go.
package armasm
import (
"bufio"
"bytes"
"encoding/hex"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
var (
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint32
enc [4]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Arch Mode
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and be run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
arch Mode,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
) {
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
Arch: arch,
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, arch, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text || inst.Len != dec.nenc {
suffix := ""
if allowedMismatch(text, size, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
const start = 0x8000 // start address of text
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "armasm")
if err != nil {
return
}
file = f.Name()
f.Seek(start, io.SeekStart)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if len(x) > 4 {
x = x[:4]
}
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
}
w.Write(x)
w.Write(zeros[len(x):])
size += len(zeros)
})
return file, f, size, nil
}
var zeros = []byte{0, 0, 0, 0}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
if len(enc) < 4 {
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
}
return enc
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, mode Mode, src []byte) (inst Inst, text string) {
// If printTests is set, we record the coverage value
// before and after, and we write out the inputs for which
// coverage went up, in the format expected in testdata/decode.text.
// This produces a fairly small set of test cases that exercise nearly
// all the code.
var cover float64
if *printTests {
cover -= coverage()
}
inst, err := Decode(src, mode)
if err != nil {
text = "error: " + err.Error()
} else {
text = inst.String()
switch syntax {
//case "arm":
// text = ARMSyntax(inst)
case "gnu":
text = GNUSyntax(inst)
//case "plan9": // [sic]
// text = GoSyntax(inst, 0, nil)
default:
text = "error: unknown syntax " + syntax
}
}
if *printTests {
cover += coverage()
if cover > 0 {
max := len(src)
if max > 4 && inst.Len <= 4 {
max = 4
}
fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
}
}
return
}
// coverage returns a floating point number denoting the
// test coverage until now. The number increases when new code paths are exercised,
// both in the Go program and in the decoder byte code.
func coverage() float64 {
/*
testing.Coverage is not in the main distribution.
The implementation, which must go in package testing, is:
// Coverage reports the current code coverage as a fraction in the range [0, 1].
func Coverage() float64 {
var n, d int64
for _, counters := range cover.Counters {
for _, c := range counters {
if c > 0 {
n++
}
d++
}
}
if d == 0 {
return 0
}
return float64(n) / float64(d)
}
*/
var f float64
f += testing.Coverage()
f += decodeCoverage()
return f
}
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for s := s; s != ""; {
if strings.HasPrefix(s, prefix) {
return true
}
i := strings.Index(s, " ")
if i < 0 {
break
}
s = s[i+1:]
}
}
return false
}
// contains reports whether the text s contains any of the given substrings.
func contains(s string, substrings ...string) bool {
for _, sub := range substrings {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool signals whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = trimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
j += 2
}
return raw, true
}
var unhex = [256]byte{
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'A': 10,
'B': 11,
'C': 12,
'D': 13,
'E': 14,
'F': 15,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = trimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// trimSpace trims leading and trailing space from s, returning a subslice of s.
func trimSpace(s []byte) []byte {
j := len(s)
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
j--
}
i := 0
for i < j && (s[i] == ' ' || s[i] == '\t') {
i++
}
return s[i:j]
}
// pcrel matches instructions using relative addressing mode.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)?) 0x([0-9a-f]+)$`)
)
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// condCases generates conditional instructions.
func condCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
// All the strides are relatively prime to 2 and therefore to 2²⁸,
// so we will not repeat any instructions until we have tried all 2²⁸.
// Using a stride other than 1 is meant to visit the instructions in a
// pseudorandom order, which gives better variety in the set of
// test cases chosen by -printtests.
stride := uint32(10007)
n := 1 << 28 / 7
if testing.Short() {
stride = 100003
n = 1 << 28 / 1001
} else if *longTest {
stride = 200000033
n = 1 << 28
}
x := uint32(0)
for i := 0; i < n; i++ {
enc := (x%15)<<28 | x&(1<<28-1)
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
x += stride
}
}
}
// uncondCases generates unconditional instructions.
func uncondCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
condCases(t)(func(enc []byte) {
enc[3] |= 0xF0
try(enc)
})
}
}
func countBits(x uint32) int {
n := 0
for ; x != 0; x >>= 1 {
n += int(x & 1)
}
return n
}
func expandBits(x, m uint32) uint32 {
var out uint32
for i := uint(0); i < 32; i++ {
out >>= 1
if m&1 != 0 {
out |= (x & 1) << 31
x >>= 1
}
m >>= 1
}
return out
}
func tryCondMask(mask, val uint32, try func([]byte)) {
n := countBits(^mask)
bits := uint32(0)
for i := 0; i < 1<<uint(n); i++ {
bits += 848251 // arbitrary prime
x := val | expandBits(bits, ^mask) | uint32(i)%15<<28
try([]byte{byte(x), byte(x >> 8), byte(x >> 16), byte(x >> 24)})
}
}
// vfpCases generates VFP instructions.
func vfpCases(t *testing.T) func(func([]byte)) {
const (
vfpmask uint32 = 0xFF00FE10
vfp uint32 = 0x0E009A00
)
return func(try func([]byte)) {
tryCondMask(0xff00fe10, 0x0e009a00, try) // standard VFP instruction space
tryCondMask(0xffc00f7f, 0x0e000b10, try) // VFP MOV core reg to/from float64 half
tryCondMask(0xffe00f7f, 0x0e000a10, try) // VFP MOV core reg to/from float32
tryCondMask(0xffef0fff, 0x0ee10a10, try) // VFP MOV core reg to/from cond codes
}
}
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/decode.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T) func(func([]byte)) {
var codes [][]byte
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}
func caller(skip int) string {
pc, _, _, _ := runtime.Caller(skip)
f := runtime.FuncForPC(pc)
name := "?"
if f != nil {
name = f.Name()
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[i+1:]
}
}
return name
}
@@ -0,0 +1,164 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package armasm
import (
"bytes"
"fmt"
"strings"
)
var saveDot = strings.NewReplacer(
".F16", "_dot_F16",
".F32", "_dot_F32",
".F64", "_dot_F64",
".S32", "_dot_S32",
".U32", "_dot_U32",
".FXS", "_dot_S",
".FXU", "_dot_U",
".32", "_dot_32",
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This form typically matches the syntax defined in the ARM Reference Manual.
func GNUSyntax(inst Inst) string {
var buf bytes.Buffer
op := inst.Op.String()
op = saveDot.Replace(op)
op = strings.Replace(op, ".", "", -1)
op = strings.Replace(op, "_dot_", ".", -1)
op = strings.ToLower(op)
buf.WriteString(op)
sep := " "
for i, arg := range inst.Args {
if arg == nil {
break
}
text := gnuArg(&inst, i, arg)
if text == "" {
continue
}
buf.WriteString(sep)
sep = ", "
buf.WriteString(text)
}
return buf.String()
}
func gnuArg(inst *Inst, argIndex int, arg Arg) string {
switch inst.Op &^ 15 {
case LDRD_EQ, LDREXD_EQ, STRD_EQ:
if argIndex == 1 {
// second argument in consecutive pair not printed
return ""
}
case STREXD_EQ:
if argIndex == 2 {
// second argument in consecutive pair not printed
return ""
}
}
switch arg := arg.(type) {
case Imm:
switch inst.Op &^ 15 {
case BKPT_EQ:
return fmt.Sprintf("%#04x", uint32(arg))
case SVC_EQ:
return fmt.Sprintf("%#08x", uint32(arg))
}
return fmt.Sprintf("#%d", int32(arg))
case ImmAlt:
return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot)
case Mem:
R := gnuArg(inst, -1, arg.Base)
X := ""
if arg.Sign != 0 {
X = ""
if arg.Sign < 0 {
X = "-"
}
X += gnuArg(inst, -1, arg.Index)
if arg.Shift == ShiftLeft && arg.Count == 0 {
// nothing
} else if arg.Shift == RotateRightExt {
X += ", rrx"
} else {
X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count)
}
} else {
X = fmt.Sprintf("#%d", arg.Offset)
}
switch arg.Mode {
case AddrOffset:
if X == "#0" {
return fmt.Sprintf("[%s]", R)
}
return fmt.Sprintf("[%s, %s]", R, X)
case AddrPreIndex:
return fmt.Sprintf("[%s, %s]!", R, X)
case AddrPostIndex:
return fmt.Sprintf("[%s], %s", R, X)
case AddrLDM:
if X == "#0" {
return R
}
case AddrLDM_WB:
if X == "#0" {
return R + "!"
}
}
return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X)
case PCRel:
return fmt.Sprintf(".%+#x", int32(arg)+4)
case Reg:
switch inst.Op &^ 15 {
case LDREX_EQ:
if argIndex == 0 {
return fmt.Sprintf("r%d", int32(arg))
}
}
switch arg {
case R10:
return "sl"
case R11:
return "fp"
case R12:
return "ip"
}
case RegList:
var buf bytes.Buffer
fmt.Fprintf(&buf, "{")
sep := ""
for i := 0; i < 16; i++ {
if arg&(1<<uint(i)) != 0 {
fmt.Fprintf(&buf, "%s%s", sep, gnuArg(inst, -1, Reg(i)))
sep = ", "
}
}
fmt.Fprintf(&buf, "}")
return buf.String()
case RegShift:
if arg.Shift == ShiftLeft && arg.Count == 0 {
return gnuArg(inst, -1, arg.Reg)
}
if arg.Shift == RotateRightExt {
return gnuArg(inst, -1, arg.Reg) + ", rrx"
}
return fmt.Sprintf("%s, %s #%d", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), arg.Count)
case RegShiftReg:
return fmt.Sprintf("%s, %s %s", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), gnuArg(inst, -1, arg.RegCount))
}
return strings.ToLower(arg.String())
}
@@ -0,0 +1,438 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package armasm
import (
"bytes"
"fmt"
)
// A Mode is an instruction execution mode.
type Mode int
const (
_ Mode = iota
ModeARM
ModeThumb
)
func (m Mode) String() string {
switch m {
case ModeARM:
return "ARM"
case ModeThumb:
return "Thumb"
}
return fmt.Sprintf("Mode(%d)", int(m))
}
// An Op is an ARM opcode.
type Op uint16
// NOTE: The actual Op values are defined in tables.go.
// They are chosen to simplify instruction decoding and
// are not a dense packing from 0 to N, although the
// density is high, probably at least 90%.
func (op Op) String() string {
if op >= Op(len(opstr)) || opstr[op] == "" {
return fmt.Sprintf("Op(%d)", int(op))
}
return opstr[op]
}
// An Inst is a single instruction.
type Inst struct {
Op Op // Opcode mnemonic
Enc uint32 // Raw encoding bits.
Len int // Length of encoding in bytes.
Args Args // Instruction arguments, in ARM manual order.
}
func (i Inst) String() string {
var buf bytes.Buffer
buf.WriteString(i.Op.String())
for j, arg := range i.Args {
if arg == nil {
break
}
if j == 0 {
buf.WriteString(" ")
} else {
buf.WriteString(", ")
}
buf.WriteString(arg.String())
}
return buf.String()
}
// An Args holds the instruction arguments.
// If an instruction has fewer than 4 arguments,
// the final elements in the array are nil.
type Args [4]Arg
// An Arg is a single instruction argument, one of these types:
// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg.
type Arg interface {
IsArg()
String() string
}
type Float32Imm float32
func (Float32Imm) IsArg() {}
func (f Float32Imm) String() string {
return fmt.Sprintf("#%v", float32(f))
}
type Float64Imm float32
func (Float64Imm) IsArg() {}
func (f Float64Imm) String() string {
return fmt.Sprintf("#%v", float64(f))
}
// An Imm is an integer constant.
type Imm uint32
func (Imm) IsArg() {}
func (i Imm) String() string {
return fmt.Sprintf("#%#x", uint32(i))
}
// An ImmAlt is an alternate encoding of an integer constant.
type ImmAlt struct {
Val uint8
Rot uint8
}
func (ImmAlt) IsArg() {}
func (i ImmAlt) Imm() Imm {
v := uint32(i.Val)
r := uint(i.Rot)
return Imm(v>>r | v<<(32-r))
}
func (i ImmAlt) String() string {
return fmt.Sprintf("#%#x, %d", i.Val, i.Rot)
}
// A Label is a text (code) address.
type Label uint32
func (Label) IsArg() {}
func (i Label) String() string {
return fmt.Sprintf("%#x", uint32(i))
}
// A Reg is a single register.
// The zero value denotes R0, not the absence of a register.
type Reg uint8
const (
R0 Reg = iota
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13
R14
R15
S0
S1
S2
S3
S4
S5
S6
S7
S8
S9
S10
S11
S12
S13
S14
S15
S16
S17
S18
S19
S20
S21
S22
S23
S24
S25
S26
S27
S28
S29
S30
S31
D0
D1
D2
D3
D4
D5
D6
D7
D8
D9
D10
D11
D12
D13
D14
D15
D16
D17
D18
D19
D20
D21
D22
D23
D24
D25
D26
D27
D28
D29
D30
D31
APSR
APSR_nzcv
FPSCR
SP = R13
LR = R14
PC = R15
)
func (Reg) IsArg() {}
func (r Reg) String() string {
switch r {
case APSR:
return "APSR"
case APSR_nzcv:
return "APSR_nzcv"
case FPSCR:
return "FPSCR"
case SP:
return "SP"
case PC:
return "PC"
case LR:
return "LR"
}
if R0 <= r && r <= R15 {
return fmt.Sprintf("R%d", int(r-R0))
}
if S0 <= r && r <= S31 {
return fmt.Sprintf("S%d", int(r-S0))
}
if D0 <= r && r <= D31 {
return fmt.Sprintf("D%d", int(r-D0))
}
return fmt.Sprintf("Reg(%d)", int(r))
}
// A RegX represents a fraction of a multi-value register.
// The Index field specifies the index number,
// but the size of the fraction is not specified.
// It must be inferred from the instruction and the register type.
// For example, in a VMOV instruction, RegX{D5, 1} represents
// the top 32 bits of the 64-bit D5 register.
type RegX struct {
Reg Reg
Index int
}
func (RegX) IsArg() {}
func (r RegX) String() string {
return fmt.Sprintf("%s[%d]", r.Reg, r.Index)
}
// A RegList is a register list.
// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list.
type RegList uint16
func (RegList) IsArg() {}
func (r RegList) String() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "{")
sep := ""
for i := 0; i < 16; i++ {
if r&(1<<uint(i)) != 0 {
fmt.Fprintf(&buf, "%s%s", sep, Reg(i).String())
sep = ","
}
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// An Endian is the argument to the SETEND instruction.
type Endian uint8
const (
LittleEndian Endian = 0
BigEndian Endian = 1
)
func (Endian) IsArg() {}
func (e Endian) String() string {
if e != 0 {
return "BE"
}
return "LE"
}
// A Shift describes an ARM shift operation.
type Shift uint8
const (
ShiftLeft Shift = 0 // left shift
ShiftRight Shift = 1 // logical (unsigned) right shift
ShiftRightSigned Shift = 2 // arithmetic (signed) right shift
RotateRight Shift = 3 // right rotate
RotateRightExt Shift = 4 // right rotate through carry (Count will always be 1)
)
var shiftName = [...]string{
"LSL", "LSR", "ASR", "ROR", "RRX",
}
func (s Shift) String() string {
if s < 5 {
return shiftName[s]
}
return fmt.Sprintf("Shift(%d)", int(s))
}
// A RegShift is a register shifted by a constant.
type RegShift struct {
Reg Reg
Shift Shift
Count uint8
}
func (RegShift) IsArg() {}
func (r RegShift) String() string {
return fmt.Sprintf("%s %s #%d", r.Reg, r.Shift, r.Count)
}
// A RegShiftReg is a register shifted by a register.
type RegShiftReg struct {
Reg Reg
Shift Shift
RegCount Reg
}
func (RegShiftReg) IsArg() {}
func (r RegShiftReg) String() string {
return fmt.Sprintf("%s %s %s", r.Reg, r.Shift, r.RegCount)
}
// A PCRel describes a memory address (usually a code label)
// as a distance relative to the program counter.
// TODO(rsc): Define which program counter (PC+4? PC+8? PC?).
type PCRel int32
func (PCRel) IsArg() {}
func (r PCRel) String() string {
return fmt.Sprintf("PC%+#x", int32(r))
}
// An AddrMode is an ARM addressing mode.
type AddrMode uint8
const (
_ AddrMode = iota
AddrPostIndex // [R], X use address R, set R = R + X
AddrPreIndex // [R, X]! use address R + X, set R = R + X
AddrOffset // [R, X] use address R + X
AddrLDM // R [R] but formats as R, for LDM/STM only
AddrLDM_WB // R! - [R], X where X is instruction-specific amount, for LDM/STM only
)
// A Mem is a memory reference made up of a base R and index expression X.
// The effective memory address is R or R+X depending on AddrMode.
// The index expression is X = Sign*(Index Shift Count) + Offset,
// but in any instruction either Sign = 0 or Offset = 0.
type Mem struct {
Base Reg
Mode AddrMode
Sign int8
Index Reg
Shift Shift
Count uint8
Offset int16
}
func (Mem) IsArg() {}
func (m Mem) String() string {
R := m.Base.String()
X := ""
if m.Sign != 0 {
X = "+"
if m.Sign < 0 {
X = "-"
}
X += m.Index.String()
if m.Shift != ShiftLeft || m.Count != 0 {
X += fmt.Sprintf(", %s #%d", m.Shift, m.Count)
}
} else {
X = fmt.Sprintf("#%d", m.Offset)
}
switch m.Mode {
case AddrOffset:
if X == "#0" {
return fmt.Sprintf("[%s]", R)
}
return fmt.Sprintf("[%s, %s]", R, X)
case AddrPreIndex:
return fmt.Sprintf("[%s, %s]!", R, X)
case AddrPostIndex:
return fmt.Sprintf("[%s], %s", R, X)
case AddrLDM:
if X == "#0" {
return R
}
case AddrLDM_WB:
if X == "#0" {
return R + "!"
}
}
return fmt.Sprintf("[%s Mode(%d) %s]", R, int(m.Mode), X)
}
@@ -0,0 +1,268 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package armasm
import (
"encoding/binary"
"strings"
"testing"
)
func TestObjdumpARMTestdata(t *testing.T) { testObjdumpARM(t, testdataCases(t)) }
func TestObjdumpARMManual(t *testing.T) { testObjdumpARM(t, hexCases(t, objdumpManualTests)) }
func TestObjdumpARMCond(t *testing.T) { testObjdumpARM(t, condCases(t)) }
func TestObjdumpARMUncond(t *testing.T) { testObjdumpARM(t, uncondCases(t)) }
func TestObjdumpARMVFP(t *testing.T) { testObjdumpARM(t, vfpCases(t)) }
// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Manual, particularly with tracing enabled.
// Note that these are byte sequences, so they must be reversed from the usual
// word presentation.
var objdumpManualTests = `
002a9b1d
001b9bed
020b8ded
003a9b1d
060b8ded
fcde1100
b4de1100
bc480000
0b008de7
0b00ade7
fdbcfaf7
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
if hasPrefix(text, "error:") {
if hasPrefix(dec.text, unsupported...) || strings.Contains(dec.text, "invalid:") || strings.HasSuffix(dec.text, "^") || strings.Contains(dec.text, "f16.f64") || strings.Contains(dec.text, "f64.f16") {
return true
}
// word 4320F02C: libopcodes says 'nopmi {44}'.
if hasPrefix(dec.text, "nop") && strings.Contains(dec.text, "{") {
return true
}
}
if hasPrefix(dec.text, "error:") && text == "undef" && inst.Enc == 0xf7fabcfd {
return true
}
// word 00f02053: libopcodes says 'noppl {0}'.
if hasPrefix(dec.text, "nop") && hasPrefix(text, "nop") && dec.text == text+" {0}" {
return true
}
// word F57FF04F. we say 'dsb #15', libopcodes says 'dsb sy'.
if hasPrefix(text, "dsb") && hasPrefix(dec.text, "dsb") {
return true
}
// word F57FF06F. we say 'isb #15', libopcodes says 'isb sy'.
if hasPrefix(text, "isb") && hasPrefix(dec.text, "isb") {
return true
}
// word F57FF053. we say 'dmb #3', libopcodes says 'dmb osh'.
if hasPrefix(text, "dmb") && hasPrefix(dec.text, "dmb") {
return true
}
// word 992D0000. push/stmdb with no registers (undefined).
// we say 'stmdbls sp!, {}', libopcodes says 'pushls {}'.
if hasPrefix(text, "stmdb") && hasPrefix(dec.text, "push") && strings.Contains(text, "{}") && strings.Contains(dec.text, "{}") {
return true
}
// word 28BD0000. pop/ldm with no registers (undefined).
// we say 'ldmcs sp!, {}', libopcodes says 'popcs {}'.
if hasPrefix(text, "ldm") && hasPrefix(dec.text, "pop") && strings.Contains(text, "{}") && strings.Contains(dec.text, "{}") {
return true
}
// word 014640F0.
// libopcodes emits #-0 for negative zero; we don't.
if strings.Replace(dec.text, "#-0", "#0", -1) == text || strings.Replace(dec.text, ", #-0", "", -1) == text {
return true
}
// word 91EF90F0. we say 'strdls r9, [pc, #0]!' but libopcodes says 'strdls r9, [pc]'.
// word D16F60F0. we say 'strdle r6, [pc, #0]!' but libopcodes says 'strdle r6, [pc, #-0]'.
if strings.Replace(text, ", #0]!", "]", -1) == strings.Replace(dec.text, ", #-0]", "]", -1) {
return true
}
// word 510F4000. we say apsr, libopcodes says CPSR.
if strings.Replace(dec.text, "CPSR", "apsr", -1) == text {
return true
}
// word 06A4B059.
// for ssat and usat, libopcodes decodes asr #0 as asr #0 but the manual seems to say it should be asr #32.
// There is never an asr #0.
if strings.Replace(dec.text, ", asr #0", ", asr #32", -1) == text {
return true
}
if len(dec.enc) >= 4 {
raw := binary.LittleEndian.Uint32(dec.enc[:4])
// word 21FFF0B5.
// the manual is clear that this is pre-indexed mode (with !) but libopcodes generates post-index (without !).
if raw&0x01200000 == 0x01200000 && strings.Replace(text, "!", "", -1) == dec.text {
return true
}
// word C100543E: libopcodes says tst, but no evidence for that.
if strings.HasPrefix(dec.text, "tst") && raw&0x0ff00000 != 0x03100000 && raw&0x0ff00000 != 0x01100000 {
return true
}
// word C3203CE8: libopcodes says teq, but no evidence for that.
if strings.HasPrefix(dec.text, "teq") && raw&0x0ff00000 != 0x03300000 && raw&0x0ff00000 != 0x01300000 {
return true
}
// word D14C552E: libopcodes says cmp but no evidence for that.
if strings.HasPrefix(dec.text, "cmp") && raw&0x0ff00000 != 0x03500000 && raw&0x0ff00000 != 0x01500000 {
return true
}
// word 2166AA4A: libopcodes says cmn but no evidence for that.
if strings.HasPrefix(dec.text, "cmn") && raw&0x0ff00000 != 0x03700000 && raw&0x0ff00000 != 0x01700000 {
return true
}
// word E70AEEEF: libopcodes says str but no evidence for that.
if strings.HasPrefix(dec.text, "str") && len(dec.text) >= 5 && (dec.text[3] == ' ' || dec.text[5] == ' ') && raw&0x0e500018 != 0x06000000 && raw&0x0e500000 != 0x0400000 {
return true
}
// word B0AF48F4: libopcodes says strd but P=0,W=1 which is unpredictable.
if hasPrefix(dec.text, "ldr", "str") && raw&0x01200000 == 0x00200000 {
return true
}
// word B6CC1C76: libopcodes inexplicably says 'uxtab16lt r1, ip, r6, ROR #24' instead of 'uxtab16lt r1, ip, r6, ror #24'
if strings.ToLower(dec.text) == text {
return true
}
// word F410FDA1: libopcodes says PLDW but the manual is clear that PLDW is F5/F7, not F4.
// word F7D0FB17: libopcodes says PLDW but the manual is clear that PLDW has 0x10 clear
if hasPrefix(dec.text, "pld") && raw&0xfd000010 != 0xf5000000 {
return true
}
// word F650FE14: libopcodes says PLI but the manual is clear that PLI has 0x10 clear
if hasPrefix(dec.text, "pli") && raw&0xff000010 != 0xf6000000 {
return true
}
}
return false
}
// Instructions known to libopcodes (or xed) but not to us.
// Most of these are floating point coprocessor instructions.
var unsupported = strings.Fields(`
abs
acs
adf
aes
asn
atn
cdp
cf
cmf
cnf
cos
cps
crc32
dvf
eret
exp
fadd
fcmp
fcpy
fcvt
fdiv
fdv
fix
fld
flt
fmac
fmd
fml
fmr
fms
fmul
fmx
fneg
fnm
frd
fsit
fsq
fst
fsu
fto
fui
hlt
hvc
lda
ldc
ldf
lfm
lgn
log
mar
mcr
mcrr
mia
mnf
mra
mrc
mrrc
mrs
msr
msr
muf
mvf
nrm
pol
pow
rdf
rfc
rfe
rfs
rmf
rnd
rpw
rsf
sdiv
sev
sfm
sha1
sha256
sin
smc
sqt
srs
stc
stf
stl
suf
tan
udf
udiv
urd
vfma
vfms
vfnma
vfnms
vrint
wfc
wfs
`)
@@ -0,0 +1,259 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and simplified from ../../x86/x86asm/objdumpext_test.go.
package armasm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
const objdumpPath = "/usr/local/bin/arm-linux-elf-objdump"
func testObjdumpARM(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, ModeARM)
}
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) {
if testing.Short() {
t.Skip("skipping objdump test in short mode")
}
if _, err := os.Stat(objdumpPath); err != nil {
t.Skip(err)
}
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if ext.Arch == ModeARM {
if err := writeELF32(ext.File, ext.Size); err != nil {
return err
}
} else {
panic("unknown arch")
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint32 = start
addr uint32
encbuf [4]byte
enc []byte
text string
)
flush := func() {
if addr == next {
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
if strings.HasPrefix(text, "stmia") {
text = "stm" + text[5:]
}
if strings.HasPrefix(text, "stmfd") {
text = "stmdb" + text[5:]
}
if strings.HasPrefix(text, "ldmfd") {
text = "ldm" + text[5:]
}
text = strings.Replace(text, "#0.0", "#0", -1)
if text == "undefined" && len(enc) == 4 {
text = "error: unknown instruction"
enc = nil
}
if len(enc) == 4 {
// prints as word but we want to record bytes
enc[0], enc[3] = enc[3], enc[0]
enc[1], enc[2] = enc[2], enc[1]
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [4]byte{}
enc = nil
next += 4
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
undefined = []byte("<UNDEFINED>")
unpredictable = []byte("<UNPREDICTABLE>")
illegalShifter = []byte("<illegal shifter operand>")
)
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok := parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = trimSpace(line[i:])
if bytes.Contains(line, undefined) {
text = "undefined"
return
}
if bytes.Contains(line, illegalShifter) {
text = "undefined"
return
}
if false && bytes.Contains(line, unpredictable) {
text = "unpredictable"
return
}
if i := bytes.IndexByte(line, ';'); i >= 0 {
line = trimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF32 writes an ELF32 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF32(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header32
var prog elf.Prog32
var sect elf.Section32
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header32{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
Type: 2,
Machine: uint16(elf.EM_ARM),
Version: 1,
Entry: start,
Phoff: uint32(off1),
Shoff: uint32(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog32{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint32(size),
Memsz: uint32(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section32{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint32(size),
Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section32{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint32(off2 + (off3-off2)*3),
Size: uint32(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
@@ -0,0 +1,401 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package armasm
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"math"
"strings"
)
// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The pc is the program counter of the instruction, used for expanding
// PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name and base
// address of the symbol containing the target, if any; otherwise it returns "", 0.
// The reader r should read from the text segment using text addresses
// as offsets; it is used to display pc-relative loads as constant loads.
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var args []string
for _, a := range inst.Args {
if a == nil {
break
}
args = append(args, plan9Arg(&inst, pc, symname, a))
}
op := inst.Op.String()
switch inst.Op &^ 15 {
case LDR_EQ, LDRB_EQ, LDRH_EQ, LDRSB_EQ, LDRSH_EQ, VLDR_EQ:
// Check for RET
reg, _ := inst.Args[0].(Reg)
mem, _ := inst.Args[1].(Mem)
if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex {
return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset)
}
// Check for PC-relative load.
if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil {
addr := uint32(pc) + 8 + uint32(mem.Offset)
buf := make([]byte, 8)
switch inst.Op &^ 15 {
case LDRB_EQ, LDRSB_EQ:
if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil {
break
}
args[1] = fmt.Sprintf("$%#x", buf[0])
case LDRH_EQ, LDRSH_EQ:
if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil {
break
}
args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf))
case LDR_EQ:
if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil {
break
}
x := binary.LittleEndian.Uint32(buf)
if s, base := symname(uint64(x)); s != "" && uint64(x) == base {
args[1] = fmt.Sprintf("$%s(SB)", s)
} else {
args[1] = fmt.Sprintf("$%#x", x)
}
case VLDR_EQ:
switch {
case strings.HasPrefix(args[0], "D"): // VLDR.F64
if _, err := text.ReadAt(buf, int64(addr)); err != nil {
break
}
args[1] = fmt.Sprintf("$%f", math.Float64frombits(binary.LittleEndian.Uint64(buf)))
case strings.HasPrefix(args[0], "S"): // VLDR.F32
if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil {
break
}
args[1] = fmt.Sprintf("$%f", math.Float32frombits(binary.LittleEndian.Uint32(buf)))
default:
panic(fmt.Sprintf("wrong FP register: %v", inst))
}
}
}
}
// Move addressing mode into opcode suffix.
suffix := ""
switch inst.Op &^ 15 {
case PLD, PLI, PLD_W:
if mem, ok := inst.Args[0].(Mem); ok {
args[0], suffix = memOpTrans(mem)
} else {
panic(fmt.Sprintf("illegal instruction: %v", inst))
}
case LDR_EQ, LDRB_EQ, LDRSB_EQ, LDRH_EQ, LDRSH_EQ, STR_EQ, STRB_EQ, STRH_EQ, VLDR_EQ, VSTR_EQ, LDREX_EQ, LDREXH_EQ, LDREXB_EQ:
if mem, ok := inst.Args[1].(Mem); ok {
args[1], suffix = memOpTrans(mem)
} else {
panic(fmt.Sprintf("illegal instruction: %v", inst))
}
case SWP_EQ, SWP_B_EQ, STREX_EQ, STREXB_EQ, STREXH_EQ:
if mem, ok := inst.Args[2].(Mem); ok {
args[2], suffix = memOpTrans(mem)
} else {
panic(fmt.Sprintf("illegal instruction: %v", inst))
}
}
// Reverse args, placing dest last.
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
args[i], args[j] = args[j], args[i]
}
// For MLA-like instructions, the addend is the third operand.
switch inst.Op &^ 15 {
case SMLAWT_EQ, SMLAWB_EQ, MLA_EQ, MLA_S_EQ, MLS_EQ, SMMLA_EQ, SMMLS_EQ, SMLABB_EQ, SMLATB_EQ, SMLABT_EQ, SMLATT_EQ, SMLAD_EQ, SMLAD_X_EQ, SMLSD_EQ, SMLSD_X_EQ:
args = []string{args[1], args[2], args[0], args[3]}
}
// For STREX like instructions, the memory operands comes first.
switch inst.Op &^ 15 {
case STREX_EQ, STREXB_EQ, STREXH_EQ, SWP_EQ, SWP_B_EQ:
args = []string{args[1], args[0], args[2]}
}
// special process for FP instructions
op, args = fpTrans(&inst, op, args)
// LDR/STR like instructions -> MOV like
switch inst.Op &^ 15 {
case MOV_EQ:
op = "MOVW" + op[3:]
case LDR_EQ, MSR_EQ, MRS_EQ:
op = "MOVW" + op[3:] + suffix
case VMRS_EQ, VMSR_EQ:
op = "MOVW" + op[4:] + suffix
case LDRB_EQ, UXTB_EQ:
op = "MOVBU" + op[4:] + suffix
case LDRSB_EQ:
op = "MOVBS" + op[5:] + suffix
case SXTB_EQ:
op = "MOVBS" + op[4:] + suffix
case LDRH_EQ, UXTH_EQ:
op = "MOVHU" + op[4:] + suffix
case LDRSH_EQ:
op = "MOVHS" + op[5:] + suffix
case SXTH_EQ:
op = "MOVHS" + op[4:] + suffix
case STR_EQ:
op = "MOVW" + op[3:] + suffix
args[0], args[1] = args[1], args[0]
case STRB_EQ:
op = "MOVB" + op[4:] + suffix
args[0], args[1] = args[1], args[0]
case STRH_EQ:
op = "MOVH" + op[4:] + suffix
args[0], args[1] = args[1], args[0]
case VSTR_EQ:
args[0], args[1] = args[1], args[0]
default:
op = op + suffix
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return op
}
// assembler syntax for the various shifts.
// @x> is a lie; the assembler uses @> 0
// instead of @x> 1, but i wanted to be clear that it
// was a different operation (rotate right extended, not rotate right).
var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"}
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
switch a := arg.(type) {
case Endian:
case Imm:
return fmt.Sprintf("$%d", uint32(a))
case Mem:
case PCRel:
addr := uint32(pc) + 8 + uint32(a)
if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
return fmt.Sprintf("%s(SB)", s)
}
return fmt.Sprintf("%#x", addr)
case Reg:
if a < 16 {
return fmt.Sprintf("R%d", int(a))
}
case RegList:
var buf bytes.Buffer
start := -2
end := -2
fmt.Fprintf(&buf, "[")
flush := func() {
if start >= 0 {
if buf.Len() > 1 {
fmt.Fprintf(&buf, ",")
}
if start == end {
fmt.Fprintf(&buf, "R%d", start)
} else {
fmt.Fprintf(&buf, "R%d-R%d", start, end)
}
start = -2
end = -2
}
}
for i := 0; i < 16; i++ {
if a&(1<<uint(i)) != 0 {
if i == end+1 {
end++
continue
}
start = i
end = i
} else {
flush()
}
}
flush()
fmt.Fprintf(&buf, "]")
return buf.String()
case RegShift:
return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count))
case RegShiftReg:
return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount))
}
return strings.ToUpper(arg.String())
}
// convert memory operand from GNU syntax to Plan 9 syntax, for example,
// [r5] -> (R5)
// [r6, #4080] -> 0xff0(R6)
// [r2, r0, ror #1] -> (R2)(R0@>1)
// inst [r2, -r0, ror #1] -> INST.U (R2)(R0@>1)
// input:
//
// a memory operand
//
// return values:
//
// corresponding memory operand in Plan 9 syntax
// .W/.P/.U suffix
func memOpTrans(mem Mem) (string, string) {
suffix := ""
switch mem.Mode {
case AddrOffset, AddrLDM:
// no suffix
case AddrPreIndex, AddrLDM_WB:
suffix = ".W"
case AddrPostIndex:
suffix = ".P"
}
off := ""
if mem.Offset != 0 {
off = fmt.Sprintf("%#x", mem.Offset)
}
base := fmt.Sprintf("(R%d)", int(mem.Base))
index := ""
if mem.Sign != 0 {
sign := ""
if mem.Sign < 0 {
suffix += ".U"
}
shift := ""
if mem.Count != 0 {
shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count)
}
index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift)
}
return off + base + index, suffix
}
type goFPInfo struct {
op Op
transArgs []int // indexes of arguments which need transformation
gnuName string // instruction name in GNU syntax
goName string // instruction name in Plan 9 syntax
}
var fpInst []goFPInfo = []goFPInfo{
{VADD_EQ_F32, []int{2, 1, 0}, "VADD", "ADDF"},
{VADD_EQ_F64, []int{2, 1, 0}, "VADD", "ADDD"},
{VSUB_EQ_F32, []int{2, 1, 0}, "VSUB", "SUBF"},
{VSUB_EQ_F64, []int{2, 1, 0}, "VSUB", "SUBD"},
{VMUL_EQ_F32, []int{2, 1, 0}, "VMUL", "MULF"},
{VMUL_EQ_F64, []int{2, 1, 0}, "VMUL", "MULD"},
{VNMUL_EQ_F32, []int{2, 1, 0}, "VNMUL", "NMULF"},
{VNMUL_EQ_F64, []int{2, 1, 0}, "VNMUL", "NMULD"},
{VMLA_EQ_F32, []int{2, 1, 0}, "VMLA", "MULAF"},
{VMLA_EQ_F64, []int{2, 1, 0}, "VMLA", "MULAD"},
{VMLS_EQ_F32, []int{2, 1, 0}, "VMLS", "MULSF"},
{VMLS_EQ_F64, []int{2, 1, 0}, "VMLS", "MULSD"},
{VNMLA_EQ_F32, []int{2, 1, 0}, "VNMLA", "NMULAF"},
{VNMLA_EQ_F64, []int{2, 1, 0}, "VNMLA", "NMULAD"},
{VNMLS_EQ_F32, []int{2, 1, 0}, "VNMLS", "NMULSF"},
{VNMLS_EQ_F64, []int{2, 1, 0}, "VNMLS", "NMULSD"},
{VDIV_EQ_F32, []int{2, 1, 0}, "VDIV", "DIVF"},
{VDIV_EQ_F64, []int{2, 1, 0}, "VDIV", "DIVD"},
{VNEG_EQ_F32, []int{1, 0}, "VNEG", "NEGF"},
{VNEG_EQ_F64, []int{1, 0}, "VNEG", "NEGD"},
{VABS_EQ_F32, []int{1, 0}, "VABS", "ABSF"},
{VABS_EQ_F64, []int{1, 0}, "VABS", "ABSD"},
{VSQRT_EQ_F32, []int{1, 0}, "VSQRT", "SQRTF"},
{VSQRT_EQ_F64, []int{1, 0}, "VSQRT", "SQRTD"},
{VCMP_EQ_F32, []int{1, 0}, "VCMP", "CMPF"},
{VCMP_EQ_F64, []int{1, 0}, "VCMP", "CMPD"},
{VCMP_E_EQ_F32, []int{1, 0}, "VCMP.E", "CMPF"},
{VCMP_E_EQ_F64, []int{1, 0}, "VCMP.E", "CMPD"},
{VLDR_EQ, []int{1}, "VLDR", "MOV"},
{VSTR_EQ, []int{1}, "VSTR", "MOV"},
{VMOV_EQ_F32, []int{1, 0}, "VMOV", "MOVF"},
{VMOV_EQ_F64, []int{1, 0}, "VMOV", "MOVD"},
{VMOV_EQ_32, []int{1, 0}, "VMOV", "MOVW"},
{VMOV_EQ, []int{1, 0}, "VMOV", "MOVW"},
{VCVT_EQ_F64_F32, []int{1, 0}, "VCVT", "MOVFD"},
{VCVT_EQ_F32_F64, []int{1, 0}, "VCVT", "MOVDF"},
{VCVT_EQ_F32_U32, []int{1, 0}, "VCVT", "MOVWF.U"},
{VCVT_EQ_F32_S32, []int{1, 0}, "VCVT", "MOVWF"},
{VCVT_EQ_S32_F32, []int{1, 0}, "VCVT", "MOVFW"},
{VCVT_EQ_U32_F32, []int{1, 0}, "VCVT", "MOVFW.U"},
{VCVT_EQ_F64_U32, []int{1, 0}, "VCVT", "MOVWD.U"},
{VCVT_EQ_F64_S32, []int{1, 0}, "VCVT", "MOVWD"},
{VCVT_EQ_S32_F64, []int{1, 0}, "VCVT", "MOVDW"},
{VCVT_EQ_U32_F64, []int{1, 0}, "VCVT", "MOVDW.U"},
}
// convert FP instructions from GNU syntax to Plan 9 syntax, for example,
// vadd.f32 s0, s3, s4 -> ADDF F0, S3, F2
// vsub.f64 d0, d2, d4 -> SUBD F0, F2, F4
// vldr s2, [r11] -> MOVF (R11), F1
// inputs: instruction name and arguments in GNU syntax
// return values: corresponding instruction name and arguments in Plan 9 syntax
func fpTrans(inst *Inst, op string, args []string) (string, []string) {
for _, fp := range fpInst {
if inst.Op&^15 == fp.op {
// remove gnu syntax suffixes
op = strings.Replace(op, ".F32", "", -1)
op = strings.Replace(op, ".F64", "", -1)
op = strings.Replace(op, ".S32", "", -1)
op = strings.Replace(op, ".U32", "", -1)
op = strings.Replace(op, ".32", "", -1)
// compose op name
if fp.op == VLDR_EQ || fp.op == VSTR_EQ {
switch {
case strings.HasPrefix(args[fp.transArgs[0]], "D"):
op = "MOVD" + op[len(fp.gnuName):]
case strings.HasPrefix(args[fp.transArgs[0]], "S"):
op = "MOVF" + op[len(fp.gnuName):]
default:
panic(fmt.Sprintf("wrong FP register: %v", inst))
}
} else {
op = fp.goName + op[len(fp.gnuName):]
}
// transform registers
for ix, ri := range fp.transArgs {
switch {
case strings.HasSuffix(args[ri], "[1]"): // MOVW Rx, Dy[1]
break
case strings.HasSuffix(args[ri], "[0]"): // Dx[0] -> Fx
args[ri] = strings.Replace(args[ri], "[0]", "", -1)
fallthrough
case strings.HasPrefix(args[ri], "D"): // Dx -> Fx
args[ri] = "F" + args[ri][1:]
case strings.HasPrefix(args[ri], "S"):
if inst.Args[ix].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even
args[ri] = fmt.Sprintf("F%d", (inst.Args[ix].(Reg)-S0)/2)
}
case strings.HasPrefix(args[ri], "$"): // CMPF/CMPD $0, Fx
break
case strings.HasPrefix(args[ri], "R"): // MOVW Rx, Dy[1]
break
default:
panic(fmt.Sprintf("wrong FP register: %v", inst))
}
}
break
}
}
return op, args
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
newdecode.txt:
cd ..; go test -cover -run 'ObjdumpARMCond' -v -timeout 10h -printtests -long 2>&1 | tee log
cd ..; go test -cover -run 'ObjdumpARMUncond' -v -timeout 10h -printtests -long 2>&1 | tee -a log
egrep ' (gnu|plan9) ' ../log |sort >newdecode.txt
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,757 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Armmap constructs the ARM opcode map from the instruction set CSV file.
//
// Usage:
//
// armmap [-fmt=format] arm.csv
//
// The known output formats are:
//
// text (default) - print decoding tree in text form
// decoder - print decoding tables for the armasm package
package main
import (
"bufio"
"encoding/csv"
"flag"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
)
var format = flag.String("fmt", "text", "output format: text, decoder")
var inputFile string
func usage() {
fmt.Fprintf(os.Stderr, "usage: armmap [-fmt=format] x86.csv\n")
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("armmap: ")
flag.Usage = usage
flag.Parse()
if flag.NArg() != 1 {
usage()
}
inputFile = flag.Arg(0)
var print func(*Prog)
switch *format {
default:
log.Fatalf("unknown output format %q", *format)
case "text":
print = printText
case "decoder":
print = printDecoder
}
p, err := readCSV(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
print(p)
}
// readCSV reads the CSV file and returns the corresponding Prog.
// It may print details about problems to standard error using the log package.
func readCSV(file string) (*Prog, error) {
// Read input.
// Skip leading blank and # comment lines.
f, err := os.Open(file)
if err != nil {
return nil, err
}
b := bufio.NewReader(f)
for {
c, err := b.ReadByte()
if err != nil {
break
}
if c == '\n' {
continue
}
if c == '#' {
b.ReadBytes('\n')
continue
}
b.UnreadByte()
break
}
table, err := csv.NewReader(b).ReadAll()
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", file, err)
}
if len(table) == 0 {
return nil, fmt.Errorf("empty csv input")
}
if len(table[0]) < 5 {
return nil, fmt.Errorf("csv too narrow: need at least five columns")
}
p := &Prog{}
for _, row := range table {
add(p, row[0], row[1], row[2], row[3], row[4])
}
return p, nil
}
type Prog struct {
Inst []Inst
OpRanges map[string]string
}
type Inst struct {
Text string
Encoding string
Mask uint32
Value uint32
Priority int
OpBase string
OpBits uint64
Args []string
}
type Arg struct {
Name string
Bits uint64
}
// add adds the entry from the CSV described by maskstr, valuestr, text, encoding, tags
// to the program p.
func add(p *Prog, maskstr, valuestr, text, encoding, tags string) {
if strings.Contains(tags, "pseudo") {
return
}
// For now, ignore the VFP floating point instructions.
if strings.HasPrefix(text, "V") && !strings.Contains(tags, "vfp") {
// TODO
return
}
mask, err := strconv.ParseUint(maskstr, 0, 32)
if err != nil {
log.Printf("invalid mask %q", maskstr)
return
}
value, err := strconv.ParseUint(valuestr, 0, 32)
if err != nil {
log.Printf("invalid value %q", valuestr)
return
}
// Parse encoding, building size and offset of each field.
// The first field in the encoding is the largest offset.
fuzzy := uint32(0) // mask of 'should be' bits
fieldOffset := map[string]int{}
fieldWidth := map[string]int{}
off := 32
for _, f := range strings.Split(encoding, "|") {
n := 1
if i := strings.Index(f, ":"); i >= 0 {
n, _ = strconv.Atoi(f[i+1:])
}
off -= n
fieldOffset[f] = off
fieldWidth[f] = n
if f == "(0)" || f == "(1)" {
fuzzy |= 1 << uint(off)
}
}
if off != 0 {
fmt.Fprintf(os.Stderr, "%s: counted %d bits in %s\n", text, 32-off, encoding)
}
// Track which encoding fields we found uses for.
// If we do not find a use for a field, that's an error in the input tables.
fieldUsed := map[string]bool{}
// Split text into opcode and arguments.
var op, argstr string
if i := strings.Index(text, " "); i >= 0 {
op = text[:i]
argstr = text[i:]
} else {
op = text
}
op = strings.TrimSpace(op)
argstr = strings.TrimSpace(argstr)
// Parse opcode suffixes.
i := strings.Index(op, "<")
if i < 0 {
i = len(op)
}
if j := strings.Index(op, "{"); j >= 0 && j < i {
i = j
}
op, suffix := op[:i], op[i:]
if suffix != "" && opSuffix[suffix] == "" {
fmt.Fprintf(os.Stderr, "%s: invalid op suffix %q in %s\n", text, suffix, op+suffix)
}
// Make sure fields needed by opcode suffix are available.
for _, f := range strings.Split(opSuffix[suffix], ",") {
if f != "" && fieldWidth[f] == 0 {
fmt.Fprintf(os.Stderr, "%s: opsuffix %s missing %s in encoding %s\n", text, suffix, f, encoding)
}
fieldUsed[f] = true
}
// Build list of opcodes that can be generated by this suffix.
// For example, the opcodes generated by ADD<c> are ADD.EQ, ADD.NE, etc.
// To simplify the decoding of instruction opcodes, we arrange that this
// sequence aligns with the encoding, so that decoding amounts to extracting
// the right bits, concatenating them, and adding them to the first opcode in
// the sequence. If the condition code is present, we always place it in the
// low order bits, so that x&^15 == FOO_EQ tests whether x is any of the
// conditional FOO instructions.
ops := []string{op}
opBits := uint64(0) // record of bits to extract and add to opcode base
opFields := strings.Split(opSuffix[suffix], ",")
// First the optional elements, like {S} meaning "" or ".S".
for strings.HasPrefix(suffix, "{") {
i := strings.Index(suffix, "}")
var f, option string
option, suffix = suffix[1:i], suffix[i+1:]
f, opFields = opFields[0], opFields[1:]
if option == "W" {
// The {W} option on PLD{W} uses the R bit which is !W.
ops = cross(ops, "."+option, "")
} else {
ops = cross(ops, "", "."+option)
}
if fieldWidth[f] != 1 {
fmt.Fprintf(os.Stderr, "%s: have %d bits for {%s}\n", text, fieldWidth[f], option)
}
// opBits is a sequence of 16-bit chunks describing contiguous bit sections.
// Each chunk is 8-bit offset followed by 8-bit size.
opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | 1
}
// Then the true field substitutions.
haveCond := false
for strings.Contains(suffix, "<") {
var f, literal, x string
if len(opFields) == 0 {
fmt.Fprintf(os.Stderr, "%s: ran out of suffix fields for <%s>\n", text, x)
break
}
f, opFields = opFields[0], opFields[1:]
i := strings.Index(suffix, "<")
j := strings.Index(suffix, ">")
literal, x, suffix = suffix[:i], suffix[i+1:j], suffix[j+1:]
// Add leading literal text to all opcodes.
ops = cross(ops, literal)
// The <c> condition can happen anywhere in the opcode text
// but we want to generate the actual variation in the low bits
// of the list index. Remember when and where we've seen <c> and apply
// it after the loop has finished.
if x == "c" && f == "cond:4" {
haveCond = true
ops = cross(ops, "_COND_")
continue
}
// Otherwise, choices[x] lists the possible expansions of <x>.
// If <x> is of the form <A,B,C> the choices are A, B, and C.
expand := choices[x]
if expand == nil && strings.Contains(x, ",") {
expand = strings.Split(x, ",")
}
if expand == nil {
fmt.Fprintf(os.Stderr, "%s: unknown choices for <%s>\n", text, x)
expand = []string{x}
} else if len(expand) != 1<<uint(fieldWidth[f]) {
fmt.Fprintf(os.Stderr, "%s: have %d choices for <%s> but %d bits\n", text, len(expand), x, fieldWidth[f])
}
opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | uint64(fieldWidth[f])
ops = cross(ops, expand...)
}
if haveCond {
// Apply condtional suffix last.
opBits = opBits<<16 | 28<<8 | 4
ops = crossCond(ops)
}
ops = cross(ops, suffix)
// Now ops is a list of opcodes generated by this opcode pattern.
// We want to make sure that we can arrange for those opcodes to
// happen consecutively in the final opcode numbering.
// Record in p.OpRanges[op] the required consecutive sequence of
// opcode that includes op. To make searches easier, we record
// the sequence as a comma-separated list of strings with commas
// on both ends: [A, B] encodes as ",A,B,".
if p.OpRanges == nil {
p.OpRanges = make(map[string]string)
}
opstr := "," + strings.Join(ops, ",") + ","
for _, op := range ops {
if old := p.OpRanges[op]; old != "" && old != opstr {
if strings.Contains(old, opstr) {
opstr = old
} else if strings.Contains(opstr, old) {
// great, do nothing
} else {
// It would also be okay if there is some subsequence s such that
// old = x+s and opstr = s+y (or vice versa), in which case we should
// record opstr = x+s+y. However, this has not come up in practice.
// Failing that, we can't satisfy the sequencing requirements.
fmt.Fprintf(os.Stderr, "%s: %s appears in both %s and %s\n", text, op, old, opstr)
}
}
}
for _, op := range strings.Split(opstr, ",") {
if op != "" {
p.OpRanges[op] = opstr
}
}
// Process the arguments, building a list of argument descriptions.
// Each argument description has the form <argument>|field@off|field@off...
// where the |field@off suffixes give the name and location of the fields
// needed by the argument. Each such string maps to a different decoding
// type in the generated table, according to the argOps map.
var args []string
for argstr != "" {
// Find longest match among argSuffixes pieces.
best := 0
for a := range argSuffixes {
if argstr == a || strings.HasPrefix(argstr, a+",") {
if best < len(a) {
best = len(a)
}
}
}
if best == 0 {
fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", text, argstr)
break
}
var arg, desc string
arg, argstr = argstr[:best], strings.TrimSpace(strings.TrimLeft(argstr[best:], ","))
desc = arg
for _, f := range strings.Split(argSuffixes[desc], ",") {
if f == "" {
continue
}
if fieldWidth[f] == 0 {
fmt.Fprintf(os.Stderr, "%s: arg %s missing %s in encoding %s\n", text, arg, f, encoding)
}
fieldUsed[f] = true
desc += fmt.Sprintf("|%s@%d", f, fieldOffset[f])
}
args = append(args, desc)
}
// Check that all encoding fields were used by suffix or argument decoding.
for f := range fieldWidth {
switch f {
case "0", "1", "(0)", "(1)":
// ok
default:
if !fieldUsed[f] {
fmt.Fprintf(os.Stderr, "%s: encoding field %s not used in %s\n", text, f, encoding)
}
}
}
// Determine decoding priority. Instructions that say 'SEE X' in the tag
// are considered lower priority than ones that don't. In theory the
// structure described by the SEE tags might be richer than that, but
// in practice it only has those two levels.
// We leave space for two more priorities according to whether the
// fuzzy bits are set correctly. The full set of priorities then is:
//
// 4 - no SEE tag, fuzzy bits all match
// 3 - no SEE tag, some fuzzy bits don't match
// 2 - SEE tag, fuzzy bits all match
// 1 - SEE tag, some fuzzy bits don't match
//
// You could argue for swapping the middle two levels but so far
// it has not been an issue.
pri := 4
if strings.Contains(tags, "SEE") {
pri = 2
}
inst := Inst{
Text: text,
Encoding: encoding,
Mask: uint32(mask),
Value: uint32(value),
Priority: pri,
OpBase: ops[0],
OpBits: opBits,
Args: args,
}
p.Inst = append(p.Inst, inst)
if fuzzy != 0 {
inst.Mask &^= fuzzy
inst.Priority--
p.Inst = append(p.Inst, inst)
}
}
// opSuffix describes the encoding fields used to resolve a given opcode suffix.
var opSuffix = map[string]string{
"<ADD,SUB>": "op",
"<BIF,BIT,BSL>": "op:2",
"<MLA,MLS><c>.F<32,64>": "op,cond:4,sz",
"<MLS,MLA><c>.F<32,64>": "op,cond:4,sz",
"<BT,TB><c>": "tb,cond:4",
"<TBL,TBX>.8": "op",
"<c>": "cond:4",
"<c>.32": "cond:4",
"<c>.F<32,64>": "cond:4,sz",
"<x><y><c>": "N,M,cond:4",
"<y><c>": "M,cond:4",
"{B}<c>": "B,cond:4",
"{E}<c>.F<32,64>": "E,cond:4,sz",
"{R}<c>": "R,cond:4",
"<c>.F<32,64>.<U,S>32": "cond:4,sz,op",
"<R,><c>.<U,S>32.F<32,64>": "op,cond:4,signed,sz",
"{S}<c>": "S,cond:4",
"{W}": "R",
"{X}<c>": "M,cond:4",
"<B,T><c>.<F32.F16,F16.F32>": "T,cond:4,op",
"<c>.<F64.F32,F32.F64>": "cond:4,sz",
"<c>.FX<S,U><16,32>.F<32,64>": "cond:4,U,sx,sz",
"<c>.F<32,64>.FX<S,U><16,32>": "cond:4,sz,U,sx",
}
// choices[x] describes the choices for filling in "<"+x+">" in an opcode suffix.
// Opcodes that end up containing ZZ take up a numeric sequence value but are
// not exported in the package API.
var choices = map[string][]string{
"c": {".EQ", ".NE", ".CS", ".CC", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", "", ".ZZ"},
"x": {"B", "T"},
"y": {"B", "T"},
}
// argOps maps from argument descriptions to internal decoder name.
var argOps = map[string]string{
// 4-bit register encodings
"<Rm>|Rm:4@0": "arg_R_0",
"<Rn>|Rn:4@0": "arg_R_0",
"<Rt>|Rt:4@0": "arg_R_0",
"<Rm>|Rm:4@8": "arg_R_8",
"<Ra>|Ra:4@12": "arg_R_12",
"<Rd>|Rd:4@12": "arg_R_12",
"<RdLo>|RdLo:4@12": "arg_R_12",
"<Rt>|Rt:4@12": "arg_R_12",
"<Rt_nzcv>|Rt:4@12": "arg_R_12_nzcv",
"<Rd>|Rd:4@16": "arg_R_16",
"<RdHi>|RdHi:4@16": "arg_R_16",
"<Rn>|Rn:4@16": "arg_R_16",
// first and second of consecutive register pair
"<Rt1>|Rt:4@0": "arg_R1_0",
"<Rt1>|Rt:4@12": "arg_R1_12",
"<Rt2>|Rt:4@0": "arg_R2_0",
"<Rt2>|Rt:4@12": "arg_R2_12",
// register arithmetic
"<Rm>,<type> <Rs>|Rm:4@0|Rs:4@8|type:2@5": "arg_R_shift_R",
"<Rm>{,<shift>}|Rm:4@0|imm5:5@7|type:2@5": "arg_R_shift_imm",
"<Rn>{,<shift>}|Rn:4@0|imm5:5@7|sh@6": "arg_R_shift_imm",
"<Rm>{,LSL #<imm5>}|Rm:4@0|imm5:5@7": "arg_R_shift_imm",
"<Rm>{,<rotation>}|Rm:4@0|rotate:2@10": "arg_R_rotate",
// memory references
"<Rn>{!}|Rn:4@16|W@21": "arg_R_16_WB",
"[<Rn>]|Rn:4@16": "arg_mem_R",
"[<Rn>,+/-<Rm>{, <shift>}]{!}|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7|P@24|W@21": "arg_mem_R_pm_R_shift_imm_W",
"[<Rn>{,#+/-<imm8>}]{!}|Rn:4@16|P@24|U@23|W@21|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_W",
"[<Rn>] {,#+/-<imm8>}|Rn:4@16|U@23|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_postindex",
"[<Rn>{,#+/-<imm12>}]{!}|Rn:4@16|P@24|U@23|W@21|imm12:12@0": "arg_mem_R_pm_imm12_W",
"[<Rn>],#+/-<imm12>|Rn:4@16|imm12:12@0|U@23": "arg_mem_R_pm_imm12_postindex",
"[<Rn>,#+/-<imm12>]|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_offset",
"[<Rn>] {,#+/-<imm12>}|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_postindex",
"[<Rn>], +/-<Rm>|Rn:4@16|U@23|Rm:4@0": "arg_mem_R_pm_R_postindex",
"[<Rn>,+/-<Rm>]{!}|Rn:4@16|U@23|Rm:4@0|P@24|W@21": "arg_mem_R_pm_R_W",
"[<Rn>],+/-<Rm>{, <shift>}|Rn:4@16|Rm:4@0|imm5:5@7|type:2@5|U@23": "arg_mem_R_pm_R_shift_imm_postindex",
"[<Rn>,+/-<Rm>{, <shift>}]|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7": "arg_mem_R_pm_R_shift_imm_offset",
"[<Rn>{,#+/-<imm8>}]|Rn:4@16|U@23|imm8:8@0": "arg_mem_R_pm_imm8at0_offset",
// pc-relative constants
"<label+12>|imm12:12@0": "arg_label_p_12",
"<label-12>|imm12:12@0": "arg_label_m_12",
"<label+/-12>|imm12:12@0|U@23": "arg_label_pm_12",
"<label+/-4+4>|imm4H:4@8|imm4L:4@0|U@23": "arg_label_pm_4_4",
// constants
"#<const>|imm12:12@0": "arg_const",
"#<imm5>|imm5:5@7": "arg_imm5",
"#<imm5_nz>|imm5:5@7": "arg_imm5_nz",
"#<imm5_32>|imm5:5@7": "arg_imm5_32",
"<label24>|imm24:24@0": "arg_label24",
"#<lsb>|lsb:5@7": "arg_imm5",
"#<width>|lsb:5@7|msb:5@16": "arg_lsb_width",
"#<imm12+4>|imm12:12@8|imm4:4@0": "arg_imm_12at8_4at0",
"#<imm12+4>|imm12:12@0|imm4:4@16": "arg_imm_4at16_12at0",
"<label24H>|imm24:24@0|H@24": "arg_label24H",
"#<option>|option:4@0": "arg_option",
"#<widthm1>|widthm1:5@16": "arg_widthm1",
"#<sat_imm4>|sat_imm:4@16": "arg_satimm4",
"#<sat_imm5>|sat_imm:5@16": "arg_satimm5",
"#<sat_imm4m1>|sat_imm:4@16": "arg_satimm4m1",
"#<sat_imm5m1>|sat_imm:5@16": "arg_satimm5m1",
"#<imm24>|imm24:24@0": "arg_imm24",
// special
"<registers>|register_list:16@0": "arg_registers",
"<registers2>|register_list:16@0": "arg_registers2",
"<registers1>|Rt:4@12": "arg_registers1",
"<endian_specifier>|E@9": "arg_endian",
"SP": "arg_SP",
"APSR": "arg_APSR",
"FPSCR": "arg_FPSCR",
// VFP floating point registers
"<Sd>|Vd:4@12|D@22": "arg_Sd",
"<Sd,Dd>|Vd:4@12|D@22|sz@8": "arg_Sd_Dd",
"<Dd,Sd>|Vd:4@12|D@22|sz@8": "arg_Dd_Sd",
"<Sn>|Vn:4@16|N@7": "arg_Sn",
"<Sn,Dn>|Vn:4@16|N@7|sz@8": "arg_Sn_Dn",
"<Sm>|Vm:4@0|M@5": "arg_Sm",
"<Sm,Dm>|Vm:4@0|M@5|sz@8": "arg_Sm_Dm",
"#0.0": "arg_fp_0",
"#<imm_vfp>|imm4H:4@16|imm4L:4@0|sz@8": "arg_imm_vfp",
"#<fbits>|sx@7|imm4:4@0|i@5": "arg_fbits",
"<Dn[x]>|N@7|Vn:4@16|opc1@21": "arg_Dn_half",
"<Dd[x]>|D@7|Vd:4@16|opc1@21": "arg_Dn_half",
}
// argSuffixes describes the encoding fields needed for a particular suffix.
// The set of keys in argSuffixes also drives the identification of suffix pieces.
// For example, <Rm> and <Rm>{, <type> <Rs>} are both keys in the map
// and matching is done 'longest first', so "<Rm>, <Rm>{, <type> <Rs>}" is
// parsed as just two arguments despite the extra ", ".
// The field order in the map values must match the order expected in
// the argument descriptions in argOps.
var argSuffixes = map[string]string{
"#0": "",
"#0.0": "",
"#<const>": "imm12:12",
"#<fbits>": "sx,imm4:4,i",
"#<imm12+4>": "imm12:12,imm4:4",
"#<imm24>": "imm24:24",
"#<imm3>": "imm3:3",
"#<imm4>": "imm4:4",
"#<imm5>": "imm5:5",
"#<imm5_nz>": "imm5:5",
"#<imm5_32>": "imm5:5",
"#<imm6>": "imm6:6",
"#<immsize>": "size:2",
"#<imm_vfp>": "imm4H:4,imm4L:4,sz",
"#<sat_imm4>": "sat_imm:4",
"#<sat_imm5>": "sat_imm:5",
"#<sat_imm4m1>": "sat_imm:4",
"#<sat_imm5m1>": "sat_imm:5",
"#<lsb>": "lsb:5",
"#<option>": "option:4",
"#<width>": "lsb:5,msb:5",
"#<widthm1>": "widthm1:5",
"+/-<Rm>": "Rm:4,U",
"<Dd>": "D,Vd:4",
"<Dd[x]>": "D,Vd:4,opc1",
"<Dm>": "M,Vm:4",
"<Dm[x]>": "M,Vm:4,size:2",
"<Dn>": "N,Vn:4",
"<Dn[x]>": "N,Vn:4,opc1",
"<Dm[size_x]>": "imm4:4",
"<Qd>": "D,Vd:4",
"<Qm>": "M,Vm:4",
"<Qn>": "N,Vn:4",
"<Ra>": "Ra:4",
"<Rd>": "Rd:4",
"<RdHi>": "RdHi:4",
"<RdLo>": "RdLo:4",
"<Rm>": "Rm:4",
"<Rm>{,<rotation>}": "Rm:4,rotate:2",
"<Rm>{,<shift>}": "Rm:4,imm5:5,type:2",
"<Rm>{,LSL #<imm5>}": "Rm:4,imm5:5",
"<Rn>": "Rn:4",
"<Rn>{!}": "Rn:4,W",
"<Rn>{,<shift>}": "Rn:4,imm5:5,sh",
"<Rs>": "Rs:4",
"<Rt1>": "Rt:4",
"<Rt2>": "Rt:4",
"<Rt>": "Rt:4",
"<Rt_nzcv>": "Rt:4",
"<Sd>": "Vd:4,D",
"<Sm1>": "Vm:4,M",
"<Sm>": "Vm:4,M",
"<Sn>": "Vn:4,N",
"<Sd,Dd>": "Vd:4,D,sz",
"<Dd,Sd>": "Vd:4,D,sz",
"<Sn,Dn>": "Vn:4,N,sz",
"<Sm,Dm>": "Vm:4,M,sz",
"<endian_specifier>": "E",
"<label+/-12>": "imm12:12,U",
"<label+12>": "imm12:12",
"<label-12>": "imm12:12",
"<label24>": "imm24:24",
"<label24H>": "imm24:24,H",
"<label+/-4+4>": "imm4H:4,imm4L:4,U",
"<list4>": "D,Vd:4,type:4",
"<list3>": "D,Vd:4,index_align:4",
"<list3t>": "D,Vd:4,T",
"<list1>": "D,Vd:4",
"<list_len>": "N,Vn:4,len:2",
"<vlist32>": "D,Vd:4,imm8:8",
"<vlist64>": "D,Vd:4,imm8:8",
"<registers>": "register_list:16",
"<registers2>": "register_list:16",
"<registers1>": "Rt:4",
"APSR": "",
"<Rm>,<type> <Rs>": "Rm:4,Rs:4,type:2",
"FPSCR": "",
"SP": "",
"[<Rn>,#+/-<imm12>]": "Rn:4,U,imm12:12",
"[<Rn>,+/-<Rm>]{!}": "Rn:4,U,Rm:4,P,W",
"[<Rn>,+/-<Rm>{, <shift>}]": "Rn:4,U,Rm:4,type:2,imm5:5",
"[<Rn>,+/-<Rm>{, <shift>}]{!}": "Rn:4,U,Rm:4,type:2,imm5:5,P,W",
"[<Rn>] {,#+/-<imm12>}": "Rn:4,U,imm12:12",
"[<Rn>] {,#+/-<imm8>}": "Rn:4,U,imm4H:4,imm4L:4",
"[<Rn>]": "Rn:4",
"[<Rn>],#+/-<imm12>": "Rn:4,imm12:12,U",
"[<Rn>],+/-<Rm>{, <shift>}": "Rn:4,Rm:4,imm5:5,type:2,U",
"[<Rn>]{!}": "Rn:4,Rm:4",
"[<Rn>{@<align>}]{!}": "XXX",
"[<Rn>{,#+/-<imm12>}]{!}": "Rn:4,P,U,W,imm12:12",
"[<Rn>{,#+/-<imm8>}]{!}": "Rn:4,P,U,W,imm4H:4,imm4L:4",
"[<Rn>{,#+/-<imm8>}]": "Rn:4,U,imm8:8",
"[<Rn>], +/-<Rm>": "Rn:4,U,Rm:4",
"#<imm_simd1>": "i,imm3:3,imm4:4,cmode:4",
"#<imm_simd>": "op,i,imm3:3,imm4:4,cmode:4",
"#<imm_vs>": "L,imm6:6",
"#<imm_vsn>": "imm6:6",
}
// cross returns the string concatenation cross product of xs and ys.
func cross(xs []string, ys ...string) []string {
var xys []string
for _, x := range xs {
for _, y := range ys {
xys = append(xys, x+y)
}
}
return xys
}
// crossCond returns the cross product of xs with all the possible
// conditional execution suffixes. It is assumed that each string x in xs
// contains a substring _COND_ marking where the conditional suffix
// should be placed.
func crossCond(xs []string) []string {
ys := choices["c"]
var xys []string
for _, x := range xs {
i := strings.Index(x, "_COND_")
pre, post := x[:i], x[i+6:]
for _, y := range ys {
xys = append(xys, pre+y+post)
}
}
return xys
}
// printText implements the -fmt=text mode, which is not implemented (yet?).
func printText(p *Prog) {
log.Fatal("-fmt=text not implemented")
}
// printDecoder implements the -fmt=decoder mode.
// It emits the tables.go for package armasm's decoder.
func printDecoder(p *Prog) {
fmt.Printf("package armasm\n\n")
// Build list of opcodes sorted by name
// but preserving the sequential ranges needed for opcode decoding.
haveRange := make(map[string]string)
for _, r := range p.OpRanges {
haveRange[r] = r
}
var ranges []string
for _, r := range haveRange {
ranges = append(ranges, r)
}
sort.Strings(ranges)
// Emit const definitions for opcodes.
fmt.Printf("const (\n")
iota := 0
fmt.Printf("\t_ Op = iota\n")
iota++
for _, r := range ranges {
for _, op := range strings.Split(r, ",") {
if op == "" {
continue
}
// Assume if opcode says .EQ it is the start of a 16-wide
// iteration through the conditional suffixes. If so, emit
// blank names until the assigned value is 16-aligned.
if strings.Contains(op, ".EQ") {
for iota&15 != 0 {
fmt.Printf("\t_\n")
iota++
}
}
fmt.Printf("\t%s\n", strings.Replace(op, ".", "_", -1))
iota++
}
}
fmt.Printf(")\n")
// Emit slice mapping opcode number to name string.
fmt.Printf("\nvar opstr = [...]string{\n")
for _, r := range ranges {
for _, op := range strings.Split(r, ",") {
if op == "" {
continue
}
fmt.Printf("\t%s: %q,\n", strings.Replace(op, ".", "_", -1), op)
}
}
fmt.Printf("}\n")
// Emit decoding table.
unknown := map[string]bool{}
fmt.Printf("\nvar instFormats = [...]instFormat{\n")
for _, inst := range p.Inst {
fmt.Printf("\t{%#08x, %#08x, %d, %s, %#x, instArgs{", inst.Mask, inst.Value, inst.Priority, strings.Replace(inst.OpBase, ".", "_", -1), inst.OpBits)
for i, a := range inst.Args {
if i > 0 {
fmt.Printf(", ")
}
str := argOps[a]
if str == "" && !unknown[a] {
fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", inst.Text, a)
unknown[a] = true
}
fmt.Printf("%s", str)
}
fmt.Printf("}}, // %s %s\n", inst.Text, inst.Encoding)
}
fmt.Printf("}\n")
}
@@ -0,0 +1,610 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Armspec reads the “ARM Architecture Reference Manual”
// to collect instruction encoding details and writes those details to standard output
// in JSON format.
//
// # Warning Warning Warning
//
// This program is unfinished. It is being published in this incomplete form
// for interested readers, but do not expect it to be runnable or useful.
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"math"
"os"
"regexp"
"sort"
"strconv"
"strings"
"rsc.io/pdf"
)
type Inst struct {
Name string
ID string
Bits string
Arch string
Syntax []string
Code string
}
const debugPage = 0
var stdout *bufio.Writer
func main() {
log.SetFlags(0)
log.SetPrefix("armspec: ")
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "usage: armspec file.pdf\n")
os.Exit(2)
}
f, err := pdf.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
// Find instruction set reference in outline, to build instruction list.
instList := instHeadings(f.Outline())
if len(instList) < 200 {
log.Fatalf("only found %d instructions in table of contents", len(instList))
}
stdout = bufio.NewWriter(os.Stdout)
fmt.Fprintf(stdout, "[")
numTable := 0
defer stdout.Flush()
// Scan document looking for instructions.
// Must find exactly the ones in the outline.
n := f.NumPage()
PageLoop:
for pageNum := 1; pageNum <= n; pageNum++ {
if debugPage > 0 && pageNum != debugPage {
continue
}
if pageNum > 1127 {
break
}
p := f.Page(pageNum)
name, table := parsePage(pageNum, p)
if name == "" {
continue
}
if len(table) < 1 {
if false {
fmt.Fprintf(os.Stderr, "no encodings for instruction %q (page %d)\n", name, pageNum)
}
continue
}
for _, inst := range table {
if numTable > 0 {
fmt.Fprintf(stdout, ",")
}
numTable++
js, _ := json.Marshal(inst)
fmt.Fprintf(stdout, "\n%s", jsFix.Replace(string(js)))
}
for j, headline := range instList {
if name == headline {
instList[j] = ""
continue PageLoop
}
}
fmt.Fprintf(os.Stderr, "unexpected instruction %q (page %d)\n", name, pageNum)
}
fmt.Fprintf(stdout, "\n]\n")
stdout.Flush()
if debugPage == 0 {
for _, headline := range instList {
if headline != "" {
switch headline {
default:
fmt.Fprintf(os.Stderr, "missing instruction %q\n", headline)
case "CHKA": // ThumbEE
case "CPS": // system instruction
case "CPY": // synonym for MOV
case "ENTERX": // ThumbEE
case "F* (former VFP instruction mnemonics)": // synonyms
case "HB, HBL, HBLP, HBP": // ThumbEE
case "LEAVEX": // ThumbEE
case "MOV (shifted register)": // pseudo instruction for ASR, LSL, LSR, ROR, and RRX
case "NEG": // synonym for RSB
case "RFE": // system instruction
case "SMC (previously SMI)": // system instruction
case "SRS": // system instruction
case "SUBS PC, LR and related instructions": // system instruction
case "VAND (immediate)": // pseudo instruction
case "VCLE (register)": // pseudo instruction
case "VCLT (register)": // pseudo instruction
case "VORN (immediate)": // pseudo instruction
}
}
}
}
}
func instHeadings(outline pdf.Outline) []string {
return appendInstHeadings(outline, nil)
}
var instRE = regexp.MustCompile(`A[\d.]+ Alphabetical list of instructions`)
var childRE = regexp.MustCompile(`A[\d.]+ (.+)`)
var sectionRE = regexp.MustCompile(`^A[\d.]+$`)
var bitRE = regexp.MustCompile(`^( |[01]|\([01]\))*$`)
func appendInstHeadings(outline pdf.Outline, list []string) []string {
if instRE.MatchString(outline.Title) {
for _, child := range outline.Child {
m := childRE.FindStringSubmatch(child.Title)
if m == nil {
fmt.Fprintf(os.Stderr, "cannot parse section title: %s\n", child.Title)
continue
}
list = append(list, m[1])
}
}
for _, child := range outline.Child {
list = appendInstHeadings(child, list)
}
return list
}
const inch = 72.0
func parsePage(num int, p pdf.Page) (name string, table []Inst) {
content := p.Content()
var text []pdf.Text
for _, t := range content.Text {
if match(t, "Times-Roman", 7.2, "") {
t.FontSize = 9
}
if match(t, "Times-Roman", 6.72, "") && '0' <= t.S[0] && t.S[0] <= '9' {
t.S = string([]rune("⁰¹²³⁴⁵⁶⁷⁸⁹")[t.S[0]-'0'])
t.FontSize = 9
t.Y -= 2.28
}
if t.Font == "Gen_Arial" {
continue
}
text = append(text, t)
}
text = findWords(text)
for i, t := range text {
if t.Font == "Times" {
t.Font = "Times-Roman"
text[i] = t
}
}
if debugPage > 0 {
for _, t := range text {
fmt.Println(t)
}
for _, r := range content.Rect {
fmt.Println(r)
}
}
// Remove text we should ignore.
out := text[:0]
skip := false
for _, t := range text {
// skip page footer
if match(t, "Helvetica", 8, "A") || match(t, "Helvetica", 8, "ARM DDI") || match(t, "Helvetica-Oblique", 8, "Copyright") {
continue
}
// skip section header and body text
if match(t, "Helvetica-Bold", 12, "") && (sectionRE.MatchString(t.S) || t.S == "Alphabetical list of instructions") {
skip = true
continue
}
if skip && match(t, "Times-Roman", 9, "") {
continue
}
skip = false
out = append(out, t)
}
text = out
// Page header must say Instruction Details.
if len(text) == 0 || !match(text[0], "Helvetica-Oblique", 8, "Instruction Details") && !match(text[0], "Times-Roman", 9, "Instruction Details") {
return "", nil
}
text = text[1:]
isSection := func(text []pdf.Text, i int) int {
if i+2 <= len(text) && match(text[i], "Helvetica-Bold", 10, "") && sectionRE.MatchString(text[i].S) && match(text[i+1], "Helvetica-Bold", 10, "") {
return 2
}
if i+1 <= len(text) && match(text[i], "Helvetica-Bold", 10, "") && childRE.MatchString(text[i].S) {
return 1
}
return 0
}
// Skip dummy headlines and sections.
for d := isSection(text, 0); d != 0; d = isSection(text, 0) {
i := d
for i < len(text) && !match(text[i], "Helvetica-Bold", 9, "Encoding") && !match(text[i], "Helvetica-Bold", 10, "") {
i++
}
if isSection(text, i) == 0 {
break
}
text = text[i:]
}
// Next line is headline. Can wrap to multiple lines.
d := isSection(text, 0)
if d == 0 {
if debugPage > 0 {
fmt.Printf("non-inst-headline: %v\n", text[0])
}
checkNoEncodings(num, text)
return "", nil
}
if d == 2 {
name = text[1].S
text = text[2:]
} else if d == 1 {
m := childRE.FindStringSubmatch(text[0].S)
name = m[1]
text = text[1:]
}
for len(text) > 0 && match(text[0], "Helvetica-Bold", 10, "") {
name += " " + text[0].S
text = text[1:]
}
// Skip description.
for len(text) > 0 && (match(text[0], "Times-Roman", 9, "") || match(text[0], "LucidaSansTypewriteX", 6.48, "") || match(text[0], "Times-Bold", 10, "Note")) {
text = text[1:]
}
// Encodings follow.
warned := false
for i := 0; i < len(text); {
if match(text[i], "Helvetica-Bold", 10, "Assembler syntax") ||
match(text[i], "Helvetica-Bold", 9, "Modified operation in ThumbEE") ||
match(text[i], "Helvetica-Bold", 9, "Unallocated memory hints") ||
match(text[i], "Helvetica-Bold", 9, "Related encodings") ||
match(text[i], "Times-Roman", 9, "Figure A") ||
match(text[i], "Helvetica-Bold", 9, "Table A") ||
match(text[i], "Helvetica-Bold", 9, "VFP Instructions") ||
match(text[i], "Helvetica-Bold", 9, "VFP instructions") ||
match(text[i], "Helvetica-Bold", 9, "VFP vectors") ||
match(text[i], "Helvetica-Bold", 9, "FLDMX") ||
match(text[i], "Helvetica-Bold", 9, "FSTMX") ||
match(text[i], "Helvetica-Bold", 9, "Advanced SIMD and VFP") {
checkNoEncodings(num, text[i:])
break
}
if match(text[i], "Helvetica-Bold", 9, "Figure A") {
y := text[i].Y
i++
for i < len(text) && math.Abs(text[i].Y-y) < 2 {
i++
}
continue
}
if !match(text[i], "Helvetica-Bold", 9, "Encoding") {
if !warned {
warned = true
fmt.Fprintln(os.Stderr, "page", num, ": unexpected:", text[i])
}
i++
continue
}
inst := Inst{
Name: name,
}
enc := text[i].S
x := text[i].X
i++
// Possible subarchitecture notes.
for i < len(text) && text[i].X > x+36 {
if inst.Arch != "" {
inst.Arch += " "
}
inst.Arch += text[i].S
i++
}
// Encoding syntaxes.
for i < len(text) && (match(text[i], "LucidaSansTypewriteX", 6.48, "") || text[i].X > x+36) {
if text[i].X < x+0.25*inch {
inst.Syntax = append(inst.Syntax, text[i].S)
} else {
s := inst.Syntax[len(inst.Syntax)-1]
if !strings.Contains(s, "\t") {
s += "\t"
} else {
s += " "
}
s += text[i].S
inst.Syntax[len(inst.Syntax)-1] = s
}
i++
}
var bits, abits, aenc string
bits, i = readBitBox(inst.Name, inst.Syntax, content, text, i)
if strings.Contains(enc, " / ") {
if i < len(text) && match(text[i], "Times-Roman", 8, "") {
abits, i = readBitBox(inst.Name, inst.Syntax, content, text, i)
} else {
abits = bits
}
slash := strings.Index(enc, " / ")
aenc = "Encoding " + enc[slash+len(" / "):]
enc = enc[:slash]
}
// pseudocode
y0 := -1 * inch
tab := 0.0
for i < len(text) && match(text[i], "LucidaSansTypewriteX", 6.48, "") {
t := text[i]
i++
if math.Abs(t.Y-y0) < 3 {
// same line as last fragment, probably just two spaces
inst.Code += " " + t.S
continue
}
if inst.Code != "" {
inst.Code += "\n"
}
if t.X > x+0.1*inch {
if tab == 0 {
tab = t.X - x
}
inst.Code += strings.Repeat("\t", int((t.X-x)/tab+0.5))
} else {
tab = 0
}
inst.Code += t.S
y0 = t.Y
}
inst.ID = strings.TrimPrefix(enc, "Encoding ")
inst.Bits = bits
table = append(table, inst)
if abits != "" {
inst.ID = strings.TrimPrefix(aenc, "Encoding ")
inst.Bits = abits
table = append(table, inst)
}
}
return name, table
}
func readBitBox(name string, syntax []string, content pdf.Content, text []pdf.Text, i int) (string, int) {
// bit headings
y2 := 0.0
x1 := 0.0
x2 := 0.0
for i < len(text) && match(text[i], "Times-Roman", 8, "") {
if y2 == 0 {
y2 = text[i].Y
}
if x1 == 0 {
x1 = text[i].X
}
i++
}
// bit fields in box
y1 := 0.0
dy1 := 0.0
for i < len(text) && match(text[i], "Times-Roman", 9, "") {
if x2 < text[i].X+text[i].W {
x2 = text[i].X + text[i].W
}
y1 = text[i].Y
dy1 = text[i].FontSize
i++
}
if debugPage > 0 {
fmt.Println("encoding box", x1, y1, x2, y2)
}
// Find lines (thin rectangles) separating bit fields.
var bottom, top pdf.Rect
const (
yMargin = 0.25 * 72
xMargin = 2 * 72
)
for _, r := range content.Rect {
if r.Max.Y-r.Min.Y < 2 && x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1-yMargin < r.Min.Y && r.Min.Y < y1 {
bottom = r
}
if y1+dy1 < r.Min.Y && r.Min.Y < y2 {
top = r
}
}
}
if debugPage > 0 {
fmt.Println("top", top, "bottom", bottom)
}
const ε = 0.1 * 72
var bars []pdf.Rect
for _, r := range content.Rect {
if r.Max.X-r.Min.X < 2 && math.Abs(r.Min.Y-bottom.Min.Y) < ε && math.Abs(r.Max.Y-top.Min.Y) < ε {
bars = append(bars, r)
}
}
sort.Sort(RectHorizontal(bars))
// There are 16-bit and 32-bit encodings.
// In practice, they are about 2.65 and 5.3 inches wide, respectively.
// Use 4 inches as a cutoff.
nbit := 32
dx := top.Max.X - top.Min.X
if top.Max.X-top.Min.X < 4*72 {
nbit = 16
}
total := 0
var buf bytes.Buffer
for i := 0; i < len(bars)-1; i++ {
if i > 0 {
fmt.Fprintf(&buf, "|")
}
var sub []pdf.Text
x1, x2 := bars[i].Min.X, bars[i+1].Min.X
for _, t := range content.Text {
tx := t.X + t.W/2
ty := t.Y + t.FontSize/2
if x1 < tx && tx < x2 && y1 < ty && ty < y2 {
sub = append(sub, t)
}
}
var str []string
for _, t := range findWords(sub) {
str = append(str, t.S)
}
s := strings.Join(str, " ")
s = strings.Replace(s, ")(", ") (", -1)
n := len(strings.Fields(s))
b := int(float64(nbit)*(x2-x1)/dx + 0.5)
if n == b {
for j, f := range strings.Fields(s) {
if j > 0 {
fmt.Fprintf(&buf, "|")
}
fmt.Fprintf(&buf, "%s", f)
}
} else {
if n != 1 {
fmt.Fprintf(os.Stderr, "%s - %s - multi-field %d-bit encoding: %s\n", name, syntax, n, s)
}
fmt.Fprintf(&buf, "%s:%d", s, b)
}
total += b
}
if total != nbit || total == 0 {
fmt.Fprintf(os.Stderr, "%s - %s - %d-bit encoding\n", name, syntax, total)
}
return buf.String(), i
}
type RectHorizontal []pdf.Rect
func (x RectHorizontal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x RectHorizontal) Less(i, j int) bool { return x[i].Min.X < x[j].Min.X }
func (x RectHorizontal) Len() int { return len(x) }
func checkNoEncodings(num int, text []pdf.Text) {
for _, t := range text {
if match(t, "Helvetica-Bold", 9, "Encoding") {
fmt.Fprintf(os.Stderr, "page %d: unexpected encoding: %s\n", num, t.S)
}
}
}
func match(t pdf.Text, font string, size float64, substr string) bool {
return t.Font == font && math.Abs(t.FontSize-size) < 0.1 && strings.Contains(t.S, substr)
}
func findWords(chars []pdf.Text) (words []pdf.Text) {
// Sort by Y coordinate and normalize.
const nudge = 1
sort.Sort(pdf.TextVertical(chars))
old := -100000.0
for i, c := range chars {
if c.Y != old && math.Abs(old-c.Y) < nudge {
chars[i].Y = old
} else {
old = c.Y
}
}
// Sort by Y coordinate, breaking ties with X.
// This will bring letters in a single word together.
sort.Sort(pdf.TextVertical(chars))
// Loop over chars.
for i := 0; i < len(chars); {
// Find all chars on line.
j := i + 1
for j < len(chars) && chars[j].Y == chars[i].Y {
j++
}
var end float64
// Split line into words (really, phrases).
for k := i; k < j; {
ck := &chars[k]
s := ck.S
end = ck.X + ck.W
charSpace := ck.FontSize / 6
wordSpace := ck.FontSize * 2 / 3
l := k + 1
for l < j {
// Grow word.
cl := &chars[l]
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
s += cl.S
end = cl.X + cl.W
l++
continue
}
// Add space to phrase before next word.
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
s += " " + cl.S
end = cl.X + cl.W
l++
continue
}
break
}
f := ck.Font
f = strings.TrimSuffix(f, ",Italic")
f = strings.TrimSuffix(f, "-Italic")
words = append(words, pdf.Text{f, ck.FontSize, ck.X, ck.Y, end - ck.X, s})
k = l
}
i = j
}
return words
}
func sameFont(f1, f2 string) bool {
f1 = strings.TrimSuffix(f1, ",Italic")
f1 = strings.TrimSuffix(f1, "-Italic")
f2 = strings.TrimSuffix(f1, ",Italic")
f2 = strings.TrimSuffix(f1, "-Italic")
return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
}
var jsFix = strings.NewReplacer(
// `\u003c`, `<`,
// `\u003e`, `>`,
// `\u0026`, `&`,
// `\u0009`, `\t`,
)
func printTable(name string, table []Inst) {
_ = strconv.Atoi
}
@@ -0,0 +1,223 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
"strconv"
"strings"
)
var _ = os.Stdout
var _ = fmt.Sprintf
type Inst struct {
Name string
ID string
Bits string
Arch string
Syntax []string
Code string
Base uint32
Mask uint32
Prog []*Stmt
}
func main() {
data, err := ioutil.ReadFile("spec.json")
if err != nil {
log.Fatal(err)
}
var insts []Inst
if err := json.Unmarshal(data, &insts); err != nil {
log.Fatal(err)
}
var out []Inst
for _, inst := range insts {
inst.Prog = parse(inst.Name+" "+inst.ID, inst.Code)
if inst.ID[0] == 'A' && !strings.HasPrefix(inst.Syntax[0], "MSR<c>") && !strings.Contains(inst.Syntax[0], "<coproc>") && !strings.Contains(inst.Syntax[0], "VLDM") && !strings.Contains(inst.Syntax[0], "VSTM") {
out = append(out, inst)
}
}
insts = out
for i := range insts {
dosize(&insts[i])
}
var cond, special []Inst
for _, inst := range insts {
if inst.Base>>28 == 0xF {
special = append(special, inst)
} else {
cond = append(cond, inst)
}
}
fmt.Printf("special:\n")
split(special, 0xF0000000, 1)
fmt.Printf("cond:\n")
split(cond, 0xF0000000, 1)
}
func dosize(inst *Inst) {
var base, mask uint32
off := 0
for _, f := range strings.Split(inst.Bits, "|") {
if i := strings.Index(f, ":"); i >= 0 {
n, _ := strconv.Atoi(f[i+1:])
off += n
continue
}
for _, bit := range strings.Fields(f) {
switch bit {
case "0", "(0)":
mask |= 1 << uint(31-off)
case "1", "(1)":
base |= 1 << uint(31-off)
}
off++
}
}
if off != 16 && off != 32 {
log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off)
}
if off == 16 {
mask >>= 16
base >>= 16
}
mask |= base
inst.Mask = mask
inst.Base = base
}
func split(insts []Inst, used uint32, depth int) {
Again:
if len(insts) <= 1 {
for _, inst := range insts {
fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1))
}
return
}
m := ^used
for _, inst := range insts {
m &= inst.Mask
}
if m == 0 {
fmt.Printf("«%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts))
for _, inst := range insts {
fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1))
}
updated := false
for i := range insts {
if updateMask(&insts[i]) {
updated = true
}
}
fmt.Printf("»\n")
if updated {
goto Again
}
fmt.Printf("%*s%#08x masked out (%d)\n", depth*2, "", used, len(insts))
for _, inst := range insts {
fmt.Printf("%*s%#08x %#08x %s %s %v\n", depth*2+2, "", inst.Mask, inst.Base, inst.Syntax[0], inst.Bits, seeRE.FindAllString(inst.Code, -1))
}
//checkOverlap(used, insts)
return
}
for i := 31; i >= 0; i-- {
if m&(1<<uint(i)) != 0 {
m = 1 << uint(i)
break
}
}
var bit [2][]Inst
for _, inst := range insts {
b := (inst.Base / m) & 1
bit[b] = append(bit[b], inst)
}
for b, list := range bit {
if len(list) > 0 {
suffix := ""
if len(bit[1-b]) == 0 {
suffix = " (only)"
}
fmt.Printf("%*sbit %#08x = %d%s\n", depth*2, "", m, b, suffix)
split(list, used|m, depth+1)
}
}
}
var seeRE = regexp.MustCompile(`SEE ([^;\n]+)`)
func updateMask(inst *Inst) bool {
defer func() {
if err := recover(); err != nil {
fmt.Println("PANIC:", err)
return
}
}()
print(".")
println(inst.Name, inst.ID, inst.Bits)
println(inst.Code)
wiggle := ^inst.Mask &^ 0xF0000000
n := countbits(wiggle)
m1 := ^uint32(0)
m2 := ^uint32(0)
for i := uint32(0); i < 1<<uint(n); i++ {
w := inst.Base | expand(i, wiggle)
if !isValid(inst, w) {
continue
}
m1 &= w
m2 &= ^w
}
m := m1 | m2
m &^= 0xF0000000
m |= 0xF0000000 & inst.Mask
if m&^inst.Mask != 0 {
fmt.Printf("%s %s: mask=%#x but decided %#x\n", inst.Name, inst.ID, inst.Mask, m)
inst.Mask = m
inst.Base = m1
return true
}
if inst.Mask&^m != 0 {
fmt.Printf("%s %s: mask=%#x but got %#x\n", inst.Name, inst.ID, inst.Mask, m)
panic("bad updateMask")
}
return false
}
func countbits(x uint32) int {
n := 0
for ; x != 0; x >>= 1 {
n += int(x & 1)
}
return n
}
func expand(x, m uint32) uint32 {
var out uint32
for i := uint(0); i < 32; i++ {
out >>= 1
if m&1 != 0 {
out |= (x & 1) << 31
x >>= 1
}
m >>= 1
}
return out
}
@@ -0,0 +1,494 @@
// Generated by ARM internal tool
// DO NOT EDIT
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
// Naming for Go decoder arguments:
//
// - arg_Wd: a W register encoded in the Rd[4:0] field (31 is wzr)
//
// - arg_Xd: a X register encoded in the Rd[4:0] field (31 is xzr)
//
// - arg_Wds: a W register encoded in the Rd[4:0] field (31 is wsp)
//
// - arg_Xds: a X register encoded in the Rd[4:0] field (31 is sp)
//
// - arg_Wn: encoded in Rn[9:5]
//
// - arg_Wm: encoded in Rm[20:16]
//
// - arg_Wm_extend__UXTB_0__UXTH_1__LSL_UXTW_2__UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4:
// a W register encoded in Rm with an extend encoded in option[15:13] and an amount
// encoded in imm3[12:10] in the range [0,4].
//
// - arg_Rm_extend__UXTB_0__UXTH_1__UXTW_2__LSL_UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4:
// a W or X register encoded in Rm with an extend encoded in option[15:13] and an
// amount encoded in imm3[12:10] in the range [0,4]. If the extend is UXTX or SXTX,
// it's an X register else, it's a W register.
//
// - arg_Wm_shift__LSL_0__LSR_1__ASR_2__0_31:
// a W register encoded in Rm with a shift encoded in shift[23:22] and an amount
// encoded in imm6[15:10] in the range [0,31].
//
// - arg_IAddSub:
// An immediate for a add/sub instruction encoded in imm12[21:10] with an optional
// left shift of 12 encoded in shift[23:22].
//
// - arg_Rt_31_1__W_0__X_1:
// a W or X register encoded in Rt[4:0]. The width specifier is encoded in the field
// [31:31] (offset 31, bit count 1) and the register is W for 0 and X for 1.
//
// - arg_[s|u]label_FIELDS_POWER:
// a program label encoded as "FIELDS" times 2^POWER in the range [MIN, MAX] (determined
// by signd/unsigned, FIELDS and POWER), e.g.
// arg_slabel_imm14_2
// arg_slabel_imm19_2
// arg_slabel_imm26_2
// arg_slabel_immhi_immlo_0
// arg_slabel_immhi_immlo_12
//
// - arg_Xns_mem_post_imm7_8_signed:
// addressing mode of post-index with a base register: Xns and a signed offset encoded
// in the "imm7" field times 8
//
// - arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__3_1:
// addressing mode of extended register with a base register: Xns, an offset register
// (<Wm>|<Xm>) with an extend encoded in option[15:13] and a shift amount encoded in
// S[12:12] in the range [0,3] (S=0:0, S=1:3).
//
// - arg_Xns_mem_optional_imm12_4_unsigned:
// addressing mode of unsigned offset with a base register: Xns and an optional unsigned
// offset encoded in the "imm12" field times 4
//
// - arg_Xns_mem_wb_imm7_4_signed:
// addressing mode of pre-index with a base register: Xns and the signed offset encoded
// in the "imm7" field times 4
//
// - arg_Xns_mem_post_size_1_8_unsigned__4_0__8_1__16_2__32_3:
// a post-index immediate offset, encoded in the "size" field. It can have the following values:
// #4 when size = 00
// #8 when size = 01
// #16 when size = 10
// #32 when size = 11
//
// - arg_immediate_0_127_CRm_op2:
// an immediate encoded in "CRm:op2" in the range 0 to 127
//
// - arg_immediate_bitmask_64_N_imms_immr:
// a bitmask immediate for 64-bit variant and encoded in "N:imms:immr"
//
// - arg_immediate_SBFX_SBFM_64M_bitfield_width_64_imms:
// an immediate for the <width> bitfield of SBFX 64-bit variant
//
// - arg_immediate_shift_32_implicit_inverse_imm16_hw:
// a 32-bit immediate of the bitwise inverse of which can be encoded in "imm16:hw"
//
// - arg_cond_[Not]AllowALNV_[Invert|Normal]:
// a standard condition, encoded in the "cond" field, excluding (NotAllow) AL and NV with
// its least significant bit [Yes|No] inverted, e.g.
// arg_cond_AllowALNV_Normal
// arg_cond_NotAllowALNV_Invert
//
// - arg_immediate_OptLSL_amount_16_0_48:
// An immediate for MOV[KNZ] instruction encoded in imm16[20:5] with an optional
// left shift of 16 in the range [0, 48] encoded in hw[22, 21]
//
// - arg_immediate_0_width_m1_immh_immb__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4__UIntimmhimmb64_8:
// the left shift amount, in the range 0 to the operand width in bits minus 1,
// encoded in the "immh:immb" field. It can have the following values:
// (UInt(immh:immb)-8) when immh = 0001
// (UInt(immh:immb)-16) when immh = 001x
// (UInt(immh:immb)-32) when immh = 01xx
// (UInt(immh:immb)-64) when immh = 1xxx
//
// - arg_immediate_1_width_immh_immb__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4:
// the right shift amount, in the range 1 to the destination operand width in
// bits, encoded in the "immh:immb" field. It can have the following values:
// (16-UInt(immh:immb)) when immh = 0001
// (32-UInt(immh:immb)) when immh = 001x
// (64-UInt(immh:immb)) when immh = 01xx
//
// - arg_immediate_8x8_a_b_c_d_e_f_g_h:
// a 64-bit immediate 'aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh',
// encoded in "a:b:c:d:e:f:g:h".
//
// - arg_immediate_fbits_min_1_max_32_sub_64_scale:
// the number of bits after the binary point in the fixed-point destination,
// in the range 1 to 32, encoded as 64 minus "scale".
//
// - arg_immediate_floatzero: #0.0
//
// - arg_immediate_exp_3_pre_4_a_b_c_d_e_f_g_h:
// a signed floating-point constant with 3-bit exponent and normalized 4 bits of precision,
// encoded in "a:b:c:d:e:f:g:h"
//
// - arg_immediate_fbits_min_1_max_0_sub_0_immh_immb__64UIntimmhimmb_4__128UIntimmhimmb_8:
// the number of fractional bits, in the range 1 to the operand width, encoded
// in the "immh:immb" field. It can have the following values:
// (64-UInt(immh:immb)) when immh = 01xx
// (128-UInt(immh:immb)) when immh = 1xxx
//
// - arg_immediate_index_Q_imm4__imm4lt20gt_00__imm4_10:
// the lowest numbered byte element to be extracted, encoded in the "Q:imm4" field.
// It can have the following values:
// imm4<2:0> when Q = 0, imm4<3> = 0
// imm4 when Q = 1, imm4<3> = x
//
// - arg_sysop_AT_SYS_CR_system:
// system operation for system instruction: AT encoded in the "op1:CRm<0>:op2" field
//
// - arg_prfop_Rt:
// prefectch operation encoded in the "Rt"
//
// - arg_sysreg_o0_op1_CRn_CRm_op2:
// system register name encoded in the "o0:op1:CRn:CRm:op2"
//
// - arg_pstatefield_op1_op2__SPSel_05__DAIFSet_36__DAIFClr_37:
// PSTATE field name encoded in the "op1:op2" field
//
// - arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31:
// one register with arrangement specifier encoded in the "size:Q" field which can have the following values:
// 8B when size = 00, Q = 0
// 16B when size = 00, Q = 1
// 4H when size = 01, Q = 0
// 8H when size = 01, Q = 1
// 2S when size = 10, Q = 0
// 4S when size = 10, Q = 1
// 2D when size = 11, Q = 1
// The encoding size = 11, Q = 0 is reserved.
//
// - arg_Vt_3_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31:
// three registers with arrangement specifier encoded in the "size:Q" field which can have the following values:
// 8B when size = 00, Q = 0
// 16B when size = 00, Q = 1
// 4H when size = 01, Q = 0
// 8H when size = 01, Q = 1
// 2S when size = 10, Q = 0
// 4S when size = 10, Q = 1
// 2D when size = 11, Q = 1
// The encoding size = 11, Q = 0 is reserved.
//
// - arg_Vt_1_arrangement_H_index__Q_S_size_1:
// one register with arrangement:H and element index encoded in "Q:S:size<1>".
type instArg uint16
const (
_ instArg = iota
arg_Bt
arg_Cm
arg_Cn
arg_cond_AllowALNV_Normal
arg_conditional
arg_cond_NotAllowALNV_Invert
arg_Da
arg_Dd
arg_Dm
arg_Dn
arg_Dt
arg_Dt2
arg_Hd
arg_Hn
arg_Ht
arg_IAddSub
arg_immediate_0_127_CRm_op2
arg_immediate_0_15_CRm
arg_immediate_0_15_nzcv
arg_immediate_0_31_imm5
arg_immediate_0_31_immr
arg_immediate_0_31_imms
arg_immediate_0_63_b5_b40
arg_immediate_0_63_immh_immb__UIntimmhimmb64_8
arg_immediate_0_63_immr
arg_immediate_0_63_imms
arg_immediate_0_65535_imm16
arg_immediate_0_7_op1
arg_immediate_0_7_op2
arg_immediate_0_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4
arg_immediate_0_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4__UIntimmhimmb64_8
arg_immediate_0_width_m1_immh_immb__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4__UIntimmhimmb64_8
arg_immediate_0_width_size__8_0__16_1__32_2
arg_immediate_1_64_immh_immb__128UIntimmhimmb_8
arg_immediate_1_width_immh_immb__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4
arg_immediate_1_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4
arg_immediate_1_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4__128UIntimmhimmb_8
arg_immediate_8x8_a_b_c_d_e_f_g_h
arg_immediate_ASR_SBFM_32M_bitfield_0_31_immr
arg_immediate_ASR_SBFM_64M_bitfield_0_63_immr
arg_immediate_BFI_BFM_32M_bitfield_lsb_32_immr
arg_immediate_BFI_BFM_32M_bitfield_width_32_imms
arg_immediate_BFI_BFM_64M_bitfield_lsb_64_immr
arg_immediate_BFI_BFM_64M_bitfield_width_64_imms
arg_immediate_BFXIL_BFM_32M_bitfield_lsb_32_immr
arg_immediate_BFXIL_BFM_32M_bitfield_width_32_imms
arg_immediate_BFXIL_BFM_64M_bitfield_lsb_64_immr
arg_immediate_BFXIL_BFM_64M_bitfield_width_64_imms
arg_immediate_bitmask_32_imms_immr
arg_immediate_bitmask_64_N_imms_immr
arg_immediate_exp_3_pre_4_a_b_c_d_e_f_g_h
arg_immediate_exp_3_pre_4_imm8
arg_immediate_fbits_min_1_max_0_sub_0_immh_immb__64UIntimmhimmb_4__128UIntimmhimmb_8
arg_immediate_fbits_min_1_max_0_sub_0_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__64UIntimmhimmb_4__128UIntimmhimmb_8
arg_immediate_fbits_min_1_max_32_sub_64_scale
arg_immediate_fbits_min_1_max_64_sub_64_scale
arg_immediate_floatzero
arg_immediate_index_Q_imm4__imm4lt20gt_00__imm4_10
arg_immediate_LSL_UBFM_32M_bitfield_0_31_immr
arg_immediate_LSL_UBFM_64M_bitfield_0_63_immr
arg_immediate_LSR_UBFM_32M_bitfield_0_31_immr
arg_immediate_LSR_UBFM_64M_bitfield_0_63_immr
arg_immediate_MSL__a_b_c_d_e_f_g_h_cmode__8_0__16_1
arg_immediate_optional_0_15_CRm
arg_immediate_optional_0_65535_imm16
arg_immediate_OptLSL__a_b_c_d_e_f_g_h_cmode__0_0__8_1
arg_immediate_OptLSL__a_b_c_d_e_f_g_h_cmode__0_0__8_1__16_2__24_3
arg_immediate_OptLSL_amount_16_0_16
arg_immediate_OptLSL_amount_16_0_48
arg_immediate_OptLSLZero__a_b_c_d_e_f_g_h
arg_immediate_SBFIZ_SBFM_32M_bitfield_lsb_32_immr
arg_immediate_SBFIZ_SBFM_32M_bitfield_width_32_imms
arg_immediate_SBFIZ_SBFM_64M_bitfield_lsb_64_immr
arg_immediate_SBFIZ_SBFM_64M_bitfield_width_64_imms
arg_immediate_SBFX_SBFM_32M_bitfield_lsb_32_immr
arg_immediate_SBFX_SBFM_32M_bitfield_width_32_imms
arg_immediate_SBFX_SBFM_64M_bitfield_lsb_64_immr
arg_immediate_SBFX_SBFM_64M_bitfield_width_64_imms
arg_immediate_shift_32_implicit_imm16_hw
arg_immediate_shift_32_implicit_inverse_imm16_hw
arg_immediate_shift_64_implicit_imm16_hw
arg_immediate_shift_64_implicit_inverse_imm16_hw
arg_immediate_UBFIZ_UBFM_32M_bitfield_lsb_32_immr
arg_immediate_UBFIZ_UBFM_32M_bitfield_width_32_imms
arg_immediate_UBFIZ_UBFM_64M_bitfield_lsb_64_immr
arg_immediate_UBFIZ_UBFM_64M_bitfield_width_64_imms
arg_immediate_UBFX_UBFM_32M_bitfield_lsb_32_immr
arg_immediate_UBFX_UBFM_32M_bitfield_width_32_imms
arg_immediate_UBFX_UBFM_64M_bitfield_lsb_64_immr
arg_immediate_UBFX_UBFM_64M_bitfield_width_64_imms
arg_immediate_zero
arg_option_DMB_BO_system_CRm
arg_option_DSB_BO_system_CRm
arg_option_ISB_BI_system_CRm
arg_prfop_Rt
arg_pstatefield_op1_op2__SPSel_05__DAIFSet_36__DAIFClr_37
arg_Qd
arg_Qn
arg_Qt
arg_Qt2
arg_Rm_extend__UXTB_0__UXTH_1__UXTW_2__LSL_UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4
arg_Rn_16_5__W_1__W_2__W_4__X_8
arg_Rt_31_1__W_0__X_1
arg_Sa
arg_Sd
arg_slabel_imm14_2
arg_slabel_imm19_2
arg_slabel_imm26_2
arg_slabel_immhi_immlo_0
arg_slabel_immhi_immlo_12
arg_Sm
arg_Sn
arg_St
arg_St2
arg_sysop_AT_SYS_CR_system
arg_sysop_DC_SYS_CR_system
arg_sysop_IC_SYS_CR_system
arg_sysop_SYS_CR_system
arg_sysop_TLBI_SYS_CR_system
arg_sysreg_o0_op1_CRn_CRm_op2
arg_Vd_16_5__B_1__H_2__S_4__D_8
arg_Vd_19_4__B_1__H_2__S_4
arg_Vd_19_4__B_1__H_2__S_4__D_8
arg_Vd_19_4__D_8
arg_Vd_19_4__S_4__D_8
arg_Vd_22_1__S_0
arg_Vd_22_1__S_0__D_1
arg_Vd_22_1__S_1
arg_Vd_22_2__B_0__H_1__S_2
arg_Vd_22_2__B_0__H_1__S_2__D_3
arg_Vd_22_2__D_3
arg_Vd_22_2__H_0__S_1__D_2
arg_Vd_22_2__H_1__S_2
arg_Vd_22_2__S_1__D_2
arg_Vd_arrangement_16B
arg_Vd_arrangement_2D
arg_Vd_arrangement_4S
arg_Vd_arrangement_D_index__1
arg_Vd_arrangement_imm5___B_1__H_2__S_4__D_8_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4__imm5lt4gt_8_1
arg_Vd_arrangement_imm5_Q___8B_10__16B_11__4H_20__8H_21__2S_40__4S_41__2D_81
arg_Vd_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__2S_40__4S_41__2D_81
arg_Vd_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41
arg_Vd_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41__2D_81
arg_Vd_arrangement_immh___SEEAdvancedSIMDmodifiedimmediate_0__8H_1__4S_2__2D_4
arg_Vd_arrangement_Q___2S_0__4S_1
arg_Vd_arrangement_Q___4H_0__8H_1
arg_Vd_arrangement_Q___8B_0__16B_1
arg_Vd_arrangement_Q_sz___2S_00__4S_10__2D_11
arg_Vd_arrangement_size___4S_1__2D_2
arg_Vd_arrangement_size___8H_0__1Q_3
arg_Vd_arrangement_size___8H_0__4S_1__2D_2
arg_Vd_arrangement_size_Q___4H_00__8H_01__2S_10__4S_11__1D_20__2D_21
arg_Vd_arrangement_size_Q___4H_10__8H_11__2S_20__4S_21
arg_Vd_arrangement_size_Q___8B_00__16B_01
arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11
arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21
arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vd_arrangement_sz___4S_0__2D_1
arg_Vd_arrangement_sz_Q___2S_00__4S_01
arg_Vd_arrangement_sz_Q___2S_00__4S_01__2D_11
arg_Vd_arrangement_sz_Q___2S_10__4S_11
arg_Vd_arrangement_sz_Q___4H_00__8H_01__2S_10__4S_11
arg_Vm_22_1__S_0__D_1
arg_Vm_22_2__B_0__H_1__S_2__D_3
arg_Vm_22_2__D_3
arg_Vm_22_2__H_1__S_2
arg_Vm_arrangement_4S
arg_Vm_arrangement_Q___8B_0__16B_1
arg_Vm_arrangement_size___8H_0__4S_1__2D_2
arg_Vm_arrangement_size___H_1__S_2_index__size_L_H_M__HLM_1__HL_2_1
arg_Vm_arrangement_size_Q___4H_10__8H_11__2S_20__4S_21
arg_Vm_arrangement_size_Q___8B_00__16B_01
arg_Vm_arrangement_size_Q___8B_00__16B_01__1D_30__2D_31
arg_Vm_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21
arg_Vm_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vm_arrangement_sz_Q___2S_00__4S_01__2D_11
arg_Vm_arrangement_sz___S_0__D_1_index__sz_L_H__HL_00__H_10_1
arg_Vn_19_4__B_1__H_2__S_4__D_8
arg_Vn_19_4__D_8
arg_Vn_19_4__H_1__S_2__D_4
arg_Vn_19_4__S_4__D_8
arg_Vn_1_arrangement_16B
arg_Vn_22_1__D_1
arg_Vn_22_1__S_0__D_1
arg_Vn_22_2__B_0__H_1__S_2__D_3
arg_Vn_22_2__D_3
arg_Vn_22_2__H_0__S_1__D_2
arg_Vn_22_2__H_1__S_2
arg_Vn_2_arrangement_16B
arg_Vn_3_arrangement_16B
arg_Vn_4_arrangement_16B
arg_Vn_arrangement_16B
arg_Vn_arrangement_4S
arg_Vn_arrangement_D_index__1
arg_Vn_arrangement_D_index__imm5_1
arg_Vn_arrangement_imm5___B_1__H_2_index__imm5__imm5lt41gt_1__imm5lt42gt_2_1
arg_Vn_arrangement_imm5___B_1__H_2__S_4__D_8_index__imm5_imm4__imm4lt30gt_1__imm4lt31gt_2__imm4lt32gt_4__imm4lt3gt_8_1
arg_Vn_arrangement_imm5___B_1__H_2__S_4__D_8_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4__imm5lt4gt_8_1
arg_Vn_arrangement_imm5___B_1__H_2__S_4_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4_1
arg_Vn_arrangement_imm5___D_8_index__imm5_1
arg_Vn_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__2S_40__4S_41__2D_81
arg_Vn_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41
arg_Vn_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41__2D_81
arg_Vn_arrangement_immh___SEEAdvancedSIMDmodifiedimmediate_0__8H_1__4S_2__2D_4
arg_Vn_arrangement_Q___8B_0__16B_1
arg_Vn_arrangement_Q_sz___2S_00__4S_10__2D_11
arg_Vn_arrangement_Q_sz___4S_10
arg_Vn_arrangement_S_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4_1
arg_Vn_arrangement_size___2D_3
arg_Vn_arrangement_size___8H_0__4S_1__2D_2
arg_Vn_arrangement_size_Q___4H_10__8H_11__2S_20__4S_21
arg_Vn_arrangement_size_Q___8B_00__16B_01
arg_Vn_arrangement_size_Q___8B_00__16B_01__1D_30__2D_31
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__4S_21
arg_Vn_arrangement_sz___2D_1
arg_Vn_arrangement_sz___2S_0__2D_1
arg_Vn_arrangement_sz___4S_0__2D_1
arg_Vn_arrangement_sz_Q___2S_00__4S_01
arg_Vn_arrangement_sz_Q___2S_00__4S_01__2D_11
arg_Vn_arrangement_sz_Q___4H_00__8H_01__2S_10__4S_11
arg_Vt_1_arrangement_B_index__Q_S_size_1
arg_Vt_1_arrangement_D_index__Q_1
arg_Vt_1_arrangement_H_index__Q_S_size_1
arg_Vt_1_arrangement_S_index__Q_S_1
arg_Vt_1_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_2_arrangement_B_index__Q_S_size_1
arg_Vt_2_arrangement_D_index__Q_1
arg_Vt_2_arrangement_H_index__Q_S_size_1
arg_Vt_2_arrangement_S_index__Q_S_1
arg_Vt_2_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_2_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vt_3_arrangement_B_index__Q_S_size_1
arg_Vt_3_arrangement_D_index__Q_1
arg_Vt_3_arrangement_H_index__Q_S_size_1
arg_Vt_3_arrangement_S_index__Q_S_1
arg_Vt_3_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_3_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vt_4_arrangement_B_index__Q_S_size_1
arg_Vt_4_arrangement_D_index__Q_1
arg_Vt_4_arrangement_H_index__Q_S_size_1
arg_Vt_4_arrangement_S_index__Q_S_1
arg_Vt_4_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_4_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Wa
arg_Wd
arg_Wds
arg_Wm
arg_Wm_extend__UXTB_0__UXTH_1__LSL_UXTW_2__UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4
arg_Wm_shift__LSL_0__LSR_1__ASR_2__0_31
arg_Wm_shift__LSL_0__LSR_1__ASR_2__ROR_3__0_31
arg_Wn
arg_Wns
arg_Ws
arg_Wt
arg_Wt2
arg_Xa
arg_Xd
arg_Xds
arg_Xm
arg_Xm_shift__LSL_0__LSR_1__ASR_2__0_63
arg_Xm_shift__LSL_0__LSR_1__ASR_2__ROR_3__0_63
arg_Xn
arg_Xns
arg_Xns_mem
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__1_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__2_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__3_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__4_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__absent_0__0_1
arg_Xns_mem_offset
arg_Xns_mem_optional_imm12_16_unsigned
arg_Xns_mem_optional_imm12_1_unsigned
arg_Xns_mem_optional_imm12_2_unsigned
arg_Xns_mem_optional_imm12_4_unsigned
arg_Xns_mem_optional_imm12_8_unsigned
arg_Xns_mem_optional_imm7_16_signed
arg_Xns_mem_optional_imm7_4_signed
arg_Xns_mem_optional_imm7_8_signed
arg_Xns_mem_optional_imm9_1_signed
arg_Xns_mem_post_fixedimm_1
arg_Xns_mem_post_fixedimm_12
arg_Xns_mem_post_fixedimm_16
arg_Xns_mem_post_fixedimm_2
arg_Xns_mem_post_fixedimm_24
arg_Xns_mem_post_fixedimm_3
arg_Xns_mem_post_fixedimm_32
arg_Xns_mem_post_fixedimm_4
arg_Xns_mem_post_fixedimm_6
arg_Xns_mem_post_fixedimm_8
arg_Xns_mem_post_imm7_16_signed
arg_Xns_mem_post_imm7_4_signed
arg_Xns_mem_post_imm7_8_signed
arg_Xns_mem_post_imm9_1_signed
arg_Xns_mem_post_Q__16_0__32_1
arg_Xns_mem_post_Q__24_0__48_1
arg_Xns_mem_post_Q__32_0__64_1
arg_Xns_mem_post_Q__8_0__16_1
arg_Xns_mem_post_size__1_0__2_1__4_2__8_3
arg_Xns_mem_post_size__2_0__4_1__8_2__16_3
arg_Xns_mem_post_size__3_0__6_1__12_2__24_3
arg_Xns_mem_post_size__4_0__8_1__16_2__32_3
arg_Xns_mem_post_Xm
arg_Xns_mem_wb_imm7_16_signed
arg_Xns_mem_wb_imm7_4_signed
arg_Xns_mem_wb_imm7_8_signed
arg_Xns_mem_wb_imm9_1_signed
arg_Xs
arg_Xt
arg_Xt2
)
@@ -0,0 +1,329 @@
// Generated by ARM internal tool
// DO NOT EDIT
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
// Following functions are used as the predicator: canDecode of according instruction
// Refer to instFormat inside decode.go for more details
func at_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x7, 0x8, (instr>>5)&0x7) == sys_AT
}
func bfi_bfm_32m_bitfield_cond(instr uint32) bool {
return (instr>>5)&0x1f != 0x1f && uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func bfi_bfm_64m_bitfield_cond(instr uint32) bool {
return (instr>>5)&0x1f != 0x1f && uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func bfxil_bfm_32m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) >= uint8((instr>>16)&0x3f)
}
func bfxil_bfm_64m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) >= uint8((instr>>16)&0x3f)
}
func cinc_csinc_32_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cinc_csinc_64_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cinv_csinv_32_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cinv_csinv_64_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cneg_csneg_32_condsel_cond(instr uint32) bool {
return instr&0xe000 != 0xe000 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cneg_csneg_64_condsel_cond(instr uint32) bool {
return instr&0xe000 != 0xe000 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func csinc_general_cond(instr uint32) bool {
return instr&0xe000 != 0xe000
}
func csinv_general_cond(instr uint32) bool {
return instr&0xe000 != 0xe000
}
func dc_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x7, (instr>>8)&0xf, (instr>>5)&0x7) == sys_DC
}
func ic_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x7, (instr>>8)&0xf, (instr>>5)&0x7) == sys_IC
}
func lsl_ubfm_32m_bitfield_cond(instr uint32) bool {
return instr&0xfc00 != 0x7c00 && (instr>>10)&0x3f+1 == (instr>>16)&0x3f
}
func lsl_ubfm_64m_bitfield_cond(instr uint32) bool {
return instr&0xfc00 != 0xfc00 && (instr>>10)&0x3f+1 == (instr>>16)&0x3f
}
func mov_orr_32_log_imm_cond(instr uint32) bool {
return !move_wide_preferred_4((instr>>31)&0x1, (instr>>22)&0x1, (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func mov_orr_64_log_imm_cond(instr uint32) bool {
return !move_wide_preferred_4((instr>>31)&0x1, (instr>>22)&0x1, (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func mov_movn_32_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0) && !is_ones_n16((instr>>5)&0xffff)
}
func mov_movn_64_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0)
}
func mov_add_32_addsub_imm_cond(instr uint32) bool {
return instr&0x1f == 0x1f || (instr>>5)&0x1f == 0x1f
}
func mov_add_64_addsub_imm_cond(instr uint32) bool {
return instr&0x1f == 0x1f || (instr>>5)&0x1f == 0x1f
}
func mov_movz_32_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0)
}
func mov_movz_64_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0)
}
func ror_extr_32_extract_cond(instr uint32) bool {
return (instr>>5)&0x1f == (instr>>16)&0x1f
}
func ror_extr_64_extract_cond(instr uint32) bool {
return (instr>>5)&0x1f == (instr>>16)&0x1f
}
func sbfiz_sbfm_32m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func sbfiz_sbfm_64m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func sbfx_sbfm_32m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func sbfx_sbfm_64m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func tlbi_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x8, (instr>>8)&0xf, (instr>>5)&0x7) == sys_TLBI
}
func ubfiz_ubfm_32m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func ubfiz_ubfm_64m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func ubfx_ubfm_32m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func ubfx_ubfm_64m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func fcvtzs_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func fcvtzs_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func fcvtzu_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func fcvtzu_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func mov_umov_asimdins_w_w_cond(instr uint32) bool {
return ((instr>>16)&0x1f)&0x7 == 0x4
}
func mov_umov_asimdins_x_x_cond(instr uint32) bool {
return ((instr>>16)&0x1f)&0xf == 0x8
}
func mov_orr_asimdsame_only_cond(instr uint32) bool {
return (instr>>16)&0x1f == (instr>>5)&0x1f
}
func rshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func scvtf_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func scvtf_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func shl_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func shl_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func shrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sli_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sli_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrun_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrun_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshl_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshl_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshlu_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshlu_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrun_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrun_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sri_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sri_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srshr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srshr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srsra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srsra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sshll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sshr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sshr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ssra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ssra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sxtl_sshll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0 && bit_count((instr>>19)&0xf) == 1
}
func ucvtf_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ucvtf_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqrshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqrshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshl_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshl_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func urshr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func urshr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ursra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ursra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ushll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ushr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ushr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func usra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func usra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uxtl_ushll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0 && bit_count((instr>>19)&0xf) == 1
}
@@ -0,0 +1,81 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
func extract_bit(value, bit uint32) uint32 {
return (value >> bit) & 1
}
func bfxpreferred_4(sf, opc1, imms, immr uint32) bool {
if imms < immr {
return false
}
if (imms>>5 == sf) && (imms&0x1f == 0x1f) {
return false
}
if immr == 0 {
if sf == 0 && (imms == 7 || imms == 15) {
return false
}
if sf == 1 && opc1 == 0 && (imms == 7 ||
imms == 15 || imms == 31) {
return false
}
}
return true
}
func move_wide_preferred_4(sf, N, imms, immr uint32) bool {
if sf == 1 && N != 1 {
return false
}
if sf == 0 && !(N == 0 && ((imms>>5)&1) == 0) {
return false
}
if imms < 16 {
return (-immr)%16 <= (15 - imms)
}
width := uint32(32)
if sf == 1 {
width = uint32(64)
}
if imms >= (width - 15) {
return (immr % 16) <= (imms - (width - 15))
}
return false
}
type sys uint8
const (
sys_AT sys = iota
sys_DC
sys_IC
sys_TLBI
sys_SYS
)
func sys_op_4(op1, crn, crm, op2 uint32) sys {
sysInst := sysInstFields{uint8(op1), uint8(crn), uint8(crm), uint8(op2)}
return sysInst.getType()
}
func is_zero(x uint32) bool {
return x == 0
}
func is_ones_n16(x uint32) bool {
return x == 0xffff
}
func bit_count(x uint32) uint8 {
var count uint8
for count = 0; x > 0; x >>= 1 {
if (x & 1) == 1 {
count++
}
}
return count
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,86 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"encoding/hex"
"io/ioutil"
"path/filepath"
"strings"
"testing"
)
func testDecode(t *testing.T, syntax string) {
input := filepath.Join("testdata", syntax+"cases.txt")
data, err := ioutil.ReadFile(input)
if err != nil {
t.Fatal(err)
}
all := string(data)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 2)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f[0])
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f[0])
}
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f[0], err)
continue
}
asm := f[1]
inst, decodeErr := Decode(code)
if decodeErr != nil && decodeErr != errUnknown {
// Some rarely used system instructions are not supported
// Following logicals will filter such unknown instructions
t.Errorf("parsing %x: %s", code, decodeErr)
continue
}
var out string
switch syntax {
case "gnu":
out = GNUSyntax(inst)
case "plan9":
out = GoSyntax(inst, 0, nil, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
// TODO: system instruction.
var Todo = strings.Fields(`
sys
at
ic
hvc
smc
`)
if strings.Replace(out, " ", "", -1) != strings.Replace(asm, " ", "", -1) && !hasPrefix(asm, Todo...) {
// Exclude MSR since GNU objdump result is incorrect. eg. 0xd504431f msr s0_4_c4_c3_0, xzr
if !strings.HasSuffix(asm, " nv") && !strings.HasPrefix(asm, "msr") {
t.Errorf("Decode(%s) [%s] = %s, want %s", strings.Trim(f[0], "|"), syntax, out, asm)
}
}
}
}
func TestDecodeGNUSyntax(t *testing.T) {
testDecode(t, "gnu")
}
func TestDecodeGoSyntax(t *testing.T) {
testDecode(t, "plan9")
}
@@ -0,0 +1,604 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
// Copied and simplified from ../../arm/armasm/ext_test.go.
package arm64asm
import (
"bufio"
"bytes"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"time"
)
var (
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint64
enc [4]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Arch Mode
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// InstJson describes instruction fields value got from ARMv8-A Reference Manual
type InstJson struct {
Name string
Bits string
Arch string
Syntax string
Code string
Alias string
Enc uint32
}
// A Mode is an instruction execution mode.
type Mode int
const (
_ Mode = iota
ModeARM64
)
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
arch Mode,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, inst *Inst, dec ExtInst) bool,
) {
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
Arch: arch,
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // Sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text && !strings.Contains(dec.text, "unknown") && syntax == "gnu" {
suffix := ""
if allowedMismatch(text, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
cmp := fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s\n", enc, text, len(enc), dec.text, dec.nenc, suffix)
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, cmp)
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
t.Logf("decoder coverage: %.1f%%;\n", decodeCoverage())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
// Start address of text.
const start = 0x8000
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "arm64asm")
if err != nil {
return
}
file = f.Name()
f.Seek(start, io.SeekStart)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
}
w.Write(x)
w.Write(zeros[len(x):])
size += len(zeros)
})
return file, f, size, nil
}
var zeros = []byte{0, 0, 0, 0}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
if len(enc) < 4 {
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
}
return enc
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, src []byte) (inst Inst, text string) {
var err error
inst, err = Decode(src)
if err != nil {
text = "error: " + err.Error()
return
}
text = inst.String()
switch syntax {
case "gnu":
text = GNUSyntax(inst)
case "plan9": // [sic]
text = GoSyntax(inst, 0, nil, nil)
default:
text = "error: unknown syntax " + syntax
}
return
}
// decodecoverage returns a floating point number denoting the
// decoder coverage.
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return 100 * float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for cur_s := s; cur_s != ""; {
if strings.HasPrefix(cur_s, prefix) {
return true
}
i := strings.Index(cur_s, " ")
if i < 0 {
break
}
cur_s = cur_s[i+1:]
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9a-fA-F).
func isHex(b byte) bool {
return ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F')
}
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool reports whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = bytes.TrimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex(hex[j])<<4|unhex(hex[j+1]))
j += 2
}
return raw, true
}
func unhex(b byte) byte {
if '0' <= b && b <= '9' {
return b - '0'
} else if 'A' <= b && b <= 'F' {
return b - 'A' + 10
} else if 'a' <= b && b <= 'f' {
return b - 'a' + 10
}
return 0
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = bytes.TrimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// Fllowing regular expressions matches instructions using relative addressing mode.
// pcrel matches B instructions and BL instructions.
// pcrelr matches instrucions which consisted of register arguments and label arguments.
// pcrelim matches instructions which consisted of register arguments, immediate
// arguments and lable arguments.
// pcrelrzr and prcelimzr matches instructions when register arguments is zero register.
// pcrelprfm matches PRFM instructions when arguments consisted of register and lable.
// pcrelprfmim matches PRFM instructions when arguments consisted of immediate and lable.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:\.)?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|nv)?) 0x([0-9a-f]+)$`)
pcrelr = regexp.MustCompile(`^((?:.*)?(?:ldr|adrp|adr|cbnz|cbz|ldrsw) (?:x|w|s|d|q)(?:[0-9]+,)) 0x([0-9a-f]+)$`)
pcrelrzr = regexp.MustCompile(`^((?:.*)?(?:ldr|adrp|adr|cbnz|cbz|ldrsw) (?:x|w)zr,) 0x([0-9a-f]+)$`)
pcrelim = regexp.MustCompile(`^((?:.*)?(?:tbnz|tbz) (?:x|w)(?:[0-9]+,) (?:#[0-9a-f]+,)) 0x([0-9a-f]+)$`)
pcrelimzr = regexp.MustCompile(`^((?:.*)?(?:tbnz|tbz) (?:x|w)zr, (?:#[0-9a-f]+,)) 0x([0-9a-f]+)$`)
pcrelprfm = regexp.MustCompile(`^((?:.*)?(?:prfm) (?:[0-9a-z]+,)) 0x([0-9a-f]+)$`)
pcrelprfmim = regexp.MustCompile(`^((?:.*)?(?:prfm) (?:#0x[0-9a-f]+,)) 0x([0-9a-f]+)$`)
)
// Round is the multiple of the number of instructions that read from Json file.
// Round used as seed value for pseudo-random number generator provides the same sequence
// in the same round run for the external disassembler and decoder.
var Round int
// condmark is used to mark conditional instructions when need to generate and test
// conditional instructions.
var condmark bool = false
// Generate instruction binary according to Json file
// Encode variable field of instruction with random value
func doFuzzy(inst *InstJson, Ninst int) {
var testdata uint32
var NonDigRE = regexp.MustCompile(`[\D]`)
rand.Seed(int64(Round + Ninst))
off := 0
DigBit := ""
if condmark == true && !strings.Contains(inst.Bits, "cond") {
inst.Enc = 0xffffffff
} else {
for _, f := range strings.Split(inst.Bits, "|") {
if i := strings.Index(f, ":"); i >= 0 {
// consider f contains "01:2" and "Rm:5"
DigBit = f[:i]
m := NonDigRE.FindStringSubmatch(DigBit)
if m == nil {
DigBit = strings.TrimSpace(DigBit)
s := strings.Split(DigBit, "")
for i := 0; i < len(s); i++ {
switch s[i] {
case "1", "(1)":
testdata |= 1 << uint(31-off)
}
off++
}
} else {
// DigBit is "Rn" or "imm3"
n, _ := strconv.Atoi(f[i+1:])
if DigBit == "cond" && condmark == true {
r := uint8(Round)
for i := n - 1; i >= 0; i-- {
switch (r >> uint(i)) & 1 {
case 1:
testdata |= 1 << uint(31-off)
}
off++
}
} else {
for i := 0; i < n; i++ {
r := rand.Intn(2)
switch r {
case 1:
testdata |= 1 << uint(31-off)
}
off++
}
}
}
continue
}
for _, bit := range strings.Fields(f) {
switch bit {
case "0", "(0)":
off++
continue
case "1", "(1)":
testdata |= 1 << uint(31-off)
default:
r := rand.Intn(2)
switch r {
case 1:
testdata |= 1 << uint(31-off)
}
}
off++
}
}
if off != 32 {
log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off)
}
inst.Enc = testdata
}
}
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// JSONCases generates ARM64 instructions according to inst.json.
func JSONCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
data, err := ioutil.ReadFile("inst.json")
if err != nil {
t.Fatal(err)
}
var insts []InstJson
var instsN []InstJson
// Change N value to get more cases only when condmark=false.
N := 100
if condmark == true {
N = 16
}
if err := json.Unmarshal(data, &insts); err != nil {
t.Fatal(err)
}
// Append instructions to get more test cases.
for i := 0; i < N; {
for _, inst := range insts {
instsN = append(instsN, inst)
}
i++
}
Round = 0
for i := range instsN {
if i%len(insts) == 0 {
Round++
}
doFuzzy(&instsN[i], i)
}
for _, inst := range instsN {
if condmark == true && inst.Enc == 0xffffffff {
continue
}
enc := inst.Enc
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
}
}
}
// condCases generates conditional instructions.
func condCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
condmark = true
JSONCases(t)(func(enc []byte) {
try(enc)
})
}
}
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/cases.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T, syntax string) func(func([]byte)) {
var codes [][]byte
input := filepath.Join("testdata", syntax+"cases.txt")
data, err := ioutil.ReadFile(input)
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}
@@ -0,0 +1,35 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"strings"
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This form typically matches the syntax defined in the ARM Reference Manual.
func GNUSyntax(inst Inst) string {
switch inst.Op {
case RET:
if r, ok := inst.Args[0].(Reg); ok && r == X30 {
return "ret"
}
case B:
if _, ok := inst.Args[0].(Cond); ok {
return strings.ToLower("b." + inst.Args[0].String() + " " + inst.Args[1].String())
}
case SYSL:
result := strings.ToLower(inst.String())
return strings.Replace(result, "c", "C", -1)
case DCPS1, DCPS2, DCPS3, CLREX:
return strings.ToLower(strings.TrimSpace(inst.String()))
case ISB:
if strings.Contains(inst.String(), "SY") {
result := strings.TrimSuffix(inst.String(), " SY")
return strings.ToLower(result)
}
}
return strings.ToLower(inst.String())
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,160 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"strings"
"testing"
)
func TestObjdumpARM64TestDecodeGNUSyntaxdata(t *testing.T) {
testObjdumpARM64(t, testdataCases(t, "gnu"))
}
func TestObjdumpARM64TestDecodeGoSyntaxdata(t *testing.T) {
testObjdumpARM64(t, testdataCases(t, "plan9"))
}
func TestObjdumpARM64Manual(t *testing.T) { testObjdumpARM64(t, hexCases(t, objdumpManualTests)) }
func TestObjdumpARM64Cond(t *testing.T) { testObjdumpARM64(t, condCases(t)) }
func TestObjdumpARM64(t *testing.T) { testObjdumpARM64(t, JSONCases(t)) }
// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Manual, particularly with tracing enabled.
// Note that these are byte sequences, so they must be reversed from the usual
// word presentation.
var objdumpManualTests = `
bf2003d5
9f2003d5
7f2003d5
5f2003d5
3f2003d5
1f2003d5
df4d03d5
ff4d03d5
28d91b14
da6cb530
15e5e514
ff4603d5
df4803d5
bf4100d5
9f3f03d5
9f3e03d5
9f3d03d5
9f3b03d5
9f3a03d5
9f3903d5
9f3703d5
9f3603d5
9f3503d5
9f3303d5
9f3203d5
9f3103d5
ff4603d5
df4803d5
bf4100d5
a3681b53
47dc78d3
0500a012
0500e092
0500a052
0500a0d2
cd5a206e
cd5a202e
743d050e
743d0a0e
743d0c0e
743d084e
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, inst *Inst, dec ExtInst) bool {
// Skip unsupported instructions
if hasPrefix(dec.text, todo...) {
return true
}
// GNU objdump has incorrect alias conditions for following instructions
if inst.Enc&0x000003ff == 0x000003ff && hasPrefix(dec.text, "negs") && hasPrefix(text, "cmp") {
return true
}
// GNU objdump "NV" is equal to our "AL"
if strings.HasSuffix(dec.text, " nv") && strings.HasSuffix(text, " al") {
return true
}
if strings.HasPrefix(dec.text, "b.nv") && strings.HasPrefix(text, "b.al") {
return true
}
// GNU objdump recognizes invalid binaries as following instructions
if hasPrefix(dec.text, "hint", "mrs", "msr", "bfc", "orr", "mov") {
return true
}
if strings.HasPrefix(text, "hint") {
return true
}
// GNU objdump recognizes reserved valuse as valid ones
if strings.Contains(text, "unknown instruction") && hasPrefix(dec.text, "fmla", "fmul", "fmulx", "fcvtzs", "fcvtzu", "fmls", "fmov", "scvtf", "ucvtf") {
return true
}
// Some old objdump recognizes ldur*/stur*/prfum as ldr*/str*/prfm
for k, v := range oldObjdumpMismatch {
if strings.HasPrefix(dec.text, k) && strings.Replace(dec.text, k, v, 1) == text {
return true
}
}
// New objdump supports some newer mnemonics than this package. This
// package should be updated to support the new mnemonics and the sense
// of this reversed to continue passing with older objdumps but that
// requires internal ARM tooling.
if newForm, ok := newMnemonics[text]; ok && newForm == dec.text {
return true
}
// GNU objdump misses spaces between operands for some instructions (e.g., "ld1 {v10.2s, v11.2s}, [x23],#16")
if strings.Replace(text, " ", "", -1) == strings.Replace(dec.text, " ", "", -1) {
return true
}
return false
}
// TODO: system instruction.
var todo = strings.Fields(`
sys
at
ic
hvc
smc
`)
// Following instructions can't be covered because they are just aliases to another instructions which are always preferred
var Ncover = strings.Fields(`
sbfm
asrv
bfm
ubfm
lslv
lsrv
rorv
ins
dup
`)
// Some old objdump wrongly decodes following instructions and allow their mismatches to avoid false alarm
var oldObjdumpMismatch = map[string]string{
//oldObjValue correctValue
"ldr": "ldur",
"ldrb": "ldurb",
"ldrh": "ldurh",
"ldrsb": "ldursb",
"ldrsh": "ldursh",
"ldrsw": "ldursw",
"str": "stur",
"strb": "sturb",
"strh": "sturh",
"prfm": "prfum",
}
var newMnemonics = map[string]string{
"dsb #0x00": "ssbb",
"dsb #0x04": "pssbb",
}
@@ -0,0 +1,299 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and simplified from ../../arm/armasm/objdumpext_test.go.
package arm64asm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"os/exec"
"strconv"
"strings"
"testing"
)
const objdumpPath = "/usr/bin/objdump"
func testObjdumpARM64(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, ModeARM64)
}
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) {
checkObjdumpAarch64(t)
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
testExtDis(t, "plan9", arch, objdump, generate, allowedMismatchObjdump)
}
func checkObjdumpAarch64(t *testing.T) {
out, err := exec.Command(objdumpPath, "-i").Output()
if err != nil {
t.Skipf("cannot run objdump: %v\n%s", err, out)
}
if !strings.Contains(string(out), "aarch64") {
t.Skip("objdump does not have aarch64 support")
}
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if ext.Arch == ModeARM64 {
if err := writeELF64(ext.File, ext.Size); err != nil {
return err
}
} else {
panic("unknown arch")
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint64 = start
addr uint64
encbuf [4]byte
enc []byte
text string
)
flush := func() {
if addr == next {
// PC-relative addresses are translated to absolute addresses based on PC by GNU objdump
// Following logical rewrites the absolute addresses back to PC-relative ones for comparing
// with our disassembler output which are PC-relative
if m := pcrelprfmim.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelprfm.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelim.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelimzr.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelr.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
if strings.Contains(m[1], "adrp") {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr&0xfffff000)))
} else {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
}
if m := pcrelrzr.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
if strings.Contains(m[1], "adrp") {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr&0xfffff000)))
} else {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if strings.HasPrefix(text, "mov") && strings.Contains(text, "//") {
s := strings.Split(text, " //")
text = s[0]
}
text = strings.Replace(text, "#0.0", "#0", -1)
if text == "undefined" && len(enc) == 4 {
text = "error: unknown instruction"
enc = nil
}
if len(enc) == 4 {
// prints as word but we want to record bytes
enc[0], enc[3] = enc[3], enc[0]
enc[1], enc[2] = enc[2], enc[1]
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [4]byte{}
enc = nil
next += 4
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint64(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
undefined = []byte("undefined")
unpredictable = []byte("unpredictable")
slashslash = []byte("//")
)
func parseLine(line []byte, encstart []byte) (addr uint64, enc []byte, text string) {
ok := false
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(bytes.TrimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint64(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok = parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = bytes.TrimSpace(line[i:])
if bytes.Contains(line, undefined) {
text = "undefined"
return
}
if false && bytes.Contains(line, unpredictable) {
text = "unpredictable"
return
}
// Strip trailing comment starting with ';'
// e.g: "csinv x23, x2, x19, cc ; xxx"
if i := bytes.IndexByte(line, ';'); i >= 0 {
line = bytes.TrimSpace(line[:i])
}
// Strip trailing comment starting with "//"
// e.g: "fccmpe s2, s9, #0x7, ne // xxx"
if i := bytes.Index(line, slashslash); i >= 0 {
line = bytes.TrimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF64 writes an ELF64 header to the file, describing a text
// segment that starts at start (0x8000) and extends for size bytes.
func writeELF64(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header64
var prog elf.Prog64
var sect elf.Section64
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header64{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
Type: 2,
Machine: uint16(elf.EM_AARCH64),
Version: 1,
Entry: start,
Phoff: uint64(off1),
Shoff: uint64(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog64{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint64(size),
Memsz: uint64(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section64{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint64(size),
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section64{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint64(off2 + (off3-off2)*3),
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
@@ -0,0 +1,742 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"fmt"
"io"
"sort"
"strings"
)
// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The pc is the program counter of the instruction, used for
// expanding PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name
// and base address of the symbol containing the target, if any;
// otherwise it returns "", 0.
// The reader text should read from the text segment using text addresses
// as offsets; it is used to display pc-relative loads as constant loads.
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var args []string
for _, a := range inst.Args {
if a == nil {
break
}
args = append(args, plan9Arg(&inst, pc, symname, a))
}
op := inst.Op.String()
switch inst.Op {
case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW:
// Check for PC-relative load.
if offset, ok := inst.Args[1].(PCRel); ok {
addr := pc + uint64(offset)
if _, ok := inst.Args[0].(Reg); !ok {
break
}
if s, base := symname(addr); s != "" && addr == base {
args[1] = fmt.Sprintf("$%s(SB)", s)
}
}
}
// Move addressing mode into opcode suffix.
suffix := ""
switch inst.Op {
case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW, STR, STRB, STRH, STUR, STURB, STURH, LD1, ST1:
switch mem := inst.Args[1].(type) {
case MemImmediate:
switch mem.Mode {
case AddrOffset:
// no suffix
case AddrPreIndex:
suffix = ".W"
case AddrPostIndex, AddrPostReg:
suffix = ".P"
}
}
case STP, LDP:
switch mem := inst.Args[2].(type) {
case MemImmediate:
switch mem.Mode {
case AddrOffset:
// no suffix
case AddrPreIndex:
suffix = ".W"
case AddrPostIndex:
suffix = ".P"
}
}
}
switch inst.Op {
case BL:
return "CALL " + args[0]
case BLR:
r := inst.Args[0].(Reg)
regno := uint16(r) & 31
return fmt.Sprintf("CALL (R%d)", regno)
case RET:
if r, ok := inst.Args[0].(Reg); ok && r == X30 {
return "RET"
}
case B:
if cond, ok := inst.Args[0].(Cond); ok {
return "B" + cond.String() + " " + args[1]
}
return "JMP" + " " + args[0]
case BR:
r := inst.Args[0].(Reg)
regno := uint16(r) & 31
return fmt.Sprintf("JMP (R%d)", regno)
case MOV:
rno := -1
switch a := inst.Args[0].(type) {
case Reg:
rno = int(a)
case RegSP:
rno = int(a)
case RegisterWithArrangementAndIndex:
op = "VMOV"
case RegisterWithArrangement:
op = "VMOV"
}
if rno >= 0 && rno <= int(WZR) {
op = "MOVW"
} else if rno >= int(X0) && rno <= int(XZR) {
op = "MOVD"
}
if _, ok := inst.Args[1].(RegisterWithArrangementAndIndex); ok {
op = "VMOV"
}
case LDR, LDUR:
var rno uint16
if r, ok := inst.Args[0].(Reg); ok {
rno = uint16(r)
} else {
rno = uint16(inst.Args[0].(RegSP))
}
if rno <= uint16(WZR) {
op = "MOVWU" + suffix
} else if rno >= uint16(B0) && rno <= uint16(B31) {
op = "FMOVB" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(H0) && rno <= uint16(H31) {
op = "FMOVH" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(S0) && rno <= uint16(S31) {
op = "FMOVS" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = "FMOVD" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(Q0) && rno <= uint16(Q31) {
op = "FMOVQ" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else {
op = "MOVD" + suffix
}
case LDRB:
op = "MOVBU" + suffix
case LDRH:
op = "MOVHU" + suffix
case LDRSW:
op = "MOVW" + suffix
case LDRSB:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op = "MOVBW" + suffix
} else {
op = "MOVB" + suffix
}
}
case LDRSH:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op = "MOVHW" + suffix
} else {
op = "MOVH" + suffix
}
}
case STR, STUR:
var rno uint16
if r, ok := inst.Args[0].(Reg); ok {
rno = uint16(r)
} else {
rno = uint16(inst.Args[0].(RegSP))
}
if rno <= uint16(WZR) {
op = "MOVW" + suffix
} else if rno >= uint16(B0) && rno <= uint16(B31) {
op = "FMOVB" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(H0) && rno <= uint16(H31) {
op = "FMOVH" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(S0) && rno <= uint16(S31) {
op = "FMOVS" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = "FMOVD" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else if rno >= uint16(Q0) && rno <= uint16(Q31) {
op = "FMOVQ" + suffix
args[0] = fmt.Sprintf("F%d", rno&31)
} else {
op = "MOVD" + suffix
}
args[0], args[1] = args[1], args[0]
case STRB, STURB:
op = "MOVB" + suffix
args[0], args[1] = args[1], args[0]
case STRH, STURH:
op = "MOVH" + suffix
args[0], args[1] = args[1], args[0]
case TBNZ, TBZ:
args[0], args[1], args[2] = args[2], args[0], args[1]
case MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[2], args[3] = args[3], args[2]
case STLR:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[0], args[1] = args[1], args[0]
case STLRB, STLRH:
args[0], args[1] = args[1], args[0]
case STLXR, STXR:
if r, ok := inst.Args[1].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[1], args[2] = args[2], args[1]
case STLXRB, STLXRH, STXRB, STXRH:
args[1], args[2] = args[2], args[1]
case BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[1], args[2], args[3] = args[3], args[1], args[2]
case LDAXP, LDXP:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1])
args[1] = args[2]
return op + " " + args[1] + ", " + args[0]
case STP, LDP:
args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1])
args[1] = args[2]
rno, ok := inst.Args[0].(Reg)
if !ok {
rno = Reg(inst.Args[0].(RegSP))
}
if rno <= WZR {
op = op + "W"
} else if rno >= S0 && rno <= S31 {
op = "F" + op + "S"
} else if rno >= D0 && rno <= D31 {
op = "F" + op + "D"
} else if rno >= Q0 && rno <= Q31 {
op = "F" + op + "Q"
}
op = op + suffix
if inst.Op.String() == "STP" {
return op + " " + args[0] + ", " + args[1]
} else {
return op + " " + args[1] + ", " + args[0]
}
case STLXP, STXP:
if r, ok := inst.Args[1].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[1] = fmt.Sprintf("(%s, %s)", args[1], args[2])
args[2] = args[3]
return op + " " + args[1] + ", " + args[2] + ", " + args[0]
case FCCMP, FCCMPE:
args[0], args[1] = args[1], args[0]
fallthrough
case FCMP, FCMPE:
if _, ok := inst.Args[1].(Imm); ok {
args[1] = "$(0.0)"
}
fallthrough
case FADD, FSUB, FMUL, FNMUL, FDIV, FMAX, FMIN, FMAXNM, FMINNM, FCSEL, FMADD, FMSUB, FNMADD, FNMSUB:
if strings.HasSuffix(op, "MADD") || strings.HasSuffix(op, "MSUB") {
args[2], args[3] = args[3], args[2]
}
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
}
}
case FCVT:
for i := 1; i >= 0; i-- {
if r, ok := inst.Args[i].(Reg); ok {
rno := uint16(r)
if rno >= uint16(H0) && rno <= uint16(H31) {
op = fmt.Sprintf("%sH", op)
} else if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
}
}
}
case FABS, FNEG, FSQRT, FRINTN, FRINTP, FRINTM, FRINTZ, FRINTA, FRINTX, FRINTI:
if r, ok := inst.Args[1].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
}
}
case FCVTZS, FCVTZU, SCVTF, UCVTF:
if _, ok := inst.Args[2].(Imm); !ok {
for i := 1; i >= 0; i-- {
if r, ok := inst.Args[i].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
} else if rno <= uint16(WZR) {
op += "W"
}
}
}
}
case FMOV:
for i := 0; i <= 1; i++ {
if r, ok := inst.Args[i].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
break
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
break
}
}
}
case SYSL:
op1 := int(inst.Args[1].(Imm).Imm)
cn := int(inst.Args[2].(Imm_c))
cm := int(inst.Args[3].(Imm_c))
op2 := int(inst.Args[4].(Imm).Imm)
sysregno := int32(op1<<16 | cn<<12 | cm<<8 | op2<<5)
args[1] = fmt.Sprintf("$%d", sysregno)
return op + " " + args[1] + ", " + args[0]
case CBNZ, CBZ:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[0], args[1] = args[1], args[0]
case ADR, ADRP:
addr := int64(inst.Args[1].(PCRel))
args[1] = fmt.Sprintf("%d(PC)", addr)
case MSR:
args[0] = inst.Args[0].String()
case ST1:
op = fmt.Sprintf("V%s", op) + suffix
args[0], args[1] = args[1], args[0]
case LD1:
op = fmt.Sprintf("V%s", op) + suffix
case UMOV:
op = "VMOV"
case NOP:
op = "NOOP"
default:
index := sort.SearchStrings(noSuffixOpSet, op)
if !(index < len(noSuffixOpSet) && noSuffixOpSet[index] == op) {
rno := -1
switch a := inst.Args[0].(type) {
case Reg:
rno = int(a)
case RegSP:
rno = int(a)
case RegisterWithArrangement:
op = fmt.Sprintf("V%s", op)
}
if rno >= int(B0) && rno <= int(Q31) && !strings.HasPrefix(op, "F") {
op = fmt.Sprintf("V%s", op)
}
if rno >= 0 && rno <= int(WZR) {
// Add "w" to opcode suffix.
op += "W"
}
}
op = op + suffix
}
// conditional instructions, replace args.
if _, ok := inst.Args[3].(Cond); ok {
if _, ok := inst.Args[2].(Reg); ok {
args[1], args[2] = args[2], args[1]
} else {
args[0], args[2] = args[2], args[0]
}
}
// Reverse args, placing dest last.
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
args[i], args[j] = args[j], args[i]
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return op
}
// No need add "W" to opcode suffix.
// Opcode must be inserted in ascending order.
var noSuffixOpSet = strings.Fields(`
AESD
AESE
AESIMC
AESMC
CRC32B
CRC32CB
CRC32CH
CRC32CW
CRC32CX
CRC32H
CRC32W
CRC32X
LDARB
LDARH
LDAXRB
LDAXRH
LDTRH
LDXRB
LDXRH
SHA1C
SHA1H
SHA1M
SHA1P
SHA1SU0
SHA1SU1
SHA256H
SHA256H2
SHA256SU0
SHA256SU1
`)
// floating point instructions without "F" prefix.
var fOpsWithoutFPrefix = map[Op]bool{
LDP: true,
STP: true,
}
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
switch a := arg.(type) {
case Imm:
return fmt.Sprintf("$%d", uint32(a.Imm))
case Imm64:
return fmt.Sprintf("$%d", int64(a.Imm))
case ImmShift:
if a.shift == 0 {
return fmt.Sprintf("$%d", a.imm)
}
return fmt.Sprintf("$(%d<<%d)", a.imm, a.shift)
case PCRel:
addr := int64(pc) + int64(a)
if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
return fmt.Sprintf("%s(SB)", s)
}
return fmt.Sprintf("%d(PC)", a/4)
case Reg:
regenum := uint16(a)
regno := uint16(a) & 31
if regenum >= uint16(B0) && regenum <= uint16(Q31) {
if strings.HasPrefix(inst.Op.String(), "F") || strings.HasSuffix(inst.Op.String(), "CVTF") || fOpsWithoutFPrefix[inst.Op] {
// FP registers are the same ones as SIMD registers
// Print Fn for scalar variant to align with assembler (e.g., FCVT, SCVTF, UCVTF, etc.)
return fmt.Sprintf("F%d", regno)
} else {
// Print Vn to align with assembler (e.g., SHA256H)
return fmt.Sprintf("V%d", regno)
}
}
return plan9gpr(a)
case RegSP:
regno := uint16(a) & 31
if regno == 31 {
return "RSP"
}
return fmt.Sprintf("R%d", regno)
case RegExtshiftAmount:
reg := plan9gpr(a.reg)
extshift := ""
amount := ""
if a.extShift != ExtShift(0) {
switch a.extShift {
default:
extshift = "." + a.extShift.String()
case lsl:
extshift = "<<"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
case lsr:
extshift = ">>"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
case asr:
extshift = "->"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
case ror:
extshift = "@>"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
}
if a.amount != 0 {
amount = fmt.Sprintf("<<%d", a.amount)
}
}
return reg + extshift + amount
case MemImmediate:
off := ""
base := ""
regno := uint16(a.Base) & 31
if regno == 31 {
base = "(RSP)"
} else {
base = fmt.Sprintf("(R%d)", regno)
}
if a.imm != 0 && a.Mode != AddrPostReg {
off = fmt.Sprintf("%d", a.imm)
} else if a.Mode == AddrPostReg {
postR := fmt.Sprintf("(R%d)", a.imm)
return base + postR
}
return off + base
case MemExtend:
base := ""
index := ""
regno := uint16(a.Base) & 31
if regno == 31 {
base = "(RSP)"
} else {
base = fmt.Sprintf("(R%d)", regno)
}
indexreg := plan9gpr(a.Index)
if a.Extend == lsl {
// Refer to ARM reference manual, for byte load/store(register), the index
// shift amount must be 0, encoded in "S" as 0 if omitted, or as 1 if present.
// a.Amount indicates the index shift amount, encoded in "S" field.
// a.ShiftMustBeZero is set true indicates the index shift amount must be 0.
// When a.ShiftMustBeZero is true, GNU syntax prints "[Xn, Xm lsl #0]" if "S"
// equals to 1, or prints "[Xn, Xm]" if "S" equals to 0.
if a.Amount != 0 && !a.ShiftMustBeZero {
index = fmt.Sprintf("(%s<<%d)", indexreg, a.Amount)
} else if a.ShiftMustBeZero && a.Amount == 1 {
// When a.ShiftMustBeZero is ture, Go syntax prints "(Rm<<0)" if "a.Amount"
// equals to 1.
index = fmt.Sprintf("(%s<<0)", indexreg)
} else {
index = fmt.Sprintf("(%s)", indexreg)
}
} else {
if a.Amount != 0 && !a.ShiftMustBeZero {
index = fmt.Sprintf("(%s.%s<<%d)", indexreg, a.Extend.String(), a.Amount)
} else {
index = fmt.Sprintf("(%s.%s)", indexreg, a.Extend.String())
}
}
return base + index
case Cond:
switch arg.String() {
case "CS":
return "HS"
case "CC":
return "LO"
}
case Imm_clrex:
return fmt.Sprintf("$%d", uint32(a))
case Imm_dcps:
return fmt.Sprintf("$%d", uint32(a))
case Imm_option:
return fmt.Sprintf("$%d", uint8(a))
case Imm_hint:
return fmt.Sprintf("$%d", uint8(a))
case Imm_fp:
var s, pre, numerator, denominator int16
var result float64
if a.s == 0 {
s = 1
} else {
s = -1
}
pre = s * int16(16+a.pre)
if a.exp > 0 {
numerator = (pre << uint8(a.exp))
denominator = 16
} else {
numerator = pre
denominator = (16 << uint8(-1*a.exp))
}
result = float64(numerator) / float64(denominator)
return strings.TrimRight(fmt.Sprintf("$%f", result), "0")
case RegisterWithArrangement:
result := a.r.String()
arrange := a.a.String()
c := []rune(arrange)
switch len(c) {
case 3:
c[1], c[2] = c[2], c[1] // .8B -> .B8
case 4:
c[1], c[2], c[3] = c[3], c[1], c[2] // 16B -> B16
}
arrange = string(c)
result += arrange
if a.cnt > 0 {
result = "[" + result
for i := 1; i < int(a.cnt); i++ {
cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
result += ", " + cur.String() + arrange
}
result += "]"
}
return result
case RegisterWithArrangementAndIndex:
result := a.r.String()
arrange := a.a.String()
result += arrange
if a.cnt > 1 {
result = "[" + result
for i := 1; i < int(a.cnt); i++ {
cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
result += ", " + cur.String() + arrange
}
result += "]"
}
return fmt.Sprintf("%s[%d]", result, a.index)
case Systemreg:
return fmt.Sprintf("$%d", uint32(a.op0&1)<<14|uint32(a.op1&7)<<11|uint32(a.cn&15)<<7|uint32(a.cm&15)<<3|uint32(a.op2)&7)
case Imm_prfop:
if strings.Contains(a.String(), "#") {
return fmt.Sprintf("$%d", a)
}
case sysOp:
result := a.op.String()
if a.r != 0 {
result += ", " + plan9gpr(a.r)
}
return result
}
return strings.ToUpper(arg.String())
}
// Convert a general-purpose register to plan9 assembly format.
func plan9gpr(r Reg) string {
regno := uint16(r) & 31
if regno == 31 {
return "ZR"
}
return fmt.Sprintf("R%d", regno)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
go test command:
cd ..; go test -run 'ObjdumpARM64Cond' -v -timeout 10h -long 2>&1 | tee log
cd ..; go test -run 'ObjdumpARM64TestGUNSyntaxdata' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'ObjdumpARM64TestGoSyntaxdata' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'ObjdumpARM64' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'ObjdumpARM64Manual' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'TestDecodeGNUSyntax'
cd ..; go test -run 'TestDecodeGoSyntax'
cd ..; go test -run '.*'
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,303 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is an automatic parser program that parses arm64
// system register XML files to get the encoding information
// and writes them to the sysRegEnc.go file. The sysRegEnc.go
// file is used for the system register encoding.
// Follow the following steps to run the automatic parser program:
// 1. The system register XML files are from
// https://developer.arm.com/-/media/Files/ATG/Beta10/SysReg_xml_v85A-2019-06.tar.gz
// 2. Extract SysReg_xml_v85A-2019-06.tar/SysReg_xml_v85A-2019-06/SysReg_xml_v85A-2019-06/AArch64-*.xml
// to a "xmlfolder" folder.
// 3. Run the command: ./sysrengen -i "xmlfolder" -o "filename"
// By default, the xmlfolder is "./files" and the filename is "sysRegEnc.go".
// 4. Put the automaically generated file into $GOROOT/src/cmd/internal/obj/arm64 directory.
package main
import (
"bufio"
"encoding/xml"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
)
// Types corresponded to the data structures in the XML file.
type RegisterPage struct {
XMLName xml.Name `xml:"register_page"`
Registers Registers `xml:"registers"`
}
type Registers struct {
XMLName xml.Name `xml:"registers"`
Register Register `xml:"register"`
}
type Register struct {
XMLName xml.Name `xml:"register"`
RegShortName string `xml:"reg_short_name"`
RegVariables RegVariables `xml:"reg_variables"`
AccessMechanisms AccessMechanisms `xml:"access_mechanisms"`
}
type RegVariables struct {
XMLName xml.Name `xml:"reg_variables"`
RegVariable RegVariable `xml:"reg_variable"`
}
type RegVariable struct {
XMLName xml.Name `xml:"reg_variable"`
Variable string `xml:"variable,attr"`
Max string `xml:"max,attr"`
}
type AccessMechanisms struct {
XMLName xml.Name `xml:"access_mechanisms"`
AccessMechanism []AccessMechanism `xml:"access_mechanism"`
}
type AccessMechanism struct {
XMLName xml.Name `xml:"access_mechanism"`
Accessor string `xml:"accessor,attr"`
Encoding Encoding `xml:"encoding"`
}
type Encoding struct {
XMLName xml.Name `xml:"encoding"`
Enc []Enc `xml:"enc"`
}
type Enc struct {
XMLName xml.Name `xml:"enc"`
V string `xml:"v,attr"`
}
type SystemReg struct {
RegName string
EncBinary uint32
RegAccessFlags string
}
func check(e error) {
if e != nil {
log.Fatal(e)
}
}
type accessFlag uint8
const (
SR_READ accessFlag = 1 << iota
SR_WRITE
)
func (a accessFlag) String() string {
switch a {
case SR_READ:
return "SR_READ"
case SR_WRITE:
return "SR_WRITE"
case SR_READ | SR_WRITE:
return "SR_READ | SR_WRITE"
default:
return ""
}
}
func main() {
// Write system register encoding to the sysRegEnc.go file.
// This file should be put into $GOROOT/src/cmd/internal/obj/arm64/ directory.
filename := flag.String("o", "sysRegEnc.go", "the name of the automatically generated file")
xmlfolder := flag.String("i", "./files", "the folder where the data XML files are")
flag.Parse()
out, err := os.Create(*filename)
check(err)
defer out.Close()
files, err := ioutil.ReadDir(*xmlfolder)
check(err)
var systemregs []SystemReg
regNum := 0
for _, file := range files {
xmlFile, err := os.Open(filepath.Join(*xmlfolder, file.Name()))
check(err)
value, err := ioutil.ReadAll(xmlFile)
check(err)
var regpage RegisterPage
err = xml.Unmarshal(value, &regpage)
if err != nil {
log.Printf("%s: The data of this file does not fit into Register_page struct\n", file.Name())
xmlFile.Close()
continue
}
sysreg := regpage.Registers.Register
sysregName := sysreg.RegShortName
if strings.Contains(sysregName, "EL2") || strings.Contains(sysregName, "EL3") {
log.Printf("%s: we do not support EL2 and EL3 system registers at the moment!\n", file.Name())
xmlFile.Close()
continue
}
if strings.Contains(sysregName, "<op1>_<Cn>_<Cm>_<op2>") {
log.Printf("%s: The register %s is reserved\n", file.Name(), sysregName)
xmlFile.Close()
continue
}
if len(sysreg.AccessMechanisms.AccessMechanism) == 0 {
log.Printf("%s: The data of this file does not fit into AccessMechanisms struct\n", file.Name())
xmlFile.Close()
continue
}
m0 := sysreg.AccessMechanisms.AccessMechanism[0]
ins := m0.Accessor
if !(strings.Contains(ins, "MRS") || strings.Contains(ins, "MSR")) {
log.Printf("%s: \"%s\" is not a system register for MSR and MRS instructions.\n", file.Name(), sysregName)
xmlFile.Close()
continue
}
m := sysreg.AccessMechanisms.AccessMechanism
accessF := accessFlag(0)
for j := range m {
accessor := m[j].Accessor
if strings.Contains(accessor, "MRS") {
accessF |= SR_READ
}
if strings.Contains(accessor, "MSR") {
accessF |= SR_WRITE
}
}
aFlags := accessF.String()
max := 0
var enc [5]uint64
if len(m0.Encoding.Enc) != 5 {
log.Printf("%s: The data of this file does not fit into S<op0>_<op1>_<Cn>_<Cm>_<op2> encoding\n", file.Name())
xmlFile.Close()
continue
}
// Special handling for system register name containing <n>.
if strings.Contains(sysregName, "<n>") {
max, err = strconv.Atoi(sysreg.RegVariables.RegVariable.Max)
check(err)
for n := 0; n <= max; n++ {
name := strings.Replace(sysregName, "<n>", strconv.Itoa(n), -1)
systemregs = append(systemregs, SystemReg{name, 0, aFlags})
regNum++
}
} else {
systemregs = append(systemregs, SystemReg{sysregName, 0, aFlags})
regNum++
}
for i := 0; i <= max; i++ {
index := regNum - 1 - max + i
for j := 0; j < len(m0.Encoding.Enc); j++ {
value := m0.Encoding.Enc[j].V
// value="0b010:n[3]"
// value="0b1:n[1:0]"
// value="ob10:n[4:3]"
if strings.Contains(value, "n") && strings.Contains(value, "b") {
v0 := strings.Split(value, "b")
v1 := strings.Split(v0[1], "n")
v2 := strings.Trim(v1[1], "[]")
bits, err := strconv.ParseUint(strings.Trim(v1[0], ":"), 2, 32)
check(err)
if strings.Contains(v1[1], ":") {
// v1[1]="[1:0]", v2="1:0"
// Get the index.
first, err := strconv.Atoi(strings.Split(v2, ":")[0])
check(err)
last, err := strconv.Atoi(strings.Split(v2, ":")[1])
check(err)
// Get the corresponding appended bits.
bitsAppend := (i >> uint(last) & (1<<uint(first-last+1) - 1))
// Join the bits to get the final bits.
finalBits := int(bits)<<uint(first-last+1) | bitsAppend
enc[j] = uint64(finalBits)
} else {
// v1[1]="[3]", v2="3"
// Get the corresponding appended bits.
first, err := strconv.Atoi(v2)
check(err)
bitsAppend := (i >> uint(first)) & 1
// Join the bits to get the final bits.
finalBits := int(bits)<<1 | bitsAppend
enc[j] = uint64(finalBits)
}
} else if strings.Contains(value, "n") && !strings.Contains(value, "b") {
// value="n[3:0]" | value="n[2:0]"
v0 := strings.Split(value, "n")
v1 := strings.Trim(v0[1], "[]")
v2 := strings.Split(v1, ":")
// Convert string format to integer.
first, err := strconv.Atoi(v2[0])
check(err)
last, err := strconv.Atoi(v2[1])
check(err)
finalBits := (i >> uint(last) & (1<<uint(first-last+1) - 1))
enc[j] = uint64(finalBits)
} else {
// value="0b110"
v := strings.Split(value, "b")
var err error = nil
enc[j], err = strconv.ParseUint(v[1], 2, 64)
check(err)
}
}
systemregs[index].EncBinary = uint32(enc[0]<<19 | enc[1]<<16 | enc[2]<<12 | enc[3]<<8 | enc[4]<<5)
}
// Close the xml file.
xmlFile.Close()
}
log.Printf("The total number of parsing registers is %d\n", regNum)
w := bufio.NewWriter(out)
fmt.Fprintf(w, "// Code generated by arm64gen -i %s -o %s. DO NOT EDIT.\n", *xmlfolder, *filename)
fmt.Fprintln(w, "\npackage arm64\n\nconst (\n\tSYSREG_BEGIN = REG_SPECIAL + iota")
for i := 0; i < regNum; i++ {
fmt.Fprintf(w, "\tREG_%s\n", systemregs[i].RegName)
}
fmt.Fprintln(w, "\tSYSREG_END\n)")
fmt.Fprintln(w, `
const (
SR_READ = 1 << iota
SR_WRITE
)
var SystemReg = []struct {
Name string
Reg int16
Enc uint32
// AccessFlags is the readable and writeable property of system register.
AccessFlags uint8
}{`)
for i := 0; i < regNum; i++ {
fmt.Fprintf(w, "\t{\"%s\", REG_%s, 0x%x, %s},\n", systemregs[i].RegName, systemregs[i].RegName, systemregs[i].EncBinary, systemregs[i].RegAccessFlags)
}
fmt.Fprintln(w, "}")
fmt.Fprintln(w, `
func SysRegEnc(r int16) (string, uint32, uint8) {
// The automatic generator guarantees that the order
// of Reg in SystemReg struct is consistent with the
// order of system register declarations
if r <= SYSREG_BEGIN || r >= SYSREG_END {
return "", 0, 0
}
v := SystemReg[r-SYSREG_BEGIN-1]
return v.Name, v.Enc, v.AccessFlags
}`)
w.Flush()
}
@@ -0,0 +1,714 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// arm64spec reads the ``ARMv8-A Reference Manual''
// to collect instruction encoding details and writes those
// details to standard output in JSON format.
// usage: arm64spec file.pdf
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"math"
"os"
"regexp"
"sort"
"strconv"
"strings"
"rsc.io/pdf"
)
type Inst struct {
Name string
Bits string
Arch string
Syntax string
Code string
Alias string
}
const debugPage = 0
var stdout *bufio.Writer
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
log.SetFlags(0)
log.SetPrefix("arm64spec: ")
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "usage: arm64spec file.pdf\n")
os.Exit(2)
}
f, err := pdf.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
// Find instruction set reference in outline, to build instruction list.
instList := instHeadings(f.Outline())
if debugPage == 0 {
fmt.Println("the number of instructions:", len(instList))
}
if len(instList) < 200 {
log.Fatalf("only found %d instructions in table of contents", len(instList))
}
file, err := os.Create("inst.json")
check(err)
w := bufio.NewWriter(file)
_, err = w.WriteString("[")
check(err)
numTable := 0
defer w.Flush()
defer file.Close()
// Scan document looking for instructions.
// Must find exactly the ones in the outline.
n := f.NumPage()
PageLoop:
for pageNum := 435; pageNum <= n; pageNum++ {
if debugPage > 0 && pageNum != debugPage {
continue
}
if pageNum == 770 {
continue
}
if pageNum > 1495 {
break
}
p := f.Page(pageNum)
name, table := parsePage(pageNum, p, f)
if name == "" {
continue
}
if len(table) < 1 {
if false {
fmt.Fprintf(os.Stderr, "no encodings for instruction %q (page %d)\n", name, pageNum)
}
continue
}
for _, inst := range table {
if numTable > 0 {
_, err = w.WriteString(jsFix.Replace(","))
check(err)
_, err = w.WriteString("\n")
check(err)
}
numTable++
js, _ := json.Marshal(inst)
_, err = w.WriteString(jsFix.Replace(string(js)))
check(err)
}
for j, headline := range instList {
if name == headline {
instList[j] = ""
continue PageLoop
}
}
fmt.Fprintf(os.Stderr, "unexpected instruction %q (page %d)\n", name, pageNum)
}
_, err = w.WriteString("\n]\n")
check(err)
w.Flush()
if debugPage == 0 {
for _, headline := range instList {
if headline != "" {
fmt.Fprintf(os.Stderr, "missing instruction %q\n", headline)
}
}
}
}
func instHeadings(outline pdf.Outline) []string {
return appendInstHeadings(outline, nil)
}
var instRE = regexp.MustCompile(`C[\d.]+ Alphabetical list of A64 base instructions`)
var instRE_A = regexp.MustCompile(`C[\d.]+ Alphabetical list of A64 floating-point and Advanced SIMD instructions`)
var childRE = regexp.MustCompile(`C[\d.]+ (.+)`)
var sectionRE = regexp.MustCompile(`^C[\d.]+$`)
var bitRE = regexp.MustCompile(`^( |[01]|\([01]\))*$`)
var IMMRE = regexp.MustCompile(`^imm[\d]+$`)
func appendInstHeadings(outline pdf.Outline, list []string) []string {
if instRE.MatchString(outline.Title) || instRE_A.MatchString(outline.Title) {
for _, child := range outline.Child {
m := childRE.FindStringSubmatch(child.Title)
if m == nil {
fmt.Fprintf(os.Stderr, "cannot parse section title: %s\n", child.Title)
continue
}
list = append(list, m[1])
}
}
for _, child := range outline.Child {
list = appendInstHeadings(child, list)
}
return list
}
const inch = 72.0
func parsePage(num int, p pdf.Page, f *pdf.Reader) (name string, table []Inst) {
content := p.Content()
var text []pdf.Text
CrossTwoPage := true
for _, t := range content.Text {
text = append(text, t)
}
text = findWords(text)
if !(instRE.MatchString(text[1].S) || instRE_A.MatchString(text[1].S)) || len(text) == 0 || !sectionRE.MatchString(text[2].S) {
return "", nil
}
// Check whether the content crosses the page.
for _, t := range text {
if match(t, "Arial,Bold", 10, "Assembler symbols") {
CrossTwoPage = false
break
}
}
// Deal with cross page issue. To the next page content.
var Ncontent pdf.Content
Npagebox := false
CrossThreePage := false
Noffset := ""
if CrossTwoPage == true {
Np := f.Page(num + 1)
Ncontent = Np.Content()
var Ntext []pdf.Text
for _, t := range Ncontent.Text {
Ntext = append(Ntext, t)
}
Ntext = findWords(Ntext)
if len(Ntext) == 0 || sectionRE.MatchString(Ntext[2].S) {
Ntext = text[:0]
} else {
for _, t := range Ntext {
if match(t, "Arial,Bold", 10, "offset") {
Noffset = t.S
Npagebox = true
}
// This istruction cross three pages.
if match(t, "Arial,Bold", 10, "Assembler symbols") {
CrossThreePage = false
} else {
CrossThreePage = true
}
text = append(text, t)
}
}
}
if CrossThreePage == true {
NNp := f.Page(num + 2)
NNcontent := NNp.Content()
var NNtext []pdf.Text
for _, t := range NNcontent.Text {
NNtext = append(NNtext, t)
}
NNtext = findWords(NNtext)
if len(NNtext) == 0 || sectionRE.MatchString(NNtext[2].S) {
NNtext = text[:0]
} else {
for _, t := range NNtext {
text = append(text, t)
}
}
}
// Get alias and remove text we should ignore.
out := text[:0]
alias := ""
for _, t := range text {
if strings.Contains(t.S, "instruction is used by the alias") || strings.Contains(t.S, "instruction is an alias of") {
alias_t := strings.SplitAfter(t.S, ".")
alias = alias_t[0]
}
// Skip page footer
if match(t, "Arial-ItalicMT", 8, "") || match(t, "ArialMT", 8, "") {
if debugPage > 0 {
fmt.Println("==the skip page footer is:==", t)
}
continue
}
// Skip the body text
if match(t, "TimesNewRoman", 9, "") || match(t, "TimesNewRomanPS-ItalicMT", 9, "") {
if debugPage > 0 {
fmt.Println("==the skip body text is:==", t)
}
continue
}
out = append(out, t)
}
text = out
// Page header must be child title.
if len(text) == 0 || !sectionRE.MatchString(text[0].S) {
return "", nil
}
name = text[1].S
inst := Inst{
Name: name,
Alias: alias,
}
text = text[2:]
// Skip body text before bits.
OffsetMark := false
k := 0
for k = 0; k < len(text); {
if !match(text[k], "Arial", 8, "31") {
k++
} else {
break
}
}
// Check offset.
if k > 0 && match(text[k-1], "Arial,Bold", 10, "") {
OffsetMark = true
text = text[k-1:]
} else {
text = text[k:]
}
// Encodings follow.
BitMark := false
bits := ""
// Find bits.
for i := 0; i < len(text); {
inst.Bits = ""
offset := ""
abits := ""
// Read bits only one time.
if OffsetMark == true {
for i < len(text) && !match(text[i], "Arial", 8, "") {
i++
}
if i < len(text) {
offset = text[i-1].S
BitMark = false
bits = ""
} else {
break
}
}
if BitMark == false {
if Npagebox == true && Noffset == offset {
bits, i = readBitBox(name, Ncontent, text, i)
} else {
bits, i = readBitBox(name, content, text, i)
}
BitMark = true
// Every time, get "then SEE" after get bits.
enc := false
if i < len(text)-1 {
m := i
for m < len(text)-1 && !match(text[m], "Arial-BoldItalicMT", 9, "encoding") {
m++
}
if match(text[m], "Arial-BoldItalicMT", 9, "encoding") && m < len(text) {
enc = true
m = m + 1
}
if enc == true {
for m < len(text) && !match(text[m], "Arial,Bold", 10, "") && match(text[m], "LucidaSansTypewriteX", 6.48, "") {
if strings.Contains(text[m].S, "then SEE") {
inst.Code = text[m].S
break
} else {
m++
}
}
}
}
}
// Possible subarchitecture notes.
ArchLoop:
for i < len(text) {
if !match(text[i], "Arial-BoldItalicMT", 9, "variant") || match(text[i], "Arial-BoldItalicMT", 9, "encoding") {
i++
continue
}
inst.Arch = ""
inst.Arch += offset
inst.Arch += " "
inst.Arch += text[i].S
inst.Arch = strings.TrimSpace(inst.Arch)
i++
// Encoding syntaxes.
sign := ""
SynMark := false
for i < len(text) && match(text[i], "LucidaSansTypewriteX", 6.48, "") && SynMark == false {
if (strings.Contains(text[i].S, "==") || strings.Contains(text[i].S, "!=")) && SynMark == false {
sign = text[i].S
i++
continue
}
// Avoid "equivalent to" another syntax.
if SynMark == false {
SynMark = true
inst.Syntax = ""
inst.Syntax = text[i].S
i++
}
}
abits = bits
// Analyse and replace some bits value.eg, sf==1
if strings.Contains(sign, "&&") {
split := strings.Split(sign, "&&")
for k := 0; k < len(split); {
if strings.Contains(split[k], "==") && !strings.Contains(split[k], "!") {
tmp := strings.Split(split[k], "==")
prefix := strings.TrimSpace(tmp[0])
value := strings.TrimSpace(tmp[1])
if strings.Contains(bits, prefix) && !strings.Contains(value, "x") {
abits = strings.Replace(abits, prefix, value, -1)
}
}
k++
}
} else if strings.Contains(sign, "==") && !strings.Contains(sign, "!") {
split := strings.Split(sign, "==")
prefix := strings.TrimSpace(split[0])
value := strings.TrimSpace(split[1])
if strings.Contains(bits, prefix) && !strings.Contains(value, "x") {
abits = strings.Replace(abits, prefix, value, -1)
}
}
// Deal with syntax contains {2}
if strings.Contains(inst.Syntax, "{2}") {
if !strings.Contains(abits, "Q") {
fmt.Fprintf(os.Stderr, "instruction%s - syntax%s: is wrong!!\n", name, inst.Syntax)
}
syn := inst.Syntax
bits := abits
for i := 0; i < 2; {
if i == 0 {
inst.Bits = strings.Replace(bits, "Q", "0", -1)
inst.Syntax = strings.Replace(syn, "{2}", "", -1)
table = append(table, inst)
}
if i == 1 {
inst.Bits = strings.Replace(bits, "Q", "1", -1)
inst.Syntax = strings.Replace(syn, "{2}", "2", -1)
table = append(table, inst)
}
i++
}
} else {
inst.Bits = abits
table = append(table, inst)
}
if OffsetMark == true && i < len(text) && match(text[i], "Arial-BoldItalicMT", 9, "variant") && !match(text[i], "Arial-BoldItalicMT", 9, "encoding") {
continue ArchLoop
} else {
break
}
}
}
return name, table
}
func readBitBox(name string, content pdf.Content, text []pdf.Text, i int) (string, int) {
// Bits headings
y3 := 0.0
x1 := 0.0
for i < len(text) && match(text[i], "Arial", 8, "") {
if y3 == 0 {
y3 = text[i].Y
}
if x1 == 0 {
x1 = text[i].X
}
if text[i].Y != y3 {
break
}
i++
}
// Bits fields in box
x2 := 0.0
y2 := 0.0
dy1 := 0.0
for i < len(text) && match(text[i], "Arial", 8, "") {
if x2 < text[i].X+text[i].W {
x2 = text[i].X + text[i].W
}
if y2 == 0 {
y2 = text[i].Y
}
if text[i].Y != y2 {
break
}
dy1 = text[i].FontSize
i++
}
// Bits fields below box
x3 := 0.0
y1 := 0.0
for i < len(text) && match(text[i], "Arial", 8, "") {
if x3 < text[i].X+text[i].W {
x3 = text[i].X + text[i].W
}
y1 = text[i].Y
if text[i].Y != y1 {
break
}
i++
}
//no bits fields below box
below_flag := true
if y1 == 0.0 {
below_flag = false
y1 = y2
}
// Encoding box
if debugPage > 0 {
fmt.Println("encoding box", x1, y3, x2, y1)
}
// Find lines (thin rectangles) separating bit fields.
var bottom, top pdf.Rect
const (
yMargin = 0.25 * 72
xMargin = 2 * 72
)
cont := 0
if below_flag == true {
for _, r := range content.Rect {
cont = cont + 1
if x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1-yMargin < r.Min.Y && r.Min.Y < y2-dy1 {
bottom = r
}
if y2+dy1 < r.Min.Y && r.Min.Y < y3+yMargin {
top = r
}
}
}
} else {
for _, r := range content.Rect {
cont = cont + 1
if x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1-yMargin-dy1 < r.Min.Y && r.Min.Y < y3-dy1 {
bottom = r
}
if y2+dy1 < r.Min.Y && r.Min.Y < y3+yMargin {
top = r
}
}
}
}
if debugPage > 0 {
fmt.Println("top", top, "bottom", bottom, "content.Rect number", cont)
}
const ε = 0.5 * 72
cont_1 := 0
var bars []pdf.Rect
for _, r := range content.Rect {
if math.Abs(r.Min.X-r.Max.X) < bottom.Max.X-bottom.Min.X-(ε/2) && math.Abs(r.Min.Y-bottom.Min.Y) < ε && math.Abs(r.Max.Y-top.Min.Y) < ε {
cont_1 = cont_1 + 1
bars = append(bars, r)
}
}
sort.Sort(RectHorizontal(bars))
if debugPage > 0 {
fmt.Println("==bars number==", cont_1)
}
// There are 16-bit and 32-bit encodings.
// In practice, they are about 2.65 and 5.3 inches wide, respectively.
// Use 4 inches as a cutoff.
nbit := 32
dx := top.Max.X - top.Min.X
if top.Max.X-top.Min.X < 4*72 {
nbit = 16
}
total := 0
var buf bytes.Buffer
for i := 0; i < len(bars); i++ {
if i > 0 {
fmt.Fprintf(&buf, "|")
}
var sub []pdf.Text
x1, x2 := bars[i].Min.X, bars[i].Max.X
for _, t := range content.Text {
tx := t.X + t.W/2
ty := t.Y
if x1 < tx && tx < x2 && y2-dy1 < ty && ty < y2+dy1 {
sub = append(sub, t)
}
}
var str []string
for _, t := range findWords(sub) {
str = append(str, t.S)
}
s := strings.Join(str, " ")
s = strings.Replace(s, ")(", ") (", -1)
// If bits contain "!" or "x", be replaced by the bits below it.
if strings.Contains(s, "!") || strings.Contains(s, "x") {
var sub1 []pdf.Text
for _, t := range content.Text {
tx := t.X + t.W/2
ty := t.Y
if x1 < tx && tx < x2 && y1-dy1 < ty && ty < y1+dy1 {
sub1 = append(sub1, t)
}
}
var str1 []string
for _, t := range findWords(sub1) {
str1 = append(str1, t.S)
}
s = strings.Join(str1, " ")
s = strings.Replace(s, ")(", ") (", -1)
}
n := len(strings.Fields(s))
var b int
if IMMRE.MatchString(s) {
bitNum := strings.TrimPrefix(s, "imm")
b, _ = strconv.Atoi(bitNum)
} else if s == "immhi" {
b = 19
} else {
b = int(float64(nbit)*(x2-x1)/dx + 0.5)
}
if n == b {
for k, f := range strings.Fields(s) {
if k > 0 {
fmt.Fprintf(&buf, "|")
}
fmt.Fprintf(&buf, "%s", f)
}
} else {
if n != 1 {
fmt.Fprintf(os.Stderr, "%s - multi-field %d-bit encoding: %s\n", name, n, s)
}
fmt.Fprintf(&buf, "%s:%d", s, b)
}
total += b
}
if total != nbit || total == 0 {
fmt.Fprintf(os.Stderr, "%s - %d-bit encoding\n", name, total)
}
return buf.String(), i
}
type RectHorizontal []pdf.Rect
func (x RectHorizontal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x RectHorizontal) Less(i, j int) bool { return x[i].Min.X < x[j].Min.X }
func (x RectHorizontal) Len() int { return len(x) }
func checkNoEncodings(num int, text []pdf.Text) {
for _, t := range text {
if match(t, "Helvetica-Bold", 9, "Encoding") {
fmt.Fprintf(os.Stderr, "page %d: unexpected encoding: %s\n", num, t.S)
}
}
}
func match(t pdf.Text, font string, size float64, substr string) bool {
return t.Font == font && math.Abs(t.FontSize-size) < 0.1 && strings.Contains(t.S, substr)
}
func findWords(chars []pdf.Text) (words []pdf.Text) {
// Sort by Y coordinate and normalize.
const nudge = 1
sort.Sort(pdf.TextVertical(chars))
old := -100000.0
for i, c := range chars {
if c.Y != old && math.Abs(old-c.Y) < nudge {
chars[i].Y = old
} else {
old = c.Y
}
}
// Sort by Y coordinate, breaking ties with X.
// This will bring letters in a single word together.
sort.Sort(pdf.TextVertical(chars))
// Loop over chars.
for i := 0; i < len(chars); {
// Find all chars on line.
j := i + 1
for j < len(chars) && chars[j].Y == chars[i].Y {
j++
}
var end float64
// Split line into words (really, phrases).
for k := i; k < j; {
ck := &chars[k]
s := ck.S
end = ck.X + ck.W
charSpace := ck.FontSize / 6
wordSpace := ck.FontSize * 2 / 3
l := k + 1
for l < j {
// Grow word.
cl := &chars[l]
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
s += cl.S
end = cl.X + cl.W
l++
continue
}
// Add space to phrase before next word.
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
s += " " + cl.S
end = cl.X + cl.W
l++
continue
}
break
}
f := ck.Font
f = strings.TrimSuffix(f, ",Italic")
f = strings.TrimSuffix(f, "-Italic")
words = append(words, pdf.Text{f, ck.FontSize, ck.X, ck.Y, end - ck.X, s})
k = l
}
i = j
}
return words
}
func sameFont(f1, f2 string) bool {
f1 = strings.TrimSuffix(f1, ",Italic")
f1 = strings.TrimSuffix(f1, "-Italic")
f2 = strings.TrimSuffix(f1, ",Italic")
f2 = strings.TrimSuffix(f1, "-Italic")
return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
}
var jsFix = strings.NewReplacer(
`\u003c`, `<`,
`\u003e`, `>`,
`\u0026`, `&`,
`\u0009`, `\t`,
)
func printTable(name string, table []Inst) {
_ = strconv.Atoi
}
@@ -0,0 +1 @@
issuerepo: golang/go
@@ -0,0 +1,5 @@
module golang.org/x/arch
go 1.18
require rsc.io/pdf v0.1.1
@@ -0,0 +1,2 @@
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,221 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"encoding/binary"
"fmt"
"log"
)
const debugDecode = false
const prefixOpcode = 1
// instFormat is a decoding rule for one specific instruction form.
// an instruction ins matches the rule if ins&Mask == Value
// DontCare bits should be zero, but the machine might not reject
// ones in those bits, they are mainly reserved for future expansion
// of the instruction set.
// The Args are stored in the same order as the instruction manual.
//
// Prefixed instructions are stored as:
//
// prefix << 32 | suffix,
//
// Regular instructions are:
//
// inst << 32
type instFormat struct {
Op Op
Mask uint64
Value uint64
DontCare uint64
Args [6]*argField
}
// argField indicate how to decode an argument to an instruction.
// First parse the value from the BitFields, shift it left by Shift
// bits to get the actual numerical value.
type argField struct {
Type ArgType
Shift uint8
BitFields
}
// Parse parses the Arg out from the given binary instruction i.
func (a argField) Parse(i [2]uint32) Arg {
switch a.Type {
default:
return nil
case TypeUnknown:
return nil
case TypeReg:
return R0 + Reg(a.BitFields.Parse(i))
case TypeCondRegBit:
return Cond0LT + CondReg(a.BitFields.Parse(i))
case TypeCondRegField:
return CR0 + CondReg(a.BitFields.Parse(i))
case TypeFPReg:
return F0 + Reg(a.BitFields.Parse(i))
case TypeVecReg:
return V0 + Reg(a.BitFields.Parse(i))
case TypeVecSReg:
return VS0 + Reg(a.BitFields.Parse(i))
case TypeVecSpReg:
return VS0 + Reg(a.BitFields.Parse(i))*2
case TypeMMAReg:
return A0 + Reg(a.BitFields.Parse(i))
case TypeSpReg:
return SpReg(a.BitFields.Parse(i))
case TypeImmSigned:
return Imm(a.BitFields.ParseSigned(i) << a.Shift)
case TypeImmUnsigned:
return Imm(a.BitFields.Parse(i) << a.Shift)
case TypePCRel:
return PCRel(a.BitFields.ParseSigned(i) << a.Shift)
case TypeLabel:
return Label(a.BitFields.ParseSigned(i) << a.Shift)
case TypeOffset:
return Offset(a.BitFields.ParseSigned(i) << a.Shift)
case TypeNegOffset:
// An oddball encoding of offset for hashchk and similar.
// e.g hashchk offset is 0b1111111000000000 | DX << 8 | D << 3
off := a.BitFields.ParseSigned(i) << a.Shift
neg := int64(-1) << (int(a.Shift) + a.BitFields.NumBits())
return Offset(neg | off)
}
}
type ArgType int8
const (
TypeUnknown ArgType = iota
TypePCRel // PC-relative address
TypeLabel // absolute address
TypeReg // integer register
TypeCondRegBit // conditional register bit (0-31)
TypeCondRegField // conditional register field (0-7)
TypeFPReg // floating point register
TypeVecReg // vector register
TypeVecSReg // VSX register
TypeVecSpReg // VSX register pair (even only encoding)
TypeMMAReg // MMA register
TypeSpReg // special register (depends on Op)
TypeImmSigned // signed immediate
TypeImmUnsigned // unsigned immediate/flag/mask, this is the catch-all type
TypeOffset // signed offset in load/store
TypeNegOffset // A negative 16 bit value 0b1111111xxxxx000 encoded as 0bxxxxx (e.g in the hashchk instruction)
TypeLast // must be the last one
)
func (t ArgType) String() string {
switch t {
default:
return fmt.Sprintf("ArgType(%d)", int(t))
case TypeUnknown:
return "Unknown"
case TypeReg:
return "Reg"
case TypeCondRegBit:
return "CondRegBit"
case TypeCondRegField:
return "CondRegField"
case TypeFPReg:
return "FPReg"
case TypeVecReg:
return "VecReg"
case TypeVecSReg:
return "VecSReg"
case TypeVecSpReg:
return "VecSpReg"
case TypeMMAReg:
return "MMAReg"
case TypeSpReg:
return "SpReg"
case TypeImmSigned:
return "ImmSigned"
case TypeImmUnsigned:
return "ImmUnsigned"
case TypePCRel:
return "PCRel"
case TypeLabel:
return "Label"
case TypeOffset:
return "Offset"
case TypeNegOffset:
return "NegOffset"
}
}
func (t ArgType) GoString() string {
s := t.String()
if t > 0 && t < TypeLast {
return "Type" + s
}
return s
}
var (
// Errors
errShort = fmt.Errorf("truncated instruction")
errUnknown = fmt.Errorf("unknown instruction")
)
var decoderCover []bool
// Decode decodes the leading bytes in src as a single instruction using
// byte order ord.
func Decode(src []byte, ord binary.ByteOrder) (inst Inst, err error) {
if len(src) < 4 {
return inst, errShort
}
if decoderCover == nil {
decoderCover = make([]bool, len(instFormats))
}
inst.Len = 4
ui_extn := [2]uint32{ord.Uint32(src[:inst.Len]), 0}
ui := uint64(ui_extn[0]) << 32
inst.Enc = ui_extn[0]
opcode := inst.Enc >> 26
if opcode == prefixOpcode {
// This is a prefixed instruction
inst.Len = 8
if len(src) < 8 {
return inst, errShort
}
// Merge the suffixed word.
ui_extn[1] = ord.Uint32(src[4:inst.Len])
ui |= uint64(ui_extn[1])
inst.SuffixEnc = ui_extn[1]
}
for i, iform := range instFormats {
if ui&iform.Mask != iform.Value {
continue
}
if ui&iform.DontCare != 0 {
if debugDecode {
log.Printf("Decode(%#x): unused bit is 1 for Op %s", ui, iform.Op)
}
// to match GNU objdump (libopcodes), we ignore don't care bits
}
for i, argfield := range iform.Args {
if argfield == nil {
break
}
inst.Args[i] = argfield.Parse(ui_extn)
}
inst.Op = iform.Op
if debugDecode {
log.Printf("%#x: search entry %d", ui, i)
continue
}
break
}
if inst.Op == 0 && inst.Enc != 0 {
return inst, errUnknown
}
return inst, nil
}
@@ -0,0 +1,98 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"encoding/binary"
"encoding/hex"
"io/ioutil"
"path"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
files, err := ioutil.ReadDir("testdata")
if err != nil {
t.Fatal(err)
}
for _, f := range files {
if !strings.HasPrefix(f.Name(), "decode") {
continue
}
filename := path.Join("testdata", f.Name())
data, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
decode(data, t, filename)
}
}
// Provide a fake symbol to verify PCrel argument decoding.
func symlookup(pc uint64) (string, uint64) {
foopc := uint64(0x100000)
if pc >= foopc && pc < foopc+0x10 {
return "foo", foopc
}
return "", 0
}
func decode(data []byte, t *testing.T, filename string) {
all := string(data)
// Simulate PC based on number of instructions found in the test file.
pc := uint64(0)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 3)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("%s: parsing %q: missing | separator", filename, f[0])
continue
}
if i%2 != 0 {
t.Errorf("%s: parsing %q: misaligned | separator", filename, f[0])
}
size := i / 2
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("%s: parsing %q: %v", filename, f[0], err)
continue
}
syntax, asm := f[1], f[2]
inst, err := Decode(code, binary.BigEndian)
var out string
if err != nil {
out = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst, pc)
case "plan9":
pc := pc
// Hack: Setting PC to 0 effectively transforms the PC relative address
// of CALL (bl) into an absolute address when decoding in GoSyntax. This
// simplifies the testing of symbol lookups via symlookup above.
if inst.Op == BL {
pc = 0
}
out = GoSyntax(inst, pc, symlookup)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
}
pc += uint64(size)
if out != asm || inst.Len != size {
t.Errorf("%s: Decode(%s) [%s] = %s want %s", filename, f[0], syntax, out, asm)
}
}
}
@@ -0,0 +1,6 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ppc64asm implements decoding of 64-bit PowerPC machine code.
package ppc64asm
@@ -0,0 +1,541 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
// Copied and simplified from rsc.io/arm/armasm/ext_test.go.
package ppc64asm
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
var (
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint32
enc [8]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and be run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
) {
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text || inst.Len != dec.nenc {
suffix := ""
if allowedMismatch(text, size, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
const start = 0x8000 // start address of text
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "ppc64asm")
if err != nil {
return
}
file = f.Name()
f.Seek(start, io.SeekStart)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if len(x) != 4 && len(x) != 8 {
panic(fmt.Sprintf("Unexpected instruction %v\n", x))
}
izeros := zeros
if len(x) == 4 {
// Only pad to 4 bytes for a 4 byte instruction word.
izeros = izeros[4:]
}
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, izeros[len(x):])
}
w.Write(x)
w.Write(izeros[len(x):])
size += len(izeros)
})
return file, f, size, nil
}
var zeros = []byte{0, 0, 0, 0, 0, 0, 0, 0}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
if len(enc) < 4 {
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
}
return enc
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, src []byte) (inst Inst, text string) {
// If printTests is set, we record the coverage value
// before and after, and we write out the inputs for which
// coverage went up, in the format expected in testdata/decode.text.
// This produces a fairly small set of test cases that exercise nearly
// all the code.
var cover float64
if *printTests {
cover -= coverage()
}
inst, err := Decode(src, binary.BigEndian)
if err != nil {
text = "error: " + err.Error()
} else {
text = inst.String()
switch syntax {
//case "arm":
// text = ARMSyntax(inst)
case "gnu":
text = GNUSyntax(inst, 0)
//case "plan9":
// text = GoSyntax(inst, 0, nil)
default:
text = "error: unknown syntax " + syntax
}
}
if *printTests {
cover += coverage()
if cover > 0 {
max := len(src)
if max > 4 && inst.Len <= 4 {
max = 4
}
fmt.Printf("%x|%x\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], syntax, text)
}
}
return
}
// coverage returns a floating point number denoting the
// test coverage until now. The number increases when new code paths are exercised,
// both in the Go program and in the decoder byte code.
func coverage() float64 {
var f float64
f += testing.Coverage()
f += decodeCoverage()
return f
}
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for s := s; s != ""; {
if strings.HasPrefix(s, prefix) {
return true
}
i := strings.Index(s, " ")
if i < 0 {
break
}
s = s[i+1:]
}
}
return false
}
// contains reports whether the text s contains any of the given substrings.
func contains(s string, substrings ...string) bool {
for _, sub := range substrings {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool signals whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = trimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
j += 2
}
return raw, true
}
var unhex = [256]byte{
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'A': 10,
'B': 11,
'C': 12,
'D': 13,
'E': 14,
'F': 15,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = trimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// trimSpace trims leading and trailing space from s, returning a subslice of s.
func trimSpace(s []byte) []byte {
j := len(s)
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
j--
}
i := 0
for i < j && (s[i] == ' ' || s[i] == '\t') {
i++
}
return s[i:j]
}
// pcrel matches instructions using relative addressing mode.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bc)[^ac ]* (?:(?:[0-9]{1,2},)|(?:[0-7]\*)|\+|lt|gt|eq|so|cr[0-7]|,)*)0x([0-9a-f]+)$`)
)
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// randomCases generates random instructions.
func randomCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
// All the strides are relatively prime to 2 and therefore to 2²⁸,
// so we will not repeat any instructions until we have tried all 2²⁸.
// Using a stride other than 1 is meant to visit the instructions in a
// pseudorandom order, which gives better variety in the set of
// test cases chosen by -printtests.
stride := uint32(10007)
n := 1 << 28 / 7
if testing.Short() {
stride = 100003
n = 1 << 28 / 1001
} else if *longTest {
stride = 2000033
n = 1 << 29
}
x := uint32(0)
for i := 0; i < n; i++ {
enc := (x%15)<<28 | x&(1<<28-1)
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
x += stride
}
}
}
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/decode.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T) func(func([]byte)) {
var codes [][]byte
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}
func caller(skip int) string {
pc, _, _, _ := runtime.Caller(skip)
f := runtime.FuncForPC(pc)
name := "?"
if f != nil {
name = f.Name()
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[i+1:]
}
}
return name
}
@@ -0,0 +1,97 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"fmt"
"strings"
)
// A BitField is a bit-field in a 32-bit word.
// Bits are counted from 0 from the MSB to 31 as the LSB.
type BitField struct {
Offs uint8 // the offset of the left-most bit.
Bits uint8 // length in bits.
// This instruction word holding this field.
// It is always 0 for ISA < 3.1 instructions. It is
// in decoding order. (0 == prefix, 1 == suffix on ISA 3.1)
Word uint8
}
func (b BitField) String() string {
if b.Bits > 1 {
return fmt.Sprintf("[%d:%d]", b.Offs, int(b.Offs+b.Bits)-1)
} else if b.Bits == 1 {
return fmt.Sprintf("[%d]", b.Offs)
} else {
return fmt.Sprintf("[%d, len=0]", b.Offs)
}
}
// Parse extracts the bitfield b from i, and return it as an unsigned integer.
// Parse will panic if b is invalid.
func (b BitField) Parse(i [2]uint32) uint32 {
if b.Bits > 32 || b.Bits == 0 || b.Offs > 31 || b.Offs+b.Bits > 32 {
panic(fmt.Sprintf("invalid bitfiled %v", b))
}
return (i[b.Word] >> (32 - b.Offs - b.Bits)) & ((1 << b.Bits) - 1)
}
// ParseSigned extracts the bitfield b from i, and return it as a signed integer.
// ParseSigned will panic if b is invalid.
func (b BitField) ParseSigned(i [2]uint32) int32 {
u := int32(b.Parse(i))
return u << (32 - b.Bits) >> (32 - b.Bits)
}
// BitFields is a series of BitFields representing a single number.
type BitFields []BitField
func (bs BitFields) String() string {
ss := make([]string, len(bs))
for i, bf := range bs {
ss[i] = bf.String()
}
return fmt.Sprintf("<%s>", strings.Join(ss, "|"))
}
func (bs *BitFields) Append(b BitField) {
*bs = append(*bs, b)
}
// parse extracts the bitfields from i, concatenate them and return the result
// as an unsigned integer and the total length of all the bitfields.
// parse will panic if any bitfield in b is invalid, but it doesn't check if
// the sequence of bitfields is reasonable.
func (bs BitFields) parse(i [2]uint32) (u uint64, Bits uint8) {
for _, b := range bs {
u = (u << b.Bits) | uint64(b.Parse(i))
Bits += b.Bits
}
return u, Bits
}
// Parse extracts the bitfields from i, concatenate them and return the result
// as an unsigned integer. Parse will panic if any bitfield in b is invalid.
func (bs BitFields) Parse(i [2]uint32) uint64 {
u, _ := bs.parse(i)
return u
}
// ParseSigned extracts the bitfields from i, concatenate them and return the result
// as a signed integer. Parse will panic if any bitfield in b is invalid.
func (bs BitFields) ParseSigned(i [2]uint32) int64 {
u, l := bs.parse(i)
return int64(u) << (64 - l) >> (64 - l)
}
// Count the number of bits in the aggregate BitFields
func (bs BitFields) NumBits() int {
num := 0
for _, b := range bs {
num += int(b.Bits)
}
return num
}
@@ -0,0 +1,109 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"testing"
)
func panicOrNot(f func()) (panicked bool) {
defer func() {
if err := recover(); err != nil {
panicked = true
}
}()
f()
return false
}
func TestBitField(t *testing.T) {
var tests = []struct {
b BitField
i uint32 // input
u uint32 // unsigned output
s int32 // signed output
fail bool // if the check should panic
}{
{BitField{0, 0, 0}, 0, 0, 0, true},
{BitField{31, 2, 0}, 0, 0, 0, true},
{BitField{31, 1, 0}, 1, 1, -1, false},
{BitField{29, 2, 0}, 0 << 1, 0, 0, false},
{BitField{29, 2, 0}, 1 << 1, 1, 1, false},
{BitField{29, 2, 0}, 2 << 1, 2, -2, false},
{BitField{29, 2, 0}, 3 << 1, 3, -1, false},
{BitField{0, 32, 0}, 1<<32 - 1, 1<<32 - 1, -1, false},
{BitField{16, 3, 0}, 1 << 15, 4, -4, false},
}
for i, tst := range tests {
var (
ou uint32
os int32
)
failed := panicOrNot(func() {
ou = tst.b.Parse([2]uint32{tst.i})
os = tst.b.ParseSigned([2]uint32{tst.i})
})
if failed != tst.fail {
t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail)
continue
}
if ou != tst.u {
t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u)
continue
}
if os != tst.s {
t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s)
}
}
}
func TestBitFields(t *testing.T) {
var tests = []struct {
b BitFields
i [2]uint32 // input
u uint64 // unsigned output
s int64 // signed output
nb int // Total number of bits in BitField
fail bool // if the check should panic
}{
{BitFields{{0, 0, 1}}, [2]uint32{0, 0}, 0, 0, 0, true},
{BitFields{{31, 2, 1}}, [2]uint32{0, 0}, 0, 0, 2, true},
{BitFields{{31, 1, 1}}, [2]uint32{0, 1}, 1, -1, 1, false},
{BitFields{{29, 2, 1}}, [2]uint32{0, 0 << 1}, 0, 0, 2, false},
{BitFields{{29, 2, 1}}, [2]uint32{0, 1 << 1}, 1, 1, 2, false},
{BitFields{{29, 2, 1}}, [2]uint32{0, 2 << 1}, 2, -2, 2, false},
{BitFields{{29, 2, 1}}, [2]uint32{0, 3 << 1}, 3, -1, 2, false},
{BitFields{{0, 32, 1}}, [2]uint32{0, 1<<32 - 1}, 1<<32 - 1, -1, 32, false},
{BitFields{{16, 3, 1}}, [2]uint32{0, 1 << 15}, 4, -4, 3, false},
{BitFields{{16, 16, 0}, {16, 16, 1}}, [2]uint32{0x8016, 0x32}, 0x80160032, -0x7FE9FFCE, 32, false},
{BitFields{{14, 18, 0}, {16, 16, 1}}, [2]uint32{0x38016, 0x32}, 0x380160032, -0x07FE9FFCE, 34, false},
}
for i, tst := range tests {
var (
ou uint64
os int64
onb int
)
failed := panicOrNot(func() {
onb = tst.b.NumBits()
ou = tst.b.Parse(tst.i)
os = tst.b.ParseSigned(tst.i)
})
if failed != tst.fail {
t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail)
continue
}
if ou != tst.u {
t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u)
continue
}
if os != tst.s {
t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s)
}
if onb != tst.nb {
t.Errorf("case %d: %v.NumBits() returned %d, expected %d", i, tst.b, onb, tst.nb)
}
}
}
@@ -0,0 +1,471 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"bytes"
"fmt"
"strings"
)
var (
// bit 3 of index is a negated check.
condBit = [8]string{
"lt", "gt", "eq", "so",
"ge", "le", "ne", "ns"}
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This form typically matches the syntax defined in the Power ISA Reference Manual.
func GNUSyntax(inst Inst, pc uint64) string {
var buf bytes.Buffer
// When there are all 0s, identify them as the disassembler
// in binutils would.
if inst.Enc == 0 {
return ".long 0x0"
} else if inst.Op == 0 {
return "error: unknown instruction"
}
PC := pc
// Special handling for some ops
startArg := 0
sep := " "
opName := inst.Op.String()
argList := inst.Args[:]
switch opName {
case "bc", "bcl", "bca", "bcla", "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl":
sfx := inst.Op.String()[2:]
bo := int(inst.Args[0].(Imm))
bi := inst.Args[1].(CondReg)
atsfx := [4]string{"", "?", "-", "+"}
decsfx := [2]string{"dnz", "dz"}
//BO field is... complicated (z == ignored bit, at == prediction hint)
//Paraphrased from ISA 3.1 Book I Section 2.4:
//
//0000z -> decrement ctr, b if ctr != 0 and CRbi == 0
//0001z -> decrement ctr, b if ctr == 0 and CRbi == 0
//001at -> b if CRbi == 0
//0100z -> decrement ctr, b if ctr != 0 and CRbi == 1
//0101z -> decrement ctr, b if ctr == 0 and CRbi == 1
//011at -> b if CRbi == 1
//1a00t -> decrement ctr, b if ctr != 0
//1a01t -> decrement ctr, b if ctr == 0
//1z1zz -> b always
// Decoding (in this order) we get
// BO & 0b00100 == 0b00000 -> dz if BO[1], else dnz (not simplified for bcctrl forms)
// BO & 0b10000 == 0b10000 -> (bc and bca forms not simplified), at = B[4]B[0] if B[2] != 0, done
// BO & 0b10000 == 0b00000 -> t if BO[3], else f
// BO & 0b10100 == 0b00100 -> at = B[0:1]
// BI fields rename as follows:
// less than : lt BI%4==0 && test == t
// less than or equal : le BI%4==1 && test == f
// equal : eq BI%4==2 && test == t
// greater than or equal: ge BI%4==0 && test == f
// greater than : gt BI%4==1 && test == t
// not less than : nl BI%4==0 && test == f
// not equal : ne BI%4==2 && test == f
// not greater than : ng BI%4==1 && test == f
// summary overflow : so BI%4==3 && test == t
// not summary overflow : ns BI%4==3 && test == f
// unordered : un BI%4==3 && test == t
// not unordered : nu BI%4==3 && test == f
//
// Note, there are only 8 possible tests, but quite a few more
// ways to name fields. For simplicity, we choose those in condBit.
at := 0 // 0 == no hint, 1 == reserved, 2 == not likely, 3 == likely
form := 1 // 1 == n/a, 0 == cr bit not set, 4 == cr bit set
cr := (bi - Cond0LT) / 4
bh := -1 // Only for lr/tar/ctr variants.
switch opName {
case "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl":
bh = int(inst.Args[2].(Imm))
}
if bo&0x14 == 0x14 {
if bo == 0x14 && bi == Cond0LT { // preferred form of unconditional branch
// Likewise, avoid printing fake b/ba/bl/bla
if opName != "bc" && opName != "bca" && opName != "bcl" && opName != "bcla" {
startArg = 2
}
}
} else if bo&0x04 == 0 { // ctr is decremented
if opName != "bcctr" && opName != "bcctrl" {
startArg = 1
tf := ""
if bo&0x10 == 0x00 {
tf = "f"
if bo&0x08 == 0x08 {
tf = "t"
}
}
sfx = decsfx[(bo>>1)&1] + tf + sfx
}
if bo&0x10 == 0x10 {
if opName != "bcctr" && opName != "bcctrl" {
startArg = 2
}
if bi != Cond0LT {
// A non-zero BI bit was encoded, but ignored by BO
startArg = 0
}
at = ((bo & 0x8) >> 2) | (bo & 0x1)
} else if bo&0x4 == 0x4 {
at = bo & 0x3
}
} else if bo&0x10 == 0x10 { // BI field is not used
if opName != "bca" && opName != "bc" {
at = ((bo & 0x8) >> 2) | (bo & 0x1)
startArg = 2
}
// If BI is encoded as a bit other than 0, no mnemonic.
if bo&0x14 == 0x14 {
startArg = 0
}
} else {
form = (bo & 0x8) >> 1
startArg = 2
if bo&0x14 == 0x04 {
at = bo & 0x3
}
}
sfx += atsfx[at]
if form != 1 {
bit := int((bi-Cond0LT)%4) | (^form)&0x4
sfx = condBit[bit] + sfx
}
if at != 1 && startArg > 0 && bh <= 0 {
str := fmt.Sprintf("b%s", sfx)
if startArg > 1 && (cr != 0 || bh > 0) {
str += fmt.Sprintf(" cr%d", cr)
sep = ","
}
buf.WriteString(str)
if startArg < 2 && bh == 0 {
str := fmt.Sprintf(" %s",
gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(str)
startArg = 3
} else if bh == 0 {
startArg = 3
}
} else {
if startArg == 0 || bh > 0 || at == 1 {
buf.WriteString(inst.Op.String())
buf.WriteString(atsfx[at])
startArg = 0
} else {
buf.WriteString("b" + sfx)
}
if bh == 0 {
str := fmt.Sprintf(" %d,%s", bo, gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(str)
startArg = 3
}
}
case "mtspr":
opcode := inst.Op.String()
buf.WriteString(opcode[0:2])
switch spr := inst.Args[0].(type) {
case SpReg:
switch spr {
case 1:
buf.WriteString("xer")
startArg = 1
case 8:
buf.WriteString("lr")
startArg = 1
case 9:
buf.WriteString("ctr")
startArg = 1
default:
buf.WriteString("spr")
}
default:
buf.WriteString("spr")
}
case "mfspr":
opcode := inst.Op.String()
buf.WriteString(opcode[0:2])
arg := inst.Args[0]
switch spr := inst.Args[1].(type) {
case SpReg:
switch spr {
case 1:
buf.WriteString("xer ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
case 8:
buf.WriteString("lr ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
case 9:
buf.WriteString("ctr ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
case 268:
buf.WriteString("tb ")
buf.WriteString(gnuArg(&inst, 0, arg, PC))
startArg = 2
default:
buf.WriteString("spr")
}
default:
buf.WriteString("spr")
}
case "mtfsfi", "mtfsfi.":
buf.WriteString(opName)
l := inst.Args[2].(Imm)
if l == 0 {
// L == 0 is an extended mnemonic for the same.
asm := fmt.Sprintf(" %s,%s",
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(asm)
startArg = 3
}
case "paste.":
buf.WriteString(opName)
l := inst.Args[2].(Imm)
if l == 1 {
// L == 1 is an extended mnemonic for the same.
asm := fmt.Sprintf(" %s,%s",
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC))
buf.WriteString(asm)
startArg = 3
}
case "mtfsf", "mtfsf.":
buf.WriteString(opName)
l := inst.Args[3].(Imm)
if l == 0 {
// L == 0 is an extended mnemonic for the same.
asm := fmt.Sprintf(" %s,%s,%s",
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC),
gnuArg(&inst, 2, inst.Args[2], PC))
buf.WriteString(asm)
startArg = 4
}
case "sync":
lsc := inst.Args[0].(Imm)<<4 | inst.Args[1].(Imm)
switch lsc {
case 0x00:
buf.WriteString("hwsync")
startArg = 2
case 0x10:
buf.WriteString("lwsync")
startArg = 2
default:
buf.WriteString(opName)
}
case "lbarx", "lharx", "lwarx", "ldarx":
// If EH == 0, omit printing EH.
eh := inst.Args[3].(Imm)
if eh == 0 {
argList = inst.Args[:3]
}
buf.WriteString(inst.Op.String())
case "paddi":
// There are several extended mnemonics. Notably, "pla" is
// the only valid mnemonic for paddi (R=1), In this case, RA must
// always be 0. Otherwise it is invalid.
r := inst.Args[3].(Imm)
ra := inst.Args[1].(Reg)
str := opName
if ra == R0 {
name := []string{"pli", "pla"}
str = fmt.Sprintf("%s %s,%s",
name[r&1],
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 2, inst.Args[2], PC))
startArg = 4
} else {
str = fmt.Sprintf("%s %s,%s,%s", opName,
gnuArg(&inst, 0, inst.Args[0], PC),
gnuArg(&inst, 1, inst.Args[1], PC),
gnuArg(&inst, 2, inst.Args[2], PC))
startArg = 4
if r == 1 {
// This is an illegal encoding (ra != 0 && r == 1) on ISA 3.1.
v := uint64(inst.Enc)<<32 | uint64(inst.SuffixEnc)
return fmt.Sprintf(".quad 0x%x", v)
}
}
buf.WriteString(str)
default:
// Prefixed load/stores do not print the displacement register when R==1 (they are PCrel).
// This also implies RA should be 0. Likewise, when R==0, printing of R can be omitted.
if strings.HasPrefix(opName, "pl") || strings.HasPrefix(opName, "pst") {
r := inst.Args[3].(Imm)
ra := inst.Args[2].(Reg)
d := inst.Args[1].(Offset)
if r == 1 && ra == R0 {
str := fmt.Sprintf("%s %s,%d", opName, gnuArg(&inst, 0, inst.Args[0], PC), d)
buf.WriteString(str)
startArg = 4
} else {
str := fmt.Sprintf("%s %s,%d(%s)", opName,
gnuArg(&inst, 0, inst.Args[0], PC),
d,
gnuArg(&inst, 2, inst.Args[2], PC))
if r == 1 {
// This is an invalid encoding (ra != 0 && r == 1) on ISA 3.1.
v := uint64(inst.Enc)<<32 | uint64(inst.SuffixEnc)
return fmt.Sprintf(".quad 0x%x", v)
}
buf.WriteString(str)
startArg = 4
}
} else {
buf.WriteString(opName)
}
}
for i, arg := range argList {
if arg == nil {
break
}
if i < startArg {
continue
}
text := gnuArg(&inst, i, arg, PC)
if text == "" {
continue
}
buf.WriteString(sep)
sep = ","
buf.WriteString(text)
}
return buf.String()
}
// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules.
// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy
// of inst, it's ok to modify inst.Args here.
func gnuArg(inst *Inst, argIndex int, arg Arg, pc uint64) string {
// special cases for load/store instructions
if _, ok := arg.(Offset); ok {
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
panic(fmt.Errorf("wrong table: offset not followed by register"))
}
}
switch arg := arg.(type) {
case Reg:
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
return "0"
}
return arg.String()
case CondReg:
// The CondReg can either be found in a CMP, where the
// condition register field is being set, or in an instruction
// like a branch or isel that is testing a bit in a condition
// register field.
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
return "" // don't show cr0 for cmp instructions
} else if arg >= CR0 {
return fmt.Sprintf("cr%d", int(arg-CR0))
}
bit := condBit[(arg-Cond0LT)%4]
if arg <= Cond0SO {
return bit
}
return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit)
case Imm:
return fmt.Sprintf("%d", arg)
case SpReg:
switch int(arg) {
case 1:
return "xer"
case 8:
return "lr"
case 9:
return "ctr"
case 268:
return "tb"
default:
return fmt.Sprintf("%d", int(arg))
}
case PCRel:
// If the arg is 0, use the relative address format.
// Otherwise the pc is meaningful, use absolute address.
if int(arg) == 0 {
return fmt.Sprintf(".%+#x", int(arg))
}
addr := pc + uint64(int64(arg))
return fmt.Sprintf("%#x", addr)
case Label:
return fmt.Sprintf("%#x", uint32(arg))
case Offset:
reg := inst.Args[argIndex+1].(Reg)
removeArg(inst, argIndex+1)
if reg == R0 {
return fmt.Sprintf("%d(0)", int(arg))
}
return fmt.Sprintf("%d(r%d)", int(arg), reg-R0)
}
return fmt.Sprintf("???(%v)", arg)
}
// removeArg removes the arg in inst.Args[index].
func removeArg(inst *Inst, index int) {
for i := index; i < len(inst.Args); i++ {
if i+1 < len(inst.Args) {
inst.Args[i] = inst.Args[i+1]
} else {
inst.Args[i] = nil
}
}
}
// isLoadStoreOp returns true if op is a load or store instruction
func isLoadStoreOp(op Op) bool {
switch op {
case LBZ, LBZU, LBZX, LBZUX:
return true
case LHZ, LHZU, LHZX, LHZUX:
return true
case LHA, LHAU, LHAX, LHAUX:
return true
case LWZ, LWZU, LWZX, LWZUX:
return true
case LWA, LWAX, LWAUX:
return true
case LD, LDU, LDX, LDUX:
return true
case LQ:
return true
case STB, STBU, STBX, STBUX:
return true
case STH, STHU, STHX, STHUX:
return true
case STW, STWU, STWX, STWUX:
return true
case STD, STDU, STDX, STDUX:
return true
case STQ:
return true
case LHBRX, LWBRX, STHBRX, STWBRX:
return true
case LBARX, LWARX, LHARX, LDARX:
return true
}
return false
}
@@ -0,0 +1,355 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"bytes"
"fmt"
)
type Inst struct {
Op Op // Opcode mnemonic
Enc uint32 // Raw encoding bits (if Len == 8, this is the prefix word)
Len int // Length of encoding in bytes.
SuffixEnc uint32 // Raw encoding bits of second word (if Len == 8)
Args Args // Instruction arguments, in Power ISA manual order.
}
func (i Inst) String() string {
var buf bytes.Buffer
buf.WriteString(i.Op.String())
for j, arg := range i.Args {
if arg == nil {
break
}
if j == 0 {
buf.WriteString(" ")
} else {
buf.WriteString(", ")
}
buf.WriteString(arg.String())
}
return buf.String()
}
// An Op is an instruction operation.
type Op uint16
func (o Op) String() string {
if int(o) >= len(opstr) || opstr[o] == "" {
return fmt.Sprintf("Op(%d)", int(o))
}
return opstr[o]
}
// An Arg is a single instruction argument, one of these types: Reg, CondReg, SpReg, Imm, PCRel, Label, or Offset.
type Arg interface {
IsArg()
String() string
}
// An Args holds the instruction arguments.
// If an instruction has fewer than 6 arguments,
// the final elements in the array are nil.
type Args [6]Arg
// A Reg is a single register. The zero value means R0, not the absence of a register.
// It also includes special registers.
type Reg uint16
const (
_ Reg = iota
R0
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13
R14
R15
R16
R17
R18
R19
R20
R21
R22
R23
R24
R25
R26
R27
R28
R29
R30
R31
F0
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
F16
F17
F18
F19
F20
F21
F22
F23
F24
F25
F26
F27
F28
F29
F30
F31
V0 // VSX extension, F0 is V0[0:63].
V1
V2
V3
V4
V5
V6
V7
V8
V9
V10
V11
V12
V13
V14
V15
V16
V17
V18
V19
V20
V21
V22
V23
V24
V25
V26
V27
V28
V29
V30
V31
VS0
VS1
VS2
VS3
VS4
VS5
VS6
VS7
VS8
VS9
VS10
VS11
VS12
VS13
VS14
VS15
VS16
VS17
VS18
VS19
VS20
VS21
VS22
VS23
VS24
VS25
VS26
VS27
VS28
VS29
VS30
VS31
VS32
VS33
VS34
VS35
VS36
VS37
VS38
VS39
VS40
VS41
VS42
VS43
VS44
VS45
VS46
VS47
VS48
VS49
VS50
VS51
VS52
VS53
VS54
VS55
VS56
VS57
VS58
VS59
VS60
VS61
VS62
VS63
A0 // MMA registers. These are effectively shadow registers of four adjacent VSR's [An*4,An*4+3]
A1
A2
A3
A4
A5
A6
A7
)
func (Reg) IsArg() {}
func (r Reg) String() string {
switch {
case R0 <= r && r <= R31:
return fmt.Sprintf("r%d", int(r-R0))
case F0 <= r && r <= F31:
return fmt.Sprintf("f%d", int(r-F0))
case V0 <= r && r <= V31:
return fmt.Sprintf("v%d", int(r-V0))
case VS0 <= r && r <= VS63:
return fmt.Sprintf("vs%d", int(r-VS0))
case A0 <= r && r <= A7:
return fmt.Sprintf("a%d", int(r-A0))
default:
return fmt.Sprintf("Reg(%d)", int(r))
}
}
// CondReg is a bit or field in the condition register.
type CondReg int8
const (
_ CondReg = iota
// Condition Regster bits
Cond0LT
Cond0GT
Cond0EQ
Cond0SO
Cond1LT
Cond1GT
Cond1EQ
Cond1SO
Cond2LT
Cond2GT
Cond2EQ
Cond2SO
Cond3LT
Cond3GT
Cond3EQ
Cond3SO
Cond4LT
Cond4GT
Cond4EQ
Cond4SO
Cond5LT
Cond5GT
Cond5EQ
Cond5SO
Cond6LT
Cond6GT
Cond6EQ
Cond6SO
Cond7LT
Cond7GT
Cond7EQ
Cond7SO
// Condition Register Fields
CR0
CR1
CR2
CR3
CR4
CR5
CR6
CR7
)
func (CondReg) IsArg() {}
func (c CondReg) String() string {
switch {
default:
return fmt.Sprintf("CondReg(%d)", int(c))
case c >= CR0:
return fmt.Sprintf("CR%d", int(c-CR0))
case c >= Cond0LT && c < CR0:
return fmt.Sprintf("Cond%d%s", int((c-Cond0LT)/4), [4]string{"LT", "GT", "EQ", "SO"}[(c-Cond0LT)%4])
}
}
// SpReg is a special register, its meaning depends on Op.
type SpReg uint16
const (
SpRegZero SpReg = 0
)
func (SpReg) IsArg() {}
func (s SpReg) String() string {
return fmt.Sprintf("SpReg(%d)", int(s))
}
// PCRel is a PC-relative offset, used only in branch instructions.
type PCRel int32
func (PCRel) IsArg() {}
func (r PCRel) String() string {
return fmt.Sprintf("PC%+#x", int32(r))
}
// A Label is a code (text) address, used only in absolute branch instructions.
type Label uint32
func (Label) IsArg() {}
func (l Label) String() string {
return fmt.Sprintf("%#x", uint32(l))
}
// Imm represents an immediate number.
type Imm int64
func (Imm) IsArg() {}
func (i Imm) String() string {
return fmt.Sprintf("%d", int32(i))
}
// Offset represents a memory offset immediate.
type Offset int64
func (Offset) IsArg() {}
func (o Offset) String() string {
return fmt.Sprintf("%+d", int32(o))
}
@@ -0,0 +1,61 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"encoding/binary"
"strings"
"testing"
)
func TestObjdumpPowerTestdata(t *testing.T) { testObjdump(t, testdataCases(t)) }
func TestObjdumpPowerManual(t *testing.T) { testObjdump(t, hexCases(t, objdumpManualTests)) }
// Disable this for now since generating all possible bit combinations within a word
// generates lots of ppc64x instructions not possible with golang so not worth supporting..
//func TestObjdumpPowerRandom(t *testing.T) { testObjdump(t, randomCases(t)) }
// objdumpManualTests holds test cases that will be run by TestObjdumpPowerManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Manual, particularly with tracing enabled.
// Note that these are byte sequences, so they must be reversed from the usual
// word presentation.
var objdumpManualTests = `
6d746162
4c040000
88000017
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
// we support more instructions than binutils
if strings.Contains(dec.text, ".long") {
return true
}
switch inst.Op {
case BC: // We don't print PC relative branches the same way.
return true
case DCBF, DCBT: // We only support extended mnemonics, and may not print 0 where R0 == 0.
return true
case MTVSRWA, MTVSRWZ, MFVSRWZ, MFVSRD, MTVSRD: // We don't support extended mnemonics using VRs or FPRs
return true
case ISEL: // We decode the BI similar to conditional branch insn, objdump doesn't.
return true
case SYNC, WAIT, RFEBB: // ISA 3.1 adds more bits and extended mnemonics for these book ii instructions.
return true
case BL:
// TODO: Ignore these for now. The output format from gnu objdump is dependent on more than the
// instruction itself e.g: decode(48100009) = "bl 0x100008", 4, want "bl .+0x100008", 4
return true
}
if len(dec.enc) >= 4 {
_ = binary.BigEndian.Uint32(dec.enc[:4])
}
return false
}
@@ -0,0 +1,271 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and simplified from rsc.io/arm/armasm/objdumpext_test.go.
package ppc64asm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"testing"
)
var objdumpPath = "objdump"
var objdumpCrossNames = [...]string{"powerpc64-linux-gnu-objdump", "powerpc64le-linux-gnu-objdump"}
func testObjdump(t *testing.T, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping objdump test in short mode")
}
if runtime.GOARCH != "ppc64le" && runtime.GOARCH != "ppc64" {
found := false
for _, c := range objdumpCrossNames {
if _, err := exec.LookPath(c); err == nil {
objdumpPath = c
found = true
break
}
}
if !found {
t.Skip("skipping; test requires host tool objdump for ppc64 or ppc64le")
}
} else if _, err := exec.LookPath(objdumpPath); err != nil {
t.Skip(err)
}
testExtDis(t, "gnu", objdump, generate, allowedMismatchObjdump)
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if err := writeELF64(ext.File, ext.Size); err != nil {
return err
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint32 = start
addr uint32
encbuf [8]byte
enc []byte
text string
)
flush := func() {
if addr == next {
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s.%+#x", m[1], int32(uint32(targ)-addr))
}
if strings.HasPrefix(text, "stmia") {
text = "stm" + text[5:]
}
if strings.HasPrefix(text, "stmfd") {
text = "stmdb" + text[5:]
}
if strings.HasPrefix(text, "ldmfd") {
text = "ldm" + text[5:]
}
text = strings.Replace(text, "#0.0", "#0", -1)
if text == "undefined" && len(enc) == 4 {
text = "error: unknown instruction"
enc = nil
}
// Prefixed instructions may not decode as expected if
// they are an invalid form. Some are tested in decode.txt.
// objdump treats these like two instructions.
//
// Look for primary opcode 1 and advance an exta 4 bytes if
// this failed to decode.
if strings.HasPrefix(text, ".long") && enc[0]>>2 == 1 {
next += 4
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [8]byte{}
next += uint32(len(enc))
enc = nil
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
undefined = []byte("<UNDEFINED>")
unpredictable = []byte("<UNPREDICTABLE>")
illegalShifter = []byte("<illegal shifter operand>")
)
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok := parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = trimSpace(line[i:])
if bytes.Contains(line, undefined) {
text = "undefined"
return
}
if bytes.Contains(line, illegalShifter) {
text = "undefined"
return
}
if false && bytes.Contains(line, unpredictable) {
text = "unpredictable"
return
}
if i := bytes.IndexByte(line, ';'); i >= 0 {
line = trimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF64 writes an ELF64 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF64(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header64
var prog elf.Prog64
var sect elf.Section64
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.BigEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.BigEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2MSB)
hdr = elf.Header64{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
Type: 2,
Machine: uint16(elf.EM_PPC64),
Version: 1,
Entry: start,
Phoff: uint64(off1),
Shoff: uint64(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.BigEndian, &hdr)
prog = elf.Prog64{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint64(size),
Memsz: uint64(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.BigEndian, &prog)
binary.Write(&buf, binary.BigEndian, &sect) // NULL section
sect = elf.Section64{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint64(size),
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.BigEndian, &sect) // .text
sect = elf.Section64{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint64(off2 + (off3-off2)*3),
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.BigEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
@@ -0,0 +1,379 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ppc64asm
import (
"fmt"
"strings"
)
// GoSyntax returns the Go assembler syntax for the instruction.
// The pc is the program counter of the first instruction, used for expanding
// PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. It returns the name and base address of the symbol
// containing the target, if any; otherwise it returns "", 0.
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
if inst.Op == 0 && inst.Enc == 0 {
return "WORD $0"
} else if inst.Op == 0 {
return "?"
}
var args []string
for i, a := range inst.Args[:] {
if a == nil {
break
}
if s := plan9Arg(&inst, i, pc, a, symname); s != "" {
args = append(args, s)
}
}
var op string
op = plan9OpMap[inst.Op]
if op == "" {
op = strings.ToUpper(inst.Op.String())
if op[len(op)-1] == '.' {
op = op[:len(op)-1] + "CC"
}
}
// laid out the instruction
switch inst.Op {
default: // dst, sA, sB, ...
switch len(args) {
case 0:
return op
case 1:
return fmt.Sprintf("%s %s", op, args[0])
case 2:
if inst.Op == COPY || inst.Op == PASTECC {
return op + " " + args[0] + "," + args[1]
}
return op + " " + args[1] + "," + args[0]
case 3:
if reverseOperandOrder(inst.Op) {
return op + " " + args[2] + "," + args[1] + "," + args[0]
}
case 4:
if reverseMiddleOps(inst.Op) {
return op + " " + args[1] + "," + args[3] + "," + args[2] + "," + args[0]
}
}
args = append(args, args[0])
return op + " " + strings.Join(args[1:], ",")
case PASTECC:
// paste. has two input registers, and an L field, unlike other 3 operand instructions.
return op + " " + args[0] + "," + args[1] + "," + args[2]
case SYNC:
if args[0] == "$1" {
return "LWSYNC"
}
return "HWSYNC"
case ISEL:
return "ISEL " + args[3] + "," + args[1] + "," + args[2] + "," + args[0]
// store instructions always have the memory operand at the end, no need to reorder
// indexed stores handled separately
case STB, STBU,
STH, STHU,
STW, STWU,
STD, STDU,
STFD, STFDU,
STFS, STFSU,
STQ, HASHST, HASHSTP:
return op + " " + strings.Join(args, ",")
case FCMPU, FCMPO, CMPD, CMPDI, CMPLD, CMPLDI, CMPW, CMPWI, CMPLW, CMPLWI:
crf := int(inst.Args[0].(CondReg) - CR0)
cmpstr := op + " " + args[1] + "," + args[2]
if crf != 0 { // print CRx as the final operand if not implied (i.e BF != 0)
cmpstr += "," + args[0]
}
return cmpstr
case LIS:
return "ADDIS $0," + args[1] + "," + args[0]
// store instructions with index registers
case STBX, STBUX, STHX, STHUX, STWX, STWUX, STDX, STDUX,
STHBRX, STWBRX, STDBRX, STSWX, STFIWX:
return "MOV" + op[2:len(op)-1] + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")"
case STDCXCC, STWCXCC, STHCXCC, STBCXCC:
return op + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")"
case STXVX, STXVD2X, STXVW4X, STXVH8X, STXVB16X, STXSDX, STVX, STVXL, STVEBX, STVEHX, STVEWX, STXSIWX, STFDX, STFDUX, STFDPX, STFSX, STFSUX:
return op + " " + args[0] + ",(" + args[2] + ")(" + args[1] + ")"
case STXV:
return op + " " + args[0] + "," + args[1]
case STXVL, STXVLL:
return op + " " + args[0] + "," + args[1] + "," + args[2]
case LWAX, LWAUX, LWZX, LHZX, LBZX, LDX, LHAX, LHAUX, LDARX, LWARX, LHARX, LBARX, LFDX, LFDUX, LFSX, LFSUX, LDBRX, LWBRX, LHBRX, LDUX, LWZUX, LHZUX, LBZUX:
if args[1] == "0" {
return op + " (" + args[2] + ")," + args[0]
}
return op + " (" + args[2] + ")(" + args[1] + ")," + args[0]
case LXVX, LXVD2X, LXVW4X, LXVH8X, LXVB16X, LVX, LVXL, LVSR, LVSL, LVEBX, LVEHX, LVEWX, LXSDX, LXSIWAX:
return op + " (" + args[2] + ")(" + args[1] + ")," + args[0]
case LXV:
return op + " " + args[1] + "," + args[0]
case LXVL, LXVLL:
return op + " " + args[1] + "," + args[2] + "," + args[0]
case DCBT, DCBTST, DCBZ, DCBST, ICBI:
if args[0] == "0" || args[0] == "R0" {
return op + " (" + args[1] + ")"
}
return op + " (" + args[1] + ")(" + args[0] + ")"
// branch instructions needs additional handling
case BCLR:
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
return "RET"
}
return op + " " + strings.Join(args, ", ")
case BC:
bo := int(inst.Args[0].(Imm))
bi := int(inst.Args[1].(CondReg) - Cond0LT)
bcname := condName[((bo&0x8)>>1)|(bi&0x3)]
if bo&0x17 == 4 { // jump only a CR bit set/unset, no hints (at bits) set.
if bi >= 4 {
return fmt.Sprintf("B%s CR%d,%s", bcname, bi>>2, args[2])
} else {
return fmt.Sprintf("B%s %s", bcname, args[2])
}
}
return op + " " + strings.Join(args, ",")
case BCCTR:
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
return "BR (CTR)"
}
return op + " " + strings.Join(args, ", ")
case BCCTRL:
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
return "BL (CTR)"
}
return op + " " + strings.Join(args, ",")
case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL:
return op + " " + strings.Join(args, ",")
}
}
// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
//
// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
// of inst, it's ok to modify inst.Args here.
func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string {
// special cases for load/store instructions
if _, ok := arg.(Offset); ok {
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
panic(fmt.Errorf("wrong table: offset not followed by register"))
}
}
switch arg := arg.(type) {
case Reg:
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
return "0"
}
if arg == R30 {
return "g"
}
return strings.ToUpper(arg.String())
case CondReg:
// This op is left as its numerical value, not mapped onto CR + condition
if inst.Op == ISEL {
return fmt.Sprintf("$%d", (arg - Cond0LT))
}
bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4]
if arg <= Cond0SO {
return bit
} else if arg > Cond0SO && arg <= Cond7SO {
return fmt.Sprintf("CR%d%s", int(arg-Cond0LT)/4, bit)
} else {
return fmt.Sprintf("CR%d", int(arg-CR0))
}
case Imm:
return fmt.Sprintf("$%d", arg)
case SpReg:
switch arg {
case 8:
return "LR"
case 9:
return "CTR"
}
return fmt.Sprintf("SPR(%d)", int(arg))
case PCRel:
addr := pc + uint64(int64(arg))
s, base := symname(addr)
if s != "" && addr == base {
return fmt.Sprintf("%s(SB)", s)
}
if inst.Op == BL && s != "" && (addr-base) == 8 {
// When decoding an object built for PIE, a CALL targeting
// a global entry point will be adjusted to the local entry
// if any. For now, assume any symname+8 PC is a local call.
return fmt.Sprintf("%s+%d(SB)", s, addr-base)
}
return fmt.Sprintf("%#x", addr)
case Label:
return fmt.Sprintf("%#x", int(arg))
case Offset:
reg := inst.Args[argIndex+1].(Reg)
removeArg(inst, argIndex+1)
if reg == R0 {
return fmt.Sprintf("%d(0)", int(arg))
}
return fmt.Sprintf("%d(R%d)", int(arg), reg-R0)
}
return fmt.Sprintf("???(%v)", arg)
}
func reverseMiddleOps(op Op) bool {
switch op {
case FMADD, FMADDCC, FMADDS, FMADDSCC, FMSUB, FMSUBCC, FMSUBS, FMSUBSCC, FNMADD, FNMADDCC, FNMADDS, FNMADDSCC, FNMSUB, FNMSUBCC, FNMSUBS, FNMSUBSCC, FSEL, FSELCC:
return true
}
return false
}
func reverseOperandOrder(op Op) bool {
switch op {
// Special case for SUBF, SUBFC: not reversed
case ADD, ADDC, ADDE, ADDCC, ADDCCC:
return true
case MULLW, MULLWCC, MULHW, MULHWCC, MULLD, MULLDCC, MULHD, MULHDCC, MULLWO, MULLWOCC, MULHWU, MULHWUCC, MULLDO, MULLDOCC:
return true
case DIVD, DIVDCC, DIVDU, DIVDUCC, DIVDE, DIVDECC, DIVDEU, DIVDEUCC, DIVDO, DIVDOCC, DIVDUO, DIVDUOCC:
return true
case MODUD, MODSD, MODUW, MODSW:
return true
case FADD, FADDS, FSUB, FSUBS, FMUL, FMULS, FDIV, FDIVS, FMADD, FMADDS, FMSUB, FMSUBS, FNMADD, FNMADDS, FNMSUB, FNMSUBS, FMULSCC:
return true
case FADDCC, FADDSCC, FSUBCC, FMULCC, FDIVCC, FDIVSCC:
return true
case OR, ORCC, ORC, ORCCC, AND, ANDCC, ANDC, ANDCCC, XOR, XORCC, NAND, NANDCC, EQV, EQVCC, NOR, NORCC:
return true
case SLW, SLWCC, SLD, SLDCC, SRW, SRAW, SRWCC, SRAWCC, SRD, SRDCC, SRAD, SRADCC:
return true
}
return false
}
// revCondMap maps a conditional register bit to its inverse, if possible.
var revCondMap = map[string]string{
"LT": "GE", "GT": "LE", "EQ": "NE",
}
// Lookup table to map BI[0:1] and BO[3] to an extended mnemonic for CR ops.
// Bits 0-1 map to a bit with a CR field, and bit 2 selects the inverted (0)
// or regular (1) extended mnemonic.
var condName = []string{
"GE",
"LE",
"NE",
"NSO",
"LT",
"GT",
"EQ",
"SO",
}
// plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics.
var plan9OpMap = map[Op]string{
LWARX: "LWAR",
LDARX: "LDAR",
LHARX: "LHAR",
LBARX: "LBAR",
LWAX: "MOVW",
LHAX: "MOVH",
LWAUX: "MOVWU",
LHAU: "MOVHU",
LHAUX: "MOVHU",
LDX: "MOVD",
LDUX: "MOVDU",
LWZX: "MOVWZ",
LWZUX: "MOVWZU",
LHZX: "MOVHZ",
LHZUX: "MOVHZU",
LBZX: "MOVBZ",
LBZUX: "MOVBZU",
LDBRX: "MOVDBR",
LWBRX: "MOVWBR",
LHBRX: "MOVHBR",
MCRF: "MOVFL",
XORI: "XOR",
ORI: "OR",
ANDICC: "ANDCC",
ANDC: "ANDN",
ANDCCC: "ANDNCC",
ADDEO: "ADDEV",
ADDEOCC: "ADDEVCC",
ADDO: "ADDV",
ADDOCC: "ADDVCC",
ADDMEO: "ADDMEV",
ADDMEOCC: "ADDMEVCC",
ADDCO: "ADDCV",
ADDCOCC: "ADDCVCC",
ADDZEO: "ADDZEV",
ADDZEOCC: "ADDZEVCC",
SUBFME: "SUBME",
SUBFMECC: "SUBMECC",
SUBFZE: "SUBZE",
SUBFZECC: "SUBZECC",
SUBFZEO: "SUBZEV",
SUBFZEOCC: "SUBZEVCC",
SUBF: "SUB",
SUBFC: "SUBC",
SUBFCC: "SUBCC",
SUBFCCC: "SUBCCC",
ORC: "ORN",
ORCCC: "ORNCC",
MULLWO: "MULLWV",
MULLWOCC: "MULLWVCC",
MULLDO: "MULLDV",
MULLDOCC: "MULLDVCC",
DIVDO: "DIVDV",
DIVDOCC: "DIVDVCC",
DIVDUO: "DIVDUV",
DIVDUOCC: "DIVDUVCC",
ADDI: "ADD",
MULLI: "MULLD",
SRADI: "SRAD",
STBCXCC: "STBCCC",
STWCXCC: "STWCCC",
STDCXCC: "STDCCC",
LI: "MOVD",
LBZ: "MOVBZ", STB: "MOVB",
LBZU: "MOVBZU", STBU: "MOVBU",
LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH",
LHZU: "MOVHZU", STHU: "MOVHU",
LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW",
LWZU: "MOVWZU", STWU: "MOVWU",
LD: "MOVD", STD: "MOVD",
LDU: "MOVDU", STDU: "MOVDU",
LFD: "FMOVD", STFD: "FMOVD",
LFS: "FMOVS", STFS: "FMOVS",
LFDX: "FMOVD", STFDX: "FMOVD",
LFDU: "FMOVDU", STFDU: "FMOVDU",
LFDUX: "FMOVDU", STFDUX: "FMOVDU",
LFSX: "FMOVS", STFSX: "FMOVS",
LFSU: "FMOVSU", STFSU: "FMOVSU",
LFSUX: "FMOVSU", STFSUX: "FMOVSU",
CMPD: "CMP", CMPDI: "CMP",
CMPW: "CMPW", CMPWI: "CMPW",
CMPLD: "CMPU", CMPLDI: "CMPU",
CMPLW: "CMPWU", CMPLWI: "CMPWU",
MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs
B: "BR",
BL: "CALL",
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,885 @@
6d746162| gnu xoris r20,r11,24930
6d746162| plan9 XORIS R11,$24930,R20
4c040000| gnu mcrf cr0,cr1
88a70002| gnu lbz r5,2(r7)
88a70002| plan9 MOVBZ 2(R7),R5
00000000| plan9 WORD $0
00010000| plan9 error: unknown instruction
00000000| gnu .long 0x0
00002000| gnu error: unknown instruction
a1841e80| gnu lhz r12,7808(r4)
a1841e80| plan9 MOVHZ 7808(R4),R12
42000004| gnu bdnz 0x30
e38d5b90| gnu lq r28,23440(r13)
84127a20| gnu lwzu r0,31264(r18)
84127a20| plan9 MOVWZU 31264(R18),R0
a8630000| gnu lha r3,0(r3)
a8630000| plan9 MOVH 0(R3),R3
ebb24fd1| gnu ldu r29,20432(r18)
ebb24fd1| plan9 MOVDU 20432(R18),R29
b1ce0612| gnu sth r14,1554(r14)
b1ce0612| plan9 MOVH R14,1554(R14)
945c62a2| gnu stwu r2,25250(r28)
f91b9c7a| gnu stq r8,-25480(r27)
2c030001| gnu cmpwi r3,1
2c030001| plan9 CMPW R3,$1
e8610032| gnu lwa r3,48(r1)
e8610032| plan9 MOVW 48(R1),R3
4320336b| gnu bdnzla+ 0x3368
7e40092e| gnu stwx r18,0,r1
7e40092e| plan9 MOVW R18,(R1)(0)
7c103c2c| gnu lwbrx r0,r16,r7
7c103c2c| plan9 MOVWBR (R7)(R16),R0
7c441d28| gnu stdbrx r2,r4,r3
7c441d28| plan9 MOVDBR R2,(R3)(R4)
3d220001| gnu addis r9,r2,1
3d220001| plan9 ADDIS R2,$1,R9
7ce628ae| gnu lbzx r7,r6,r5
7ce628ae| plan9 MOVBZ (R5)(R6),R7
7c0e1e99| gnu lxvd2x vs32,r14,r3
7c0e1e99| plan9 LXVD2X (R3)(R14),VS32
7c00422c| gnu dcbt r0,r8,0
7c00422c| plan9 DCBT (R8)
7fab3040| gnu cmpld cr7,r11,r6
7fab3040| plan9 CMPU R11,R6,CR7
2c030001| gnu cmpwi r3,1
2c030001| plan9 CMPW R3,$1
7c2b4840| gnu cmpld r11,r9
7c2b4840| plan9 CMPU R11,R9
7c6521ad| gnu stdcx. r3,r5,r4
7c6521ad| plan9 STDCCC R3,(R4)(R5)
fbe1ffd1| gnu stdu r31,-48(r1)
fbe1ffd1| plan9 MOVDU R31,-48(R1)
7c941f19| gnu stxvw4x vs36,r20,r3
7c941f19| plan9 STXVW4X VS36,(R3)(R20)
7c941f59| gnu stxvh8x vs36,r20,r3
7c941f59| plan9 STXVH8X VS36,(R3)(R20)
7c941fd9| gnu stxvb16x vs36,r20,r3
7c941fd9| plan9 STXVB16X VS36,(R3)(R20)
7c6520a8| gnu ldarx r3,r5,r4
7c6520a8| plan9 LDAR (R4)(R5),R3
7c6803a6| plan9 MOVD R3,LR
7c6802a6| plan9 MOVD LR,R3
7c6803a6| gnu mtlr r3
7c6802a6| gnu mflr r3
7c6903a6| plan9 MOVD R3,CTR
7c6902a6| plan9 MOVD CTR,R3
7c6903a6| gnu mtctr r3
7c6902a6| gnu mfctr r3
7c6c42a6| gnu mftb r3
7c6c42a6| plan9 MOVD SPR(268),R3
7c8202a6| plan9 MOVD SPR(2),R4
7c8202a6| gnu mfspr r4,2
41820010| plan9 BEQ 0x130
41820010| gnu beq 0x134
4086000c| plan9 BNE CR1,0x134
4086000c| gnu bne cr1,0x138
41880008| plan9 BLT CR2,0x138
41880008| gnu blt cr2,0x13c
418d0004| plan9 BGT CR3,0x13c
418d0004| gnu bgt cr3,0x140
e8830008| plan9 MOVD 8(R3),R4
7ca4182a| plan9 MOVD (R3)(R4),R5
e8830006| plan9 MOVW 4(R3),R4
7ca41aaa| plan9 MOVW (R3)(R4),R5
80830004| plan9 MOVWZ 4(R3),R4
7ca4182e| plan9 MOVWZ (R3)(R4),R5
a8830004| plan9 MOVH 4(R3),R4
7ca41aae| plan9 MOVH (R3)(R4),R5
a0830002| plan9 MOVHZ 2(R3),R4
7ca41a2e| plan9 MOVHZ (R3)(R4),R5
7ca418ae| plan9 MOVBZ (R3)(R4),R5
7ca41c28| plan9 MOVDBR (R3)(R4),R5
7ca41c2c| plan9 MOVWBR (R3)(R4),R5
7ca41e2c| plan9 MOVHBR (R3)(R4),R5
e8830009| plan9 MOVDU 8(R3),R4
7ca4186a| plan9 MOVDU (R3)(R4),R5
7ca41aea| plan9 MOVWU (R3)(R4),R5
84830004| plan9 MOVWZU 4(R3),R4
7ca4186e| plan9 MOVWZU (R3)(R4),R5
ac830002| plan9 MOVHU 2(R3),R4
7ca41aee| plan9 MOVHU (R3)(R4),R5
a4830002| plan9 MOVHZU 2(R3),R4
7ca41a6e| plan9 MOVHZU (R3)(R4),R5
8c830001| plan9 MOVBZU 1(R3),R4
7ca418ee| plan9 MOVBZU (R3)(R4),R5
f8830008| plan9 MOVD R4,8(R3)
7ca4192a| plan9 MOVD R5,(R3)(R4)
90830004| plan9 MOVW R4,4(R3)
7ca4192e| plan9 MOVW R5,(R3)(R4)
b0830002| plan9 MOVH R4,2(R3)
7ca41b2e| plan9 MOVH R5,(R3)(R4)
98830001| plan9 MOVB R4,1(R3)
7ca419ae| plan9 MOVB R5,(R3)(R4)
7ca41d28| plan9 MOVDBR R5,(R3)(R4)
7ca41d2c| plan9 MOVWBR R5,(R3)(R4)
7ca41f2c| plan9 MOVHBR R5,(R3)(R4)
f8830009| plan9 MOVDU R4,8(R3)
7ca4196a| plan9 MOVDU R5,(R3)(R4)
94830004| plan9 MOVWU R4,4(R3)
7ca4196e| plan9 MOVWU R5,(R3)(R4)
b4830002| plan9 MOVHU R4,2(R3)
7ca41b6e| plan9 MOVHU R5,(R3)(R4)
9c830001| plan9 MOVBU R4,1(R3)
7ca419ee| plan9 MOVBU R5,(R3)(R4)
7c232040| plan9 CMPU R3,R4
7c032000| plan9 CMPW R3,R4
7c032040| plan9 CMPWU R3,R4
7ca41a14| plan9 ADD R3,R4,R5
7ca41a15| plan9 ADDCC R3,R4,R5
7ca41814| plan9 ADDC R3,R4,R5
7ca41815| plan9 ADDCCC R3,R4,R5
7c851838| plan9 AND R3,R4,R5
7c851839| plan9 ANDCC R3,R4,R5
7c851878| plan9 ANDN R3,R4,R5
7c851879| plan9 ANDNCC R3,R4,R5
7c851b78| plan9 OR R3,R4,R5
7c851b79| plan9 ORCC R3,R4,R5
7c851b38| plan9 ORN R3,R4,R5
7c851b39| plan9 ORNCC R3,R4,R5
7c851a78| plan9 XOR R3,R4,R5
7c851a79| plan9 XORCC R3,R4,R5
7c851bb8| plan9 NAND R3,R4,R5
7c851bb9| plan9 NANDCC R3,R4,R5
7c851a38| plan9 EQV R3,R4,R5
7c851a39| plan9 EQVCC R3,R4,R5
7c8300d0| plan9 NEG R3,R4
7c8300d1| plan9 NEGCC R3,R4
7c8518f8| plan9 NOR R3,R4,R5
7c8518f9| plan9 NORCC R3,R4,R5
7ca32050| plan9 SUB R3,R4,R5
7ca32051| plan9 SUBCC R3,R4,R5
7ca32010| plan9 SUBC R3,R4,R5
7ca32011| plan9 SUBCCC R3,R4,R5
7ca419d6| plan9 MULLW R3,R4,R5
7ca419d7| plan9 MULLWCC R3,R4,R5
7ca41896| plan9 MULHW R3,R4,R5
7ca41816| plan9 MULHWU R3,R4,R5
7ca421d2| plan9 MULLD R4,R4,R5
1c63000a| plan9 MULLD R3,$10,R3
7ca419d3| plan9 MULLDCC R3,R4,R5
7ca41892| plan9 MULHD R3,R4,R5
7ca41893| plan9 MULHDCC R3,R4,R5
7ca41dd6| plan9 MULLWV R3,R4,R5
7ca41dd7| plan9 MULLWVCC R3,R4,R5
7ca41817| plan9 MULHWUCC R3,R4,R5
7ca41dd2| plan9 MULLDV R3,R4,R5
7ca41dd3| plan9 MULLDVCC R3,R4,R5
7ca41bd2| plan9 DIVD R3,R4,R5
7ca41bd3| plan9 DIVDCC R3,R4,R5
7ca41b92| plan9 DIVDU R3,R4,R5
7ca41fd2| plan9 DIVDV R3,R4,R5
7ca41b93| plan9 DIVDUCC R3,R4,R5
7ca41fd3| plan9 DIVDVCC R3,R4,R5
7ca41f92| plan9 DIVDUV R3,R4,R5
7ca41f93| plan9 DIVDUVCC R3,R4,R5
7ca41b52| plan9 DIVDE R3,R4,R5
7ca41b53| plan9 DIVDECC R3,R4,R5
7ca41b12| plan9 DIVDEU R3,R4,R5
7ca41b13| plan9 DIVDEUCC R3,R4,R5
7ca41a12| plan9 MODUD R3,R4,R5
7ca41a16| plan9 MODUW R3,R4,R5
7ca41e12| plan9 MODSD R3,R4,R5
7ca41e16| plan9 MODSW R3,R4,R5
7c851830| plan9 SLW R3,R4,R5
7c851836| plan9 SLD R3,R4,R5
7c851c30| plan9 SRW R3,R4,R5
7c851e30| plan9 SRAW R3,R4,R5
7c851c36| plan9 SRD R3,R4,R5
7c851e34| plan9 SRAD R3,R4,R5
7c631ef4| plan9 EXTSWSLI R3,$3,R3
7c6400f4| plan9 POPCNTB R3,R4
7c6402f4| plan9 POPCNTW R3,R4
7c6403f4| plan9 POPCNTD R3,R4
7c23270d| plan9 PASTECC R3,R4,$1
7c23260c| plan9 COPY R3,R4
7ca01868| plan9 LBAR (R3),R5
7ca018e8| plan9 LHAR (R3),R5
7ca01828| plan9 LWAR (R3),R5
7ca018a8| plan9 LDAR (R3),R5
7c65256d| plan9 STBCCC R3,(R4)(R5)
7c65212d| plan9 STWCCC R3,(R4)(R5)
7c6521ad| plan9 STDCCC R3,(R4)(R5)
7c0004ac| plan9 HWSYNC
4c00012c| plan9 ISYNC
7c2004ac| plan9 LWSYNC
7c04186c| plan9 DCBST (R3)(R4)
7c041fec| plan9 DCBZ (R3)(R4)
7c041a2c| plan9 DCBT (R3)(R4)
7c041fac| plan9 ICBI (R3)(R4)
c8230008| plan9 FMOVD 8(R3),F1
7c241cae| plan9 FMOVD (R3)(R4),F1
cc230008| plan9 FMOVDU 8(R3),F1
7c241cee| plan9 FMOVDU (R3)(R4),F1
c0230004| plan9 FMOVS 4(R3),F1
7c241c2e| plan9 FMOVS (R3)(R4),F1
c4230004| plan9 FMOVSU 4(R3),F1
7c241c6e| plan9 FMOVSU (R3)(R4),F1
d8230008| plan9 FMOVD F1,8(R3)
7c241dae| plan9 FMOVD F1,(R3)(R4)
dc230008| plan9 FMOVDU F1,8(R3)
7c241dee| plan9 FMOVDU F1,(R3)(R4)
d0230004| plan9 FMOVS F1,4(R3)
7c241d2e| plan9 FMOVS F1,(R3)(R4)
d4230004| plan9 FMOVSU F1,4(R3)
7c241d6e| plan9 FMOVSU F1,(R3)(R4)
fc62082a| plan9 FADD F1,F2,F3
fc62082b| plan9 FADDCC F1,F2,F3
ec62082a| plan9 FADDS F1,F2,F3
ec62082b| plan9 FADDSCC F1,F2,F3
fc620828| plan9 FSUB F1,F2,F3
fc620829| plan9 FSUBCC F1,F2,F3
ec620828| plan9 FSUBS F1,F2,F3
fc620829| plan9 FSUBCC F1,F2,F3
fc620072| plan9 FMUL F1,F2,F3
fc620073| plan9 FMULCC F1,F2,F3
ec620072| plan9 FMULS F1,F2,F3
ec620073| plan9 FMULSCC F1,F2,F3
fc620824| plan9 FDIV F1,F2,F3
fc620825| plan9 FDIVCC F1,F2,F3
ec620824| plan9 FDIVS F1,F2,F3
ec620825| plan9 FDIVSCC F1,F2,F3
fc8110fa| plan9 FMADD F1,F2,F3,F4
fc8110fb| plan9 FMADDCC F1,F2,F3,F4
ec8110fa| plan9 FMADDS F1,F2,F3,F4
ec8110fb| plan9 FMADDSCC F1,F2,F3,F4
fc8110f8| plan9 FMSUB F1,F2,F3,F4
fc8110f9| plan9 FMSUBCC F1,F2,F3,F4
ec8110f8| plan9 FMSUBS F1,F2,F3,F4
ec8110f9| plan9 FMSUBSCC F1,F2,F3,F4
fc8110fe| plan9 FNMADD F1,F2,F3,F4
fc8110ff| plan9 FNMADDCC F1,F2,F3,F4
ec8110fe| plan9 FNMADDS F1,F2,F3,F4
ec8110ff| plan9 FNMADDSCC F1,F2,F3,F4
fc8110fc| plan9 FNMSUB F1,F2,F3,F4
fc8110fd| plan9 FNMSUBCC F1,F2,F3,F4
ec8110fc| plan9 FNMSUBS F1,F2,F3,F4
ec8110fd| plan9 FNMSUBSCC F1,F2,F3,F4
fc8110ee| plan9 FSEL F1,F2,F3,F4
fc8110ef| plan9 FSELCC F1,F2,F3,F4
fc400a10| plan9 FABS F1,F2
fc400a11| plan9 FABSCC F1,F2
fc400850| plan9 FNEG F1,F2
fc400a11| plan9 FABSCC F1,F2
fc400818| plan9 FRSP F1,F2
fc400819| plan9 FRSPCC F1,F2
fc40081c| plan9 FCTIW F1,F2
fc40081d| plan9 FCTIWCC F1,F2
fc40081e| plan9 FCTIWZ F1,F2
fc40081f| plan9 FCTIWZCC F1,F2
fc400e5c| plan9 FCTID F1,F2
fc400e5d| plan9 FCTIDCC F1,F2
fc400e5e| plan9 FCTIDZ F1,F2
fc400e5f| plan9 FCTIDZCC F1,F2
fc400e9c| plan9 FCFID F1,F2
fc400e9d| plan9 FCFIDCC F1,F2
fc400f9c| plan9 FCFIDU F1,F2
fc400f9d| plan9 FCFIDUCC F1,F2
ec400e9c| plan9 FCFIDS F1,F2
ec400e9d| plan9 FCFIDSCC F1,F2
ec400830| plan9 FRES F1,F2
ec400831| plan9 FRESCC F1,F2
fc400bd0| plan9 FRIM F1,F2
fc400bd1| plan9 FRIMCC F1,F2
fc400b90| plan9 FRIP F1,F2
fc400b91| plan9 FRIPCC F1,F2
fc400b50| plan9 FRIZ F1,F2
fc400b51| plan9 FRIZCC F1,F2
fc400b10| plan9 FRIN F1,F2
fc400b11| plan9 FRINCC F1,F2
fc400834| plan9 FRSQRTE F1,F2
fc400835| plan9 FRSQRTECC F1,F2
fc40082c| plan9 FSQRT F1,F2
fc40082d| plan9 FSQRTCC F1,F2
ec40082c| plan9 FSQRTS F1,F2
ec40082d| plan9 FSQRTSCC F1,F2
fc011040| plan9 FCMPO F1,F2
fc011000| plan9 FCMPU F1,F2
7c2418ce| plan9 LVX (R3)(R4),V1
7c241ace| plan9 LVXL (R3)(R4),V1
7c24180c| plan9 LVSL (R3)(R4),V1
7c24184c| plan9 LVSR (R3)(R4),V1
7c24180e| plan9 LVEBX (R3)(R4),V1
7c24184e| plan9 LVEHX (R3)(R4),V1
7c24188e| plan9 LVEWX (R3)(R4),V1
7c2419ce| plan9 STVX V1,(R3)(R4)
7c241bce| plan9 STVXL V1,(R3)(R4)
7c24190e| plan9 STVEBX V1,(R3)(R4)
7c24194e| plan9 STVEHX V1,(R3)(R4)
7c24198e| plan9 STVEWX V1,(R3)(R4)
10611404| plan9 VAND V1,V2,V3
10611444| plan9 VANDC V1,V2,V3
10611584| plan9 VNAND V1,V2,V3
10611484| plan9 VOR V1,V2,V3
10611544| plan9 VORC V1,V2,V3
106114c4| plan9 VXOR V1,V2,V3
10611504| plan9 VNOR V1,V2,V3
10611684| plan9 VEQV V1,V2,V3
10611000| plan9 VADDUBM V1,V2,V3
10611040| plan9 VADDUHM V1,V2,V3
10611080| plan9 VADDUWM V1,V2,V3
106110c0| plan9 VADDUDM V1,V2,V3
10611100| plan9 VADDUQM V1,V2,V3
10611140| plan9 VADDCUQ V1,V2,V3
10611180| plan9 VADDCUW V1,V2,V3
10611200| plan9 VADDUBS V1,V2,V3
10611240| plan9 VADDUHS V1,V2,V3
10611280| plan9 VADDUWS V1,V2,V3
10611400| plan9 VSUBUBM V1,V2,V3
10611440| plan9 VSUBUHM V1,V2,V3
10611480| plan9 VSUBUWM V1,V2,V3
106114c0| plan9 VSUBUDM V1,V2,V3
10611500| plan9 VSUBUQM V1,V2,V3
10611540| plan9 VSUBCUQ V1,V2,V3
10611580| plan9 VSUBCUW V1,V2,V3
10611600| plan9 VSUBUBS V1,V2,V3
10611640| plan9 VSUBUHS V1,V2,V3
10611680| plan9 VSUBUWS V1,V2,V3
10611700| plan9 VSUBSBS V1,V2,V3
10611740| plan9 VSUBSHS V1,V2,V3
10611780| plan9 VSUBSWS V1,V2,V3
108110fe| plan9 VSUBEUQM V1,V2,V3,V4
108110ff| plan9 VSUBECUQ V1,V2,V3,V4
10611308| plan9 VMULESB V1,V2,V3
10611108| plan9 VMULOSB V1,V2,V3
10611208| plan9 VMULEUB V1,V2,V3
10611008| plan9 VMULOUB V1,V2,V3
10611348| plan9 VMULESH V1,V2,V3
10611148| plan9 VMULOSH V1,V2,V3
10611248| plan9 VMULEUH V1,V2,V3
10611048| plan9 VMULOUH V1,V2,V3
10611348| plan9 VMULESH V1,V2,V3
10611188| plan9 VMULOSW V1,V2,V3
10611288| plan9 VMULEUW V1,V2,V3
10611088| plan9 VMULOUW V1,V2,V3
10611089| plan9 VMULUWM V1,V2,V3
10611408| plan9 VPMSUMB V1,V2,V3
10611448| plan9 VPMSUMH V1,V2,V3
10611488| plan9 VPMSUMW V1,V2,V3
106114c8| plan9 VPMSUMD V1,V2,V3
108110e3| plan9 VMSUMUDM V1,V2,V3,V4
10611004| plan9 VRLB V1,V2,V3
10611044| plan9 VRLH V1,V2,V3
10611084| plan9 VRLW V1,V2,V3
106110c4| plan9 VRLD V1,V2,V3
10611104| plan9 VSLB V1,V2,V3
10611144| plan9 VSLH V1,V2,V3
10611184| plan9 VSLW V1,V2,V3
106111c4| plan9 VSL V1,V2,V3
1061140c| plan9 VSLO V1,V2,V3
10611204| plan9 VSRB V1,V2,V3
10611244| plan9 VSRH V1,V2,V3
10611284| plan9 VSRW V1,V2,V3
106112c4| plan9 VSR V1,V2,V3
1061144c| plan9 VSRO V1,V2,V3
106115c4| plan9 VSLD V1,V2,V3
10611304| plan9 VSRAB V1,V2,V3
10611344| plan9 VSRAH V1,V2,V3
10611384| plan9 VSRAW V1,V2,V3
106113c4| plan9 VSRAD V1,V2,V3
10400f02| plan9 VCLZB V1,V2
10400f42| plan9 VCLZH V1,V2
10400f82| plan9 VCLZW V1,V2
10400fc2| plan9 VCLZD V1,V2
10400f03| plan9 VPOPCNTB V1,V2
10400f43| plan9 VPOPCNTH V1,V2
10400f83| plan9 VPOPCNTW V1,V2
10400fc3| plan9 VPOPCNTD V1,V2
10611006| plan9 VCMPEQUB V1,V2,V3
10611406| plan9 VCMPEQUBCC V1,V2,V3
10611046| plan9 VCMPEQUH V1,V2,V3
10611446| plan9 VCMPEQUHCC V1,V2,V3
10611086| plan9 VCMPEQUW V1,V2,V3
10611486| plan9 VCMPEQUWCC V1,V2,V3
106110c7| plan9 VCMPEQUD V1,V2,V3
106114c7| plan9 VCMPEQUDCC V1,V2,V3
10611206| plan9 VCMPGTUB V1,V2,V3
10611606| plan9 VCMPGTUBCC V1,V2,V3
10611246| plan9 VCMPGTUH V1,V2,V3
10611646| plan9 VCMPGTUHCC V1,V2,V3
10611286| plan9 VCMPGTUW V1,V2,V3
10611686| plan9 VCMPGTUWCC V1,V2,V3
106112c7| plan9 VCMPGTUD V1,V2,V3
106116c7| plan9 VCMPGTUDCC V1,V2,V3
10611306| plan9 VCMPGTSB V1,V2,V3
10611706| plan9 VCMPGTSBCC V1,V2,V3
10611346| plan9 VCMPGTSH V1,V2,V3
10611746| plan9 VCMPGTSHCC V1,V2,V3
10611386| plan9 VCMPGTSW V1,V2,V3
10611786| plan9 VCMPGTSWCC V1,V2,V3
106113c7| plan9 VCMPGTSD V1,V2,V3
106117c7| plan9 VCMPGTSDCC V1,V2,V3
10611107| plan9 VCMPNEZB V1,V2,V3
10611507| plan9 VCMPNEZBCC V1,V2,V3
10611007| plan9 VCMPNEB V1,V2,V3
10611407| plan9 VCMPNEBCC V1,V2,V3
10611047| plan9 VCMPNEH V1,V2,V3
10611447| plan9 VCMPNEHCC V1,V2,V3
10611087| plan9 VCMPNEW V1,V2,V3
10611487| plan9 VCMPNEWCC V1,V2,V3
108110eb| plan9 VPERM V1,V2,V3,V4
108110fb| plan9 VPERMR V1,V2,V3,V4
108110ed| plan9 VPERMXOR V1,V2,V3,V4
1061154c| plan9 VBPERMQ V1,V2,V3
106115cc| plan9 VBPERMD V1,V2,V3
108110ea| plan9 VSEL V1,V2,V3,V4
10611508| plan9 VCIPHER V1,V2,V3
10611509| plan9 VCIPHERLAST V1,V2,V3
10611548| plan9 VNCIPHER V1,V2,V3
10611549| plan9 VNCIPHERLAST V1,V2,V3
104105c8| plan9 VSBOX V1,V2
7c241e98| plan9 LXVD2X (R3)(R4),VS1
7c241e18| plan9 LXVW4X (R3)(R4),VS1
7c241e58| plan9 LXVH8X (R3)(R4),VS1
7c241ed8| plan9 LXVB16X (R3)(R4),VS1
f4230011| plan9 LXV 16(R3),VS1
7c23221a| plan9 LXVL R3,R4,VS1
7c23225a| plan9 LXVLL R3,R4,VS1
7c241a18| plan9 LXVX (R3)(R4),VS1
7c241c98| plan9 LXSDX (R3)(R4),VS1
7c241f98| plan9 STXVD2X VS1,(R3)(R4)
f4230015| plan9 STXV VS1,16(R3)
7c23231a| plan9 STXVL VS1,R3,R4
7c23235a| plan9 STXVLL VS1,R3,R4
7c241b18| plan9 STXVX VS1,(R3)(R4)
7c241d98| plan9 STXSDX VS1,(R3)(R4)
7c241898| plan9 LXSIWAX (R3)(R4),VS1
7c241918| plan9 STXSIWX VS1,(R3)(R4)
7c230066| plan9 MFVSRD VS1,R3
7c230166| plan9 MTVSRD R3,VS1
f0611410| plan9 XXLAND VS1,VS2,VS3
f0611490| plan9 XXLOR VS1,VS2,VS3
f0611550| plan9 XXLORC VS1,VS2,VS3
f06114d0| plan9 XXLXOR VS1,VS2,VS3
f08110f0| plan9 XXSEL VS1,VS2,VS3,VS4
f0570f6c| plan9 XXBRD VS1,VS2
f04f0f6c| plan9 XXBRW VS1,VS2
f0470f6c| plan9 XXBRH VS1,VS2
f0611090| plan9 XXMRGHW VS1,VS2,VS3
f0410a90| plan9 XXSPLTW VS1,$1,VS2
f06110d0| plan9 XXPERM VS1,VS2,VS3
f0611110| plan9 XXSLDWI VS1,VS2,$1,VS3
f0400c24| plan9 XSCVDPSP VS1,VS2
f0400e24| plan9 XVCVDPSP VS1,VS2
f0400de0| plan9 XSCVSXDDP VS1,VS2
f0400f60| plan9 XVCVDPSXDS VS1,VS2
f0400fe0| plan9 XVCVSXDDP VS1,VS2
7c6803a6| plan9 MOVD R3,LR
7c6903a6| plan9 MOVD R3,CTR
7c6802a6| plan9 MOVD LR,R3
7c6902a6| plan9 MOVD CTR,R3
4c8c0000| plan9 MOVFL CR3,CR1
48100001| plan9 CALL foo(SB)
48100009| plan9 CALL foo+8(SB)
4810000d| plan9 CALL 0x10000c
7c6803a6| gnu mtlr r3
7c6802a6| gnu mflr r3
7c6903a6| gnu mtctr r3
7c6902a6| gnu mfctr r3
7c6c42a6| gnu mftb r3
7c8202a6| gnu mfspr r4,2
e8830008| gnu ld r4,8(r3)
7ca4182a| gnu ldx r5,r4,r3
e8830006| gnu lwa r4,4(r3)
7ca41aaa| gnu lwax r5,r4,r3
80830004| gnu lwz r4,4(r3)
7ca4182e| gnu lwzx r5,r4,r3
a8830004| gnu lha r4,4(r3)
7ca41aae| gnu lhax r5,r4,r3
a0830002| gnu lhz r4,2(r3)
7ca41a2e| gnu lhzx r5,r4,r3
7ca418ae| gnu lbzx r5,r4,r3
7ca41c28| gnu ldbrx r5,r4,r3
7ca41c2c| gnu lwbrx r5,r4,r3
7ca41e2c| gnu lhbrx r5,r4,r3
e8830009| gnu ldu r4,8(r3)
7ca4186a| gnu ldux r5,r4,r3
7ca41aea| gnu lwaux r5,r4,r3
84830004| gnu lwzu r4,4(r3)
7ca4186e| gnu lwzux r5,r4,r3
ac830002| gnu lhau r4,2(r3)
7ca41aee| gnu lhaux r5,r4,r3
a4830002| gnu lhzu r4,2(r3)
7ca41a6e| gnu lhzux r5,r4,r3
8c830001| gnu lbzu r4,1(r3)
7ca418ee| gnu lbzux r5,r4,r3
f8830008| gnu std r4,8(r3)
7ca4192a| gnu stdx r5,r4,r3
90830004| gnu stw r4,4(r3)
7ca4192e| gnu stwx r5,r4,r3
b0830002| gnu sth r4,2(r3)
7ca41b2e| gnu sthx r5,r4,r3
98830001| gnu stb r4,1(r3)
7ca419ae| gnu stbx r5,r4,r3
7ca41d28| gnu stdbrx r5,r4,r3
7ca41d2c| gnu stwbrx r5,r4,r3
7ca41f2c| gnu sthbrx r5,r4,r3
f8830009| gnu stdu r4,8(r3)
7ca4196a| gnu stdux r5,r4,r3
94830004| gnu stwu r4,4(r3)
7ca4196e| gnu stwux r5,r4,r3
b4830002| gnu sthu r4,2(r3)
7ca41b6e| gnu sthux r5,r4,r3
9c830001| gnu stbu r4,1(r3)
7ca419ee| gnu stbux r5,r4,r3
7c232040| gnu cmpld r3,r4
7c032000| gnu cmpw r3,r4
7c032040| gnu cmplw r3,r4
7ca41a14| gnu add r5,r4,r3
7ca41814| gnu addc r5,r4,r3
7ca41815| gnu addc. r5,r4,r3
7c851838| gnu and r5,r4,r3
7c851878| gnu andc r5,r4,r3
7c851839| gnu and. r5,r4,r3
7c851b78| gnu or r5,r4,r3
7c851b38| gnu orc r5,r4,r3
7c851b79| gnu or. r5,r4,r3
7c851a78| gnu xor r5,r4,r3
7c851a79| gnu xor. r5,r4,r3
7c851bb8| gnu nand r5,r4,r3
7c851bb9| gnu nand. r5,r4,r3
7c851a38| gnu eqv r5,r4,r3
7c851a39| gnu eqv. r5,r4,r3
7c8518f8| gnu nor r5,r4,r3
7c8518f9| gnu nor. r5,r4,r3
7ca32050| gnu subf r5,r3,r4
7ca32010| gnu subfc r5,r3,r4
7ca419d6| gnu mullw r5,r4,r3
7ca419d7| gnu mullw. r5,r4,r3
7ca41896| gnu mulhw r5,r4,r3
7ca41816| gnu mulhwu r5,r4,r3
7ca421d2| gnu mulld r5,r4,r4
7ca419d3| gnu mulld. r5,r4,r3
7ca41892| gnu mulhd r5,r4,r3
7ca41893| gnu mulhd. r5,r4,r3
7ca41dd6| gnu mullwo r5,r4,r3
7ca41dd7| gnu mullwo. r5,r4,r3
7ca41817| gnu mulhwu. r5,r4,r3
7ca41dd2| gnu mulldo r5,r4,r3
7ca41dd3| gnu mulldo. r5,r4,r3
7ca41bd2| gnu divd r5,r4,r3
7ca41bd3| gnu divd. r5,r4,r3
7ca41b92| gnu divdu r5,r4,r3
7ca41fd2| gnu divdo r5,r4,r3
7ca41b93| gnu divdu. r5,r4,r3
7ca41fd3| gnu divdo. r5,r4,r3
7ca41f92| gnu divduo r5,r4,r3
7ca41f93| gnu divduo. r5,r4,r3
7ca41b52| gnu divde r5,r4,r3
7ca41b53| gnu divde. r5,r4,r3
7ca41b12| gnu divdeu r5,r4,r3
7ca41b13| gnu divdeu. r5,r4,r3
7ca41a12| gnu modud r5,r4,r3
7ca41a16| gnu moduw r5,r4,r3
7ca41e12| gnu modsd r5,r4,r3
7ca41e16| gnu modsw r5,r4,r3
7c851830| gnu slw r5,r4,r3
7c851836| gnu sld r5,r4,r3
7c851c30| gnu srw r5,r4,r3
7c851e30| gnu sraw r5,r4,r3
7c851c36| gnu srd r5,r4,r3
7c851e34| gnu srad r5,r4,r3
7c6400f4| gnu popcntb r4,r3
7c6402f4| gnu popcntw r4,r3
7c6403f4| gnu popcntd r4,r3
7c23270d| gnu paste. r3,r4
7c23260c| gnu copy r3,r4
7ca01868| gnu lbarx r5,0,r3
7ca018e8| gnu lharx r5,0,r3
7ca01828| gnu lwarx r5,0,r3
7ca018a8| gnu ldarx r5,0,r3
7c65256d| gnu stbcx. r3,r5,r4
7c65212d| gnu stwcx. r3,r5,r4
7c6521ad| gnu stdcx. r3,r5,r4
4c00012c| gnu isync
7c0004ac| gnu hwsync
7c2004ac| gnu lwsync
7c04186c| gnu dcbst r4,r3
7c041fec| gnu dcbz r4,r3
7c041a2c| gnu dcbt r4,r3,0
7c041fac| gnu icbi r4,r3
c8230008| gnu lfd f1,8(r3)
7c241cae| gnu lfdx f1,r4,r3
cc230008| gnu lfdu f1,8(r3)
7c241cee| gnu lfdux f1,r4,r3
c0230004| gnu lfs f1,4(r3)
7c241c2e| gnu lfsx f1,r4,r3
c4230004| gnu lfsu f1,4(r3)
7c241c6e| gnu lfsux f1,r4,r3
d8230008| gnu stfd f1,8(r3)
7c241dae| gnu stfdx f1,r4,r3
dc230008| gnu stfdu f1,8(r3)
7c241dee| gnu stfdux f1,r4,r3
d0230004| gnu stfs f1,4(r3)
7c241d2e| gnu stfsx f1,r4,r3
d4230004| gnu stfsu f1,4(r3)
7c241d6e| gnu stfsux f1,r4,r3
fc62082a| gnu fadd f3,f2,f1
fc62082b| gnu fadd. f3,f2,f1
ec62082a| gnu fadds f3,f2,f1
ec62082b| gnu fadds. f3,f2,f1
fc620828| gnu fsub f3,f2,f1
fc620829| gnu fsub. f3,f2,f1
ec620828| gnu fsubs f3,f2,f1
fc620829| gnu fsub. f3,f2,f1
fc620072| gnu fmul f3,f2,f1
fc620073| gnu fmul. f3,f2,f1
ec620072| gnu fmuls f3,f2,f1
ec620073| gnu fmuls. f3,f2,f1
fc620824| gnu fdiv f3,f2,f1
fc620825| gnu fdiv. f3,f2,f1
ec620824| gnu fdivs f3,f2,f1
ec620825| gnu fdivs. f3,f2,f1
fc8110fa| gnu fmadd f4,f1,f3,f2
fc8110fb| gnu fmadd. f4,f1,f3,f2
ec8110fa| gnu fmadds f4,f1,f3,f2
ec8110fb| gnu fmadds. f4,f1,f3,f2
fc8110f8| gnu fmsub f4,f1,f3,f2
fc8110f9| gnu fmsub. f4,f1,f3,f2
ec8110f8| gnu fmsubs f4,f1,f3,f2
ec8110f9| gnu fmsubs. f4,f1,f3,f2
fc8110fe| gnu fnmadd f4,f1,f3,f2
fc8110ff| gnu fnmadd. f4,f1,f3,f2
ec8110fe| gnu fnmadds f4,f1,f3,f2
ec8110ff| gnu fnmadds. f4,f1,f3,f2
fc8110fc| gnu fnmsub f4,f1,f3,f2
fc8110fd| gnu fnmsub. f4,f1,f3,f2
ec8110fc| gnu fnmsubs f4,f1,f3,f2
ec8110fd| gnu fnmsubs. f4,f1,f3,f2
fc8110ee| gnu fsel f4,f1,f3,f2
fc8110ef| gnu fsel. f4,f1,f3,f2
fc400a10| gnu fabs f2,f1
fc400a11| gnu fabs. f2,f1
fc400850| gnu fneg f2,f1
fc400a11| gnu fabs. f2,f1
fc400818| gnu frsp f2,f1
fc400819| gnu frsp. f2,f1
fc40081c| gnu fctiw f2,f1
fc40081d| gnu fctiw. f2,f1
fc40081e| gnu fctiwz f2,f1
fc40081f| gnu fctiwz. f2,f1
fc400e5c| gnu fctid f2,f1
fc400e5d| gnu fctid. f2,f1
fc400e5e| gnu fctidz f2,f1
fc400e5f| gnu fctidz. f2,f1
fc400e9c| gnu fcfid f2,f1
fc400e9d| gnu fcfid. f2,f1
fc400f9c| gnu fcfidu f2,f1
fc400f9d| gnu fcfidu. f2,f1
ec400e9c| gnu fcfids f2,f1
ec400e9d| gnu fcfids. f2,f1
ec400830| gnu fres f2,f1
ec400831| gnu fres. f2,f1
fc400bd0| gnu frim f2,f1
fc400bd1| gnu frim. f2,f1
fc400b90| gnu frip f2,f1
fc400b91| gnu frip. f2,f1
fc400b50| gnu friz f2,f1
fc400b51| gnu friz. f2,f1
fc400b10| gnu frin f2,f1
fc400b11| gnu frin. f2,f1
fc400834| gnu frsqrte f2,f1
fc400835| gnu frsqrte. f2,f1
fc40082c| gnu fsqrt f2,f1
fc40082d| gnu fsqrt. f2,f1
ec40082c| gnu fsqrts f2,f1
ec40082d| gnu fsqrts. f2,f1
fc011040| gnu fcmpo cr0,f1,f2
fc011000| gnu fcmpu cr0,f1,f2
7c2418ce| gnu lvx v1,r4,r3
7c241ace| gnu lvxl v1,r4,r3
7c24180c| gnu lvsl v1,r4,r3
7c24184c| gnu lvsr v1,r4,r3
7c24180e| gnu lvebx v1,r4,r3
7c24184e| gnu lvehx v1,r4,r3
7c24188e| gnu lvewx v1,r4,r3
7c2419ce| gnu stvx v1,r4,r3
7c241bce| gnu stvxl v1,r4,r3
7c24190e| gnu stvebx v1,r4,r3
7c24194e| gnu stvehx v1,r4,r3
7c24198e| gnu stvewx v1,r4,r3
10611404| gnu vand v3,v1,v2
10611444| gnu vandc v3,v1,v2
10611584| gnu vnand v3,v1,v2
10611484| gnu vor v3,v1,v2
10611544| gnu vorc v3,v1,v2
106114c4| gnu vxor v3,v1,v2
10611504| gnu vnor v3,v1,v2
10611684| gnu veqv v3,v1,v2
10611000| gnu vaddubm v3,v1,v2
10611040| gnu vadduhm v3,v1,v2
10611080| gnu vadduwm v3,v1,v2
106110c0| gnu vaddudm v3,v1,v2
10611100| gnu vadduqm v3,v1,v2
10611140| gnu vaddcuq v3,v1,v2
10611180| gnu vaddcuw v3,v1,v2
10611200| gnu vaddubs v3,v1,v2
10611240| gnu vadduhs v3,v1,v2
10611280| gnu vadduws v3,v1,v2
10611400| gnu vsububm v3,v1,v2
10611440| gnu vsubuhm v3,v1,v2
10611480| gnu vsubuwm v3,v1,v2
106114c0| gnu vsubudm v3,v1,v2
10611500| gnu vsubuqm v3,v1,v2
10611540| gnu vsubcuq v3,v1,v2
10611580| gnu vsubcuw v3,v1,v2
10611600| gnu vsububs v3,v1,v2
10611640| gnu vsubuhs v3,v1,v2
10611680| gnu vsubuws v3,v1,v2
10611700| gnu vsubsbs v3,v1,v2
10611740| gnu vsubshs v3,v1,v2
10611780| gnu vsubsws v3,v1,v2
108110fe| gnu vsubeuqm v4,v1,v2,v3
108110ff| gnu vsubecuq v4,v1,v2,v3
10611308| gnu vmulesb v3,v1,v2
10611108| gnu vmulosb v3,v1,v2
10611208| gnu vmuleub v3,v1,v2
10611008| gnu vmuloub v3,v1,v2
10611348| gnu vmulesh v3,v1,v2
10611148| gnu vmulosh v3,v1,v2
10611248| gnu vmuleuh v3,v1,v2
10611048| gnu vmulouh v3,v1,v2
10611348| gnu vmulesh v3,v1,v2
10611188| gnu vmulosw v3,v1,v2
10611288| gnu vmuleuw v3,v1,v2
10611088| gnu vmulouw v3,v1,v2
10611089| gnu vmuluwm v3,v1,v2
10611408| gnu vpmsumb v3,v1,v2
10611448| gnu vpmsumh v3,v1,v2
10611488| gnu vpmsumw v3,v1,v2
106114c8| gnu vpmsumd v3,v1,v2
108110e3| gnu vmsumudm v4,v1,v2,v3
10611004| gnu vrlb v3,v1,v2
10611044| gnu vrlh v3,v1,v2
10611084| gnu vrlw v3,v1,v2
106110c4| gnu vrld v3,v1,v2
10611104| gnu vslb v3,v1,v2
10611144| gnu vslh v3,v1,v2
10611184| gnu vslw v3,v1,v2
106111c4| gnu vsl v3,v1,v2
1061140c| gnu vslo v3,v1,v2
10611204| gnu vsrb v3,v1,v2
10611244| gnu vsrh v3,v1,v2
10611284| gnu vsrw v3,v1,v2
106112c4| gnu vsr v3,v1,v2
1061144c| gnu vsro v3,v1,v2
106115c4| gnu vsld v3,v1,v2
10611304| gnu vsrab v3,v1,v2
10611344| gnu vsrah v3,v1,v2
10611384| gnu vsraw v3,v1,v2
106113c4| gnu vsrad v3,v1,v2
10400f02| gnu vclzb v2,v1
10400f42| gnu vclzh v2,v1
10400f82| gnu vclzw v2,v1
10400fc2| gnu vclzd v2,v1
10400f03| gnu vpopcntb v2,v1
10400f43| gnu vpopcnth v2,v1
10400f83| gnu vpopcntw v2,v1
10400fc3| gnu vpopcntd v2,v1
10611006| gnu vcmpequb v3,v1,v2
10611406| gnu vcmpequb. v3,v1,v2
10611046| gnu vcmpequh v3,v1,v2
10611446| gnu vcmpequh. v3,v1,v2
10611086| gnu vcmpequw v3,v1,v2
10611486| gnu vcmpequw. v3,v1,v2
106110c7| gnu vcmpequd v3,v1,v2
106114c7| gnu vcmpequd. v3,v1,v2
10611206| gnu vcmpgtub v3,v1,v2
10611606| gnu vcmpgtub. v3,v1,v2
10611246| gnu vcmpgtuh v3,v1,v2
10611646| gnu vcmpgtuh. v3,v1,v2
10611286| gnu vcmpgtuw v3,v1,v2
10611686| gnu vcmpgtuw. v3,v1,v2
106112c7| gnu vcmpgtud v3,v1,v2
106116c7| gnu vcmpgtud. v3,v1,v2
10611306| gnu vcmpgtsb v3,v1,v2
10611706| gnu vcmpgtsb. v3,v1,v2
10611346| gnu vcmpgtsh v3,v1,v2
10611746| gnu vcmpgtsh. v3,v1,v2
10611386| gnu vcmpgtsw v3,v1,v2
10611786| gnu vcmpgtsw. v3,v1,v2
106113c7| gnu vcmpgtsd v3,v1,v2
106117c7| gnu vcmpgtsd. v3,v1,v2
10611107| gnu vcmpnezb v3,v1,v2
10611507| gnu vcmpnezb. v3,v1,v2
10611007| gnu vcmpneb v3,v1,v2
10611407| gnu vcmpneb. v3,v1,v2
10611047| gnu vcmpneh v3,v1,v2
10611447| gnu vcmpneh. v3,v1,v2
10611087| gnu vcmpnew v3,v1,v2
10611487| gnu vcmpnew. v3,v1,v2
108110eb| gnu vperm v4,v1,v2,v3
108110fb| gnu vpermr v4,v1,v2,v3
108110ed| gnu vpermxor v4,v1,v2,v3
1061154c| gnu vbpermq v3,v1,v2
106115cc| gnu vbpermd v3,v1,v2
108110ea| gnu vsel v4,v1,v2,v3
10611508| gnu vcipher v3,v1,v2
10611509| gnu vcipherlast v3,v1,v2
10611548| gnu vncipher v3,v1,v2
10611549| gnu vncipherlast v3,v1,v2
104105c8| gnu vsbox v2,v1
7c241e98| gnu lxvd2x vs1,r4,r3
f4230011| gnu lxv vs1,16(r3)
7c23221a| gnu lxvl vs1,r3,r4
7c23225a| gnu lxvll vs1,r3,r4
7c241c98| gnu lxsdx vs1,r4,r3
7c241a18| gnu lxvx vs1,r4,r3
7c241b18| gnu stxvx vs1,r4,r3
7c241f98| gnu stxvd2x vs1,r4,r3
f4230015| gnu stxv vs1,16(r3)
7c23231a| gnu stxvl vs1,r3,r4
7c23235a| gnu stxvll vs1,r3,r4
7c241d98| gnu stxsdx vs1,r4,r3
7c241898| gnu lxsiwax vs1,r4,r3
7c241918| gnu stxsiwx vs1,r4,r3
7c230066| gnu mfvsrd r3,vs1
7c230166| gnu mtvsrd vs1,r3
f0611410| gnu xxland vs3,vs1,vs2
f0611490| gnu xxlor vs3,vs1,vs2
f0611550| gnu xxlorc vs3,vs1,vs2
f06114d0| gnu xxlxor vs3,vs1,vs2
f08110f0| gnu xxsel vs4,vs1,vs2,vs3
f0611090| gnu xxmrghw vs3,vs1,vs2
f0410a90| gnu xxspltw vs2,vs1,1
f06110d0| gnu xxperm vs3,vs1,vs2
f0611110| gnu xxsldwi vs3,vs1,vs2,1
f0400c24| gnu xscvdpsp vs2,vs1
f0400e24| gnu xvcvdpsp vs2,vs1
f0400de0| gnu xscvsxddp vs2,vs1
f0400f60| gnu xvcvdpsxds vs2,vs1
f0400fe0| gnu xvcvsxddp vs2,vs1
7c6803a6| gnu mtlr r3
7c6903a6| gnu mtctr r3
7c6802a6| gnu mflr r3
7c6902a6| gnu mfctr r3
4c8c0000| gnu mcrf cr1,cr3
7c2101a7| gnu mtvsrwa vs33,r1
7c2101e7| gnu mtvsrwz vs33,r1
7ce10067| gnu mfvsrd r1,vs39
7ce100e7| gnu mfvsrwz r1,vs39
7c210167| gnu mtvsrd vs33,r1
7c8112de| gnu isel r4,r1,r2,4*cr2+so
7c2104ac| gnu sync 1,1
7c2110ac| gnu dcbf r1,r2,1
7c20003c| gnu wait 1,0
4c000924| gnu rfebb 1
0602000138800007| gnu pli r4,-8589869049
7c5b03c0| plan9 SETNBCR CR6SO,R2
fc811000| plan9 FCMPU F1,F2,CR1
7c220176| plan9 BRD R1,R2
7c2201b6| plan9 BRH R1,R2
7c220136| plan9 BRW R1,R2
7c2311b8| plan9 CFUGED R1,R2,R3
04100016e4820032| gnu .quad 0x4100016e4820032
0612000138820007| gnu .quad 0x612000138820007
7fe20de5| plan9 HASHCHK -8(R2),R1
7fe20da5| plan9 HASHST R1,-8(R2)
7c020de4| plan9 HASHCHK -512(R2),R1
7c020da4| plan9 HASHST R1,-512(R2)
7c020de5| plan9 HASHCHK -256(R2),R1
7c020da5| plan9 HASHST R1,-256(R2)
7fe115a5| plan9 HASHST R2,-8(R1)
7fe11525| plan9 HASHSTP R2,-8(R1)
7fe115e5| plan9 HASHCHK -8(R1),R2
7fe11565| plan9 HASHCHKP -8(R1),R2
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,503 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Power64spec reads the “Power ISA V2.07” Manual
// to collect instruction encoding details and writes those details to standard output
// in CSV format.
//
// Usage:
//
// ppc64spec PowerISA_V2.07_PUBLIC.pdf >ppc64.csv
//
// Each CSV line contains four fields:
//
// instruction
// The instruction heading, such as "AAD imm8".
// mnemonic
// The instruction mnemonics, separated by | symbols.
// encoding
// The instruction encoding, a sequence of name@startbit| describing each bit field in turn.
// tags
// For now, empty.
//
// For more on the exact meaning of these fields, see the Power manual.
package main
import (
"bufio"
"fmt"
"log"
"math"
"os"
"regexp"
"sort"
"strconv"
"strings"
"rsc.io/pdf"
)
type Inst struct {
Name string
Text string
Enc string
}
const debugPage = 0
var stdout *bufio.Writer
func main() {
log.SetFlags(0)
log.SetPrefix("ppc64spec: ")
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "usage: ppc64spec file.pdf\n")
os.Exit(2)
}
f, err := pdf.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
// Find instruction set reference in outline, to build instruction list.
instList := instHeadings(f.Outline())
if len(instList) < 200 {
log.Fatalf("only found %d instructions in table of contents", len(instList))
}
var all = []Inst{
// Split across multiple columns and pages!
{"Count Leading Zeros Word X-form", "cntlzw RA, RS (Rc=0)\ncntlzw. RA, RS (Rc=1)", "31@0|RS@6|RA@11|///@16|26@21|Rc@31|"},
}
for j, headline := range instList {
for _, inst := range all {
if headline == inst.Name {
instList[j] = ""
break
}
}
}
// Scan document looking for instructions.
// Must find exactly the ones in the outline.
n := f.NumPage()
for pageNum := 1; pageNum <= n; pageNum++ {
if debugPage > 0 && pageNum != debugPage {
continue
}
p := f.Page(pageNum)
table := parsePage(pageNum, p)
if len(table) == 0 {
continue
}
InstLoop:
for _, inst := range table {
for j, headline := range instList {
if inst.Name == headline {
instList[j] = ""
continue InstLoop
}
}
fmt.Fprintf(os.Stderr, "page %d: unexpected instruction %q\n", pageNum, inst.Name)
}
all = append(all, table...)
}
if debugPage == 0 {
for _, headline := range instList {
if headline != "" {
switch headline {
default:
fmt.Fprintf(os.Stderr, "missing instruction %q\n", headline)
case "CHKA": // ThumbEE
case "CPS": // system instruction
case "CPY": // synonym for MOV
case "ENTERX": // ThumbEE
case "F* (former VFP instruction mnemonics)": // synonyms
case "HB, HBL, HBLP, HBP": // ThumbEE
case "LEAVEX": // ThumbEE
case "MOV (shifted register)": // pseudo instruction for ASR, LSL, LSR, ROR, and RRX
case "NEG": // synonym for RSB
case "RFE": // system instruction
case "SMC (previously SMI)": // system instruction
case "SRS": // system instruction
case "SUBS PC, LR and related instructions": // system instruction
case "VAND (immediate)": // pseudo instruction
case "VCLE (register)": // pseudo instruction
case "VCLT (register)": // pseudo instruction
case "VORN (immediate)": // pseudo instruction
}
}
}
}
stdout = bufio.NewWriter(os.Stdout)
for _, inst := range all {
fmt.Fprintf(stdout, "%q,%q,%q,%q\n", inst.Name, strings.Replace(inst.Text, "\n", "|", -1), inst.Enc, "")
}
stdout.Flush()
}
func instHeadings(outline pdf.Outline) []string {
return appendInstHeadings(outline, nil)
}
var instRE = regexp.MustCompile(` ([A-Z0-9]+-form|Byte|Word|Doubleword|Halfword)($| \[)`)
var sectionRE = regexp.MustCompile(`^[0-9A-Z]+\.[0-9]`)
func appendInstHeadings(outline pdf.Outline, list []string) []string {
if strings.Contains(outline.Title, "Variable Length Encoding (VLE) Encoding") {
for _, child := range outline.Child {
vle = appendInstHeadings(child, vle)
}
return list
}
if instRE.MatchString(outline.Title) && !sectionRE.MatchString(outline.Title) {
list = append(list, outline.Title)
}
if outline.Title == "Transaction Abort Word Conditional" {
list = append(list, outline.Title+" X-form")
}
for _, child := range outline.Child {
list = appendInstHeadings(child, list)
}
return list
}
const inch = 72.0
func parsePage(num int, p pdf.Page) []Inst {
content := p.Content()
var text []pdf.Text
for _, t := range content.Text {
text = append(text, t)
}
text = findWords(text)
if debugPage > 0 {
for _, t := range text {
fmt.Println(t)
}
for _, r := range content.Rect {
fmt.Println(r)
}
}
// Look for instruction encodings.
// Some begin with a Helvetica-BoldOblique size 11 headline like "AND X-Form",
// is followed by Helvetica 9 mnemonic, and then a bit box with
// Helvetica 9 fields and Helvetica 7 bit offsets.
// Others use Arial,BoldItalic 11 for the headline,
// Arial 8 for the mnemonic, and Arial 4.2 for the bit offsets.
var insts []Inst
for {
// Heading
for len(text) > 0 && !match(text[0], "Helvetica-BoldOblique", 11, "") && !match(text[0], "Arial,BoldItalic", 11, "") && !match(text[0], "Arial,BoldItalic", 10, "") {
text = text[1:]
}
if len(text) == 0 {
break
}
heading := text[0].S
text = text[1:]
for len(text) > 0 && (match(text[0], "Helvetica-BoldOblique", 11, "") || match(text[0], "Arial,BoldItalic", 11, "") || match(text[0], "Arial,BoldItalic", 10, "")) {
heading += " " + text[0].S
text = text[1:]
}
heading = strings.Replace(heading, "]", "] ", -1)
heading = strings.Replace(heading, " ", " ", -1)
heading = strings.Replace(heading, "rEVX-form", "r EVX-form", -1)
heading = strings.Replace(heading, "eX-form", "e X-form", -1)
heading = strings.Replace(heading, "mSD4-form", "m SD4-form", -1)
heading = strings.Replace(heading, "eSCI8-form", "e SCI8-form", -1)
heading = strings.TrimSpace(heading)
if isVLE(heading) {
continue
}
// Mnemonic
if len(text) == 0 || (!match(text[0], "Helvetica", 9, "") && !match(text[0], "Helvetica-BoldOblique", 9, "") && !match(text[0], "Arial", 9, "") && !match(text[0], "Arial", 10, "")) {
continue
}
mnemonic := ""
y := text[0].Y
x0 := text[0].X
for len(text) > 0 && (match(text[0], "Helvetica", 9, "") || match(text[0], "Helvetica-BoldOblique", 9, "") || match(text[0], "Arial", 9, "") || match(text[0], "Courier", 8, "") || match(text[0], "LucidaConsole", 7.17, "") || text[0].Y == y) {
if text[0].Y != y {
if math.Abs(text[0].X-x0) > 4 {
break
}
mnemonic += "\n"
y = text[0].Y
} else if mnemonic != "" {
mnemonic += " "
}
mnemonic += text[0].S
text = text[1:]
}
// Encoding
bits, i := readBitBox(heading, content, text, num)
if i == 0 {
continue
}
insts = append(insts, Inst{heading, mnemonic, bits})
}
return insts
}
var vle = []string{
"System Call C-form,ESC-form",
}
func isVLE(s string) bool {
for _, v := range vle {
if s == v {
return true
}
}
return false
}
func readBitBox(headline string, content pdf.Content, text []pdf.Text, pageNum int) (string, int) {
// fields
i := 0
if len(text) == 0 || (!match(text[i], "Helvetica", 9, "") && !match(text[i], "Helvetica", 7.26, "") && !match(text[i], "Arial", 9, "") && !match(text[i], "Arial", 7.98, "") && !match(text[i], "Arial", 7.2, "")) {
fmt.Fprintf(os.Stderr, "page %d: no bit fields for %q\n", pageNum, headline)
if len(text) > 0 {
fmt.Fprintf(os.Stderr, "\tlast text: %v\n", text[0])
}
return "", 0
}
sz := text[i].FontSize
y2 := text[i].Y
x2 := 0.0
for i < len(text) && text[i].Y == y2 {
if x2 < text[i].X+text[i].W {
x2 = text[i].X + text[i].W
}
i++
}
y2 += sz / 2
// bit numbers
if i >= len(text) || text[i].S != "0" {
if headline == "Transaction Abort Doubleword Conditional X-form" {
// Split across the next page.
return "31@0|TO@6|RA@11|RB@16|814@21|1@31|", i
}
if headline == "Add Scaled Immediate SCI8-form" {
// Very strange fonts.
return "06@0|RT@6|RA@11|8@16|Rc@20|F@21|SCL@22|UI8@24|", i
}
fmt.Fprintf(os.Stderr, "page %d: no bit numbers for %s\n", pageNum, headline)
if i < len(text) {
fmt.Fprintf(os.Stderr, "\tlast text: %v\n", text[i])
}
return "", 0
}
sz = text[i].FontSize
y1 := text[i].Y
x1 := text[i].X
for i < len(text) && text[i].Y == y1 {
if x2 < text[i].X+text[i].W {
x2 = text[i].X + text[i].W
}
i++
}
if debugPage > 0 {
fmt.Println("encoding box", x1, y1, x2, y2, i, text[0], text[i])
}
// Find lines (thin rectangles) separating bit fields.
var bottom, top pdf.Rect
const (
yMargin = 0.25 * 72
xMargin = 1 * 72
)
for _, r := range content.Rect {
// Only consider lines in the same column.
if (x1 < 306) != (r.Max.X < 306) {
continue
}
if r.Max.Y-r.Min.Y < 2 && x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1-yMargin < r.Min.Y && r.Min.Y < y1 {
bottom = r
}
if y2 < r.Min.Y && r.Min.Y < y2+8 {
top = r
}
}
}
if bottom.Min.X == 0 {
// maybe bit numbers are outside box; see doze, nap, sleep, rvwinkle.
for _, r := range content.Rect {
// Only consider lines in the same column.
if (x1 < 306) != (r.Max.X < 306) {
continue
}
if r.Max.Y-r.Min.Y < 2 && x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1+sz/2 < r.Min.Y && r.Min.Y < y2 {
bottom = r
}
}
}
}
if debugPage > 0 {
fmt.Println("top", top, "bottom", bottom)
}
const ε = 0.1 * 72
var bars []pdf.Rect
for _, r := range content.Rect {
if r.Max.X-r.Min.X < 2 && math.Abs(r.Min.Y-bottom.Min.Y) < ε && math.Abs(r.Max.Y-top.Min.Y) < ε && (bottom.Min.X < 306) == (r.Max.X < 306) {
bars = append(bars, r)
}
}
sort.Sort(RectHorizontal(bars))
out := ""
for i := 0; i < len(bars)-1; i++ {
var sub []pdf.Text
x1, x2 := bars[i].Min.X, bars[i+1].Min.X
for _, t := range content.Text {
tx := t.X + t.W/2
ty := t.Y + t.FontSize/4
if x1 < tx && tx < x2 && y1 < ty && ty < y2 {
sub = append(sub, t)
}
}
var str []string
for _, t := range findWords(sub) {
str = append(str, t.S)
}
s := strings.Join(str, "@")
out += s + "|"
}
if out == "" {
fmt.Fprintf(os.Stderr, "page %d: no bit encodings for %s\n", pageNum, headline)
}
return out, i
}
type RectHorizontal []pdf.Rect
func (x RectHorizontal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x RectHorizontal) Less(i, j int) bool { return x[i].Min.X < x[j].Min.X }
func (x RectHorizontal) Len() int { return len(x) }
func checkNoEncodings(num int, text []pdf.Text) {
for _, t := range text {
if match(t, "Helvetica-Bold", 9, "Encoding") {
fmt.Fprintf(os.Stderr, "page %d: unexpected encoding: %s\n", num, t.S)
}
}
}
func match(t pdf.Text, font string, size float64, substr string) bool {
return t.Font == font && (size == 0 || math.Abs(t.FontSize-size) < 0.1) && strings.Contains(t.S, substr)
}
func findWords(chars []pdf.Text) (words []pdf.Text) {
// Sort by Y coordinate and normalize.
const nudge = 1.5
sort.Sort(pdf.TextVertical(chars))
old := -100000.0
for i, c := range chars {
if c.Y != old && math.Abs(old-c.Y) < nudge {
chars[i].Y = old
} else {
old = c.Y
}
}
// Sort by Y coordinate, breaking ties with X.
// This will bring letters in a single word together.
sort.Sort(pdf.TextVertical(chars))
// Loop over chars.
for i := 0; i < len(chars); {
// Find all chars on line.
j := i + 1
for j < len(chars) && chars[j].Y == chars[i].Y {
j++
}
var end float64
// Split line into words (really, phrases).
for k := i; k < j; {
ck := &chars[k]
s := ck.S
end = ck.X + ck.W
charSpace := ck.FontSize / 6
wordSpace := ck.FontSize * 2 / 3
l := k + 1
for l < j {
// Grow word.
cl := &chars[l]
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
s += cl.S
end = cl.X + cl.W
l++
continue
}
// Add space to phrase before next word.
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
s += " " + cl.S
end = cl.X + cl.W
l++
continue
}
break
}
f := ck.Font
f = strings.TrimSuffix(f, ",Italic")
f = strings.TrimSuffix(f, "-Italic")
words = append(words, pdf.Text{f, ck.FontSize, ck.X, ck.Y, end - ck.X, s})
k = l
}
i = j
}
// Split into two columns.
var col1, col2 []pdf.Text
for _, w := range words {
if w.X > 306 {
col2 = append(col2, w)
} else {
col1 = append(col1, w)
}
}
return append(col1, col2...)
}
func sameFont(f1, f2 string) bool {
f1 = strings.TrimSuffix(f1, ",Italic")
f1 = strings.TrimSuffix(f1, "-Italic")
f2 = strings.TrimSuffix(f1, ",Italic")
f2 = strings.TrimSuffix(f1, "-Italic")
return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
}
var jsFix = strings.NewReplacer(
// `\u003c`, `<`,
// `\u003e`, `>`,
// `\u0026`, `&`,
// `\u0009`, `\t`,
)
func printTable(name string, table []Inst) {
_ = strconv.Atoi
}
@@ -0,0 +1,163 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// This file requires gcc and binutils with -mcpu=power10 support.
// ppc64util runs a series of commands like:
// go run map.go -fmt=asm ../pp64.csv > asm.S
// powerpc64le-linux-gnu-gcc -c asm.S -mcpu=power10 -mbig
// powerpc64le-linux-gnu-objdump -d asm.o
// to create the file decode_generated.txt used to verify the disassembler.
//
// Note, the golang disassembler is not expected to support every extended
// mnemonic, but it should support those which frequently show up in object
// files compiled by the golang toolchain.
#define RA 1
#define RB 2
#define RS 3
#define RT 4
#define RC 5
#define RSp 6
#define RTp 8
#define MB 1
#define ME 7
#define NB 2
#define CY 1
#define LEV 1
#define FRBp 2
#define FRAp 4
#define FRTp 6
#define FRSp 8
#define FRT 3
#define FRA 5
#define FRB 7
#define FRC 9
#define FRS 11
#define FLM 8
#define U 3
#define W 0
#define TE 15
#define SP 1
#define S 1
#define DRM 0x7
#define RM 0x3
#define BF 3
#define SH 7
#define XT 33
#define XA 35
#define XB 37
#define XS 39
#define XC 41
#define XAp 36
#define XTp 38
#define XSp 40
#define DM 1
#define SHW 2
#define VRA 1
#define VRB 2
#define VRC 3
#define VRT 4
#define VRS 5
#define SHB 3
#define SIX 1
#define ST 1
#define PS 0
#define MP 1
#define bm 0x45FF
#define N 3
#define AT 7
#define AS 6
#define RMC 3
#define UIM 1
#define DCMX 0x23
#define DCM 0x11
#define DGM 0x11
#define R 1
#define BA 1
#define BB 2
#define BT 3
#define BO 4
#define BI 6
#define BH 0
#define BFA 7
#define FXM 8
#define BC 11
#define L 1
#define EH 1
#define SPR 69
#define BHRBE 69
#define TO 0x11
#define TBR 268
#define CT 2
#define FC 2
#define TH 3
#define WC 1
#define PL 0
#define IH 4
#define RIC 1
#define PRS 1
#define SIM 6
#define IMM 13
#define IMM8 14
#define D 0x80
#define SC 1
#define target_addr 0x690
#define XMSK 0x9
#define YMSK 0x3
#define PMSK 0x2
#define IX 1
#define IMM32 0x1234567
#define Dpfx 0x160032
#define RApfx 0x0
#define Rpfx 1
#define SIpfx 0xFFFFFFFE00010007
// A valid displacement value for the hash check and hash store instructions.
#define offset -128
// These decode as m.fpr* or m.vr*. This is a matter of preference. We
// don't support these mnemonics, and I don't think they improve reading
// disassembled code in most cases. so ignore.
//
// Likewise, if you add to this list, add tests to decode.txt to ensure we
// still test these, while ignoring the extended mnemonics which get
// generated.
#define mfvsrd xsrsp
#define mfvsrwz xsrsp
#define mtvsrd xsrsp
#define mtvsrwz xsrsp
#define mtvsrwa xsrsp
// isel BC bit is not decoded like other BC fields.
// A special test case is added to decode.txt to verify this.
// We decode it like other BC fields.
#define isel rldicl
// Likewise, these are obscure book ii instructions with extended mnemonics
// which are almost guaranteed never to show up in go code
#define dcbf add
#define sync xsrsp
#define wait xsrsp
#define rfebb sc
// sync 1,1 is the stncisync extended mnemonic. Similar to the above, but
// the lwsync/hwsync extended mnemonics are tested in decode.txt
#define sync xsrsp
@@ -0,0 +1,149 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// Generate interesting test cases from ppc64 objdump via
// go run util.go
//
// This requires powerpc64le-linux-gnu-gcc and powerpc64le-linux-gnu-objdump be in
// the PATH this command is run.
//
// These tools can be acquired from the IBM advance toolchain for amd64 hosts too.
package main
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
)
// Generator for branch on spr (bclr, bctar, bcctr)
func emitBSpr(bo, bi, l uint32, out io.Writer) {
var insn [3]uint32 = [3]uint32{19<<26 | 16<<1, 19<<26 | 528<<1, 19<<26 | 560<<1}
for bh := uint32(0); bh < 3; bh++ {
for _, m := range insn {
m |= bo << 21
m |= bi << 16
m |= bh << 11
m |= l << 0
fmt.Fprintf(out, "\t.long 0x%08x\n", m)
}
}
}
// Generator for bc
func emitBc(bo, bi, l uint32, out io.Writer) {
for aa := uint32(0); aa < 2; aa++ {
m := uint32(16 << 26)
m |= bo << 21
m |= bi << 16
m |= l << 0
m |= aa << 1
m |= 128
fmt.Fprintf(out, "\t.long 0x%08x\n", m)
}
}
// Generator all interesting conditional branch type instructions
func emitBranches(out io.Writer) {
fmt.Fprintf(out, ".text\n")
for bo := 0; bo < 0x20; bo++ {
// objdump behaves strangely on some cases when a z bit is set.
// Ignore these, they should never show up in correct code.
if bo&0x15 == 0x1 {
// skip 0b0.0.z cases where z != 0
continue
}
if bo&0x14 == 0x14 && bo != 14 {
// skip 0b1z1zz cases where z != 0
continue
}
// skip at == 1 cases. objdump doesn't handle these well either.
reserved_at := map[int]bool{5: true, 13: true, 17: true, 19: true}
if reserved_at[bo] {
continue
}
// only test cr0/cr1 bits. cr2-cr7 cases are basically identical to cr1.
for bi := 0; bi < 0x8; bi++ {
for l := 0; l < 2; l++ {
emitBSpr(uint32(bo), uint32(bi), uint32(l), out)
emitBc(uint32(bo), uint32(bi), uint32(l), out)
}
}
}
}
// Emit a test file using the generator called name.txt. This requires
// a GCC toolchain which supports -mcpu=power10.
func genOutput(name, tcPfx string, generator func(io.Writer)) {
// Generate object code from gcc
cmd := exec.Command(tcPfx+"gcc", "-c", "-mbig", "-mcpu=power10", "-x", "assembler-with-cpp", "-o", name+".o", "-")
input, _ := cmd.StdinPipe()
cmd.Stderr = os.Stderr
go func() {
defer input.Close()
generator(input.(io.Writer))
}()
if cmd.Run() != nil {
fmt.Printf("Failed running gcc for: %s\n", name)
return
}
defer os.Remove(name + ".o")
cmd = exec.Command(tcPfx+"objdump", "-d", name+".o")
// Run objdump and parse output into test format
output, _ := cmd.StdoutPipe()
defer output.Close()
scanner := bufio.NewScanner(output)
spacere := regexp.MustCompile("[[:space:]]+")
outf, _ := os.Create(name + ".txt")
defer outf.Close()
if cmd.Start() != nil {
fmt.Printf("Failed running objdump for: %s\n", name)
return
}
pfx := ""
dec := ""
for scanner.Scan() {
ln := spacere.Split(scanner.Text(), -1)
if len(ln) >= 7 {
opc := strings.Join(ln[2:6], "")
if len(pfx) == 0 {
dec = strings.Join(ln[6:], " ")
}
if v, _ := strconv.ParseInt(ln[2], 16, 16); v&0xFC == 0x04 {
pfx = opc
continue
}
fmt.Fprintf(outf, "%s%s|\tgnu\t%s\n", pfx, opc, dec)
pfx = ""
}
}
cmd.Wait()
}
// Generate representative instructions for all[1] instructions in pp64.csv.
//
// [1] See hack.h for a few minor, exceptional workarounds.
func emitGenerated(out io.Writer) {
cmd := exec.Command("go", "run", "../ppc64map/map.go", "-fmt=asm", "../pp64.csv")
cmdout, _ := cmd.Output()
out.Write(cmdout)
}
// Produce generated test outputs. This should be run every so often with
// new versions of objdump to ensure we stay up to date.
func main() {
genOutput("decode_branch", "powerpc64le-linux-gnu-", emitBranches)
genOutput("decode_generated", "powerpc64le-linux-gnu-", emitGenerated)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
tables.go: ../x86map/map.go ../x86.csv
go run ../x86map/map.go -fmt=decoder ../x86.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"encoding/hex"
"io/ioutil"
"strconv"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
all := string(data)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 4)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f[0])
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f[0])
}
size := i / 2
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f[0], err)
continue
}
mode, err := strconv.Atoi(f[1])
if err != nil {
t.Errorf("invalid mode %q in: %s", f[1], line)
continue
}
syntax, asm := f[2], f[3]
inst, err := Decode(code, mode)
var out string
if err != nil {
out = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst, 0, nil)
case "intel":
out = IntelSyntax(inst, 0, nil)
case "plan9": // [sic]
out = GoSyntax(inst, 0, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
}
if out != asm || inst.Len != size {
t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size)
}
}
}
func TestDecodeDoesNotCrash(t *testing.T) {
cases := [...][]byte{
[]byte{},
[]byte{0xc5},
[]byte{0xc4},
}
for _, test := range cases {
_, err := Decode([]byte(test), 64) // the only goal is that this line does not panic
if err == nil {
t.Errorf("expected error on invalid instruction %x", test)
}
}
}
@@ -0,0 +1,816 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
package x86asm
import (
"bufio"
"bytes"
"encoding/hex"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
var (
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint32
enc [32]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Arch int
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and be run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
arch int,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
) {
decoderCover = make([]bool, len(decoder))
defer func() {
decoderCover = nil
}()
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
Arch: arch,
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, arch, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text || inst.Len != dec.nenc {
suffix := ""
if allowedMismatch(text, size, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
const start = 0x8000 // start address of text
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "x86map")
if err != nil {
return
}
file = f.Name()
f.Seek(start, io.SeekStart)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if len(x) > 16 {
x = x[:16]
}
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):])
}
w.Write(x)
w.Write(pops[len(x):])
size += len(pops)
})
return file, f, size, nil
}
// 0x5F is a single-byte pop instruction.
// We pad the bytes we want decoded with enough 0x5Fs
// that no matter what state the instruction stream is in
// after reading our bytes, the pops will get us back to
// a forced instruction boundary.
var pops = []byte{
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
return append(enc[:len(enc):len(enc)], pops...)
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, mode int, src []byte) (inst Inst, text string) {
// If printTests is set, we record the coverage value
// before and after, and we write out the inputs for which
// coverage went up, in the format expected in testdata/decode.text.
// This produces a fairly small set of test cases that exercise nearly
// all the code.
var cover float64
if *printTests {
cover -= coverage()
}
inst, err := decode1(src, mode, syntax == "gnu")
if err != nil {
text = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
text = GNUSyntax(inst, 0, nil)
case "intel":
text = IntelSyntax(inst, 0, nil)
case "plan9": // [sic]
text = GoSyntax(inst, 0, nil)
default:
text = "error: unknown syntax " + syntax
}
}
if *printTests {
cover += coverage()
if cover > 0 {
max := len(src)
if max > 16 && inst.Len <= 16 {
max = 16
}
fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
}
}
return
}
// coverage returns a floating point number denoting the
// test coverage until now. The number increases when new code paths are exercised,
// both in the Go program and in the decoder byte code.
func coverage() float64 {
/*
testing.Coverage is not in the main distribution.
The implementation, which must go in package testing, is:
// Coverage reports the current code coverage as a fraction in the range [0, 1].
func Coverage() float64 {
var n, d int64
for _, counters := range cover.Counters {
for _, c := range counters {
if c > 0 {
n++
}
d++
}
}
if d == 0 {
return 0
}
return float64(n) / float64(d)
}
*/
var f float64
// f += testing.Coverage()
f += decodeCoverage()
return f
}
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// isPrefix reports whether text is the name of an instruction prefix.
func isPrefix(text string) bool {
return prefixByte[text] > 0
}
// prefixByte maps instruction prefix text to actual prefix byte values.
var prefixByte = map[string]byte{
"es": 0x26,
"cs": 0x2e,
"ss": 0x36,
"ds": 0x3e,
"fs": 0x64,
"gs": 0x65,
"data16": 0x66,
"addr16": 0x67,
"lock": 0xf0,
"repn": 0xf2,
"repne": 0xf2,
"rep": 0xf3,
"repe": 0xf3,
"xacquire": 0xf2,
"xrelease": 0xf3,
"bnd": 0xf2,
"addr32": 0x66,
"data32": 0x67,
}
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for s := s; s != ""; {
if strings.HasPrefix(s, prefix) {
return true
}
i := strings.Index(s, " ")
if i < 0 {
break
}
s = s[i+1:]
}
}
return false
}
// contains reports whether the text s contains any of the given substrings.
func contains(s string, substrings ...string) bool {
for _, sub := range substrings {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool signals whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = trimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
j += 2
}
return raw, true
}
var unhex = [256]byte{
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'A': 10,
'B': 11,
'C': 12,
'D': 13,
'E': 14,
'F': 15,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = trimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// trimSpace trims leading and trailing space from s, returning a subslice of s.
func trimSpace(s []byte) []byte {
j := len(s)
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
j--
}
i := 0
for i < j && (s[i] == ' ' || s[i] == '\t') {
i++
}
return s[i:j]
}
// pcrel and pcrelw match instructions using relative addressing mode.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`)
pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`)
)
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/decode.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T) func(func([]byte)) {
var codes [][]byte
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}
// manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes.
// The relative ordering of the prefixes within the combinations varies deterministically.
func manyPrefixes(try func([]byte)) {
var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67}
var enc []byte
for i := 0; i < 1<<uint(len(prefixBytes)); i++ {
enc = enc[:0]
for j, p := range prefixBytes {
if i&(1<<uint(j)) != 0 {
enc = append(enc, p)
}
}
if len(enc) > 0 {
k := i % len(enc)
enc[0], enc[k] = enc[k], enc[0]
}
try(enc)
}
}
// basicPrefixes geneartes 8 different possible prefix cases: no prefix
// and then one each of seven different prefix bytes.
func basicPrefixes(try func([]byte)) {
try(nil)
for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} {
try([]byte{b})
}
}
func rexPrefixes(try func([]byte)) {
try(nil)
for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} {
try([]byte{b})
}
}
// concat takes two generators and returns a generator for the
// cross product of the two, concatenating the results from each.
func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
try(append(enc1[:len(enc1):len(enc1)], enc2...))
})
})
}
}
// concat3 takes three generators and returns a generator for the
// cross product of the three, concatenating the results from each.
func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
gen3(func(enc3 []byte) {
try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...))
})
})
})
}
}
// concat4 takes four generators and returns a generator for the
// cross product of the four, concatenating the results from each.
func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
gen3(func(enc3 []byte) {
gen4(func(enc4 []byte) {
try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...))
})
})
})
})
}
}
// filter generates the sequences from gen that satisfy ok.
func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) {
return func(try func([]byte)) {
gen(func(enc []byte) {
if ok(enc) {
try(enc)
}
})
}
}
// enum8bit generates all possible 1-byte sequences, followed by distinctive padding.
func enum8bit(try func([]byte)) {
for i := 0; i < 1<<8; i++ {
try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enum8bit generates all possible 2-byte sequences, followed by distinctive padding.
func enum16bit(try func([]byte)) {
for i := 0; i < 1<<16; i++ {
try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enum24bit generates all possible 3-byte sequences, followed by distinctive padding.
func enum24bit(try func([]byte)) {
for i := 0; i < 1<<24; i++ {
try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enumModRM generates all possible modrm bytes and, for modrm values that indicate
// a following sib byte, all possible modrm, sib combinations.
func enumModRM(try func([]byte)) {
for i := 0; i < 256; i++ {
if (i>>3)&07 == 04 && i>>6 != 3 { // has sib
for j := 0; j < 256; j++ {
try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
}
} else {
try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
}
}
}
// fixed generates the single case b.
// It's mainly useful to prepare an argument for concat or concat3.
func fixed(b ...byte) func(func([]byte)) {
return func(try func([]byte)) {
try(b)
}
}
// testBasic runs the given test function with cases all using opcode as the initial opcode bytes.
// It runs three phases:
//
// First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values.
// If in -short mode, that's all.
//
// Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values.
// If not in -long mode, that's all. This phase and the next run in parallel with other tests
// (using t.Parallel).
//
// Finally, opcode followed by all possible 3-byte values. The test can take a very long time
// and prints progress messages to package log.
func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit))
if testing.Short() {
return
}
t.Parallel()
testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit))
if !*longTest {
return
}
name := caller(2)
op1 := make([]byte, len(opcode)+1)
copy(op1, opcode)
for i := 0; i < 256; i++ {
log.Printf("%s 24-bit: %d/256\n", name, i)
op1[len(opcode)] = byte(i)
testfn(t, concat(fixed(op1...), enum16bit))
}
}
func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX))
if testing.Short() {
return
}
t.Parallel()
testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX))
if !*longTest {
return
}
name := caller(2)
op1 := make([]byte, len(opcode)+1)
copy(op1, opcode)
for i := 0; i < 256; i++ {
log.Printf("%s 24-bit: %d/256\n", name, i)
op1[len(opcode)] = byte(i)
testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX))
}
}
// testPrefix runs the given test function for all many prefix possibilities
// followed by all possible 1-byte sequences.
//
// If in -long mode, it then runs a test of all the prefix possibilities followed
// by all possible 2-byte sequences.
func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
t.Parallel()
testfn(t, concat(manyPrefixes, enum8bit))
if testing.Short() || !*longTest {
return
}
name := caller(2)
for i := 0; i < 256; i++ {
log.Printf("%s 16-bit: %d/256\n", name, i)
testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit))
}
}
func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
t.Parallel()
testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX))
if testing.Short() || !*longTest {
return
}
name := caller(2)
for i := 0; i < 256; i++ {
log.Printf("%s 16-bit: %d/256\n", name, i)
testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX))
}
}
func caller(skip int) string {
pc, _, _, _ := runtime.Caller(skip)
f := runtime.FuncForPC(pc)
name := "?"
if f != nil {
name = f.Name()
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[i+1:]
}
}
return name
}
func isValidREX(x []byte) bool {
i := 0
for i < len(x) && isPrefixByte(x[i]) {
i++
}
if i < len(x) && Prefix(x[i]).IsREX() {
i++
if i < len(x) {
return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX()
}
}
return true
}
func isPrefixByte(b byte) bool {
switch b {
case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3:
return true
}
return false
}
@@ -0,0 +1,68 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"encoding/hex"
"testing"
)
func testFormattingSymname(addr uint64) (string, uint64) {
switch addr {
case 0x424080:
return "runtime.printint", 0x424080
case 0x4c8068:
return "main.A", 0x4c8068
}
return "", 0
}
func TestFormatting(t *testing.T) {
testCases := []struct {
PC uint64
bytes string
goSyntax, intelSyntax, gnuSyntax string
}{
{0x4816b2, "0f8677010000",
"JBE 0x48182f",
"jbe 0x48182f",
"jbe 0x48182f"},
{0x45065b, "488b442408",
"MOVQ 0x8(SP), AX",
"mov rax, qword ptr [rsp+0x8]",
"mov 0x8(%rsp),%rax"},
{0x450678, "488b05e9790700",
"MOVQ main.A(SB), AX",
"mov rax, qword ptr [main.A]",
"mov main.A,%rax"},
{0x450664, "e8173afdff",
"CALL runtime.printint(SB)",
"call runtime.printint",
"callq runtime.printint"},
{0x45069b, "488d0575d90100",
"LEAQ 0x1d975(IP), AX",
"lea rax, ptr [rip+0x1d975]",
"lea 0x1d975(%rip),%rax"},
}
for _, testCase := range testCases {
t.Logf("%#x %s %s", testCase.PC, testCase.bytes, testCase.goSyntax)
bs, _ := hex.DecodeString(testCase.bytes)
inst, err := Decode(bs, 64)
if err != nil {
t.Errorf("decode error %v", err)
}
if out := GoSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.goSyntax {
t.Errorf("GoSyntax: %q", out)
}
if out := IntelSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.intelSyntax {
t.Errorf("IntelSyntax: %q expected: %q", out, testCase.intelSyntax)
}
if out := GNUSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.gnuSyntax {
t.Errorf("GNUSyntax: %q expected: %q", out, testCase.gnuSyntax)
}
}
}
@@ -0,0 +1,956 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"fmt"
"strings"
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This general form is often called “AT&T syntax” as a reference to AT&T System V Unix.
func GNUSyntax(inst Inst, pc uint64, symname SymLookup) string {
// Rewrite instruction to mimic GNU peculiarities.
// Note that inst has been passed by value and contains
// no pointers, so any changes we make here are local
// and will not propagate back out to the caller.
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
// Adjust opcode [sic].
switch inst.Op {
case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP:
// DC E0, DC F0: libopcodes swaps FSUBR/FSUB and FDIVR/FDIV, at least
// if you believe the Intel manual is correct (the encoding is irregular as given;
// libopcodes uses the more regular expected encoding).
// TODO(rsc): Test to ensure Intel manuals are correct and report to libopcodes maintainers?
// NOTE: iant thinks this is deliberate, but we can't find the history.
_, reg1 := inst.Args[0].(Reg)
_, reg2 := inst.Args[1].(Reg)
if reg1 && reg2 && (inst.Opcode>>24 == 0xDC || inst.Opcode>>24 == 0xDE) {
switch inst.Op {
case FDIV:
inst.Op = FDIVR
case FDIVR:
inst.Op = FDIV
case FSUB:
inst.Op = FSUBR
case FSUBR:
inst.Op = FSUB
case FDIVP:
inst.Op = FDIVRP
case FDIVRP:
inst.Op = FDIVP
case FSUBP:
inst.Op = FSUBRP
case FSUBRP:
inst.Op = FSUBP
}
}
case MOVNTSD:
// MOVNTSD is F2 0F 2B /r.
// MOVNTSS is F3 0F 2B /r (supposedly; not in manuals).
// Usually inner prefixes win for display,
// so that F3 F2 0F 2B 11 is REP MOVNTSD
// and F2 F3 0F 2B 11 is REPN MOVNTSS.
// Libopcodes always prefers MOVNTSS regardless of prefix order.
if countPrefix(&inst, 0xF3) > 0 {
found := false
for i := len(inst.Prefix) - 1; i >= 0; i-- {
switch inst.Prefix[i] & 0xFF {
case 0xF3:
if !found {
found = true
inst.Prefix[i] |= PrefixImplicit
}
case 0xF2:
inst.Prefix[i] &^= PrefixImplicit
}
}
inst.Op = MOVNTSS
}
}
// Add implicit arguments.
switch inst.Op {
case MONITOR:
inst.Args[0] = EDX
inst.Args[1] = ECX
inst.Args[2] = EAX
if inst.AddrSize == 16 {
inst.Args[2] = AX
}
case MWAIT:
if inst.Mode == 64 {
inst.Args[0] = RCX
inst.Args[1] = RAX
} else {
inst.Args[0] = ECX
inst.Args[1] = EAX
}
}
// Adjust which prefixes will be displayed.
// The rule is to display all the prefixes not implied by
// the usual instruction display, that is, all the prefixes
// except the ones with PrefixImplicit set.
// However, of course, there are exceptions to the rule.
switch inst.Op {
case CRC32:
// CRC32 has a mandatory F2 prefix.
// If there are multiple F2s and no F3s, the extra F2s do not print.
// (And Decode has already marked them implicit.)
// However, if there is an F3 anywhere, then the extra F2s do print.
// If there are multiple F2 prefixes *and* an (ignored) F3,
// then libopcodes prints the extra F2s as REPNs.
if countPrefix(&inst, 0xF2) > 1 {
unmarkImplicit(&inst, 0xF2)
markLastImplicit(&inst, 0xF2)
}
// An unused data size override should probably be shown,
// to distinguish DATA16 CRC32B from plain CRC32B,
// but libopcodes always treats the final override as implicit
// and the others as explicit.
unmarkImplicit(&inst, PrefixDataSize)
markLastImplicit(&inst, PrefixDataSize)
case CVTSI2SD, CVTSI2SS:
if !isMem(inst.Args[1]) {
markLastImplicit(&inst, PrefixDataSize)
}
case CVTSD2SI, CVTSS2SI, CVTTSD2SI, CVTTSS2SI,
ENTER, FLDENV, FNSAVE, FNSTENV, FRSTOR, LGDT, LIDT, LRET,
POP, PUSH, RET, SGDT, SIDT, SYSRET, XBEGIN:
markLastImplicit(&inst, PrefixDataSize)
case LOOP, LOOPE, LOOPNE, MONITOR:
markLastImplicit(&inst, PrefixAddrSize)
case MOV:
// The 16-bit and 32-bit forms of MOV Sreg, dst and MOV src, Sreg
// cannot be distinguished when src or dst refers to memory, because
// Sreg is always a 16-bit value, even when we're doing a 32-bit
// instruction. Because the instruction tables distinguished these two,
// any operand size prefix has been marked as used (to decide which
// branch to take). Unmark it, so that it will show up in disassembly,
// so that the reader can tell the size of memory operand.
// up with the same arguments
dst, _ := inst.Args[0].(Reg)
src, _ := inst.Args[1].(Reg)
if ES <= src && src <= GS && isMem(inst.Args[0]) || ES <= dst && dst <= GS && isMem(inst.Args[1]) {
unmarkImplicit(&inst, PrefixDataSize)
}
case MOVDQU:
if countPrefix(&inst, 0xF3) > 1 {
unmarkImplicit(&inst, 0xF3)
markLastImplicit(&inst, 0xF3)
}
case MOVQ2DQ:
markLastImplicit(&inst, PrefixDataSize)
case SLDT, SMSW, STR, FXRSTOR, XRSTOR, XSAVE, XSAVEOPT, CMPXCHG8B:
if isMem(inst.Args[0]) {
unmarkImplicit(&inst, PrefixDataSize)
}
case SYSEXIT:
unmarkImplicit(&inst, PrefixDataSize)
}
if isCondJmp[inst.Op] || isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ {
if countPrefix(&inst, PrefixCS) > 0 && countPrefix(&inst, PrefixDS) > 0 {
for i, p := range inst.Prefix {
switch p & 0xFFF {
case PrefixPN, PrefixPT:
inst.Prefix[i] &= 0xF0FF // cut interpretation bits, producing original segment prefix
}
}
}
}
// XACQUIRE/XRELEASE adjustment.
if inst.Op == MOV {
// MOV into memory is a candidate for turning REP into XRELEASE.
// However, if the REP is followed by a REPN, that REPN blocks the
// conversion.
haveREPN := false
for i := len(inst.Prefix) - 1; i >= 0; i-- {
switch inst.Prefix[i] &^ PrefixIgnored {
case PrefixREPN:
haveREPN = true
case PrefixXRELEASE:
if haveREPN {
inst.Prefix[i] = PrefixREP
}
}
}
}
// We only format the final F2/F3 as XRELEASE/XACQUIRE.
haveXA := false
haveXR := false
for i := len(inst.Prefix) - 1; i >= 0; i-- {
switch inst.Prefix[i] &^ PrefixIgnored {
case PrefixXRELEASE:
if !haveXR {
haveXR = true
} else {
inst.Prefix[i] = PrefixREP
}
case PrefixXACQUIRE:
if !haveXA {
haveXA = true
} else {
inst.Prefix[i] = PrefixREPN
}
}
}
// Determine opcode.
op := strings.ToLower(inst.Op.String())
if alt := gnuOp[inst.Op]; alt != "" {
op = alt
}
// Determine opcode suffix.
// Libopcodes omits the suffix if the width of the operation
// can be inferred from a register arguments. For example,
// add $1, %ebx has no suffix because you can tell from the
// 32-bit register destination that it is a 32-bit add,
// but in addl $1, (%ebx), the destination is memory, so the
// size is not evident without the l suffix.
needSuffix := true
SuffixLoop:
for i, a := range inst.Args {
if a == nil {
break
}
switch a := a.(type) {
case Reg:
switch inst.Op {
case MOVSX, MOVZX:
continue
case SHL, SHR, RCL, RCR, ROL, ROR, SAR:
if i == 1 {
// shift count does not tell us operand size
continue
}
case CRC32:
// The source argument does tell us operand size,
// but libopcodes still always puts a suffix on crc32.
continue
case PUSH, POP:
// Even though segment registers are 16-bit, push and pop
// can save/restore them from 32-bit slots, so they
// do not imply operand size.
if ES <= a && a <= GS {
continue
}
case CVTSI2SD, CVTSI2SS:
// The integer register argument takes priority.
if X0 <= a && a <= X15 {
continue
}
}
if AL <= a && a <= R15 || ES <= a && a <= GS || X0 <= a && a <= X15 || M0 <= a && a <= M7 {
needSuffix = false
break SuffixLoop
}
}
}
if needSuffix {
switch inst.Op {
case CMPXCHG8B, FLDCW, FNSTCW, FNSTSW, LDMXCSR, LLDT, LMSW, LTR, PCLMULQDQ,
SETA, SETAE, SETB, SETBE, SETE, SETG, SETGE, SETL, SETLE, SETNE, SETNO, SETNP, SETNS, SETO, SETP, SETS,
SLDT, SMSW, STMXCSR, STR, VERR, VERW:
// For various reasons, libopcodes emits no suffix for these instructions.
case CRC32:
op += byteSizeSuffix(argBytes(&inst, inst.Args[1]))
case LGDT, LIDT, SGDT, SIDT:
op += byteSizeSuffix(inst.DataSize / 8)
case MOVZX, MOVSX:
// Integer size conversions get two suffixes.
op = op[:4] + byteSizeSuffix(argBytes(&inst, inst.Args[1])) + byteSizeSuffix(argBytes(&inst, inst.Args[0]))
case LOOP, LOOPE, LOOPNE:
// Add w suffix to indicate use of CX register instead of ECX.
if inst.AddrSize == 16 {
op += "w"
}
case CALL, ENTER, JMP, LCALL, LEAVE, LJMP, LRET, RET, SYSRET, XBEGIN:
// Add w suffix to indicate use of 16-bit target.
// Exclude JMP rel8.
if inst.Opcode>>24 == 0xEB {
break
}
if inst.DataSize == 16 && inst.Mode != 16 {
markLastImplicit(&inst, PrefixDataSize)
op += "w"
} else if inst.Mode == 64 {
op += "q"
}
case FRSTOR, FNSAVE, FNSTENV, FLDENV:
// Add s suffix to indicate shortened FPU state (I guess).
if inst.DataSize == 16 {
op += "s"
}
case PUSH, POP:
if markLastImplicit(&inst, PrefixDataSize) {
op += byteSizeSuffix(inst.DataSize / 8)
} else if inst.Mode == 64 {
op += "q"
} else {
op += byteSizeSuffix(inst.MemBytes)
}
default:
if isFloat(inst.Op) {
// I can't explain any of this, but it's what libopcodes does.
switch inst.MemBytes {
default:
if (inst.Op == FLD || inst.Op == FSTP) && isMem(inst.Args[0]) {
op += "t"
}
case 4:
if isFloatInt(inst.Op) {
op += "l"
} else {
op += "s"
}
case 8:
if isFloatInt(inst.Op) {
op += "ll"
} else {
op += "l"
}
}
break
}
op += byteSizeSuffix(inst.MemBytes)
}
}
// Adjust special case opcodes.
switch inst.Op {
case 0:
if inst.Prefix[0] != 0 {
return strings.ToLower(inst.Prefix[0].String())
}
case INT:
if inst.Opcode>>24 == 0xCC {
inst.Args[0] = nil
op = "int3"
}
case CMPPS, CMPPD, CMPSD_XMM, CMPSS:
imm, ok := inst.Args[2].(Imm)
if ok && 0 <= imm && imm < 8 {
inst.Args[2] = nil
op = cmppsOps[imm] + op[3:]
}
case PCLMULQDQ:
imm, ok := inst.Args[2].(Imm)
if ok && imm&^0x11 == 0 {
inst.Args[2] = nil
op = pclmulqOps[(imm&0x10)>>3|(imm&1)]
}
case XLATB:
if markLastImplicit(&inst, PrefixAddrSize) {
op = "xlat" // not xlatb
}
}
// Build list of argument strings.
var (
usedPrefixes bool // segment prefixes consumed by Mem formatting
args []string // formatted arguments
)
for i, a := range inst.Args {
if a == nil {
break
}
switch inst.Op {
case MOVSB, MOVSW, MOVSD, MOVSQ, OUTSB, OUTSW, OUTSD:
if i == 0 {
usedPrefixes = true // disable use of prefixes for first argument
} else {
usedPrefixes = false
}
}
if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
continue
}
args = append(args, gnuArg(&inst, pc, symname, a, &usedPrefixes))
}
// The default is to print the arguments in reverse Intel order.
// A few instructions inhibit this behavior.
switch inst.Op {
case BOUND, LCALL, ENTER, LJMP:
// no reverse
default:
// reverse args
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
args[i], args[j] = args[j], args[i]
}
}
// Build prefix string.
// Must be after argument formatting, which can turn off segment prefixes.
var (
prefix = "" // output string
numAddr = 0
numData = 0
implicitData = false
)
for _, p := range inst.Prefix {
if p&0xFF == PrefixDataSize && p&PrefixImplicit != 0 {
implicitData = true
}
}
for _, p := range inst.Prefix {
if p == 0 || p.IsVEX() {
break
}
if p&PrefixImplicit != 0 {
continue
}
switch p &^ (PrefixIgnored | PrefixInvalid) {
default:
if p.IsREX() {
if p&0xFF == PrefixREX {
prefix += "rex "
} else {
prefix += "rex." + p.String()[4:] + " "
}
break
}
prefix += strings.ToLower(p.String()) + " "
case PrefixPN:
op += ",pn"
continue
case PrefixPT:
op += ",pt"
continue
case PrefixAddrSize, PrefixAddr16, PrefixAddr32:
// For unknown reasons, if the addr16 prefix is repeated,
// libopcodes displays all but the last as addr32, even though
// the addressing form used in a memory reference is clearly
// still 16-bit.
n := 32
if inst.Mode == 32 {
n = 16
}
numAddr++
if countPrefix(&inst, PrefixAddrSize) > numAddr {
n = inst.Mode
}
prefix += fmt.Sprintf("addr%d ", n)
continue
case PrefixData16, PrefixData32:
if implicitData && countPrefix(&inst, PrefixDataSize) > 1 {
// Similar to the addr32 logic above, but it only kicks in
// when something used the data size prefix (one is implicit).
n := 16
if inst.Mode == 16 {
n = 32
}
numData++
if countPrefix(&inst, PrefixDataSize) > numData {
if inst.Mode == 16 {
n = 16
} else {
n = 32
}
}
prefix += fmt.Sprintf("data%d ", n)
continue
}
prefix += strings.ToLower(p.String()) + " "
}
}
// Finally! Put it all together.
text := prefix + op
if args != nil {
text += " "
// Indirect call/jmp gets a star to distinguish from direct jump address.
if (inst.Op == CALL || inst.Op == JMP || inst.Op == LJMP || inst.Op == LCALL) && (isMem(inst.Args[0]) || isReg(inst.Args[0])) {
text += "*"
}
text += strings.Join(args, ",")
}
return text
}
// gnuArg returns the GNU syntax for the argument x from the instruction inst.
// If *usedPrefixes is false and x is a Mem, then the formatting
// includes any segment prefixes and sets *usedPrefixes to true.
func gnuArg(inst *Inst, pc uint64, symname SymLookup, x Arg, usedPrefixes *bool) string {
if x == nil {
return "<nil>"
}
switch x := x.(type) {
case Reg:
switch inst.Op {
case CVTSI2SS, CVTSI2SD, CVTSS2SI, CVTSD2SI, CVTTSD2SI, CVTTSS2SI:
if inst.DataSize == 16 && EAX <= x && x <= R15L {
x -= EAX - AX
}
case IN, INSB, INSW, INSD, OUT, OUTSB, OUTSW, OUTSD:
// DX is the port, but libopcodes prints it as if it were a memory reference.
if x == DX {
return "(%dx)"
}
case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ:
return strings.Replace(gccRegName[x], "xmm", "ymm", -1)
}
return gccRegName[x]
case Mem:
if s, disp := memArgToSymbol(x, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return fmt.Sprintf("%s%s", s, suffix)
}
seg := ""
var haveCS, haveDS, haveES, haveFS, haveGS, haveSS bool
switch x.Segment {
case CS:
haveCS = true
case DS:
haveDS = true
case ES:
haveES = true
case FS:
haveFS = true
case GS:
haveGS = true
case SS:
haveSS = true
}
switch inst.Op {
case INSB, INSW, INSD, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ:
// These do not accept segment prefixes, at least in the GNU rendering.
default:
if *usedPrefixes {
break
}
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i] &^ PrefixIgnored
if p == 0 {
continue
}
switch p {
case PrefixCS:
if !haveCS {
haveCS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixDS:
if !haveDS {
haveDS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixES:
if !haveES {
haveES = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixFS:
if !haveFS {
haveFS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixGS:
if !haveGS {
haveGS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixSS:
if !haveSS {
haveSS = true
inst.Prefix[i] |= PrefixImplicit
}
}
}
*usedPrefixes = true
}
if haveCS {
seg += "%cs:"
}
if haveDS {
seg += "%ds:"
}
if haveSS {
seg += "%ss:"
}
if haveES {
seg += "%es:"
}
if haveFS {
seg += "%fs:"
}
if haveGS {
seg += "%gs:"
}
disp := ""
if x.Disp != 0 {
disp = fmt.Sprintf("%#x", x.Disp)
}
if x.Scale == 0 || x.Index == 0 && x.Scale == 1 && (x.Base == ESP || x.Base == RSP || x.Base == 0 && inst.Mode == 64) {
if x.Base == 0 {
return seg + disp
}
return fmt.Sprintf("%s%s(%s)", seg, disp, gccRegName[x.Base])
}
base := gccRegName[x.Base]
if x.Base == 0 {
base = ""
}
index := gccRegName[x.Index]
if x.Index == 0 {
if inst.AddrSize == 64 {
index = "%riz"
} else {
index = "%eiz"
}
}
if AX <= x.Base && x.Base <= DI {
// 16-bit addressing - no scale
return fmt.Sprintf("%s%s(%s,%s)", seg, disp, base, index)
}
return fmt.Sprintf("%s%s(%s,%s,%d)", seg, disp, base, index, x.Scale)
case Rel:
if pc == 0 {
return fmt.Sprintf(".%+#x", int64(x))
} else {
addr := pc + uint64(inst.Len) + uint64(x)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s", s)
} else {
addr := pc + uint64(inst.Len) + uint64(x)
return fmt.Sprintf("%#x", addr)
}
}
case Imm:
if s, base := symname(uint64(x)); s != "" {
suffix := ""
if uint64(x) != base {
suffix = fmt.Sprintf("%+d", uint64(x)-base)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(x))
}
return fmt.Sprintf("$%#x", int64(x))
}
return x.String()
}
var gccRegName = [...]string{
0: "REG0",
AL: "%al",
CL: "%cl",
BL: "%bl",
DL: "%dl",
AH: "%ah",
CH: "%ch",
BH: "%bh",
DH: "%dh",
SPB: "%spl",
BPB: "%bpl",
SIB: "%sil",
DIB: "%dil",
R8B: "%r8b",
R9B: "%r9b",
R10B: "%r10b",
R11B: "%r11b",
R12B: "%r12b",
R13B: "%r13b",
R14B: "%r14b",
R15B: "%r15b",
AX: "%ax",
CX: "%cx",
BX: "%bx",
DX: "%dx",
SP: "%sp",
BP: "%bp",
SI: "%si",
DI: "%di",
R8W: "%r8w",
R9W: "%r9w",
R10W: "%r10w",
R11W: "%r11w",
R12W: "%r12w",
R13W: "%r13w",
R14W: "%r14w",
R15W: "%r15w",
EAX: "%eax",
ECX: "%ecx",
EDX: "%edx",
EBX: "%ebx",
ESP: "%esp",
EBP: "%ebp",
ESI: "%esi",
EDI: "%edi",
R8L: "%r8d",
R9L: "%r9d",
R10L: "%r10d",
R11L: "%r11d",
R12L: "%r12d",
R13L: "%r13d",
R14L: "%r14d",
R15L: "%r15d",
RAX: "%rax",
RCX: "%rcx",
RDX: "%rdx",
RBX: "%rbx",
RSP: "%rsp",
RBP: "%rbp",
RSI: "%rsi",
RDI: "%rdi",
R8: "%r8",
R9: "%r9",
R10: "%r10",
R11: "%r11",
R12: "%r12",
R13: "%r13",
R14: "%r14",
R15: "%r15",
IP: "%ip",
EIP: "%eip",
RIP: "%rip",
F0: "%st",
F1: "%st(1)",
F2: "%st(2)",
F3: "%st(3)",
F4: "%st(4)",
F5: "%st(5)",
F6: "%st(6)",
F7: "%st(7)",
M0: "%mm0",
M1: "%mm1",
M2: "%mm2",
M3: "%mm3",
M4: "%mm4",
M5: "%mm5",
M6: "%mm6",
M7: "%mm7",
X0: "%xmm0",
X1: "%xmm1",
X2: "%xmm2",
X3: "%xmm3",
X4: "%xmm4",
X5: "%xmm5",
X6: "%xmm6",
X7: "%xmm7",
X8: "%xmm8",
X9: "%xmm9",
X10: "%xmm10",
X11: "%xmm11",
X12: "%xmm12",
X13: "%xmm13",
X14: "%xmm14",
X15: "%xmm15",
CS: "%cs",
SS: "%ss",
DS: "%ds",
ES: "%es",
FS: "%fs",
GS: "%gs",
GDTR: "%gdtr",
IDTR: "%idtr",
LDTR: "%ldtr",
MSW: "%msw",
TASK: "%task",
CR0: "%cr0",
CR1: "%cr1",
CR2: "%cr2",
CR3: "%cr3",
CR4: "%cr4",
CR5: "%cr5",
CR6: "%cr6",
CR7: "%cr7",
CR8: "%cr8",
CR9: "%cr9",
CR10: "%cr10",
CR11: "%cr11",
CR12: "%cr12",
CR13: "%cr13",
CR14: "%cr14",
CR15: "%cr15",
DR0: "%db0",
DR1: "%db1",
DR2: "%db2",
DR3: "%db3",
DR4: "%db4",
DR5: "%db5",
DR6: "%db6",
DR7: "%db7",
TR0: "%tr0",
TR1: "%tr1",
TR2: "%tr2",
TR3: "%tr3",
TR4: "%tr4",
TR5: "%tr5",
TR6: "%tr6",
TR7: "%tr7",
}
var gnuOp = map[Op]string{
CBW: "cbtw",
CDQ: "cltd",
CMPSD: "cmpsl",
CMPSD_XMM: "cmpsd",
CWD: "cwtd",
CWDE: "cwtl",
CQO: "cqto",
INSD: "insl",
IRET: "iretw",
IRETD: "iret",
IRETQ: "iretq",
LODSB: "lods",
LODSD: "lods",
LODSQ: "lods",
LODSW: "lods",
MOVSD: "movsl",
MOVSD_XMM: "movsd",
OUTSD: "outsl",
POPA: "popaw",
POPAD: "popa",
POPF: "popfw",
POPFD: "popf",
PUSHA: "pushaw",
PUSHAD: "pusha",
PUSHF: "pushfw",
PUSHFD: "pushf",
SCASB: "scas",
SCASD: "scas",
SCASQ: "scas",
SCASW: "scas",
STOSB: "stos",
STOSD: "stos",
STOSQ: "stos",
STOSW: "stos",
XLATB: "xlat",
}
var cmppsOps = []string{
"cmpeq",
"cmplt",
"cmple",
"cmpunord",
"cmpneq",
"cmpnlt",
"cmpnle",
"cmpord",
}
var pclmulqOps = []string{
"pclmullqlqdq",
"pclmulhqlqdq",
"pclmullqhqdq",
"pclmulhqhqdq",
}
func countPrefix(inst *Inst, target Prefix) int {
n := 0
for _, p := range inst.Prefix {
if p&0xFF == target&0xFF {
n++
}
}
return n
}
func markLastImplicit(inst *Inst, prefix Prefix) bool {
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i]
if p&0xFF == prefix {
inst.Prefix[i] |= PrefixImplicit
return true
}
}
return false
}
func unmarkImplicit(inst *Inst, prefix Prefix) {
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i]
if p&0xFF == prefix {
inst.Prefix[i] &^= PrefixImplicit
}
}
}
func byteSizeSuffix(b int) string {
switch b {
case 1:
return "b"
case 2:
return "w"
case 4:
return "l"
case 8:
return "q"
}
return ""
}
func argBytes(inst *Inst, arg Arg) int {
if isMem(arg) {
return inst.MemBytes
}
return regBytes(arg)
}
func isFloat(op Op) bool {
switch op {
case FADD, FCOM, FCOMP, FDIV, FDIVR, FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FIST, FISTP, FISTTP, FISUB, FISUBR, FLD, FMUL, FST, FSTP, FSUB, FSUBR:
return true
}
return false
}
func isFloatInt(op Op) bool {
switch op {
case FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FIST, FISTP, FISTTP, FISUB, FISUBR:
return true
}
return false
}
@@ -0,0 +1,649 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package x86asm implements decoding of x86 machine code.
package x86asm
import (
"bytes"
"fmt"
)
// An Inst is a single instruction.
type Inst struct {
Prefix Prefixes // Prefixes applied to the instruction.
Op Op // Opcode mnemonic
Opcode uint32 // Encoded opcode bits, left aligned (first byte is Opcode>>24, etc)
Args Args // Instruction arguments, in Intel order
Mode int // processor mode in bits: 16, 32, or 64
AddrSize int // address size in bits: 16, 32, or 64
DataSize int // operand size in bits: 16, 32, or 64
MemBytes int // size of memory argument in bytes: 1, 2, 4, 8, 16, and so on.
Len int // length of encoded instruction in bytes
PCRel int // length of PC-relative address in instruction encoding
PCRelOff int // index of start of PC-relative address in instruction encoding
}
// Prefixes is an array of prefixes associated with a single instruction.
// The prefixes are listed in the same order as found in the instruction:
// each prefix byte corresponds to one slot in the array. The first zero
// in the array marks the end of the prefixes.
type Prefixes [14]Prefix
// A Prefix represents an Intel instruction prefix.
// The low 8 bits are the actual prefix byte encoding,
// and the top 8 bits contain distinguishing bits and metadata.
type Prefix uint16
const (
// Metadata about the role of a prefix in an instruction.
PrefixImplicit Prefix = 0x8000 // prefix is implied by instruction text
PrefixIgnored Prefix = 0x4000 // prefix is ignored: either irrelevant or overridden by a later prefix
PrefixInvalid Prefix = 0x2000 // prefix makes entire instruction invalid (bad LOCK)
// Memory segment overrides.
PrefixES Prefix = 0x26 // ES segment override
PrefixCS Prefix = 0x2E // CS segment override
PrefixSS Prefix = 0x36 // SS segment override
PrefixDS Prefix = 0x3E // DS segment override
PrefixFS Prefix = 0x64 // FS segment override
PrefixGS Prefix = 0x65 // GS segment override
// Branch prediction.
PrefixPN Prefix = 0x12E // predict not taken (conditional branch only)
PrefixPT Prefix = 0x13E // predict taken (conditional branch only)
// Size attributes.
PrefixDataSize Prefix = 0x66 // operand size override
PrefixData16 Prefix = 0x166
PrefixData32 Prefix = 0x266
PrefixAddrSize Prefix = 0x67 // address size override
PrefixAddr16 Prefix = 0x167
PrefixAddr32 Prefix = 0x267
// One of a kind.
PrefixLOCK Prefix = 0xF0 // lock
PrefixREPN Prefix = 0xF2 // repeat not zero
PrefixXACQUIRE Prefix = 0x1F2
PrefixBND Prefix = 0x2F2
PrefixREP Prefix = 0xF3 // repeat
PrefixXRELEASE Prefix = 0x1F3
// The REX prefixes must be in the range [PrefixREX, PrefixREX+0x10).
// the other bits are set or not according to the intended use.
PrefixREX Prefix = 0x40 // REX 64-bit extension prefix
PrefixREXW Prefix = 0x08 // extension bit W (64-bit instruction width)
PrefixREXR Prefix = 0x04 // extension bit R (r field in modrm)
PrefixREXX Prefix = 0x02 // extension bit X (index field in sib)
PrefixREXB Prefix = 0x01 // extension bit B (r/m field in modrm or base field in sib)
PrefixVEX2Bytes Prefix = 0xC5 // Short form of vex prefix
PrefixVEX3Bytes Prefix = 0xC4 // Long form of vex prefix
)
// IsREX reports whether p is a REX prefix byte.
func (p Prefix) IsREX() bool {
return p&0xF0 == PrefixREX
}
func (p Prefix) IsVEX() bool {
return p&0xFF == PrefixVEX2Bytes || p&0xFF == PrefixVEX3Bytes
}
func (p Prefix) String() string {
p &^= PrefixImplicit | PrefixIgnored | PrefixInvalid
if s := prefixNames[p]; s != "" {
return s
}
if p.IsREX() {
s := "REX."
if p&PrefixREXW != 0 {
s += "W"
}
if p&PrefixREXR != 0 {
s += "R"
}
if p&PrefixREXX != 0 {
s += "X"
}
if p&PrefixREXB != 0 {
s += "B"
}
return s
}
return fmt.Sprintf("Prefix(%#x)", int(p))
}
// An Op is an x86 opcode.
type Op uint32
func (op Op) String() string {
i := int(op)
if i < 0 || i >= len(opNames) || opNames[i] == "" {
return fmt.Sprintf("Op(%d)", i)
}
return opNames[i]
}
// An Args holds the instruction arguments.
// If an instruction has fewer than 4 arguments,
// the final elements in the array are nil.
type Args [4]Arg
// An Arg is a single instruction argument,
// one of these types: Reg, Mem, Imm, Rel.
type Arg interface {
String() string
isArg()
}
// Note that the implements of Arg that follow are all sized
// so that on a 64-bit machine the data can be inlined in
// the interface value instead of requiring an allocation.
// A Reg is a single register.
// The zero Reg value has no name but indicates “no register.”
type Reg uint8
const (
_ Reg = iota
// 8-bit
AL
CL
DL
BL
AH
CH
DH
BH
SPB
BPB
SIB
DIB
R8B
R9B
R10B
R11B
R12B
R13B
R14B
R15B
// 16-bit
AX
CX
DX
BX
SP
BP
SI
DI
R8W
R9W
R10W
R11W
R12W
R13W
R14W
R15W
// 32-bit
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
R8L
R9L
R10L
R11L
R12L
R13L
R14L
R15L
// 64-bit
RAX
RCX
RDX
RBX
RSP
RBP
RSI
RDI
R8
R9
R10
R11
R12
R13
R14
R15
// Instruction pointer.
IP // 16-bit
EIP // 32-bit
RIP // 64-bit
// 387 floating point registers.
F0
F1
F2
F3
F4
F5
F6
F7
// MMX registers.
M0
M1
M2
M3
M4
M5
M6
M7
// XMM registers.
X0
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
X11
X12
X13
X14
X15
// Segment registers.
ES
CS
SS
DS
FS
GS
// System registers.
GDTR
IDTR
LDTR
MSW
TASK
// Control registers.
CR0
CR1
CR2
CR3
CR4
CR5
CR6
CR7
CR8
CR9
CR10
CR11
CR12
CR13
CR14
CR15
// Debug registers.
DR0
DR1
DR2
DR3
DR4
DR5
DR6
DR7
DR8
DR9
DR10
DR11
DR12
DR13
DR14
DR15
// Task registers.
TR0
TR1
TR2
TR3
TR4
TR5
TR6
TR7
)
const regMax = TR7
func (Reg) isArg() {}
func (r Reg) String() string {
i := int(r)
if i < 0 || i >= len(regNames) || regNames[i] == "" {
return fmt.Sprintf("Reg(%d)", i)
}
return regNames[i]
}
// A Mem is a memory reference.
// The general form is Segment:[Base+Scale*Index+Disp].
type Mem struct {
Segment Reg
Base Reg
Scale uint8
Index Reg
Disp int64
}
func (Mem) isArg() {}
func (m Mem) String() string {
var base, plus, scale, index, disp string
if m.Base != 0 {
base = m.Base.String()
}
if m.Scale != 0 {
if m.Base != 0 {
plus = "+"
}
if m.Scale > 1 {
scale = fmt.Sprintf("%d*", m.Scale)
}
index = m.Index.String()
}
if m.Disp != 0 || m.Base == 0 && m.Scale == 0 {
disp = fmt.Sprintf("%+#x", m.Disp)
}
return "[" + base + plus + scale + index + disp + "]"
}
// A Rel is an offset relative to the current instruction pointer.
type Rel int32
func (Rel) isArg() {}
func (r Rel) String() string {
return fmt.Sprintf(".%+d", r)
}
// An Imm is an integer constant.
type Imm int64
func (Imm) isArg() {}
func (i Imm) String() string {
return fmt.Sprintf("%#x", int64(i))
}
func (i Inst) String() string {
var buf bytes.Buffer
for _, p := range i.Prefix {
if p == 0 {
break
}
if p&PrefixImplicit != 0 {
continue
}
fmt.Fprintf(&buf, "%v ", p)
}
fmt.Fprintf(&buf, "%v", i.Op)
sep := " "
for _, v := range i.Args {
if v == nil {
break
}
fmt.Fprintf(&buf, "%s%v", sep, v)
sep = ", "
}
return buf.String()
}
func isReg(a Arg) bool {
_, ok := a.(Reg)
return ok
}
func isSegReg(a Arg) bool {
r, ok := a.(Reg)
return ok && ES <= r && r <= GS
}
func isMem(a Arg) bool {
_, ok := a.(Mem)
return ok
}
func isImm(a Arg) bool {
_, ok := a.(Imm)
return ok
}
func regBytes(a Arg) int {
r, ok := a.(Reg)
if !ok {
return 0
}
if AL <= r && r <= R15B {
return 1
}
if AX <= r && r <= R15W {
return 2
}
if EAX <= r && r <= R15L {
return 4
}
if RAX <= r && r <= R15 {
return 8
}
return 0
}
func isSegment(p Prefix) bool {
switch p {
case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
return true
}
return false
}
// The Op definitions and string list are in tables.go.
var prefixNames = map[Prefix]string{
PrefixCS: "CS",
PrefixDS: "DS",
PrefixES: "ES",
PrefixFS: "FS",
PrefixGS: "GS",
PrefixSS: "SS",
PrefixLOCK: "LOCK",
PrefixREP: "REP",
PrefixREPN: "REPN",
PrefixAddrSize: "ADDRSIZE",
PrefixDataSize: "DATASIZE",
PrefixAddr16: "ADDR16",
PrefixData16: "DATA16",
PrefixAddr32: "ADDR32",
PrefixData32: "DATA32",
PrefixBND: "BND",
PrefixXACQUIRE: "XACQUIRE",
PrefixXRELEASE: "XRELEASE",
PrefixREX: "REX",
PrefixPT: "PT",
PrefixPN: "PN",
}
var regNames = [...]string{
AL: "AL",
CL: "CL",
BL: "BL",
DL: "DL",
AH: "AH",
CH: "CH",
BH: "BH",
DH: "DH",
SPB: "SPB",
BPB: "BPB",
SIB: "SIB",
DIB: "DIB",
R8B: "R8B",
R9B: "R9B",
R10B: "R10B",
R11B: "R11B",
R12B: "R12B",
R13B: "R13B",
R14B: "R14B",
R15B: "R15B",
AX: "AX",
CX: "CX",
BX: "BX",
DX: "DX",
SP: "SP",
BP: "BP",
SI: "SI",
DI: "DI",
R8W: "R8W",
R9W: "R9W",
R10W: "R10W",
R11W: "R11W",
R12W: "R12W",
R13W: "R13W",
R14W: "R14W",
R15W: "R15W",
EAX: "EAX",
ECX: "ECX",
EDX: "EDX",
EBX: "EBX",
ESP: "ESP",
EBP: "EBP",
ESI: "ESI",
EDI: "EDI",
R8L: "R8L",
R9L: "R9L",
R10L: "R10L",
R11L: "R11L",
R12L: "R12L",
R13L: "R13L",
R14L: "R14L",
R15L: "R15L",
RAX: "RAX",
RCX: "RCX",
RDX: "RDX",
RBX: "RBX",
RSP: "RSP",
RBP: "RBP",
RSI: "RSI",
RDI: "RDI",
R8: "R8",
R9: "R9",
R10: "R10",
R11: "R11",
R12: "R12",
R13: "R13",
R14: "R14",
R15: "R15",
IP: "IP",
EIP: "EIP",
RIP: "RIP",
F0: "F0",
F1: "F1",
F2: "F2",
F3: "F3",
F4: "F4",
F5: "F5",
F6: "F6",
F7: "F7",
M0: "M0",
M1: "M1",
M2: "M2",
M3: "M3",
M4: "M4",
M5: "M5",
M6: "M6",
M7: "M7",
X0: "X0",
X1: "X1",
X2: "X2",
X3: "X3",
X4: "X4",
X5: "X5",
X6: "X6",
X7: "X7",
X8: "X8",
X9: "X9",
X10: "X10",
X11: "X11",
X12: "X12",
X13: "X13",
X14: "X14",
X15: "X15",
CS: "CS",
SS: "SS",
DS: "DS",
ES: "ES",
FS: "FS",
GS: "GS",
GDTR: "GDTR",
IDTR: "IDTR",
LDTR: "LDTR",
MSW: "MSW",
TASK: "TASK",
CR0: "CR0",
CR1: "CR1",
CR2: "CR2",
CR3: "CR3",
CR4: "CR4",
CR5: "CR5",
CR6: "CR6",
CR7: "CR7",
CR8: "CR8",
CR9: "CR9",
CR10: "CR10",
CR11: "CR11",
CR12: "CR12",
CR13: "CR13",
CR14: "CR14",
CR15: "CR15",
DR0: "DR0",
DR1: "DR1",
DR2: "DR2",
DR3: "DR3",
DR4: "DR4",
DR5: "DR5",
DR6: "DR6",
DR7: "DR7",
DR8: "DR8",
DR9: "DR9",
DR10: "DR10",
DR11: "DR11",
DR12: "DR12",
DR13: "DR13",
DR14: "DR14",
DR15: "DR15",
TR0: "TR0",
TR1: "TR1",
TR2: "TR2",
TR3: "TR3",
TR4: "TR4",
TR5: "TR5",
TR6: "TR6",
TR7: "TR7",
}
@@ -0,0 +1,20 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"strings"
"testing"
)
func TestRegString(t *testing.T) {
for r := Reg(1); r <= regMax; r++ {
if regNames[r] == "" {
t.Errorf("regNames[%d] is missing", int(r))
} else if s := r.String(); strings.Contains(s, "Reg(") {
t.Errorf("Reg(%d).String() = %s, want proper name", int(r), s)
}
}
}
@@ -0,0 +1,560 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"fmt"
"strings"
)
// IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var iargs []Arg
for _, a := range inst.Args {
if a == nil {
break
}
iargs = append(iargs, a)
}
switch inst.Op {
case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, LOOPNE, JCXZ, JECXZ, JRCXZ, LOOP, LOOPE, MOV, XLATB:
if inst.Op == MOV && (inst.Opcode>>16)&0xFFFC != 0x0F20 {
break
}
for i, p := range inst.Prefix {
if p&0xFF == PrefixAddrSize {
inst.Prefix[i] &^= PrefixImplicit
}
}
}
switch inst.Op {
case MOV:
dst, _ := inst.Args[0].(Reg)
src, _ := inst.Args[1].(Reg)
if ES <= dst && dst <= GS && EAX <= src && src <= R15L {
src -= EAX - AX
iargs[1] = src
}
if ES <= dst && dst <= GS && RAX <= src && src <= R15 {
src -= RAX - AX
iargs[1] = src
}
if inst.Opcode>>24&^3 == 0xA0 {
for i, p := range inst.Prefix {
if p&0xFF == PrefixAddrSize {
inst.Prefix[i] |= PrefixImplicit
}
}
}
}
switch inst.Op {
case AAM, AAD:
if imm, ok := iargs[0].(Imm); ok {
if inst.DataSize == 32 {
iargs[0] = Imm(uint32(int8(imm)))
} else if inst.DataSize == 16 {
iargs[0] = Imm(uint16(int8(imm)))
}
}
case PUSH:
if imm, ok := iargs[0].(Imm); ok {
iargs[0] = Imm(uint32(imm))
}
}
for _, p := range inst.Prefix {
if p&PrefixImplicit != 0 {
for j, pj := range inst.Prefix {
if pj&0xFF == p&0xFF {
inst.Prefix[j] |= PrefixImplicit
}
}
}
}
if inst.Op != 0 {
for i, p := range inst.Prefix {
switch p &^ PrefixIgnored {
case PrefixData16, PrefixData32, PrefixCS, PrefixDS, PrefixES, PrefixSS:
inst.Prefix[i] |= PrefixImplicit
}
if p.IsREX() {
inst.Prefix[i] |= PrefixImplicit
}
if p.IsVEX() {
if p == PrefixVEX3Bytes {
inst.Prefix[i+2] |= PrefixImplicit
}
inst.Prefix[i] |= PrefixImplicit
inst.Prefix[i+1] |= PrefixImplicit
}
}
}
if isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ {
for i, p := range inst.Prefix {
if p == PrefixPT || p == PrefixPN {
inst.Prefix[i] |= PrefixImplicit
}
}
}
switch inst.Op {
case AAA, AAS, CBW, CDQE, CLC, CLD, CLI, CLTS, CMC, CPUID, CQO, CWD, DAA, DAS,
FDECSTP, FINCSTP, FNCLEX, FNINIT, FNOP, FWAIT, HLT,
ICEBP, INSB, INSD, INSW, INT, INTO, INVD, IRET, IRETQ,
LAHF, LEAVE, LRET, MONITOR, MWAIT, NOP, OUTSB, OUTSD, OUTSW,
PAUSE, POPA, POPF, POPFQ, PUSHA, PUSHF, PUSHFQ,
RDMSR, RDPMC, RDTSC, RDTSCP, RET, RSM,
SAHF, STC, STD, STI, SYSENTER, SYSEXIT, SYSRET,
UD2, WBINVD, WRMSR, XEND, XLATB, XTEST:
if inst.Op == NOP && inst.Opcode>>24 != 0x90 {
break
}
if inst.Op == RET && inst.Opcode>>24 != 0xC3 {
break
}
if inst.Op == INT && inst.Opcode>>24 != 0xCC {
break
}
if inst.Op == LRET && inst.Opcode>>24 != 0xcb {
break
}
for i, p := range inst.Prefix {
if p&0xFF == PrefixDataSize {
inst.Prefix[i] &^= PrefixImplicit | PrefixIgnored
}
}
case 0:
// ok
}
switch inst.Op {
case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, MONITOR, MWAIT, XLATB:
iargs = nil
case STOSB, STOSW, STOSD, STOSQ:
iargs = iargs[:1]
case LODSB, LODSW, LODSD, LODSQ, SCASB, SCASW, SCASD, SCASQ:
iargs = iargs[1:]
}
const (
haveData16 = 1 << iota
haveData32
haveAddr16
haveAddr32
haveXacquire
haveXrelease
haveLock
haveHintTaken
haveHintNotTaken
haveBnd
)
var prefixBits uint32
prefix := ""
for _, p := range inst.Prefix {
if p == 0 {
break
}
if p&0xFF == 0xF3 {
prefixBits &^= haveBnd
}
if p&(PrefixImplicit|PrefixIgnored) != 0 {
continue
}
switch p {
default:
prefix += strings.ToLower(p.String()) + " "
case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
if inst.Op == 0 {
prefix += strings.ToLower(p.String()) + " "
}
case PrefixREPN:
prefix += "repne "
case PrefixLOCK:
prefixBits |= haveLock
case PrefixData16, PrefixDataSize:
prefixBits |= haveData16
case PrefixData32:
prefixBits |= haveData32
case PrefixAddrSize, PrefixAddr16:
prefixBits |= haveAddr16
case PrefixAddr32:
prefixBits |= haveAddr32
case PrefixXACQUIRE:
prefixBits |= haveXacquire
case PrefixXRELEASE:
prefixBits |= haveXrelease
case PrefixPT:
prefixBits |= haveHintTaken
case PrefixPN:
prefixBits |= haveHintNotTaken
case PrefixBND:
prefixBits |= haveBnd
}
}
switch inst.Op {
case JMP:
if inst.Opcode>>24 == 0xEB {
prefixBits &^= haveBnd
}
case RET, LRET:
prefixBits &^= haveData16 | haveData32
}
if prefixBits&haveXacquire != 0 {
prefix += "xacquire "
}
if prefixBits&haveXrelease != 0 {
prefix += "xrelease "
}
if prefixBits&haveLock != 0 {
prefix += "lock "
}
if prefixBits&haveBnd != 0 {
prefix += "bnd "
}
if prefixBits&haveHintTaken != 0 {
prefix += "hint-taken "
}
if prefixBits&haveHintNotTaken != 0 {
prefix += "hint-not-taken "
}
if prefixBits&haveAddr16 != 0 {
prefix += "addr16 "
}
if prefixBits&haveAddr32 != 0 {
prefix += "addr32 "
}
if prefixBits&haveData16 != 0 {
prefix += "data16 "
}
if prefixBits&haveData32 != 0 {
prefix += "data32 "
}
if inst.Op == 0 {
if prefix == "" {
return "<no instruction>"
}
return prefix[:len(prefix)-1]
}
var args []string
for _, a := range iargs {
if a == nil {
break
}
args = append(args, intelArg(&inst, pc, symname, a))
}
var op string
switch inst.Op {
case NOP:
if inst.Opcode>>24 == 0x0F {
if inst.DataSize == 16 {
args = append(args, "ax")
} else {
args = append(args, "eax")
}
}
case BLENDVPD, BLENDVPS, PBLENDVB:
args = args[:2]
case INT:
if inst.Opcode>>24 == 0xCC {
args = nil
op = "int3"
}
case LCALL, LJMP:
if len(args) == 2 {
args[0], args[1] = args[1], args[0]
}
case FCHS, FABS, FTST, FLDPI, FLDL2E, FLDLG2, F2XM1, FXAM, FLD1, FLDL2T, FSQRT, FRNDINT, FCOS, FSIN:
if len(args) == 0 {
args = append(args, "st0")
}
case FPTAN, FSINCOS, FUCOMPP, FCOMPP, FYL2X, FPATAN, FXTRACT, FPREM1, FPREM, FYL2XP1, FSCALE:
if len(args) == 0 {
args = []string{"st0", "st1"}
}
case FST, FSTP, FISTTP, FIST, FISTP, FBSTP:
if len(args) == 1 {
args = append(args, "st0")
}
case FLD, FXCH, FCOM, FCOMP, FIADD, FIMUL, FICOM, FICOMP, FISUBR, FIDIV, FUCOM, FUCOMP, FILD, FBLD, FADD, FMUL, FSUB, FSUBR, FISUB, FDIV, FDIVR, FIDIVR:
if len(args) == 1 {
args = []string{"st0", args[0]}
}
case MASKMOVDQU, MASKMOVQ, XLATB, OUTSB, OUTSW, OUTSD:
FixSegment:
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i] & 0xFF
switch p {
case PrefixCS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
if inst.Mode != 64 || p == PrefixFS || p == PrefixGS {
args = append(args, strings.ToLower((inst.Prefix[i] & 0xFF).String()))
break FixSegment
}
case PrefixDS:
if inst.Mode != 64 {
break FixSegment
}
}
}
}
if op == "" {
op = intelOp[inst.Op]
}
if op == "" {
op = strings.ToLower(inst.Op.String())
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return prefix + op
}
func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
switch a := arg.(type) {
case Imm:
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
suffix = fmt.Sprintf("%+d", uint64(a)-base)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("%#x", uint32(a))
}
if Imm(int32(a)) == a {
return fmt.Sprintf("%#x", int64(a))
}
return fmt.Sprintf("%#x", uint64(a))
case Mem:
if a.Base == EIP {
a.Base = RIP
}
prefix := ""
switch inst.MemBytes {
case 1:
prefix = "byte "
case 2:
prefix = "word "
case 4:
prefix = "dword "
case 8:
prefix = "qword "
case 16:
prefix = "xmmword "
case 32:
prefix = "ymmword "
}
switch inst.Op {
case INVLPG:
prefix = "byte "
case STOSB, MOVSB, CMPSB, LODSB, SCASB:
prefix = "byte "
case STOSW, MOVSW, CMPSW, LODSW, SCASW:
prefix = "word "
case STOSD, MOVSD, CMPSD, LODSD, SCASD:
prefix = "dword "
case STOSQ, MOVSQ, CMPSQ, LODSQ, SCASQ:
prefix = "qword "
case LAR:
prefix = "word "
case BOUND:
if inst.Mode == 32 {
prefix = "qword "
} else {
prefix = "dword "
}
case PREFETCHW, PREFETCHNTA, PREFETCHT0, PREFETCHT1, PREFETCHT2, CLFLUSH:
prefix = "zmmword "
}
switch inst.Op {
case MOVSB, MOVSW, MOVSD, MOVSQ, CMPSB, CMPSW, CMPSD, CMPSQ, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ, LODSB, LODSW, LODSD, LODSQ:
switch a.Base {
case DI, EDI, RDI:
if a.Segment == ES {
a.Segment = 0
}
case SI, ESI, RSI:
if a.Segment == DS {
a.Segment = 0
}
}
case LEA:
a.Segment = 0
default:
switch a.Base {
case SP, ESP, RSP, BP, EBP, RBP:
if a.Segment == SS {
a.Segment = 0
}
default:
if a.Segment == DS {
a.Segment = 0
}
}
}
if inst.Mode == 64 && a.Segment != FS && a.Segment != GS {
a.Segment = 0
}
prefix += "ptr "
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return prefix + fmt.Sprintf("[%s%s]", s, suffix)
}
if a.Segment != 0 {
prefix += strings.ToLower(a.Segment.String()) + ":"
}
prefix += "["
if a.Base != 0 {
prefix += intelArg(inst, pc, symname, a.Base)
}
if a.Scale != 0 && a.Index != 0 {
if a.Base != 0 {
prefix += "+"
}
prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale)
}
if a.Disp != 0 {
if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
prefix += fmt.Sprintf("%#x", uint64(a.Disp))
} else {
prefix += fmt.Sprintf("%+#x", a.Disp)
}
}
prefix += "]"
return prefix
case Rel:
if pc == 0 {
return fmt.Sprintf(".%+#x", int64(a))
} else {
addr := pc + uint64(inst.Len) + uint64(a)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s", s)
} else {
addr := pc + uint64(inst.Len) + uint64(a)
return fmt.Sprintf("%#x", addr)
}
}
case Reg:
if int(a) < len(intelReg) && intelReg[a] != "" {
switch inst.Op {
case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ:
return strings.Replace(intelReg[a], "xmm", "ymm", -1)
default:
return intelReg[a]
}
}
}
return strings.ToLower(arg.String())
}
var intelOp = map[Op]string{
JAE: "jnb",
JA: "jnbe",
JGE: "jnl",
JNE: "jnz",
JG: "jnle",
JE: "jz",
SETAE: "setnb",
SETA: "setnbe",
SETGE: "setnl",
SETNE: "setnz",
SETG: "setnle",
SETE: "setz",
CMOVAE: "cmovnb",
CMOVA: "cmovnbe",
CMOVGE: "cmovnl",
CMOVNE: "cmovnz",
CMOVG: "cmovnle",
CMOVE: "cmovz",
LCALL: "call far",
LJMP: "jmp far",
LRET: "ret far",
ICEBP: "int1",
MOVSD_XMM: "movsd",
XLATB: "xlat",
}
var intelReg = [...]string{
F0: "st0",
F1: "st1",
F2: "st2",
F3: "st3",
F4: "st4",
F5: "st5",
F6: "st6",
F7: "st7",
M0: "mmx0",
M1: "mmx1",
M2: "mmx2",
M3: "mmx3",
M4: "mmx4",
M5: "mmx5",
M6: "mmx6",
M7: "mmx7",
X0: "xmm0",
X1: "xmm1",
X2: "xmm2",
X3: "xmm3",
X4: "xmm4",
X5: "xmm5",
X6: "xmm6",
X7: "xmm7",
X8: "xmm8",
X9: "xmm9",
X10: "xmm10",
X11: "xmm11",
X12: "xmm12",
X13: "xmm13",
X14: "xmm14",
X15: "xmm15",
// TODO: Maybe the constants are named wrong.
SPB: "spl",
BPB: "bpl",
SIB: "sil",
DIB: "dil",
R8L: "r8d",
R9L: "r9d",
R10L: "r10d",
R11L: "r11d",
R12L: "r12d",
R13L: "r13d",
R14L: "r14d",
R15L: "r15d",
}
@@ -0,0 +1,384 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"strings"
"testing"
)
func TestObjdump32Manual(t *testing.T) { testObjdump32(t, hexCases(t, objdumpManualTests)) }
func TestObjdump32Testdata(t *testing.T) { testObjdump32(t, concat(basicPrefixes, testdataCases(t))) }
func TestObjdump32ModRM(t *testing.T) { testObjdump32(t, concat(basicPrefixes, enumModRM)) }
func TestObjdump32OneByte(t *testing.T) { testBasic(t, testObjdump32) }
func TestObjdump320F(t *testing.T) { testBasic(t, testObjdump32, 0x0F) }
func TestObjdump320F38(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x38) }
func TestObjdump320F3A(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x3A) }
func TestObjdump32Prefix(t *testing.T) { testPrefix(t, testObjdump32) }
func TestObjdump64Manual(t *testing.T) { testObjdump64(t, hexCases(t, objdumpManualTests)) }
func TestObjdump64Testdata(t *testing.T) { testObjdump64(t, concat(basicPrefixes, testdataCases(t))) }
func TestObjdump64ModRM(t *testing.T) { testObjdump64(t, concat(basicPrefixes, enumModRM)) }
func TestObjdump64OneByte(t *testing.T) { testBasic(t, testObjdump64) }
func TestObjdump640F(t *testing.T) { testBasic(t, testObjdump64, 0x0F) }
func TestObjdump640F38(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x38) }
func TestObjdump640F3A(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x3A) }
func TestObjdump64Prefix(t *testing.T) { testPrefix(t, testObjdump64) }
func TestObjdump64REXTestdata(t *testing.T) {
testObjdump64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestObjdump64REXModRM(t *testing.T) {
testObjdump64(t, concat3(basicPrefixes, rexPrefixes, enumModRM))
}
func TestObjdump64REXOneByte(t *testing.T) { testBasicREX(t, testObjdump64) }
func TestObjdump64REX0F(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F) }
func TestObjdump64REX0F38(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x38) }
func TestObjdump64REX0F3A(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x3A) }
func TestObjdump64REXPrefix(t *testing.T) { testPrefixREX(t, testObjdump64) }
// objdumpManualTests holds test cases that will be run by TestObjdumpManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=ObjdumpManual, particularly with tracing enabled.
var objdumpManualTests = `
4883FE017413
488DFC2500000000
488D3D00000000
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
if size == 15 && dec.nenc == 15 && contains(text, "truncated") && contains(dec.text, "(bad)") {
return true
}
if i := strings.LastIndex(dec.text, " "); isPrefix(dec.text[i+1:]) && size == 1 && isPrefix(text) {
return true
}
if size == dec.nenc && contains(dec.text, "movupd") && contains(dec.text, "data32") {
s := strings.Replace(dec.text, "data32 ", "", -1)
if text == s {
return true
}
}
// Simplify our invalid instruction text.
if text == "error: unrecognized instruction" {
text = "BAD"
}
// Invalid instructions for which libopcodes prints %? register.
// FF E8 11 22 33 44:
// Invalid instructions for which libopcodes prints "internal disassembler error".
// Invalid instructions for which libopcodes prints 8087 only (e.g., DB E0)
// or prints 287 only (e.g., DB E4).
if contains(dec.text, "%?", "<internal disassembler error>", "(8087 only)", "(287 only)") {
dec.text = "(bad)"
}
// 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: libopcodes says nop,
// but the Intel manuals say that the only NOP there is 0F 1F /0.
// Perhaps libopcodes is reporting an older encoding.
i := bytes.IndexByte(dec.enc[:], 0x0F)
if contains(dec.text, "nop") && i >= 0 && i+2 < len(dec.enc) && dec.enc[i+1]&^7 == 0x18 && (dec.enc[i+1] != 0x1F || (dec.enc[i+2]>>3)&7 != 0) {
dec.text = "(bad)"
}
// Any invalid instruction.
if text == "BAD" && contains(dec.text, "(bad)") {
return true
}
// Instructions libopcodes knows but we do not (e.g., 0F 19 11).
if (text == "BAD" || size == 1 && isPrefix(text)) && hasPrefix(dec.text, unsupported...) {
return true
}
// Instructions we know but libopcodes does not (e.g., 0F D0 11).
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && hasPrefix(text, libopcodesUnsupported...) {
return true
}
// Libopcodes rejects F2 90 as NOP. Not sure why.
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && inst.Opcode>>24 == 0x90 && countPrefix(inst, 0xF2) > 0 {
return true
}
// 0F 20 11, 0F 21 11, 0F 22 11, 0F 23 11, 0F 24 11:
// Moves into and out of some control registers seem to be unsupported by libopcodes.
// TODO(rsc): Are they invalid somehow?
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && contains(text, "%cr", "%db", "%tr") {
return true
}
if contains(dec.text, "fwait") && dec.nenc == 1 && dec.enc[0] != 0x9B {
return true
}
// 9B D9 11: libopcodes reports FSTSW instead of FWAIT + FNSTSW.
// This is correct in that FSTSW is a pseudo-op for the pair, but it really
// is a pair of instructions: execution can stop between them.
// Our decoder chooses to separate them.
if (text == "fwait" || strings.HasSuffix(text, " fwait")) && dec.nenc >= len(strings.Fields(text)) && dec.enc[len(strings.Fields(text))-1] == 0x9B {
return true
}
// 0F 18 77 11:
// Invalid instructions for which libopcodes prints "nop/reserved".
// Perhaps libopcodes is reporting an older encoding.
if text == "BAD" && contains(dec.text, "nop/reserved") {
return true
}
// 0F C7 B0 11 22 33 44: libopcodes says vmptrld 0x44332211(%eax); we say rdrand %eax.
// TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual.
if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") {
return true
}
// DD C8: libopcodes says FNOP but the Intel manual is clear FNOP is only D9 D0.
// Perhaps libopcodes is reporting an older encoding.
if text == "BAD" && contains(dec.text, "fnop") && (dec.enc[0] != 0xD9 || dec.enc[1] != 0xD0) {
return true
}
// 66 90: libopcodes says xchg %ax,%ax; we say 'data16 nop'.
// The 16-bit swap will preserve the high bits of the register,
// so they are the same.
if contains(text, "nop") && contains(dec.text, "xchg %ax,%ax") {
return true
}
// If there are multiple prefixes, allow libopcodes to use an alternate name.
if size == 1 && dec.nenc == 1 && prefixByte[text] > 0 && prefixByte[text] == prefixByte[dec.text] {
return true
}
// 26 9B: libopcodes reports "fwait"/1, ignoring segment prefix.
// https://sourceware.org/bugzilla/show_bug.cgi?id=16891
// F0 82: Decode="lock"/1 but libopcodes="lock (bad)"/2.
if size == 1 && dec.nenc >= 1 && prefixByte[text] == dec.enc[0] && contains(dec.text, "(bad)", "fwait", "fnop") {
return true
}
// libopcodes interprets 660f801122 as taking a rel16 but
// truncating the address at 16 bits. Not sure what is correct.
if contains(text, ".+0x2211", ".+0x11") && contains(dec.text, " .-") {
return true
}
// 66 F3 0F D6 C5, 66 F2 0F D6 C0: libopcodes reports use of XMM register instead of MMX register,
// but only when the instruction has a 66 prefix. Maybe they know something we don't.
if countPrefix(inst, 0x66) > 0 && contains(dec.text, "movdq2q", "movq2dq") && !contains(dec.text, "%mm") {
return true
}
// 0F 01 F8, 0F 05, 0F 07: these are 64-bit instructions but libopcodes accepts them.
if (text == "BAD" || size == 1 && isPrefix(text)) && contains(dec.text, "swapgs", "syscall", "sysret", "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") {
return true
}
return false
}
// Instructions known to libopcodes (or xed) but not to us.
// Most of these come from supplementary manuals of one form or another.
var unsupported = strings.Fields(`
bndc
bndl
bndm
bnds
clac
clgi
femms
fldln
getsec
invlpga
kmov
montmul
pavg
pf2i
pfacc
pfadd
pfcmp
pfmax
pfmin
pfmul
pfna
pfpnac
pfrc
pfrs
pfsub
phadd
phsub
pi2f
pmulhr
prefetch
pswap
ptest
rdseed
sha1
sha256
skinit
stac
stgi
vadd
vand
vcmp
vcomis
vcvt
vcvt
vdiv
vhadd
vhsub
vld
vmax
vmcall
vmfunc
vmin
vmlaunch
vmload
vmmcall
vmov
vmov
vmov
vmptrld
vmptrst
vmread
vmresume
vmrun
vmsave
vmul
vmwrite
vmxoff
vor
vpack
vpadd
vpand
vpavg
vpcmp
vpcmp
vpins
vpmadd
vpmax
vpmin
vpmul
vpmul
vpor
vpsad
vpshuf
vpsll
vpsra
vpsrad
vpsrl
vpsub
vpunp
vpxor
vrcp
vrsqrt
vshuf
vsqrt
vsub
vucomis
vunp
vxor
vzero
xcrypt
xsha1
xsha256
xstore-rng
insertq
extrq
vmclear
invvpid
adox
vmxon
invept
adcx
vmclear
prefetchwt1
enclu
encls
salc
fstpnce
fdisi8087_nop
fsetpm287_nop
feni8087_nop
syscall
sysret
`)
// Instructions known to us but not to libopcodes (at least in binutils 2.24).
var libopcodesUnsupported = strings.Fields(`
addsubps
aes
blend
cvttpd2dq
dpp
extract
haddps
hsubps
insert
invpcid
lddqu
movmsk
movnt
movq2dq
mps
pack
pblend
pclmul
pcmp
pext
phmin
pins
pmax
pmin
pmov
pmovmsk
pmul
popcnt
pslld
psllq
psllw
psrad
psraw
psrl
ptest
punpck
round
xrstor
xsavec
xsaves
comis
ucomis
movhps
movntps
rsqrt
rcpp
puncpck
bsf
movq2dq
cvttpd2dq
movq
hsubpd
movdqa
movhpd
addsubpd
movd
haddpd
cvtps2dq
bsr
cvtdq2ps
rdrand
maskmov
movq2dq
movlhps
movbe
movlpd
`)
@@ -0,0 +1,313 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
// Apologies for the proprietary path, but we need objdump 2.24 + some committed patches that will land in 2.25.
const objdumpPath = "/Users/rsc/bin/objdump2"
func testObjdump32(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, 32)
}
func testObjdump64(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, 64)
}
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch int) {
if testing.Short() {
t.Skip("skipping objdump test in short mode")
}
if _, err := os.Stat(objdumpPath); err != nil {
t.Skip(err)
}
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if ext.Arch == 32 {
if err := writeELF32(ext.File, ext.Size); err != nil {
return err
}
} else {
if err := writeELF64(ext.File, ext.Size); err != nil {
return err
}
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint32 = start
addr uint32
encbuf [32]byte
enc []byte
text string
)
flush := func() {
if addr == next {
switch text {
case "repz":
text = "rep"
case "repnz":
text = "repn"
default:
text = strings.Replace(text, "repz ", "rep ", -1)
text = strings.Replace(text, "repnz ", "repn ", -1)
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
text = strings.Replace(text, "0x0(", "(", -1)
text = strings.Replace(text, "%st(0)", "%st", -1)
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok := parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = trimSpace(line[i:])
if i := bytes.IndexByte(line, '#'); i >= 0 {
line = trimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF32 writes an ELF32 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF32(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header32
var prog elf.Prog32
var sect elf.Section32
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header32{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
Type: 2,
Machine: uint16(elf.EM_386),
Version: 1,
Entry: start,
Phoff: uint32(off1),
Shoff: uint32(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog32{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint32(size),
Memsz: uint32(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section32{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint32(size),
Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section32{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint32(off2 + (off3-off2)*3),
Size: uint32(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
// writeELF64 writes an ELF64 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF64(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header64
var prog elf.Prog64
var sect elf.Section64
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header64{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
Type: 2,
Machine: uint16(elf.EM_X86_64),
Version: 1,
Entry: start,
Phoff: uint64(off1),
Shoff: uint64(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog64{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint64(size),
Memsz: uint64(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section64{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint64(size),
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section64{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint64(off2 + (off3-off2)*3),
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
@@ -0,0 +1,119 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"testing"
)
const plan9Path = "testdata/libmach8db"
func testPlan9Arch(t *testing.T, arch int, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping libmach test in short mode")
}
if _, err := os.Stat(plan9Path); err != nil {
t.Skip(err)
}
testExtDis(t, "plan9", arch, plan9, generate, allowedMismatchPlan9)
}
func testPlan932(t *testing.T, generate func(func([]byte))) {
testPlan9Arch(t, 32, generate)
}
func testPlan964(t *testing.T, generate func(func([]byte))) {
testPlan9Arch(t, 64, generate)
}
func plan9(ext *ExtDis) error {
flag := "-8"
if ext.Arch == 64 {
flag = "-6"
}
b, err := ext.Run(plan9Path, flag, ext.File.Name())
if err != nil {
return err
}
nmatch := 0
next := uint32(start)
var (
addr uint32
encbuf [32]byte
enc []byte
text string
)
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading libmach8db output: %v", err)
}
if debug {
os.Stdout.Write(line)
}
nmatch++
addr, enc, text = parseLinePlan9(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
if addr < next {
continue
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
func parseLinePlan9(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
i := bytes.IndexByte(line, ' ')
if i < 0 || line[0] != '0' || line[1] != 'x' {
log.Fatalf("cannot parse disassembly: %q", line)
}
j := bytes.IndexByte(line[i+1:], ' ')
if j < 0 {
log.Fatalf("cannot parse disassembly: %q", line)
}
j += i + 1
x, err := strconv.ParseUint(string(trimSpace(line[2:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", line)
}
addr = uint32(x)
enc, ok := parseHex(line[i+1:j], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", line)
}
return addr, enc, string(fixSpace(line[j+1:]))
}
@@ -0,0 +1,392 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"fmt"
"strings"
)
type SymLookup func(uint64) (string, uint64)
// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The pc is the program counter of the instruction, used for expanding
// PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name and base
// address of the symbol containing the target, if any; otherwise it returns "", 0.
func GoSyntax(inst Inst, pc uint64, symname SymLookup) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var args []string
for i := len(inst.Args) - 1; i >= 0; i-- {
a := inst.Args[i]
if a == nil {
continue
}
args = append(args, plan9Arg(&inst, pc, symname, a))
}
var rep string
var last Prefix
for _, p := range inst.Prefix {
if p == 0 || p.IsREX() || p.IsVEX() {
break
}
switch {
// Don't show prefixes implied by the instruction text.
case p&0xFF00 == PrefixImplicit:
continue
// Only REP and REPN are recognized repeaters. Plan 9 syntax
// treats them as separate opcodes.
case p&0xFF == PrefixREP:
rep = "REP; "
case p&0xFF == PrefixREPN:
rep = "REPNE; "
default:
last = p
}
}
prefix := ""
switch last & 0xFF {
case 0, 0x66, 0x67:
// ignore
default:
prefix += last.String() + " "
}
op := inst.Op.String()
if plan9Suffix[inst.Op] {
s := inst.DataSize
if inst.MemBytes != 0 {
s = inst.MemBytes * 8
} else if inst.Args[1] == nil { // look for register-only 64-bit instruction, like PUSHQ AX
if r, ok := inst.Args[0].(Reg); ok && RAX <= r && r <= R15 {
s = 64
}
}
switch s {
case 8:
op += "B"
case 16:
op += "W"
case 32:
op += "L"
case 64:
op += "Q"
}
}
if inst.Op == CMP {
// Use reads-left-to-right ordering for comparisons.
// See issue 60920.
args[0], args[1] = args[1], args[0]
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return rep + prefix + op
}
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
switch a := arg.(type) {
case Reg:
return plan9Reg[a]
case Rel:
if pc == 0 {
break
}
// If the absolute address is the start of a symbol, use the name.
// Otherwise use the raw address, so that things like relative
// jumps show up as JMP 0x123 instead of JMP f+10(SB).
// It is usually easier to search for 0x123 than to do the mental
// arithmetic to find f+10.
addr := pc + uint64(inst.Len) + uint64(a)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s(SB)", s)
}
return fmt.Sprintf("%#x", addr)
case Imm:
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
suffix = fmt.Sprintf("%+d", uint64(a)-base)
}
return fmt.Sprintf("$%s%s(SB)", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(a))
}
if Imm(int32(a)) == a {
return fmt.Sprintf("$%#x", int64(a))
}
return fmt.Sprintf("$%#x", uint64(a))
case Mem:
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return fmt.Sprintf("%s%s(SB)", s, suffix)
}
s := ""
if a.Segment != 0 {
s += fmt.Sprintf("%s:", plan9Reg[a.Segment])
}
if a.Disp != 0 {
s += fmt.Sprintf("%#x", a.Disp)
} else {
s += "0"
}
if a.Base != 0 {
s += fmt.Sprintf("(%s)", plan9Reg[a.Base])
}
if a.Index != 0 && a.Scale != 0 {
s += fmt.Sprintf("(%s*%d)", plan9Reg[a.Index], a.Scale)
}
return s
}
return arg.String()
}
func memArgToSymbol(a Mem, pc uint64, instrLen int, symname SymLookup) (string, int64) {
if a.Segment != 0 || a.Disp == 0 || a.Index != 0 || a.Scale != 0 {
return "", 0
}
var disp uint64
switch a.Base {
case IP, EIP, RIP:
disp = uint64(a.Disp + int64(pc) + int64(instrLen))
case 0:
disp = uint64(a.Disp)
default:
return "", 0
}
s, base := symname(disp)
return s, int64(disp) - int64(base)
}
var plan9Suffix = [maxOp + 1]bool{
ADC: true,
ADD: true,
AND: true,
BSF: true,
BSR: true,
BT: true,
BTC: true,
BTR: true,
BTS: true,
CMP: true,
CMPXCHG: true,
CVTSI2SD: true,
CVTSI2SS: true,
CVTSD2SI: true,
CVTSS2SI: true,
CVTTSD2SI: true,
CVTTSS2SI: true,
DEC: true,
DIV: true,
FLDENV: true,
FRSTOR: true,
IDIV: true,
IMUL: true,
IN: true,
INC: true,
LEA: true,
MOV: true,
MOVNTI: true,
MUL: true,
NEG: true,
NOP: true,
NOT: true,
OR: true,
OUT: true,
POP: true,
POPA: true,
POPCNT: true,
PUSH: true,
PUSHA: true,
RCL: true,
RCR: true,
ROL: true,
ROR: true,
SAR: true,
SBB: true,
SHL: true,
SHLD: true,
SHR: true,
SHRD: true,
SUB: true,
TEST: true,
XADD: true,
XCHG: true,
XOR: true,
}
var plan9Reg = [...]string{
AL: "AL",
CL: "CL",
BL: "BL",
DL: "DL",
AH: "AH",
CH: "CH",
BH: "BH",
DH: "DH",
SPB: "SP",
BPB: "BP",
SIB: "SI",
DIB: "DI",
R8B: "R8",
R9B: "R9",
R10B: "R10",
R11B: "R11",
R12B: "R12",
R13B: "R13",
R14B: "R14",
R15B: "R15",
AX: "AX",
CX: "CX",
BX: "BX",
DX: "DX",
SP: "SP",
BP: "BP",
SI: "SI",
DI: "DI",
R8W: "R8",
R9W: "R9",
R10W: "R10",
R11W: "R11",
R12W: "R12",
R13W: "R13",
R14W: "R14",
R15W: "R15",
EAX: "AX",
ECX: "CX",
EDX: "DX",
EBX: "BX",
ESP: "SP",
EBP: "BP",
ESI: "SI",
EDI: "DI",
R8L: "R8",
R9L: "R9",
R10L: "R10",
R11L: "R11",
R12L: "R12",
R13L: "R13",
R14L: "R14",
R15L: "R15",
RAX: "AX",
RCX: "CX",
RDX: "DX",
RBX: "BX",
RSP: "SP",
RBP: "BP",
RSI: "SI",
RDI: "DI",
R8: "R8",
R9: "R9",
R10: "R10",
R11: "R11",
R12: "R12",
R13: "R13",
R14: "R14",
R15: "R15",
IP: "IP",
EIP: "IP",
RIP: "IP",
F0: "F0",
F1: "F1",
F2: "F2",
F3: "F3",
F4: "F4",
F5: "F5",
F6: "F6",
F7: "F7",
M0: "M0",
M1: "M1",
M2: "M2",
M3: "M3",
M4: "M4",
M5: "M5",
M6: "M6",
M7: "M7",
X0: "X0",
X1: "X1",
X2: "X2",
X3: "X3",
X4: "X4",
X5: "X5",
X6: "X6",
X7: "X7",
X8: "X8",
X9: "X9",
X10: "X10",
X11: "X11",
X12: "X12",
X13: "X13",
X14: "X14",
X15: "X15",
CS: "CS",
SS: "SS",
DS: "DS",
ES: "ES",
FS: "FS",
GS: "GS",
GDTR: "GDTR",
IDTR: "IDTR",
LDTR: "LDTR",
MSW: "MSW",
TASK: "TASK",
CR0: "CR0",
CR1: "CR1",
CR2: "CR2",
CR3: "CR3",
CR4: "CR4",
CR5: "CR5",
CR6: "CR6",
CR7: "CR7",
CR8: "CR8",
CR9: "CR9",
CR10: "CR10",
CR11: "CR11",
CR12: "CR12",
CR13: "CR13",
CR14: "CR14",
CR15: "CR15",
DR0: "DR0",
DR1: "DR1",
DR2: "DR2",
DR3: "DR3",
DR4: "DR4",
DR5: "DR5",
DR6: "DR6",
DR7: "DR7",
DR8: "DR8",
DR9: "DR9",
DR10: "DR10",
DR11: "DR11",
DR12: "DR12",
DR13: "DR13",
DR14: "DR14",
DR15: "DR15",
TR0: "TR0",
TR1: "TR1",
TR2: "TR2",
TR3: "TR3",
TR4: "TR4",
TR5: "TR5",
TR6: "TR6",
TR7: "TR7",
}
@@ -0,0 +1,56 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"strings"
"testing"
)
func TestPlan932Manual(t *testing.T) { testPlan932(t, hexCases(t, plan9ManualTests)) }
func TestPlan932Testdata(t *testing.T) { testPlan932(t, concat(basicPrefixes, testdataCases(t))) }
func TestPlan932ModRM(t *testing.T) { testPlan932(t, concat(basicPrefixes, enumModRM)) }
func TestPlan932OneByte(t *testing.T) { testBasic(t, testPlan932) }
func TestPlan9320F(t *testing.T) { testBasic(t, testPlan932, 0x0F) }
func TestPlan9320F38(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x38) }
func TestPlan9320F3A(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x3A) }
func TestPlan932Prefix(t *testing.T) { testPrefix(t, testPlan932) }
func TestPlan964Manual(t *testing.T) { testPlan964(t, hexCases(t, plan9ManualTests)) }
func TestPlan964Testdata(t *testing.T) { testPlan964(t, concat(basicPrefixes, testdataCases(t))) }
func TestPlan964ModRM(t *testing.T) { testPlan964(t, concat(basicPrefixes, enumModRM)) }
func TestPlan964OneByte(t *testing.T) { testBasic(t, testPlan964) }
func TestPlan9640F(t *testing.T) { testBasic(t, testPlan964, 0x0F) }
func TestPlan9640F38(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x38) }
func TestPlan9640F3A(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x3A) }
func TestPlan964Prefix(t *testing.T) { testPrefix(t, testPlan964) }
func TestPlan964REXTestdata(t *testing.T) {
testPlan964(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestPlan964REXModRM(t *testing.T) {
testPlan964(t, concat3(basicPrefixes, rexPrefixes, enumModRM))
}
func TestPlan964REXOneByte(t *testing.T) { testBasicREX(t, testPlan964) }
func TestPlan964REX0F(t *testing.T) { testBasicREX(t, testPlan964, 0x0F) }
func TestPlan964REX0F38(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x38) }
func TestPlan964REX0F3A(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x3A) }
func TestPlan964REXPrefix(t *testing.T) { testPrefixREX(t, testPlan964) }
// plan9ManualTests holds test cases that will be run by TestPlan9Manual32 and TestPlan9Manual64.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Plan9Manual, particularly with tracing enabled.
var plan9ManualTests = `
`
// allowedMismatchPlan9 reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchPlan9(text string, size int, inst *Inst, dec ExtInst) bool {
return false
}
// Instructions known to us but not to plan9.
var plan9Unsupported = strings.Fields(`
`)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
libmach8db: libmach8db.c
9c libmach8db.c && 9l -o libmach8db libmach8db.o; rm libmach8db.o
newdecode.txt:
cd ..; go test -cover -run 'Objdump.*32' -v -timeout 10h -printtests 2>&1 | tee log
cd ..; go test -cover -run 'Objdump.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Xed.*32' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Xed.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Plan9.*32' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Plan9.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
egrep ' (gnu|intel|plan9) ' ../log |sort >newdecode.txt
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,211 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"strings"
"testing"
)
func TestXed32Manual(t *testing.T) { testXed32(t, hexCases(t, xedManualTests)) }
func TestXed32Testdata(t *testing.T) { testXed32(t, concat(basicPrefixes, testdataCases(t))) }
func TestXed32ModRM(t *testing.T) { testXed32(t, concat(basicPrefixes, enumModRM)) }
func TestXed32OneByte(t *testing.T) { testBasic(t, testXed32) }
func TestXed320F(t *testing.T) { testBasic(t, testXed32, 0x0F) }
func TestXed320F38(t *testing.T) { testBasic(t, testXed32, 0x0F, 0x38) }
func TestXed320F3A(t *testing.T) { testBasic(t, testXed32, 0x0F, 0x3A) }
func TestXed32Prefix(t *testing.T) { testPrefix(t, testXed32) }
func TestXed64Manual(t *testing.T) { testXed64(t, hexCases(t, xedManualTests)) }
func TestXed64Testdata(t *testing.T) { testXed64(t, concat(basicPrefixes, testdataCases(t))) }
func TestXed64ModRM(t *testing.T) { testXed64(t, concat(basicPrefixes, enumModRM)) }
func TestXed64OneByte(t *testing.T) { testBasic(t, testXed64) }
func TestXed640F(t *testing.T) { testBasic(t, testXed64, 0x0F) }
func TestXed640F38(t *testing.T) { testBasic(t, testXed64, 0x0F, 0x38) }
func TestXed640F3A(t *testing.T) { testBasic(t, testXed64, 0x0F, 0x3A) }
func TestXed64Prefix(t *testing.T) { testPrefix(t, testXed64) }
func TestXed64REXTestdata(t *testing.T) {
testXed64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestXed64REXModRM(t *testing.T) { testXed64(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) }
func TestXed64REXOneByte(t *testing.T) { testBasicREX(t, testXed64) }
func TestXed64REX0F(t *testing.T) { testBasicREX(t, testXed64, 0x0F) }
func TestXed64REX0F38(t *testing.T) { testBasicREX(t, testXed64, 0x0F, 0x38) }
func TestXed64REX0F3A(t *testing.T) { testBasicREX(t, testXed64, 0x0F, 0x3A) }
func TestXed64REXPrefix(t *testing.T) { testPrefixREX(t, testXed64) }
// xedManualTests holds test cases that will be run by TestXedManual32 and TestXedManual64.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=XedManual, particularly with tracing enabled.
var xedManualTests = `
6690
`
// allowedMismatchXed reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchXed(text string, size int, inst *Inst, dec ExtInst) bool {
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "GENERAL_ERROR", "INSTR_TOO_LONG", "BAD_LOCK_PREFIX") {
return true
}
if contains(dec.text, "BAD_LOCK_PREFIX") && countExactPrefix(inst, PrefixLOCK|PrefixInvalid) > 0 {
return true
}
if contains(dec.text, "BAD_LOCK_PREFIX", "GENERAL_ERROR") && countExactPrefix(inst, PrefixLOCK|PrefixImplicit) > 0 {
return true
}
if text == "lock" && size == 1 && contains(dec.text, "BAD_LOCK_PREFIX") {
return true
}
// Instructions not known to us.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, unsupported...) {
return true
}
// Instructions not known to xed.
if contains(text, xedUnsupported...) && contains(dec.text, "ERROR") {
return true
}
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "shl ") && (inst.Opcode>>16)&0xEC38 == 0xC030 {
return true
}
// 82 11 22: xed says 'adc byte ptr [ecx], 0x22' but there is no justification in the manuals for that.
// C0 30 11: xed says 'shl byte ptr [eax], 0x11' but there is no justification in the manuals for that.
// F6 08 11: xed says 'test byte ptr [eax], 0x11' but there is no justification in the manuals for that.
if (contains(text, "error:") || isPrefix(text) && size == 1) && hasByte(dec.enc[:dec.nenc], 0x82, 0xC0, 0xC1, 0xD0, 0xD1, 0xD2, 0xD3, 0xF6, 0xF7) {
return true
}
// F3 11 22 and many others: xed allows and drops misused rep/repn prefix.
if (text == "rep" && dec.enc[0] == 0xF3 || (text == "repn" || text == "repne") && dec.enc[0] == 0xF2) && (!contains(dec.text, "ins", "outs", "movs", "lods", "cmps", "scas") || contains(dec.text, "xmm")) {
return true
}
// 0F C7 30: xed says vmptrld qword ptr [eax]; we say rdrand eax.
// TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual.
if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") {
return true
}
// F3 0F AE 00: we say 'rdfsbase dword ptr [eax]' but RDFSBASE needs a register.
// Also, this is a 64-bit only instruction.
// TODO(rsc): Fix to reject this encoding.
if contains(text, "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") && contains(dec.text, "ERROR") {
return true
}
// 0F 01 F8: we say swapgs but that's only valid in 64-bit mode.
// TODO(rsc): Fix.
if contains(text, "swapgs") {
return true
}
// 0F 24 11: 'mov ecx, tr2' except there is no TR2.
// Or maybe the MOV to TR registers doesn't use RMF.
if contains(text, "cr1", "cr5", "cr6", "cr7", "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7") && contains(dec.text, "ERROR") {
return true
}
// 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: xed says nop,
// but the Intel manuals say that the only NOP there is 0F 1F /0.
// Perhaps xed is reporting an older encoding.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "nop ") && (inst.Opcode>>8)&0xFFFF38 != 0x0F1F00 {
return true
}
// 66 0F AE 38: clflushopt but we only know clflush
if contains(text, "clflush") && contains(dec.text, "clflushopt") {
return true
}
// 0F 20 04 11: MOV SP, CR0 but has mod!=3 despite register argument.
// (This encoding ignores the mod bits.) The decoder sees the non-register
// mod and reads farther ahead to decode the memory reference that
// isn't really there, causing the size to be too large.
// TODO(rsc): Fix.
if text == dec.text && size > dec.nenc && contains(text, " cr", " dr", " tr") {
return true
}
// 0F AE E9: xed says lfence, which is wrong (only 0F AE E8 is lfence). And so on.
if contains(dec.text, "fence") && hasByte(dec.enc[:dec.nenc], 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF) {
return true
}
// DD C9, DF C9: xed says 'fxch st0, st1' but that instruction is D9 C9.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fxch ") && hasByte(dec.enc[:dec.nenc], 0xDD, 0xDF) {
return true
}
// DC D4: xed says 'fcom st0, st4' but that instruction is D8 D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fcom ") && hasByte(dec.enc[:dec.nenc], 0xD8, 0xDC) {
return true
}
// DE D4: xed says 'fcomp st0, st4' but that instruction is D8 D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fcomp ") && hasByte(dec.enc[:dec.nenc], 0xDC, 0xDE) {
return true
}
// DF D4: xed says 'fstp st4, st0' but that instruction is DD D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fstp ") && hasByte(dec.enc[:dec.nenc], 0xDF) {
return true
}
return false
}
func countExactPrefix(inst *Inst, target Prefix) int {
n := 0
for _, p := range inst.Prefix {
if p == target {
n++
}
}
return n
}
func hasByte(src []byte, target ...byte) bool {
for _, b := range target {
if bytes.IndexByte(src, b) >= 0 {
return true
}
}
return false
}
// Instructions known to us but not to xed.
var xedUnsupported = strings.Fields(`
xrstor
xsave
xsave
ud1
xgetbv
xsetbv
fxsave
fxrstor
clflush
lfence
mfence
sfence
rsqrtps
rcpps
emms
ldmxcsr
stmxcsr
movhpd
movnti
rdrand
movbe
movlpd
sysret
`)
@@ -0,0 +1,205 @@
package x86asm
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
// xed binary from Intel sde-external-6.22.0-2014-03-06.
const xedPath = "/Users/rsc/bin/xed"
func testXedArch(t *testing.T, arch int, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping xed test in short mode")
}
if _, err := os.Stat(xedPath); err != nil {
t.Skip(err)
}
testExtDis(t, "intel", arch, xed, generate, allowedMismatchXed)
}
func testXed32(t *testing.T, generate func(func([]byte))) {
testXedArch(t, 32, generate)
}
func testXed64(t *testing.T, generate func(func([]byte))) {
testXedArch(t, 64, generate)
}
func xed(ext *ExtDis) error {
b, err := ext.Run(xedPath, fmt.Sprintf("-%d", ext.Arch), "-n", "1G", "-ir", ext.File.Name())
if err != nil {
return err
}
nmatch := 0
next := uint32(start)
var (
addr uint32
encbuf [32]byte
enc []byte
text string
)
var xedEnd = []byte("# end of text section")
var xedEnd1 = []byte("# Errors")
eof := false
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if debug {
os.Stdout.Write(line)
}
if bytes.HasPrefix(line, xedEnd) || bytes.HasPrefix(line, xedEnd1) {
eof = true
}
if eof {
continue
}
nmatch++
addr, enc, text = parseLineXed(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
if addr < next {
continue
}
switch text {
case "repz":
text = "rep"
case "repnz":
text = "repn"
default:
text = strings.Replace(text, "repz ", "rep ", -1)
text = strings.Replace(text, "repnz ", "repn ", -1)
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
xedInRaw = []byte("In raw...")
xedDots = []byte("...")
xdis = []byte("XDIS ")
xedError = []byte("ERROR: ")
xedNoDecode = []byte("Could not decode at offset: 0x")
)
func parseLineXed(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
if bytes.HasPrefix(line, xedInRaw) || bytes.HasPrefix(line, xedDots) {
return 0, nil, ""
}
if bytes.HasPrefix(line, xedError) {
i := bytes.IndexByte(line[len(xedError):], ' ')
if i < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
errstr := string(line[len(xedError):])
i = bytes.Index(line, xedNoDecode)
if i < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
i += len(xedNoDecode)
j := bytes.IndexByte(line[i:], ' ')
if j < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[i:i+j])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
return addr, nil, errstr
}
if !bytes.HasPrefix(line, xdis) {
log.Fatalf("cannot parse disassembly: %q", oline)
}
i := bytes.IndexByte(line, ':')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[len(xdis):i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
// spaces
i++
for i < len(line) && line[i] == ' ' {
i++
}
// instruction class, spaces
for i < len(line) && line[i] != ' ' {
i++
}
for i < len(line) && line[i] == ' ' {
i++
}
// instruction set, spaces
for i < len(line) && line[i] != ' ' {
i++
}
for i < len(line) && line[i] == ' ' {
i++
}
// hex
hexStart := i
for i < len(line) && line[i] != ' ' {
i++
}
hexEnd := i
for i < len(line) && line[i] == ' ' {
i++
}
// text
textStart := i
for i < len(line) && line[i] != '\n' {
i++
}
textEnd := i
enc, ok := parseHex(line[hexStart:hexEnd], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
return addr, enc, string(fixSpace(line[textStart:textEnd]))
}
@@ -0,0 +1,913 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"io/ioutil"
"path/filepath"
"regexp"
"strings"
"sync"
"testing"
"golang.org/x/arch/x86/xeddata"
)
func newTestContext(t testing.TB) *context {
ctx := &context{xedPath: filepath.Join("testdata", "xedpath")}
db, err := xeddata.NewDatabase(ctx.xedPath)
if err != nil {
t.Fatalf("open test database: %v", err)
}
ctx.db = db
return ctx
}
func newStringSet(keys ...string) map[string]bool {
set := make(map[string]bool)
for _, k := range keys {
set[k] = true
}
return set
}
func generateToString(t *testing.T) string {
ctx := newTestContext(t)
buildTables(ctx)
var buf bytes.Buffer
writeTables(&buf, ctx)
return buf.String()
}
func TestOutput(t *testing.T) {
// Ytab lists and optabs output checks.
//
// These tests are very fragile.
// Slight changes can invalidate them.
// It is better to keep testCases count at the minimum.
type testCase struct {
opcode string
ytabs string
optabLines string
}
var testCases []testCase
{
opcodeRE := regexp.MustCompile(`as: ([A-Z][A-Z0-9]*)`)
data, err := ioutil.ReadFile(filepath.Join("testdata", "golden.txt"))
if err != nil {
t.Fatalf("read golden file: %v", err)
}
for _, entry := range bytes.Split(data, []byte("======")) {
parts := bytes.Split(entry, []byte("----"))
ytabs := parts[0]
optabLines := parts[1]
opcode := opcodeRE.FindSubmatch(optabLines)[1]
testCases = append(testCases, testCase{
ytabs: strings.TrimSpace(string(ytabs)),
optabLines: strings.TrimSpace(string(optabLines)),
opcode: string(opcode)[len("A"):],
})
}
}
output := generateToString(t)
for _, tc := range testCases {
if !strings.Contains(output, tc.ytabs) {
t.Errorf("%s: ytabs not matched", tc.opcode)
}
if !strings.Contains(output, tc.optabLines) {
t.Errorf("%s: optab lines not matched", tc.opcode)
}
}
}
func TestOutputStability(t *testing.T) {
// Generate output count+1 times and check that every time
// it is exactly the same string.
//
// The output should be deterministic to avoid unwanted diffs
// between each code generation.
const count = 8
want := generateToString(t)
var wg sync.WaitGroup
for i := 0; i < count; i++ {
wg.Add(1)
go func(i int) {
if want != generateToString(t) {
t.Errorf("output #%d mismatches", i)
}
wg.Done()
}(i)
}
wg.Wait()
}
func TestOpcodeCoverage(t *testing.T) {
// Check that generator produces all expected opcodes from testdata files.
// All opcodes are in Go syntax.
// VEX/EVEX opcodes collected from XED-based x86.csv.
expectedOpcodes := newStringSet(
"ANDNL",
"ANDNQ",
"BEXTRL",
"BEXTRQ",
"BLSIL",
"BLSIQ",
"BLSMSKL",
"BLSMSKQ",
"BLSRL",
"BLSRQ",
"BZHIL",
"BZHIQ",
"KADDB",
"KADDD",
"KADDQ",
"KADDW",
"KANDB",
"KANDD",
"KANDNB",
"KANDND",
"KANDNQ",
"KANDNW",
"KANDQ",
"KANDW",
"KMOVB",
"KMOVD",
"KMOVQ",
"KMOVW",
"KNOTB",
"KNOTD",
"KNOTQ",
"KNOTW",
"KORB",
"KORD",
"KORQ",
"KORTESTB",
"KORTESTD",
"KORTESTQ",
"KORTESTW",
"KORW",
"KSHIFTLB",
"KSHIFTLD",
"KSHIFTLQ",
"KSHIFTLW",
"KSHIFTRB",
"KSHIFTRD",
"KSHIFTRQ",
"KSHIFTRW",
"KTESTB",
"KTESTD",
"KTESTQ",
"KTESTW",
"KUNPCKBW",
"KUNPCKDQ",
"KUNPCKWD",
"KXNORB",
"KXNORD",
"KXNORQ",
"KXNORW",
"KXORB",
"KXORD",
"KXORQ",
"KXORW",
"MULXL",
"MULXQ",
"PDEPL",
"PDEPQ",
"PEXTL",
"PEXTQ",
"RORXL",
"RORXQ",
"SARXL",
"SARXQ",
"SHLXL",
"SHLXQ",
"SHRXL",
"SHRXQ",
"V4FMADDPS",
"V4FMADDSS",
"V4FNMADDPS",
"V4FNMADDSS",
"VADDPD",
"VADDPS",
"VADDSD",
"VADDSS",
"VADDSUBPD",
"VADDSUBPS",
"VAESDEC",
"VAESDECLAST",
"VAESENC",
"VAESENCLAST",
"VAESIMC",
"VAESKEYGENASSIST",
"VALIGND",
"VALIGNQ",
"VANDNPD",
"VANDNPS",
"VANDPD",
"VANDPS",
"VBLENDMPD",
"VBLENDMPS",
"VBLENDPD",
"VBLENDPS",
"VBLENDVPD",
"VBLENDVPS",
"VBROADCASTF128",
"VBROADCASTF32X2",
"VBROADCASTF32X4",
"VBROADCASTF32X8",
"VBROADCASTF64X2",
"VBROADCASTF64X4",
"VBROADCASTI128",
"VBROADCASTI32X2",
"VBROADCASTI32X4",
"VBROADCASTI32X8",
"VBROADCASTI64X2",
"VBROADCASTI64X4",
"VBROADCASTSD",
"VBROADCASTSS",
"VCMPPD",
"VCMPPS",
"VCMPSD",
"VCMPSS",
"VCOMISD",
"VCOMISS",
"VCOMPRESSPD",
"VCOMPRESSPS",
"VCVTDQ2PD",
"VCVTDQ2PS",
"VCVTPD2DQ",
"VCVTPD2DQX",
"VCVTPD2DQY",
"VCVTPD2PS",
"VCVTPD2PSX",
"VCVTPD2PSY",
"VCVTPD2QQ",
"VCVTPD2UDQ",
"VCVTPD2UDQX",
"VCVTPD2UDQY",
"VCVTPD2UQQ",
"VCVTPH2PS",
"VCVTPS2DQ",
"VCVTPS2PD",
"VCVTPS2PH",
"VCVTPS2QQ",
"VCVTPS2UDQ",
"VCVTPS2UQQ",
"VCVTQQ2PD",
"VCVTQQ2PS",
"VCVTQQ2PSX",
"VCVTQQ2PSY",
"VCVTSD2SI",
"VCVTSD2SIQ",
"VCVTSD2SS",
"VCVTSD2USIL",
"VCVTSD2USIQ",
"VCVTSI2SDL",
"VCVTSI2SDQ",
"VCVTSI2SSL",
"VCVTSI2SSQ",
"VCVTSS2SD",
"VCVTSS2SI",
"VCVTSS2SIQ",
"VCVTSS2USIL",
"VCVTSS2USIQ",
"VCVTTPD2DQ",
"VCVTTPD2DQX",
"VCVTTPD2DQY",
"VCVTTPD2QQ",
"VCVTTPD2UDQ",
"VCVTTPD2UDQX",
"VCVTTPD2UDQY",
"VCVTTPD2UQQ",
"VCVTTPS2DQ",
"VCVTTPS2QQ",
"VCVTTPS2UDQ",
"VCVTTPS2UQQ",
"VCVTTSD2SI",
"VCVTTSD2SIQ",
"VCVTTSD2USIL",
"VCVTTSD2USIQ",
"VCVTTSS2SI",
"VCVTTSS2SIQ",
"VCVTTSS2USIL",
"VCVTTSS2USIQ",
"VCVTUDQ2PD",
"VCVTUDQ2PS",
"VCVTUQQ2PD",
"VCVTUQQ2PS",
"VCVTUQQ2PSX",
"VCVTUQQ2PSY",
"VCVTUSI2SDL",
"VCVTUSI2SDQ",
"VCVTUSI2SSL",
"VCVTUSI2SSQ",
"VDBPSADBW",
"VDIVPD",
"VDIVPS",
"VDIVSD",
"VDIVSS",
"VDPPD",
"VDPPS",
"VEXP2PD",
"VEXP2PS",
"VEXPANDPD",
"VEXPANDPS",
"VEXTRACTF128",
"VEXTRACTF32X4",
"VEXTRACTF32X8",
"VEXTRACTF64X2",
"VEXTRACTF64X4",
"VEXTRACTI128",
"VEXTRACTI32X4",
"VEXTRACTI32X8",
"VEXTRACTI64X2",
"VEXTRACTI64X4",
"VEXTRACTPS",
"VFIXUPIMMPD",
"VFIXUPIMMPS",
"VFIXUPIMMSD",
"VFIXUPIMMSS",
"VFMADD132PD",
"VFMADD132PS",
"VFMADD132SD",
"VFMADD132SS",
"VFMADD213PD",
"VFMADD213PS",
"VFMADD213SD",
"VFMADD213SS",
"VFMADD231PD",
"VFMADD231PS",
"VFMADD231SD",
"VFMADD231SS",
"VFMADDPD",
"VFMADDPS",
"VFMADDSD",
"VFMADDSS",
"VFMADDSUB132PD",
"VFMADDSUB132PS",
"VFMADDSUB213PD",
"VFMADDSUB213PS",
"VFMADDSUB231PD",
"VFMADDSUB231PS",
"VFMADDSUBPD",
"VFMADDSUBPS",
"VFMSUB132PD",
"VFMSUB132PS",
"VFMSUB132SD",
"VFMSUB132SS",
"VFMSUB213PD",
"VFMSUB213PS",
"VFMSUB213SD",
"VFMSUB213SS",
"VFMSUB231PD",
"VFMSUB231PS",
"VFMSUB231SD",
"VFMSUB231SS",
"VFMSUBADD132PD",
"VFMSUBADD132PS",
"VFMSUBADD213PD",
"VFMSUBADD213PS",
"VFMSUBADD231PD",
"VFMSUBADD231PS",
"VFMSUBADDPD",
"VFMSUBADDPS",
"VFMSUBPD",
"VFMSUBPS",
"VFMSUBSD",
"VFMSUBSS",
"VFNMADD132PD",
"VFNMADD132PS",
"VFNMADD132SD",
"VFNMADD132SS",
"VFNMADD213PD",
"VFNMADD213PS",
"VFNMADD213SD",
"VFNMADD213SS",
"VFNMADD231PD",
"VFNMADD231PS",
"VFNMADD231SD",
"VFNMADD231SS",
"VFNMADDPD",
"VFNMADDPS",
"VFNMADDSD",
"VFNMADDSS",
"VFNMSUB132PD",
"VFNMSUB132PS",
"VFNMSUB132SD",
"VFNMSUB132SS",
"VFNMSUB213PD",
"VFNMSUB213PS",
"VFNMSUB213SD",
"VFNMSUB213SS",
"VFNMSUB231PD",
"VFNMSUB231PS",
"VFNMSUB231SD",
"VFNMSUB231SS",
"VFNMSUBPD",
"VFNMSUBPS",
"VFNMSUBSD",
"VFNMSUBSS",
"VFPCLASSPDX",
"VFPCLASSPDY",
"VFPCLASSPDZ",
"VFPCLASSPSX",
"VFPCLASSPSY",
"VFPCLASSPSZ",
"VFPCLASSSD",
"VFPCLASSSS",
"VGATHERDPD",
"VGATHERDPS",
"VGATHERPF0DPD",
"VGATHERPF0DPS",
"VGATHERPF0QPD",
"VGATHERPF0QPS",
"VGATHERPF1DPD",
"VGATHERPF1DPS",
"VGATHERPF1QPD",
"VGATHERPF1QPS",
"VGATHERQPD",
"VGATHERQPS",
"VGETEXPPD",
"VGETEXPPS",
"VGETEXPSD",
"VGETEXPSS",
"VGETMANTPD",
"VGETMANTPS",
"VGETMANTSD",
"VGETMANTSS",
"VGF2P8AFFINEINVQB",
"VGF2P8AFFINEQB",
"VGF2P8MULB",
"VHADDPD",
"VHADDPS",
"VHSUBPD",
"VHSUBPS",
"VINSERTF128",
"VINSERTF32X4",
"VINSERTF32X8",
"VINSERTF64X2",
"VINSERTF64X4",
"VINSERTI128",
"VINSERTI32X4",
"VINSERTI32X8",
"VINSERTI64X2",
"VINSERTI64X4",
"VINSERTPS",
"VLDDQU",
"VLDMXCSR",
"VMASKMOVDQU",
"VMASKMOVPD",
"VMASKMOVPS",
"VMAXPD",
"VMAXPS",
"VMAXSD",
"VMAXSS",
"VMINPD",
"VMINPS",
"VMINSD",
"VMINSS",
"VMOVAPD",
"VMOVAPS",
"VMOVD",
"VMOVDDUP",
"VMOVDQA",
"VMOVDQA32",
"VMOVDQA64",
"VMOVDQU",
"VMOVDQU16",
"VMOVDQU32",
"VMOVDQU64",
"VMOVDQU8",
"VMOVHLPS",
"VMOVHPD",
"VMOVHPS",
"VMOVLHPS",
"VMOVLPD",
"VMOVLPS",
"VMOVMSKPD",
"VMOVMSKPS",
"VMOVNTDQ",
"VMOVNTDQA",
"VMOVNTPD",
"VMOVNTPS",
"VMOVQ",
"VMOVSD",
"VMOVSHDUP",
"VMOVSLDUP",
"VMOVSS",
"VMOVUPD",
"VMOVUPS",
"VMPSADBW",
"VMULPD",
"VMULPS",
"VMULSD",
"VMULSS",
"VORPD",
"VORPS",
"VP4DPWSSD",
"VP4DPWSSDS",
"VPABSB",
"VPABSD",
"VPABSQ",
"VPABSW",
"VPACKSSDW",
"VPACKSSWB",
"VPACKUSDW",
"VPACKUSWB",
"VPADDB",
"VPADDD",
"VPADDQ",
"VPADDSB",
"VPADDSW",
"VPADDUSB",
"VPADDUSW",
"VPADDW",
"VPALIGNR",
"VPAND",
"VPANDD",
"VPANDN",
"VPANDND",
"VPANDNQ",
"VPANDQ",
"VPAVGB",
"VPAVGW",
"VPBLENDD",
"VPBLENDMB",
"VPBLENDMD",
"VPBLENDMQ",
"VPBLENDMW",
"VPBLENDVB",
"VPBLENDW",
"VPBROADCASTB",
"VPBROADCASTD",
"VPBROADCASTMB2Q",
"VPBROADCASTMW2D",
"VPBROADCASTQ",
"VPBROADCASTW",
"VPCLMULQDQ",
"VPCMPB",
"VPCMPD",
"VPCMPEQB",
"VPCMPEQD",
"VPCMPEQQ",
"VPCMPEQW",
"VPCMPESTRI",
"VPCMPESTRM",
"VPCMPGTB",
"VPCMPGTD",
"VPCMPGTQ",
"VPCMPGTW",
"VPCMPISTRI",
"VPCMPISTRM",
"VPCMPQ",
"VPCMPUB",
"VPCMPUD",
"VPCMPUQ",
"VPCMPUW",
"VPCMPW",
"VPCOMPRESSB",
"VPCOMPRESSD",
"VPCOMPRESSQ",
"VPCOMPRESSW",
"VPCONFLICTD",
"VPCONFLICTQ",
"VPDPBUSD",
"VPDPBUSDS",
"VPDPWSSD",
"VPDPWSSDS",
"VPERM2F128",
"VPERM2I128",
"VPERMB",
"VPERMD",
"VPERMI2B",
"VPERMI2D",
"VPERMI2PD",
"VPERMI2PS",
"VPERMI2Q",
"VPERMI2W",
"VPERMIL2PD",
"VPERMIL2PS",
"VPERMILPD",
"VPERMILPS",
"VPERMPD",
"VPERMPS",
"VPERMQ",
"VPERMT2B",
"VPERMT2D",
"VPERMT2PD",
"VPERMT2PS",
"VPERMT2Q",
"VPERMT2W",
"VPERMW",
"VPEXPANDB",
"VPEXPANDD",
"VPEXPANDQ",
"VPEXPANDW",
"VPEXTRB",
"VPEXTRD",
"VPEXTRQ",
"VPEXTRW",
"VPGATHERDD",
"VPGATHERDQ",
"VPGATHERQD",
"VPGATHERQQ",
"VPHADDD",
"VPHADDSW",
"VPHADDW",
"VPHMINPOSUW",
"VPHSUBD",
"VPHSUBSW",
"VPHSUBW",
"VPINSRB",
"VPINSRD",
"VPINSRQ",
"VPINSRW",
"VPLZCNTD",
"VPLZCNTQ",
"VPMADD52HUQ",
"VPMADD52LUQ",
"VPMADDUBSW",
"VPMADDWD",
"VPMASKMOVD",
"VPMASKMOVQ",
"VPMAXSB",
"VPMAXSD",
"VPMAXSQ",
"VPMAXSW",
"VPMAXUB",
"VPMAXUD",
"VPMAXUQ",
"VPMAXUW",
"VPMINSB",
"VPMINSD",
"VPMINSQ",
"VPMINSW",
"VPMINUB",
"VPMINUD",
"VPMINUQ",
"VPMINUW",
"VPMOVB2M",
"VPMOVD2M",
"VPMOVDB",
"VPMOVDW",
"VPMOVM2B",
"VPMOVM2D",
"VPMOVM2Q",
"VPMOVM2W",
"VPMOVMSKB",
"VPMOVQ2M",
"VPMOVQB",
"VPMOVQD",
"VPMOVQW",
"VPMOVSDB",
"VPMOVSDW",
"VPMOVSQB",
"VPMOVSQD",
"VPMOVSQW",
"VPMOVSWB",
"VPMOVSXBD",
"VPMOVSXBQ",
"VPMOVSXBW",
"VPMOVSXDQ",
"VPMOVSXWD",
"VPMOVSXWQ",
"VPMOVUSDB",
"VPMOVUSDW",
"VPMOVUSQB",
"VPMOVUSQD",
"VPMOVUSQW",
"VPMOVUSWB",
"VPMOVW2M",
"VPMOVWB",
"VPMOVZXBD",
"VPMOVZXBQ",
"VPMOVZXBW",
"VPMOVZXDQ",
"VPMOVZXWD",
"VPMOVZXWQ",
"VPMULDQ",
"VPMULHRSW",
"VPMULHUW",
"VPMULHW",
"VPMULLD",
"VPMULLQ",
"VPMULLW",
"VPMULTISHIFTQB",
"VPMULUDQ",
"VPOPCNTB",
"VPOPCNTD",
"VPOPCNTQ",
"VPOPCNTW",
"VPOR",
"VPORD",
"VPORQ",
"VPROLD",
"VPROLQ",
"VPROLVD",
"VPROLVQ",
"VPRORD",
"VPRORQ",
"VPRORVD",
"VPRORVQ",
"VPSADBW",
"VPSCATTERDD",
"VPSCATTERDQ",
"VPSCATTERQD",
"VPSCATTERQQ",
"VPSHLDD",
"VPSHLDQ",
"VPSHLDVD",
"VPSHLDVQ",
"VPSHLDVW",
"VPSHLDW",
"VPSHRDD",
"VPSHRDQ",
"VPSHRDVD",
"VPSHRDVQ",
"VPSHRDVW",
"VPSHRDW",
"VPSHUFB",
"VPSHUFBITQMB",
"VPSHUFD",
"VPSHUFHW",
"VPSHUFLW",
"VPSIGNB",
"VPSIGND",
"VPSIGNW",
"VPSLLD",
"VPSLLDQ",
"VPSLLQ",
"VPSLLVD",
"VPSLLVQ",
"VPSLLVW",
"VPSLLW",
"VPSRAD",
"VPSRAQ",
"VPSRAVD",
"VPSRAVQ",
"VPSRAVW",
"VPSRAW",
"VPSRLD",
"VPSRLDQ",
"VPSRLQ",
"VPSRLVD",
"VPSRLVQ",
"VPSRLVW",
"VPSRLW",
"VPSUBB",
"VPSUBD",
"VPSUBQ",
"VPSUBSB",
"VPSUBSW",
"VPSUBUSB",
"VPSUBUSW",
"VPSUBW",
"VPTERNLOGD",
"VPTERNLOGQ",
"VPTEST",
"VPTESTMB",
"VPTESTMD",
"VPTESTMQ",
"VPTESTMW",
"VPTESTNMB",
"VPTESTNMD",
"VPTESTNMQ",
"VPTESTNMW",
"VPUNPCKHBW",
"VPUNPCKHDQ",
"VPUNPCKHQDQ",
"VPUNPCKHWD",
"VPUNPCKLBW",
"VPUNPCKLDQ",
"VPUNPCKLQDQ",
"VPUNPCKLWD",
"VPXOR",
"VPXORD",
"VPXORQ",
"VRANGEPD",
"VRANGEPS",
"VRANGESD",
"VRANGESS",
"VRCP14PD",
"VRCP14PS",
"VRCP14SD",
"VRCP14SS",
"VRCP28PD",
"VRCP28PS",
"VRCP28SD",
"VRCP28SS",
"VRCPPS",
"VRCPSS",
"VREDUCEPD",
"VREDUCEPS",
"VREDUCESD",
"VREDUCESS",
"VRNDSCALEPD",
"VRNDSCALEPS",
"VRNDSCALESD",
"VRNDSCALESS",
"VROUNDPD",
"VROUNDPS",
"VROUNDSD",
"VROUNDSS",
"VRSQRT14PD",
"VRSQRT14PS",
"VRSQRT14SD",
"VRSQRT14SS",
"VRSQRT28PD",
"VRSQRT28PS",
"VRSQRT28SD",
"VRSQRT28SS",
"VRSQRTPS",
"VRSQRTSS",
"VSCALEFPD",
"VSCALEFPS",
"VSCALEFSD",
"VSCALEFSS",
"VSCATTERDPD",
"VSCATTERDPS",
"VSCATTERPF0DPD",
"VSCATTERPF0DPS",
"VSCATTERPF0QPD",
"VSCATTERPF0QPS",
"VSCATTERPF1DPD",
"VSCATTERPF1DPS",
"VSCATTERPF1QPD",
"VSCATTERPF1QPS",
"VSCATTERQPD",
"VSCATTERQPS",
"VSHUFF32X4",
"VSHUFF64X2",
"VSHUFI32X4",
"VSHUFI64X2",
"VSHUFPD",
"VSHUFPS",
"VSQRTPD",
"VSQRTPS",
"VSQRTSD",
"VSQRTSS",
"VSTMXCSR",
"VSUBPD",
"VSUBPS",
"VSUBSD",
"VSUBSS",
"VTESTPD",
"VTESTPS",
"VUCOMISD",
"VUCOMISS",
"VUNPCKHPD",
"VUNPCKHPS",
"VUNPCKLPD",
"VUNPCKLPS",
"VXORPD",
"VXORPS",
"VZEROALL",
"VZEROUPPER")
// AMD-specific VEX opcodes.
// Excluded from x86avxgen output for now.
amdOpcodes := newStringSet(
"VFMADDPD",
"VFMADDPS",
"VFMADDSD",
"VFMADDSS",
"VFMADDSUBPD",
"VFMADDSUBPS",
"VFMSUBADDPD",
"VFMSUBADDPS",
"VFMSUBPD",
"VFMSUBPS",
"VFMSUBSD",
"VFMSUBSS",
"VFNMADDPD",
"VFNMADDPS",
"VFNMADDSD",
"VFNMADDSS",
"VFNMSUBPD",
"VFNMSUBPS",
"VFNMSUBSD",
"VFNMSUBSS",
"VPERMIL2PD",
"VPERMIL2PS")
ctx := newTestContext(t)
buildTables(ctx)
for op := range amdOpcodes {
delete(expectedOpcodes, op)
}
for op := range ctx.optabs {
delete(expectedOpcodes, op)
}
for op := range expectedOpcodes {
t.Errorf("missing opcode: %s", op)
}
}
@@ -0,0 +1,395 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"log"
"regexp"
"strings"
"golang.org/x/arch/x86/xeddata"
)
// encoding is decoded XED instruction pattern.
type encoding struct {
// opbyte is opcode byte (one that follows [E]VEX prefix).
// It's called "opcode" in Intel manual, but we use that for
// instruction name (iclass in XED terms).
opbyte string
// opdigit is ModRM.Reg field used to encode opcode extension.
// In Intel manual, "/digit" notation is used.
opdigit string
// vex represents [E]VEX fields that are used in a first [E]VEX
// opBytes element (see prefixExpr function).
vex struct {
P string // 66/F2/F3
L string // 128/256/512
M string // 0F/0F38/0F3A
W string // W0/W1
}
// evexScale is a scaling factor used to calculate compact disp-8.
evexScale string
// evexBcstScale is like evexScale, but used during broadcasting.
// Empty for optab entries that do not have broadcasting support.
evexBcstScale string
// evex describes which features of EVEX can be used by optab entry.
// All flags are "false" for VEX-encoded insts.
evex struct {
// There is no "broadcast" flag because it's inferred
// from non-empty evexBcstScale.
SAE bool // EVEX.b controls SAE for reg-reg insts
Rounding bool // EVEX.b + EVEX.RC (VL) control rounding for FP insts
Zeroing bool // Instruction can use zeroing.
}
}
type decoder struct {
ctx *context
insts []*instruction
}
// decodeGroups fills ctx.groups with decoded instruction groups.
//
// Reads XED objects from ctx.xedPath.
func decodeGroups(ctx *context) {
d := decoder{ctx: ctx}
groups := make(map[string][]*instruction)
for _, inst := range d.DecodeAll() {
groups[inst.opcode] = append(groups[inst.opcode], inst)
}
for op, insts := range groups {
ctx.groups = append(ctx.groups, &instGroup{
opcode: op,
list: insts,
})
}
}
// DecodeAll decodes every XED instruction.
func (d *decoder) DecodeAll() []*instruction {
err := xeddata.WalkInsts(d.ctx.xedPath, func(inst *xeddata.Inst) {
inst.Pattern = xeddata.ExpandStates(d.ctx.db, inst.Pattern)
pset := xeddata.NewPatternSet(inst.Pattern)
opcode := inst.Iclass
switch {
case inst.HasAttribute("AMDONLY") || inst.Extension == "XOP":
return // Only VEX and EVEX are supported
case !pset.Is("VEX") && !pset.Is("EVEX"):
return // Skip non-AVX instructions
case inst.RealOpcode == "N":
return // Skip unstable instructions
}
// Expand some patterns to simplify decodePattern.
pset.Replace("FIX_ROUND_LEN128()", "VL=0")
pset.Replace("FIX_ROUND_LEN512()", "VL=2")
mask, args := d.decodeArgs(pset, inst)
d.insts = append(d.insts, &instruction{
pset: pset,
opcode: opcode,
mask: mask,
args: args,
enc: d.decodePattern(pset, inst),
})
})
if err != nil {
log.Fatalf("walk insts: %v", err)
}
return d.insts
}
// registerArgs maps XED argument name RHS to its decoded version.
var registerArgs = map[string]argument{
"GPR32_R()": {"Yrl", "reg"},
"GPR64_R()": {"Yrl", "reg"},
"VGPR32_R()": {"Yrl", "reg"},
"VGPR64_R()": {"Yrl", "reg"},
"VGPR32_N()": {"Yrl", "regV"},
"VGPR64_N()": {"Yrl", "regV"},
"GPR32_B()": {"Yrl", "reg/mem"},
"GPR64_B()": {"Yrl", "reg/mem"},
"VGPR32_B()": {"Yrl", "reg/mem"},
"VGPR64_B()": {"Yrl", "reg/mem"},
"XMM_R()": {"Yxr", "reg"},
"XMM_R3()": {"YxrEvex", "reg"},
"XMM_N()": {"Yxr", "regV"},
"XMM_N3()": {"YxrEvex", "regV"},
"XMM_B()": {"Yxr", "reg/mem"},
"XMM_B3()": {"YxrEvex", "reg/mem"},
"XMM_SE()": {"Yxr", "regIH"},
"YMM_R()": {"Yyr", "reg"},
"YMM_R3()": {"YyrEvex", "reg"},
"YMM_N()": {"Yyr", "regV"},
"YMM_N3()": {"YyrEvex", "regV"},
"YMM_B()": {"Yyr", "reg/mem"},
"YMM_B3()": {"YyrEvex", "reg/mem"},
"YMM_SE()": {"Yyr", "regIH"},
"ZMM_R3()": {"Yzr", "reg"},
"ZMM_N3()": {"Yzr", "regV"},
"ZMM_B3()": {"Yzr", "reg/mem"},
"MASK_R()": {"Yk", "reg"},
"MASK_N()": {"Yk", "regV"},
"MASK_B()": {"Yk", "reg/mem"},
"MASKNOT0()": {"Yknot0", "kmask"},
// Handled specifically in "generate".
"MASK1()": {"MASK1()", "MASK1()"},
}
func (d *decoder) decodeArgs(pset xeddata.PatternSet, inst *xeddata.Inst) (mask *argument, args []*argument) {
for i, f := range strings.Fields(inst.Operands) {
xarg, err := xeddata.NewOperand(d.ctx.db, f)
if err != nil {
log.Fatalf("%s: args[%d]: %v", inst, i, err)
}
switch {
case xarg.Action == "":
continue // Skip meta args like EMX_BROADCAST_1TO32_8
case !xarg.IsVisible():
continue
}
arg := &argument{}
args = append(args, arg)
switch xarg.NameLHS() {
case "IMM0":
if xarg.Width != "b" {
log.Fatalf("%s: args[%d]: expected width=b, found %s", inst, i, xarg.Width)
}
if pset["IMM0SIGNED=1"] {
arg.ytype = "Yi8"
} else {
arg.ytype = "Yu8"
}
arg.zkind = "imm8"
case "REG0", "REG1", "REG2", "REG3":
rhs := xarg.NameRHS()
if rhs == "MASK1()" {
mask = arg
}
*arg = registerArgs[rhs]
if arg.ytype == "" {
log.Fatalf("%s: args[%d]: unexpected %s reg", inst, i, rhs)
}
if xarg.Attributes["MULTISOURCE4"] {
arg.ytype += "Multi4"
}
case "MEM0":
arg.ytype = pset.MatchOrDefault("Ym",
"VMODRM_XMM()", "Yxvm",
"VMODRM_YMM()", "Yyvm",
"UISA_VMODRM_XMM()", "YxvmEvex",
"UISA_VMODRM_YMM()", "YyvmEvex",
"UISA_VMODRM_ZMM()", "Yzvm",
)
arg.zkind = "reg/mem"
default:
log.Fatalf("%s: args[%d]: unexpected %s", inst, i, xarg.NameRHS())
}
}
// Reverse args.
for i := len(args)/2 - 1; i >= 0; i-- {
j := len(args) - 1 - i
args[i], args[j] = args[j], args[i]
}
return mask, args
}
func (d *decoder) decodePattern(pset xeddata.PatternSet, inst *xeddata.Inst) *encoding {
var enc encoding
enc.opdigit = d.findOpdigit(pset)
enc.opbyte = d.findOpbyte(pset, inst)
if strings.Contains(inst.Attributes, "DISP8_") {
enc.evexScale = d.findEVEXScale(pset)
enc.evexBcstScale = d.findEVEXBcstScale(pset, inst)
}
enc.vex.P = pset.Match(
"VEX_PREFIX=1", "66",
"VEX_PREFIX=2", "F2",
"VEX_PREFIX=3", "F3")
enc.vex.M = pset.Match(
"MAP=1", "0F",
"MAP=2", "0F38",
"MAP=3", "0F3A")
enc.vex.L = pset.MatchOrDefault("128",
"VL=0", "128",
"VL=1", "256",
"VL=2", "512")
enc.vex.W = pset.MatchOrDefault("W0",
"REXW=0", "W0",
"REXW=1", "W1")
if pset.Is("EVEX") {
enc.evex.SAE = strings.Contains(inst.Operands, "TXT=SAESTR")
enc.evex.Rounding = strings.Contains(inst.Operands, "TXT=ROUNDC")
enc.evex.Zeroing = strings.Contains(inst.Operands, "TXT=ZEROSTR")
}
// Prefix each non-empty part with vex or evex.
parts := [...]*string{
&enc.evexScale, &enc.evexBcstScale,
&enc.vex.P, &enc.vex.M, &enc.vex.L, &enc.vex.W,
}
for _, p := range parts {
if *p == "" {
continue
}
if pset.Is("EVEX") {
*p = "evex" + *p
} else {
*p = "vex" + *p
}
}
return &enc
}
func (d *decoder) findOpdigit(pset xeddata.PatternSet) string {
reg := pset.Index(
"REG[0b000]",
"REG[0b001]",
"REG[0b010]",
"REG[0b011]",
"REG[0b100]",
"REG[0b101]",
"REG[0b110]",
"REG[0b111]",
)
// Fixed ModRM.Reg field means that it is used for opcode extension.
if reg != -1 {
return fmt.Sprintf("0%d", reg)
}
return ""
}
// opbyteRE matches uint8 hex literal.
var opbyteRE = regexp.MustCompile(`0x[0-9A-F]{2}`)
func (d *decoder) findOpbyte(pset xeddata.PatternSet, inst *xeddata.Inst) string {
opbyte := ""
for k := range pset {
if opbyteRE.MatchString(k) {
if opbyte == "" {
opbyte = k
} else {
log.Fatalf("%s: multiple opbytes", inst)
}
}
}
return opbyte
}
func (d *decoder) findEVEXScale(pset xeddata.PatternSet) string {
switch {
case pset["NELEM_FULL()"], pset["NELEM_FULLMEM()"]:
return pset.Match(
"VL=0", "N16",
"VL=1", "N32",
"VL=2", "N64")
case pset["NELEM_MOVDDUP()"]:
return pset.Match(
"VL=0", "N8",
"VL=1", "N32",
"VL=2", "N64")
case pset["NELEM_HALF()"], pset["NELEM_HALFMEM()"]:
return pset.Match(
"VL=0", "N8",
"VL=1", "N16",
"VL=2", "N32")
case pset["NELEM_QUARTERMEM()"]:
return pset.Match(
"VL=0", "N4",
"VL=1", "N8",
"VL=2", "N16")
case pset["NELEM_EIGHTHMEM()"]:
return pset.Match(
"VL=0", "N2",
"VL=1", "N4",
"VL=2", "N8")
case pset["NELEM_TUPLE2()"]:
return pset.Match(
"ESIZE_32_BITS()", "N8",
"ESIZE_64_BITS()", "N16")
case pset["NELEM_TUPLE4()"]:
return pset.Match(
"ESIZE_32_BITS()", "N16",
"ESIZE_64_BITS()", "N32")
case pset["NELEM_TUPLE8()"]:
return "N32"
case pset["NELEM_MEM128()"], pset["NELEM_TUPLE1_4X()"]:
return "N16"
}
// Explicit list is required to make it possible to
// detect unhandled nonterminals for the caller.
scalars := [...]string{
"NELEM_SCALAR()",
"NELEM_GSCAT()",
"NELEM_GPR_READER()",
"NELEM_GPR_READER_BYTE()",
"NELEM_GPR_READER_WORD()",
"NELEM_GPR_WRITER_STORE()",
"NELEM_GPR_WRITER_STORE_BYTE()",
"NELEM_GPR_WRITER_STORE_WORD()",
"NELEM_GPR_WRITER_LDOP_D()",
"NELEM_GPR_WRITER_LDOP_Q()",
"NELEM_TUPLE1()",
"NELEM_TUPLE1_BYTE()",
"NELEM_TUPLE1_WORD()",
}
for _, scalar := range scalars {
if pset[scalar] {
return pset.Match(
"ESIZE_8_BITS()", "N1",
"ESIZE_16_BITS()", "N2",
"ESIZE_32_BITS()", "N4",
"ESIZE_64_BITS()", "N8")
}
}
return ""
}
func (d *decoder) findEVEXBcstScale(pset xeddata.PatternSet, inst *xeddata.Inst) string {
// Only FULL and HALF tuples are affected by the broadcasting.
switch {
case pset["NELEM_FULL()"]:
return pset.Match(
"ESIZE_32_BITS()", "BcstN4",
"ESIZE_64_BITS()", "BcstN8")
case pset["NELEM_HALF()"]:
return "BcstN4"
default:
if inst.HasAttribute("BROADCAST_ENABLED") {
log.Fatalf("%s: unexpected tuple for bcst", inst)
}
return ""
}
}
@@ -0,0 +1,255 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"log"
"strings"
)
// ytab is ytabList element.
type ytab struct {
Zcase string
Zoffset int
ArgList string // Ytypes that are matched by this ytab.
}
// ytabList is a named set of ytab objects.
// In asm6.go represented as []ytab.
type ytabList struct {
Name string
Ytabs []ytab
}
// optab describes instruction encodings for specific opcode.
type optab struct {
Opcode string
YtabList *ytabList
OpLines []string
}
type generator struct {
ctx *context
ytabLists map[string]*ytabList
}
// generateOptabs fills ctx.optabs and ctx.ytabLists with objects created
// from decoded instructions.
func generateOptabs(ctx *context) {
gen := generator{ctx: ctx, ytabLists: make(map[string]*ytabList)}
optabs := make(map[string]*optab)
for _, g := range ctx.groups {
optabs[g.opcode] = gen.GenerateGroup(g)
}
ctx.optabs = optabs
ctx.ytabLists = gen.ytabLists
}
// GenerateGroup converts g into optab.
// Populates internal ytab list map.
func (gen *generator) GenerateGroup(g *instGroup) *optab {
var opLines []string
for _, inst := range g.list {
opLines = append(opLines, gen.generateOpLine(inst))
}
return &optab{
Opcode: "A" + g.opcode,
OpLines: opLines,
YtabList: gen.internYtabList(g),
}
}
// generateOpLine returns string that describes opBytes for single instruction form.
func (gen *generator) generateOpLine(inst *instruction) string {
parts := []string{gen.prefixExpr(inst)}
if inst.pset.Is("EVEX") {
parts = append(parts, gen.evexPrefixExpr(inst))
}
parts = append(parts, inst.enc.opbyte)
if inst.enc.opdigit != "" {
parts = append(parts, inst.enc.opdigit)
}
return strings.Join(parts, ", ")
}
func (gen *generator) prefixExpr(inst *instruction) string {
enc := inst.enc
return gen.joinPrefixParts([]string{
// Special constant that makes AVX byte different from 0x0F,
// making it unnecessary to check for both VEX+EVEX when
// assigning dealing with legacy instructions that skip it
// without advancing "z" counter.
"avxEscape",
enc.vex.L,
enc.vex.P,
enc.vex.M,
enc.vex.W,
})
}
func (gen *generator) evexPrefixExpr(inst *instruction) string {
enc := inst.enc
parts := []string{
enc.evexScale,
enc.evexBcstScale,
}
if enc.evex.SAE {
parts = append(parts, "evexSaeEnabled")
}
if enc.evex.Rounding {
parts = append(parts, "evexRoundingEnabled")
}
if enc.evex.Zeroing {
parts = append(parts, "evexZeroingEnabled")
}
return gen.joinPrefixParts(parts)
}
// joinPrefixParts returns the Go OR-expression for every non-empty name.
// If every name is empty, returns "0".
func (gen *generator) joinPrefixParts(names []string) string {
filterEmptyStrings := func(xs []string) []string {
ys := xs[:0]
for _, x := range xs {
if x != "" {
ys = append(ys, x)
}
}
return ys
}
names = filterEmptyStrings(names)
if len(names) == 0 {
return "0"
}
return strings.Join(names, "|")
}
// internYtabList returns ytabList for given group.
//
// Returned ytab lists are interned.
// Same ytab list can be returned for different groups.
func (gen *generator) internYtabList(g *instGroup) *ytabList {
var key string
{
var buf bytes.Buffer
for _, inst := range g.list {
buf.WriteString(inst.zform)
buf.WriteByte('=')
buf.WriteString(inst.YtypeListString())
buf.WriteByte(';')
}
key = buf.String()
}
if ylist := gen.ytabLists[key]; ylist != nil {
return ylist
}
var ytabs []ytab
for _, inst := range g.list {
zoffset := 2
if inst.pset.Is("EVEX") {
zoffset++ // Always at least 3 bytes
}
if inst.enc.opdigit != "" {
zoffset++
}
if inst.mask != nil {
ytabs = append(ytabs, gen.makeMaskYtabs(zoffset, inst)...)
} else {
ytabs = append(ytabs, gen.makeYtab(zoffset, inst.zform, inst.args))
}
}
ylist := &ytabList{
Name: "_y" + strings.ToLower(g.opcode),
Ytabs: ytabs,
}
gen.ytabLists[key] = ylist
return ylist
}
var zcaseByZform = map[string]string{
"evex imm8 reg kmask reg/mem": "Zevex_i_r_k_rm",
"evex imm8 reg reg/mem": "Zevex_i_r_rm",
"evex imm8 reg/mem kmask reg": "Zevex_i_rm_k_r",
"evex imm8 reg/mem kmask regV opdigit": "Zevex_i_rm_k_vo",
"evex imm8 reg/mem reg": "Zevex_i_rm_r",
"evex imm8 reg/mem regV opdigit": "Zevex_i_rm_vo",
"evex imm8 reg/mem regV kmask reg": "Zevex_i_rm_v_k_r",
"evex imm8 reg/mem regV reg": "Zevex_i_rm_v_r",
"evex kmask reg/mem opdigit": "Zevex_k_rmo",
"evex reg kmask reg/mem": "Zevex_r_k_rm",
"evex reg reg/mem": "Zevex_r_v_rm",
"evex reg regV kmask reg/mem": "Zevex_r_v_k_rm",
"evex reg regV reg/mem": "Zevex_r_v_rm",
"evex reg/mem kmask reg": "Zevex_rm_k_r",
"evex reg/mem reg": "Zevex_rm_v_r",
"evex reg/mem regV kmask reg": "Zevex_rm_v_k_r",
"evex reg/mem regV reg": "Zevex_rm_v_r",
"": "Zvex",
"imm8 reg reg/mem": "Zvex_i_r_rm",
"imm8 reg/mem reg": "Zvex_i_rm_r",
"imm8 reg/mem regV opdigit": "Zvex_i_rm_vo",
"imm8 reg/mem regV reg": "Zvex_i_rm_v_r",
"reg reg/mem": "Zvex_r_v_rm",
"reg regV reg/mem": "Zvex_r_v_rm",
"reg/mem opdigit": "Zvex_rm_v_ro",
"reg/mem reg": "Zvex_rm_v_r",
"reg/mem regV opdigit": "Zvex_rm_r_vo",
"reg/mem regV reg": "Zvex_rm_v_r",
"reg/mem": "Zvex_rm_v_r",
"regIH reg/mem regV reg": "Zvex_hr_rm_v_r",
"regV reg/mem reg": "Zvex_v_rm_r",
}
func (gen *generator) makeYtab(zoffset int, zform string, args []*argument) ytab {
var ytypes []string
for _, arg := range args {
if arg.ytype != "Ynone" {
ytypes = append(ytypes, arg.ytype)
}
}
argList := strings.Join(ytypes, ", ")
zcase := zcaseByZform[zform]
if zcase == "" {
log.Fatalf("no zcase for %q", zform)
}
return ytab{
Zcase: zcase,
Zoffset: zoffset,
ArgList: argList,
}
}
// makeMaskYtabs returns 2 ytabs created from instruction with MASK1() argument.
//
// This is required due to how masking is implemented in asm6.
// Single MASK1() instruction produces 2 ytabs, for example:
// 1. OP xmm, mem | Yxr, Yxm | Does not permit K arguments (K0 implied)
// 2. OP xmm, K2, mem | Yxr, Yknot0, Yxm | Does not permit K0 argument
//
// This function also exploits that both ytab entries have same opbytes,
// hence it is efficient to emit only one opbytes line and 0 Z-offset
// for first ytab object.
func (gen *generator) makeMaskYtabs(zoffset int, inst *instruction) []ytab {
var k0 ytab
{
zform := strings.Replace(inst.zform, "MASK1() ", "", 1)
inst.mask.ytype = "Ynone"
k0 = gen.makeYtab(0, zform, inst.args)
}
var knot0 ytab
{
zform := strings.Replace(inst.zform, "MASK1() ", "kmask ", 1)
inst.mask.ytype = "Yknot0"
knot0 = gen.makeYtab(zoffset, zform, inst.args)
}
inst.mask.ytype = "MASK1()" // Restore Y-type
return []ytab{k0, knot0}
}
@@ -0,0 +1,64 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"strings"
"golang.org/x/arch/x86/xeddata"
)
// argument is a describes single instruction operand properties.
type argument struct {
// ytype is argument class as returned by asm6 "oclass" function.
ytype string
// zkind is a partial Z-case matcher.
// Determines which Z-case handles the encoding of instruction.
zkind string
}
// instruction is decoded XED instruction.
// Used to produce ytabs and optabs in later phases.
type instruction struct {
// opcode is instruction symbolic name.
opcode string
pset xeddata.PatternSet
enc *encoding
// mask is EVEX K-register argument; points to args element.
// Used to emit Yk0+Yknot0 table entries.
// Nil for VEX-encoded insts.
mask *argument
args []*argument
// zform is a pattern that determines which encoder Z-case is used.
// We store zform instead of zcase directly because it's further
// expanded during optabs generation.
zform string
}
// String returns short inst printed representation.
func (inst *instruction) String() string { return inst.opcode }
// YtypeListString joins each argument Y-type and returns the result.
func (inst *instruction) YtypeListString() string {
var parts []string
for _, arg := range inst.args {
parts = append(parts, arg.ytype)
}
return strings.Join(parts, " ")
}
// ArgIndexByZkind returns first argument matching given zkind or -1.
func (inst *instruction) ArgIndexByZkind(zkind string) int {
for i, arg := range inst.args {
if arg.zkind == zkind {
return i
}
}
return -1
}
@@ -0,0 +1,361 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"log"
"os"
"sort"
"strings"
"golang.org/x/arch/x86/xeddata"
)
// instGroup holds a list of instructions with same opcode.
type instGroup struct {
opcode string
list []*instruction
}
// context is x86avxgen program execution state.
type context struct {
db *xeddata.Database
groups []*instGroup
optabs map[string]*optab
ytabLists map[string]*ytabList
// Command line arguments:
xedPath string
}
func main() {
log.SetPrefix("x86avxgen: ")
log.SetFlags(log.Lshortfile)
var ctx context
runSteps(&ctx,
parseFlags,
openDatabase,
buildTables,
printTables)
}
func buildTables(ctx *context) {
// Order of steps is significant.
runSteps(ctx,
decodeGroups,
mergeRegMem,
addGoSuffixes,
mergeWIG,
assignZforms,
sortGroups,
generateOptabs)
}
func runSteps(ctx *context, steps ...func(*context)) {
for _, f := range steps {
f(ctx)
}
}
func parseFlags(ctx *context) {
flag.StringVar(&ctx.xedPath, "xedPath", "./xedpath",
"XED datafiles location")
flag.Parse()
}
func openDatabase(ctx *context) {
db, err := xeddata.NewDatabase(ctx.xedPath)
if err != nil {
log.Fatalf("open database: %v", err)
}
ctx.db = db
}
// mergeRegMem merges reg-only with mem-only instructions.
// For example: {MOVQ reg, mem} + {MOVQ reg, reg} = {MOVQ reg, reg/mem}.
func mergeRegMem(ctx *context) {
mergeKey := func(inst *instruction) string {
return strings.Join([]string{
fmt.Sprint(len(inst.args)),
inst.enc.opbyte,
inst.enc.opdigit,
inst.enc.vex.P,
inst.enc.vex.L,
inst.enc.vex.M,
inst.enc.vex.W,
}, " ")
}
for _, g := range ctx.groups {
regOnly := make(map[string]*instruction)
memOnly := make(map[string]*instruction)
list := g.list[:0]
for _, inst := range g.list {
switch {
case inst.pset.Is("RegOnly"):
regOnly[mergeKey(inst)] = inst
case inst.pset.Is("MemOnly"):
memOnly[mergeKey(inst)] = inst
default:
if len(inst.args) == 0 {
list = append(list, inst)
continue
}
log.Fatalf("%s: unexpected MOD value", inst)
}
}
for k, m := range memOnly {
r := regOnly[k]
if r != nil {
index := m.ArgIndexByZkind("reg/mem")
arg := m.args[index]
switch ytype := r.args[index].ytype; ytype {
case "Yrl":
arg.ytype = "Yml"
case "Yxr":
arg.ytype = "Yxm"
case "YxrEvex":
arg.ytype = "YxmEvex"
case "Yyr":
arg.ytype = "Yym"
case "YyrEvex":
arg.ytype = "YymEvex"
case "Yzr":
arg.ytype = "Yzm"
case "Yk":
arg.ytype = "Ykm"
default:
log.Fatalf("%s: unexpected register type: %s", r, ytype)
}
// Merge EVEX flags into m.
m.enc.evex.SAE = m.enc.evex.SAE || r.enc.evex.SAE
m.enc.evex.Rounding = m.enc.evex.Rounding || r.enc.evex.Rounding
m.enc.evex.Zeroing = m.enc.evex.Zeroing || r.enc.evex.Zeroing
delete(regOnly, k)
}
list = append(list, m)
}
for _, r := range regOnly {
list = append(list, r)
}
g.list = list
}
}
// mergeWIG merges [E]VEX.W0 + [E]VEX.W1 into [E]VEX.WIG.
func mergeWIG(ctx *context) {
mergeKey := func(inst *instruction) string {
return strings.Join([]string{
fmt.Sprint(len(inst.args)),
inst.enc.opbyte,
inst.enc.opdigit,
inst.enc.vex.P,
inst.enc.vex.L,
inst.enc.vex.M,
}, " ")
}
for _, g := range ctx.groups {
w0map := make(map[string]*instruction)
w1map := make(map[string]*instruction)
list := g.list[:0]
for _, inst := range g.list {
switch w := inst.enc.vex.W; w {
case "evexW0", "vexW0":
w0map[mergeKey(inst)] = inst
case "evexW1", "vexW1":
w1map[mergeKey(inst)] = inst
default:
log.Fatalf("%s: unexpected vex.W: %s", inst, w)
}
}
for k, w0 := range w0map {
w1 := w1map[k]
if w1 != nil {
w0.enc.vex.W = strings.Replace(w0.enc.vex.W, "W0", "WIG", 1)
delete(w1map, k)
}
list = append(list, w0)
}
for _, w1 := range w1map {
list = append(list, w1)
}
g.list = list
}
}
// assignZforms initializes zform field of every instruction in ctx.
func assignZforms(ctx *context) {
for _, g := range ctx.groups {
for _, inst := range g.list {
var parts []string
if inst.pset.Is("EVEX") {
parts = append(parts, "evex")
}
for _, arg := range inst.args {
parts = append(parts, arg.zkind)
}
if inst.enc.opdigit != "" {
parts = append(parts, "opdigit")
}
inst.zform = strings.Join(parts, " ")
}
}
}
// sortGroups sorts each instruction group by opcode as well as instructions
// inside groups by special rules (see below).
//
// The order of instructions inside group determine ytab
// elements order inside ytabList.
//
// We want these rules to be satisfied:
// - EVEX-encoded entries go after VEX-encoded entries.
// This way, VEX forms are selected over EVEX variants.
// - EVEX forms with SAE/RC must go before forms without them.
// This helps to avoid problems with reg-reg instructions
// that encode either of them in ModRM.R/M which causes
// ambiguity in ytabList (more than 1 ytab can match args).
// If first matching ytab has SAE/RC, problem will not occur.
// - Memory argument position affects order.
// Required to be in sync with XED encoder when there
// are multiple choices of how to encode instruction.
func sortGroups(ctx *context) {
sort.SliceStable(ctx.groups, func(i, j int) bool {
return ctx.groups[i].opcode < ctx.groups[j].opcode
})
for _, g := range ctx.groups {
sortInstList(g.list)
}
}
func sortInstList(insts []*instruction) {
// Use strings for sorting to get reliable transitive "less".
order := make(map[*instruction]string)
for _, inst := range insts {
encTag := 'a'
if inst.pset.Is("EVEX") {
encTag = 'b'
}
memTag := 'a'
if index := inst.ArgIndexByZkind("reg/mem"); index != -1 {
memTag = 'z' - rune(index)
}
rcsaeTag := 'a'
if !(inst.enc.evex.SAE || inst.enc.evex.Rounding) {
rcsaeTag = 'b'
}
order[inst] = fmt.Sprintf("%c%c%c %s",
encTag, memTag, rcsaeTag, inst.YtypeListString())
}
sort.SliceStable(insts, func(i, j int) bool {
return order[insts[i]] < order[insts[j]]
})
}
// addGoSuffixes splits some groups into several groups by introducing a suffix.
// For example, ANDN group becomes ANDNL and ANDNQ (ANDN becomes empty itself).
// Empty groups are removed.
func addGoSuffixes(ctx *context) {
var opcodeSuffixMatchers map[string][]string
{
opXY := []string{"VL=0", "X", "VL=1", "Y"}
opXYZ := []string{"VL=0", "X", "VL=1", "Y", "VL=2", "Z"}
opQ := []string{"REXW=1", "Q"}
opLQ := []string{"REXW=0", "L", "REXW=1", "Q"}
opcodeSuffixMatchers = map[string][]string{
"VCVTPD2DQ": opXY,
"VCVTPD2PS": opXY,
"VCVTTPD2DQ": opXY,
"VCVTQQ2PS": opXY,
"VCVTUQQ2PS": opXY,
"VCVTPD2UDQ": opXY,
"VCVTTPD2UDQ": opXY,
"VFPCLASSPD": opXYZ,
"VFPCLASSPS": opXYZ,
"VCVTSD2SI": opQ,
"VCVTTSD2SI": opQ,
"VCVTTSS2SI": opQ,
"VCVTSS2SI": opQ,
"VCVTSD2USI": opLQ,
"VCVTSS2USI": opLQ,
"VCVTTSD2USI": opLQ,
"VCVTTSS2USI": opLQ,
"VCVTUSI2SD": opLQ,
"VCVTUSI2SS": opLQ,
"VCVTSI2SD": opLQ,
"VCVTSI2SS": opLQ,
"ANDN": opLQ,
"BEXTR": opLQ,
"BLSI": opLQ,
"BLSMSK": opLQ,
"BLSR": opLQ,
"BZHI": opLQ,
"MULX": opLQ,
"PDEP": opLQ,
"PEXT": opLQ,
"RORX": opLQ,
"SARX": opLQ,
"SHLX": opLQ,
"SHRX": opLQ,
}
}
newGroups := make(map[string][]*instruction)
for _, g := range ctx.groups {
kv := opcodeSuffixMatchers[g.opcode]
if kv == nil {
continue
}
list := g.list[:0]
for _, inst := range g.list {
newOp := inst.opcode + inst.pset.Match(kv...)
if newOp != inst.opcode {
inst.opcode = newOp
newGroups[newOp] = append(newGroups[newOp], inst)
} else {
list = append(list, inst)
}
}
g.list = list
}
groups := ctx.groups[:0] // Filled with non-empty groups
// Some groups may become empty due to opcode split.
for _, g := range ctx.groups {
if len(g.list) != 0 {
groups = append(groups, g)
}
}
for op, insts := range newGroups {
groups = append(groups, &instGroup{
opcode: op,
list: insts,
})
}
ctx.groups = groups
}
func printTables(ctx *context) {
writeTables(os.Stdout, ctx)
}
@@ -0,0 +1,116 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"go/format"
"io"
"log"
"sort"
"text/template"
)
var tablesTemplate = template.Must(template.New("avx_optabs").Parse(`
// Code generated by x86avxgen. DO NOT EDIT.
package x86
// VEX instructions that come in two forms:
// VTHING xmm2/m128, xmmV, xmm1
// VTHING ymm2/m256, ymmV, ymm1
//
// The opcode array in the corresponding Optab entry
// should contain the (VEX prefixes, opcode byte) pair
// for each of the two forms.
// For example, the entries for VPXOR are:
//
// VPXOR xmm2/m128, xmmV, xmm1
// VEX.NDS.128.66.0F.WIG EF /r
//
// VPXOR ymm2/m256, ymmV, ymm1
// VEX.NDS.256.66.0F.WIG EF /r
//
// Produce this optab entry:
//
// {AVPXOR, yvex_xy3, Pavx, opBytes{vex128|vex66|vex0F|vexWIG, 0xEF, vex256|vex66|vex0F|vexWIG, 0xEF}}
//
// VEX requires at least 2 bytes inside opBytes:
// - VEX prefixes (vex-prefixed constants)
// - Opcode byte
//
// EVEX instructions extend VEX form variety:
// VTHING zmm2/m512, zmmV, zmm1 -- implicit K0 (merging)
// VTHING zmm2/m512, zmmV, K, zmm1 -- explicit K mask (can't use K0)
//
// EVEX requires at least 3 bytes inside opBytes:
// - EVEX prefixes (evex-prefixed constants); similar to VEX
// - Displacement multiplier info (scale / broadcast scale)
// - Opcode byte; similar to VEX
//
// Both VEX and EVEX instructions may have opdigit (opcode extension) byte
// which follows the primary opcode byte.
// Because it can only have value of 0-7, it is written in octal notation.
//
// x86.csv can be very useful for figuring out proper [E]VEX parts.
{{ range .Ylists }}
var {{.Name}} = []ytab{
{{- range .Ytabs }}
{zcase: {{.Zcase}}, zoffset: {{.Zoffset}}, args: argList{ {{.ArgList}} }},
{{- end }}
}
{{ end }}
var avxOptab = [...]Optab{
{{- range .Optabs }}
{as: {{.Opcode}}, ytab: {{.YtabList.Name}}, prefix: Pavx, op: opBytes{
{{- range .OpLines }}
{{.}},
{{- end }}
}},
{{- end }}
}
`))
// writeTables writes avx optabs file contents to w.
func writeTables(w io.Writer, ctx *context) {
ylists := make([]*ytabList, 0, len(ctx.ytabLists))
for _, ylist := range ctx.ytabLists {
ylists = append(ylists, ylist)
}
sort.Slice(ylists, func(i, j int) bool {
return ylists[i].Name < ylists[j].Name
})
optabs := make([]*optab, 0, len(ctx.optabs))
for _, o := range ctx.optabs {
optabs = append(optabs, o)
}
sort.Slice(optabs, func(i, j int) bool {
return optabs[i].Opcode < optabs[j].Opcode
})
var buf bytes.Buffer
err := tablesTemplate.Execute(&buf, struct {
Ylists []*ytabList
Optabs []*optab
}{
Ylists: ylists,
Optabs: optabs,
})
if err != nil {
log.Fatalf("template execute error: %v", err)
}
// TODO: invoke "go fmt" or format.Gofmt? #22695.
prettyCode, err := format.Source(buf.Bytes())
if err != nil {
log.Fatalf("gofmt error: %v", err)
}
if _, err := w.Write(prettyCode); err != nil {
log.Fatalf("write output: %v", err)
}
}
@@ -0,0 +1,160 @@
var _yvmovsd = []ytab{
{zcase: Zvex_r_v_rm, zoffset: 2, args: argList{Yxr, Yxr, Yxr}},
{zcase: Zvex_r_v_rm, zoffset: 2, args: argList{Yxr, Ym}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Ym, Yxr}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yxr, Yxr, Yxr}},
{zcase: Zevex_r_v_rm, zoffset: 0, args: argList{YxrEvex, YxrEvex, YxrEvex}},
{zcase: Zevex_r_v_k_rm, zoffset: 3, args: argList{YxrEvex, YxrEvex, Yknot0, YxrEvex}},
{zcase: Zevex_r_v_rm, zoffset: 0, args: argList{YxrEvex, Ym}},
{zcase: Zevex_r_k_rm, zoffset: 3, args: argList{YxrEvex, Yknot0, Ym}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{Ym, YxrEvex}},
{zcase: Zevex_rm_k_r, zoffset: 3, args: argList{Ym, Yknot0, YxrEvex}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YxrEvex, YxrEvex, YxrEvex}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{YxrEvex, YxrEvex, Yknot0, YxrEvex}},
}
----
{as: AVMOVSD, ytab: _yvmovsd, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vexF2 | vex0F | vexW0, 0x11,
avxEscape | vex128 | vexF2 | vex0F | vexW0, 0x11,
avxEscape | vex128 | vexF2 | vex0F | vexW0, 0x10,
avxEscape | vex128 | vexF2 | vex0F | vexW0, 0x10,
avxEscape | evex128 | evexF2 | evex0F | evexW1, evexZeroingEnabled, 0x11,
avxEscape | evex128 | evexF2 | evex0F | evexW1, evexN8, 0x11,
avxEscape | evex128 | evexF2 | evex0F | evexW1, evexN8 | evexZeroingEnabled, 0x10,
avxEscape | evex128 | evexF2 | evex0F | evexW1, evexZeroingEnabled, 0x10,
}}
======
var _yvaddpd = []ytab{
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yxm, Yxr, Yxr}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yym, Yyr, Yyr}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{Yzm, Yzr, Yzr}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{Yzm, Yzr, Yknot0, Yzr}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YxmEvex, YxrEvex, YxrEvex}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{YxmEvex, YxrEvex, Yknot0, YxrEvex}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YymEvex, YyrEvex, YyrEvex}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{YymEvex, YyrEvex, Yknot0, YyrEvex}},
}
----
{as: AVADDPD, ytab: _yvaddpd, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vex66 | vex0F | vexW0, 0x58,
avxEscape | vex256 | vex66 | vex0F | vexW0, 0x58,
avxEscape | evex512 | evex66 | evex0F | evexW1, evexN64 | evexBcstN8 | evexRoundingEnabled | evexZeroingEnabled, 0x58,
avxEscape | evex128 | evex66 | evex0F | evexW1, evexN16 | evexBcstN8 | evexZeroingEnabled, 0x58,
avxEscape | evex256 | evex66 | evex0F | evexW1, evexN32 | evexBcstN8 | evexZeroingEnabled, 0x58,
}}
======
var _yvcmppd = []ytab{
{zcase: Zvex_i_rm_v_r, zoffset: 2, args: argList{Yu8, Yxm, Yxr, Yxr}},
{zcase: Zvex_i_rm_v_r, zoffset: 2, args: argList{Yu8, Yym, Yyr, Yyr}},
{zcase: Zevex_i_rm_v_r, zoffset: 0, args: argList{Yu8, Yzm, Yzr, Yk}},
{zcase: Zevex_i_rm_v_k_r, zoffset: 3, args: argList{Yu8, Yzm, Yzr, Yknot0, Yk}},
{zcase: Zevex_i_rm_v_r, zoffset: 0, args: argList{Yu8, YxmEvex, YxrEvex, Yk}},
{zcase: Zevex_i_rm_v_k_r, zoffset: 3, args: argList{Yu8, YxmEvex, YxrEvex, Yknot0, Yk}},
{zcase: Zevex_i_rm_v_r, zoffset: 0, args: argList{Yu8, YymEvex, YyrEvex, Yk}},
{zcase: Zevex_i_rm_v_k_r, zoffset: 3, args: argList{Yu8, YymEvex, YyrEvex, Yknot0, Yk}},
}
----
{as: AVCMPPD, ytab: _yvcmppd, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vex66 | vex0F | vexW0, 0xC2,
avxEscape | vex256 | vex66 | vex0F | vexW0, 0xC2,
avxEscape | evex512 | evex66 | evex0F | evexW1, evexN64 | evexBcstN8 | evexSaeEnabled, 0xC2,
avxEscape | evex128 | evex66 | evex0F | evexW1, evexN16 | evexBcstN8, 0xC2,
avxEscape | evex256 | evex66 | evex0F | evexW1, evexN32 | evexBcstN8, 0xC2,
}}
======
var _yvmovapd = []ytab{
{zcase: Zvex_r_v_rm, zoffset: 2, args: argList{Yxr, Yxm}},
{zcase: Zvex_r_v_rm, zoffset: 2, args: argList{Yyr, Yym}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yxm, Yxr}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yym, Yyr}},
{zcase: Zevex_r_v_rm, zoffset: 0, args: argList{YxrEvex, YxmEvex}},
{zcase: Zevex_r_k_rm, zoffset: 3, args: argList{YxrEvex, Yknot0, YxmEvex}},
{zcase: Zevex_r_v_rm, zoffset: 0, args: argList{YyrEvex, YymEvex}},
{zcase: Zevex_r_k_rm, zoffset: 3, args: argList{YyrEvex, Yknot0, YymEvex}},
{zcase: Zevex_r_v_rm, zoffset: 0, args: argList{Yzr, Yzm}},
{zcase: Zevex_r_k_rm, zoffset: 3, args: argList{Yzr, Yknot0, Yzm}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YxmEvex, YxrEvex}},
{zcase: Zevex_rm_k_r, zoffset: 3, args: argList{YxmEvex, Yknot0, YxrEvex}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YymEvex, YyrEvex}},
{zcase: Zevex_rm_k_r, zoffset: 3, args: argList{YymEvex, Yknot0, YyrEvex}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{Yzm, Yzr}},
{zcase: Zevex_rm_k_r, zoffset: 3, args: argList{Yzm, Yknot0, Yzr}},
}
----
{as: AVMOVAPD, ytab: _yvmovapd, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vex66 | vex0F | vexW0, 0x29,
avxEscape | vex256 | vex66 | vex0F | vexW0, 0x29,
avxEscape | vex128 | vex66 | vex0F | vexW0, 0x28,
avxEscape | vex256 | vex66 | vex0F | vexW0, 0x28,
avxEscape | evex128 | evex66 | evex0F | evexW1, evexN16 | evexZeroingEnabled, 0x29,
avxEscape | evex256 | evex66 | evex0F | evexW1, evexN32 | evexZeroingEnabled, 0x29,
avxEscape | evex512 | evex66 | evex0F | evexW1, evexN64 | evexZeroingEnabled, 0x29,
avxEscape | evex128 | evex66 | evex0F | evexW1, evexN16 | evexZeroingEnabled, 0x28,
avxEscape | evex256 | evex66 | evex0F | evexW1, evexN32 | evexZeroingEnabled, 0x28,
avxEscape | evex512 | evex66 | evex0F | evexW1, evexN64 | evexZeroingEnabled, 0x28,
}}
======
var _yvpslld = []ytab{
{zcase: Zvex_i_rm_vo, zoffset: 3, args: argList{Yu8, Yxr, Yxr}},
{zcase: Zvex_i_rm_vo, zoffset: 3, args: argList{Yu8, Yyr, Yyr}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yxm, Yxr, Yxr}},
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yxm, Yyr, Yyr}},
{zcase: Zevex_i_rm_vo, zoffset: 0, args: argList{Yu8, YxmEvex, YxrEvex}},
{zcase: Zevex_i_rm_k_vo, zoffset: 4, args: argList{Yu8, YxmEvex, Yknot0, YxrEvex}},
{zcase: Zevex_i_rm_vo, zoffset: 0, args: argList{Yu8, YymEvex, YyrEvex}},
{zcase: Zevex_i_rm_k_vo, zoffset: 4, args: argList{Yu8, YymEvex, Yknot0, YyrEvex}},
{zcase: Zevex_i_rm_vo, zoffset: 0, args: argList{Yu8, Yzm, Yzr}},
{zcase: Zevex_i_rm_k_vo, zoffset: 4, args: argList{Yu8, Yzm, Yknot0, Yzr}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YxmEvex, YxrEvex, YxrEvex}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{YxmEvex, YxrEvex, Yknot0, YxrEvex}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YxmEvex, YyrEvex, YyrEvex}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{YxmEvex, YyrEvex, Yknot0, YyrEvex}},
{zcase: Zevex_rm_v_r, zoffset: 0, args: argList{YxmEvex, Yzr, Yzr}},
{zcase: Zevex_rm_v_k_r, zoffset: 3, args: argList{YxmEvex, Yzr, Yknot0, Yzr}},
}
----
{as: AVPSLLW, ytab: _yvpslld, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vex66 | vex0F | vexW0, 0x71, 06,
avxEscape | vex256 | vex66 | vex0F | vexW0, 0x71, 06,
avxEscape | vex128 | vex66 | vex0F | vexW0, 0xF1,
avxEscape | vex256 | vex66 | vex0F | vexW0, 0xF1,
avxEscape | evex128 | evex66 | evex0F | evexW0, evexN16 | evexZeroingEnabled, 0x71, 06,
avxEscape | evex256 | evex66 | evex0F | evexW0, evexN32 | evexZeroingEnabled, 0x71, 06,
avxEscape | evex512 | evex66 | evex0F | evexW0, evexN64 | evexZeroingEnabled, 0x71, 06,
avxEscape | evex128 | evex66 | evex0F | evexW0, evexN16 | evexZeroingEnabled, 0xF1,
avxEscape | evex256 | evex66 | evex0F | evexW0, evexN16 | evexZeroingEnabled, 0xF1,
avxEscape | evex512 | evex66 | evex0F | evexW0, evexN16 | evexZeroingEnabled, 0xF1,
}}
======
var _yvzeroall = []ytab{
{zcase: Zvex, zoffset: 2, args: argList{}},
}
----
{as: AVZEROALL, ytab: _yvzeroall, prefix: Pavx, op: opBytes{
avxEscape | vex256 | vex0F | vexW0, 0x77,
}}
======
var _yvzeroall = []ytab{
{zcase: Zvex, zoffset: 2, args: argList{}},
}
----
{as: AVZEROUPPER, ytab: _yvzeroall, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vex0F | vexW0, 0x77,
}}
======
var _yvcomisd = []ytab{
{zcase: Zvex_rm_v_r, zoffset: 2, args: argList{Yxm, Yxr}},
{zcase: Zevex_rm_v_r, zoffset: 3, args: argList{YxmEvex, YxrEvex}},
}
----
{as: AVUCOMISD, ytab: _yvcomisd, prefix: Pavx, op: opBytes{
avxEscape | vex128 | vex66 | vex0F | vexW0, 0x2E,
avxEscape | evex128 | evex66 | evex0F | evexW1, evexN8 | evexSaeEnabled, 0x2E,
}}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,69 @@
###FILE: ./datafiles/xed-operand-types.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
#
#
#XTYPE TYPE BITS-PER-ELEM
#
var VARIABLE 0 # instruction must set NELEM and ELEMENT_SIZE
struct STRUCT 0 # many elements of different widths
int INT 0 # one element, all the bits, width varies
uint UINT 0 # one element, all the bits, width varies
#
i1 INT 1
i8 INT 8
i16 INT 16
i32 INT 32
i64 INT 64
u8 UINT 8
u16 UINT 16
u32 UINT 32
u64 UINT 64
u128 UINT 128
u256 UINT 256
f32 SINGLE 32
f64 DOUBLE 64
f80 LONGDOUBLE 80
b80 LONGBCD 80
###FILE: ./datafiles/ivbavx/fp16-operand-types.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
#XTYPE TYPE BITS-PER-ELEM
f16 FLOAT16 16
@@ -0,0 +1,341 @@
###FILE: ./datafiles/xed-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
###########################################################################
## file: xed-state-bits.txt
###########################################################################
# These are just shorthand for some conditions or captures.
# Simple macro replacement
#all_modes ALL_MODES=1
not64 MODE!=2
mode64 MODE=2
mode32 MODE=1
mode16 MODE=0
# effective addressing mode
eanot16 EASZ!=1
eamode16 EASZ=1
eamode32 EASZ=2
eamode64 EASZ=3
# stack addressing mode
smode16 SMODE=0
smode32 SMODE=1
smode64 SMODE=2
eosz8 EOSZ=0
eosz16 EOSZ=1
not_eosz16 EOSZ!=1
eosz32 EOSZ=2
eosz64 EOSZ=3
eosznot64 EOSZ!=3
# for OD expansion in graph partitioning FIXME
mod0 MOD=0
mod1 MOD=1
mod2 MOD=2
mod3 MOD=3
rex_reqd REX=1
no_rex REX=0
reset_rex REX=0 REXW=0 REXB=0 REXR=0 REXX=0
rexb_prefix REXB=1
rexx_prefix REXX=1
rexr_prefix REXR=1
# 2013-09-25 FIXME: we were inconsistent. some things use W0/W1, some
# use the more verbose form. We should converge on W0/W1.
#
rexw_prefix REXW=1 SKIP_OSZ=1
norexw_prefix REXW=0 SKIP_OSZ=1
W1 REXW=1 SKIP_OSZ=1
W0 REXW=0 SKIP_OSZ=1
norexb_prefix REXB=0
norexx_prefix REXX=0
norexr_prefix REXR=0
############################################################3333
f2_prefix REP=2 # REPNZ, REPNE
f3_prefix REP=3 # REPZ, REPE
repne REP=2
repe REP=3
norep REP=0
66_prefix OSZ=1
nof3_prefix REP!=3
no66_prefix OSZ=0
not_refining REP=0 # dummy setting for state values 2007-08-06 FIXME
refining_f2 REP=2
refining_f3 REP=3
not_refining_f3 REP!=3 # for pause vs xchg
no_refining_prefix REP=0 OSZ=0 # critical:REP must be first for decoding partitioning
osz_refining_prefix REP=0 OSZ=1
f2_refining_prefix REP=2
f3_refining_prefix REP=3
no67_prefix ASZ=0
67_prefix ASZ=1
lock_prefix LOCK=1
nolock_prefix LOCK=0
default_ds DEFAULT_SEG=0
default_ss DEFAULT_SEG=1
default_es DEFAULT_SEG=2 # for string ops
no_seg_prefix SEG_OVD=0
some_seg_prefix SEG_OVD!=0
cs_prefix SEG_OVD=1
ds_prefix SEG_OVD=2
es_prefix SEG_OVD=3
fs_prefix SEG_OVD=4
gs_prefix SEG_OVD=5
ss_prefix SEG_OVD=6
# default (or not) to 64b width in 64b mode
nrmw DF64=0
df64 DF64=1
# default choice for encoder when there are multiple choices for a
# nonterminal. The ISA is not uniquely determined for encoding so we
# must express preferences for certain forms!
enc ENCODER_PREFERRED=1
# for the legacy prefix encoder, tell it to keep trying rules and not
# return after successfully finding one that applies
no_return NO_RETURN=1
# indicate an encoding or decoding error occurred
error ERROR=XED_ERROR_GENERAL_ERROR
# dummy constraint which always satisfies
true DUMMY=0
###FILE: ./datafiles/amdxop/xop-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
XMAP8 MAP=8
XMAP9 MAP=9
XMAPA MAP=10
XOPV VEXVALID=3
###FILE: ./datafiles/avx/avx-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
VL128 VL=0
VL256 VL=1
VV1 VEXVALID=1
VV0 VEXVALID=0
VMAP0 MAP=0
V0F MAP=1
V0F38 MAP=2
V0F3A MAP=3
VNP VEX_PREFIX=0
V66 VEX_PREFIX=1
VF2 VEX_PREFIX=2
VF3 VEX_PREFIX=3
# No VEX-SPECIFIED-REGISTER
NOVSR VEXDEST3=0b1 VEXDEST210=0b111
EMX_BROADCAST_1TO4_32 BCAST=10 # 128
EMX_BROADCAST_1TO4_64 BCAST=13 # 256
EMX_BROADCAST_1TO8_32 BCAST=3 # 256
EMX_BROADCAST_2TO4_64 BCAST=20 # 256
###FILE: ./datafiles/avxhsw/hsw-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
EMX_BROADCAST_1TO2_64 BCAST=11 # 128
EMX_BROADCAST_1TO8_16 BCAST=14 # 128
EMX_BROADCAST_1TO16_16 BCAST=15 # 256
EMX_BROADCAST_1TO16_8 BCAST=17 # 128
EMX_BROADCAST_1TO32_8 BCAST=18 # 256
###FILE: /home/quasilyte/CODE/intel/xed/datafiles/knc/uisa-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
VL512 VL=2
VLBAD VL=3 # unused VL setting to cause things not to decode.
# KNC EVEX is KVV
#
KVV VEXVALID=4
# No VEX-SPECIFIED-REGISTER
NOEVSR VEXDEST3=0b1 VEXDEST210=0b111 VEXDEST4=0b0
# No VEX-SPECIFIED-REGISTER for GATHERS/SCATTERS -- index reg 5th bit is VEXTDEST4
NO_SPARSE_EVSR VEXDEST3=0b1 VEXDEST210=0b111
# These conflict w/another chip ... so if you ever build a combo
# model you'll have to remove these somehow.
#
EMX_BROADCAST_1TO16_32 BCAST=1 # 512
EMX_BROADCAST_4TO16_32 BCAST=2 # 512
EMX_BROADCAST_1TO8_64 BCAST=5 # 512
EMX_BROADCAST_4TO8_64 BCAST=6 # 512
###FILE: ./datafiles/avx512f/avx512-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
EVV VEXVALID=2
EMX_BROADCAST_1TO16_32 BCAST=1 # 512
EMX_BROADCAST_4TO16_32 BCAST=2 # 512
EMX_BROADCAST_1TO8_64 BCAST=5 # 512
EMX_BROADCAST_4TO8_64 BCAST=6 # 512
EMX_BROADCAST_2TO16_32 BCAST=7 # 512
EMX_BROADCAST_2TO8_64 BCAST=8 # 512
EMX_BROADCAST_8TO16_32 BCAST=9 # 512
EMX_BROADCAST_1TO32_16 BCAST=16 # 512
EMX_BROADCAST_1TO64_8 BCAST=19 # 512
# these do not show up on earlier processors
EMX_BROADCAST_4TO8_32 BCAST=4 # 256
EMX_BROADCAST_2TO4_32 BCAST=12 # 128
EMX_BROADCAST_2TO8_32 BCAST=21 # 256
EMX_BROADCAST_1TO2_32 BCAST=22 # 128
###FILE: ./datafiles/avx512-skx/skx-state-bits.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
EMX_BROADCAST_1TO2_8 BCAST=23
EMX_BROADCAST_1TO4_8 BCAST=24
EMX_BROADCAST_1TO8_8 BCAST=25
EMX_BROADCAST_1TO2_16 BCAST=26
EMX_BROADCAST_1TO4_16 BCAST=27
@@ -0,0 +1,224 @@
###FILE: ./datafiles/xed-operand-width.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
# @file xed-operand-width.txt
# the default xtype can be overridden in each operand using a ":" followed by an explicit xtype
##
## the width defaults to bytes. But it can be bits if it has a "bits" suffix
##
#
# default
#oc2-code XTYPE width16 width32 width64 (if only one width is shown, it is for all widths)
#
INVALID INVALID 0
#
# 3 strange things:
#
asz int 2 4 8 # varies with the effective address width
ssz int 2 4 8 # varies with the stack address width
pseudo struct 0 # these are for unusual registers
pseudox87 struct 0 # these are for unusual registers
#
#
#
#1 i1 1 # FIXME: this is not used...
a16 i16 4 # bound
a32 i32 8 # bound
b u8 1
d i32 4
#
i8 i8 1
u8 u8 1
i16 i16 2
u16 u16 2
i32 i32 4
u32 u32 4
i64 i64 8
u64 u64 8
f16 f16 2 # IVB converts
f32 f32 4
f64 f64 8
#
dq i32 16
#
xub u8 16
xuw u16 16
xud u32 16
xuq u64 16
x128 u128 16
#
xb i8 16
xw i16 16
xd i32 16
xq i64 16
#
#
mb i8 8
mw i16 8
md i32 8
mq i64 8
#
m64int i64 8
m64real f64 8
mem108 struct 108
mem14 struct 14
mem16 struct 2
mem16int i16 2
mem28 struct 28
mem32int i32 4
mem32real f32 4
mem80dec b80 10
mem80real f80 10
f80 f80 10 # for X87 registers:
mem94 struct 94
mfpxenv struct 512
mxsave struct 576
mprefetch i64 64 # made up width for prefetches
p struct 4 6 6
p2 struct 4 6 10
pd f64 16
ps f32 16
pi i32 8
q i64 8
s struct 6 6 10
s64 struct 10
sd f64 8
si i32 4
ss f32 4
v int 2 4 8
y int 4 4 8
w i16 2
z int 2 4 4
spw8 int 16 32 0 # varies (64b invalid) STACK POINTER WIDTH
spw int 2 4 8 # varies STACK POINTER WIDTH
spw5 int 10 20 40 # varies (IRET approx) STACK POINTER WIDTH
spw3 int 6 12 24 # varies (IRET approx) STACK POINTER WIDTH
spw2 int 4 8 16 # varies (FAR call/ret approx) STACK POINTER WIDTH
i1 int 1bits
i2 int 2bits
i3 int 3bits
i4 int 4bits
i5 int 5bits
i6 int 6bits
i7 int 7bits
i8 int 8bits
var var 0 # relies on NELEM * ELEMENT_SIZE to get the number of bits.
bnd32 u32 12 # MPX 32b BNDLDX/BNDSTX memop 3x4B
bnd64 u64 24 # MPX 32b BNDLDX/BNDSTX memop 3x8B
###FILE: ./datafiles/avx/avx-operand-width.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
#
#code XTYPE width16 width32 width64 (if only one width is presented, it is for all widths)
#
qq i32 32
yub u8 32
yuw u16 32
yud u32 32
yuq u64 32
y128 u128 32
yb i8 32
yw i16 32
yd i32 32
yq i64 32
yps f32 32
ypd f64 32
###FILE: ./datafiles/avx512f/avx512-operand-widths.txt
#BEGIN_LEGAL
#
#Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
#
#code XTYPE width16 width32 width64 (if only one width is presented, it is for all widths)
#
vv var 0 # relies on nelem * elem_size
zv var 0 # relies on nelem * elem_size
wrd u16 16bits
mskw i1 64bits # FIXME: bad name
zmskw i1 512bits
zf32 f32 512bits
zf64 f64 512bits
zb i8 512bits
zw i16 512bits
zd i32 512bits
zq i64 512bits
zub u8 512bits
zuw u16 512bits
zud u32 512bits
zuq u64 512bits
# alternative names...
zi8 i8 512bits
zi16 i16 512bits
zi32 i32 512bits
zi64 i64 512bits
zu8 u8 512bits
zu16 u16 512bits
zu32 u32 512bits
zu64 u64 512bits
zu128 u128 512bits
@@ -0,0 +1,69 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86csv
import (
"encoding/csv"
"io"
)
// A Reader reads entries from an "x86.csv" file.
type Reader struct {
csv *csv.Reader
}
// NewReader returns a Reader reading from r, which should
// be of the content of the "x86.csv" (format version=0.2).
func NewReader(r io.Reader) *Reader {
rcsv := csv.NewReader(r)
rcsv.Comment = '#'
return &Reader{csv: rcsv}
}
// ReadAll reads all remaining rows from r.
//
// If error has occurred, still returns all rows
// that have been read during method execution.
//
// A successful call returns err == nil, not err == io.EOF.
// Because ReadAll is defined to read until EOF,
// it does not treat end of file as an error to be reported.
func (r *Reader) ReadAll() ([]*Inst, error) {
var err error
var insts []*Inst
for inst, err := r.Read(); err == nil; inst, err = r.Read() {
insts = append(insts, inst)
}
if err == io.EOF {
return insts, nil
}
return insts, err
}
// Read reads and returns the next Row from the "x86.csv" file.
// If there is no data left to be read, Read returns {nil, io.EOF}.
func (r *Reader) Read() (*Inst, error) {
cols, err := r.csv.Read()
if err != nil {
return nil, err
}
// This should be the only place where indexes
// are used. Everything else should rely on Row records.
inst := &Inst{
Intel: cols[0],
Go: cols[1],
GNU: cols[2],
Encoding: cols[3],
Mode32: cols[4],
Mode64: cols[5],
CPUID: cols[6],
Tags: cols[7],
Action: cols[8],
Multisize: cols[9],
DataSize: cols[10],
}
return inst, nil
}
@@ -0,0 +1,111 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package x86csv provides means to work with "x86.csv".
// Only latest version of "x86.csv" format is supported.
//
// Terminology:
// given "OPCODE [ARGS...]" line;
// Opcode - instruction name/mnemonic/class.
// Args - instruction operands.
// Syntax - Opcode with Args.
package x86csv
import (
"strings"
)
// An Inst describes single x86 instruction encoding form.
type Inst struct {
// Intel syntax (example: "SHR r/m32, imm8").
Intel string
// Go assembler syntax (example: "SHRL imm8, r/m32").
Go string
// GNU binutils syntax (example: "shrl imm8, r/m32").
GNU string
// Binary encoding (example: "C1 /4 ib").
Encoding string
// Validity in 32bit mode ("V", "I" or "N.E.").
Mode32 string
// Validity in 64bit mode ("V", "I", "N.E.", "N.P.", "N.I." or "N.S.").
Mode64 string
// CPUID feature flags required (comma-separated).
CPUID string
// Hints about instruction (comma-separated).
// See "x86spec" package to see detailed overview of possible
// tags and their meaning.
Tags string
// Read/write action of the instruction on its arguments, in Intel order.
// For example, "rw,r" denotes that "SHR r/m32, imm8" reads and writes
// its first argument but only reads its second argument.
Action string
// Whether Intel syntax has encoding forms distinguished only by
// operand size, like most arithmetic instructions ("" or "Y").
Multisize string
// DataSize is the size of the data operation in bits ("8" for MOVB, "16" for MOVW, and so on).
DataSize string
}
// IntelOpcode returns the opcode in the Intel syntax.
func (inst *Inst) IntelOpcode() string { return instOpcode(inst.Intel) }
// GoOpcode returns the opcode in Go (Plan9) syntax.
func (inst *Inst) GoOpcode() string { return instOpcode(inst.Go) }
// GNUOpcode returns the opcode in GNU binutils (mostly AT&T) syntax.
func (inst *Inst) GNUOpcode() string { return instOpcode(inst.GNU) }
// IntelArgs returns the arguments in the Intel syntax.
func (inst *Inst) IntelArgs() []string { return instArgs(inst.Intel) }
// GoArgs returns the arguments in Go (Plan9) syntax.
func (inst *Inst) GoArgs() []string { return instArgs(inst.Go) }
// GNUArgs returns the arguments in GNU binutils (mostly AT&T) syntax.
func (inst *Inst) GNUArgs() []string { return instArgs(inst.GNU) }
// HasTag reports whether inst tag list contains the specified tag.
func (inst *Inst) HasTag(tag string) bool {
i := strings.Index(inst.Tags, tag)
if i == -1 {
return false
}
leftOK := i == 0 ||
(inst.Tags[i-1] == ',')
rigthOK := i+len(tag) == len(inst.Tags) ||
(inst.Tags[i+len(tag)] == ',')
return leftOK && rigthOK
}
// instOpcode returns the opcode from an instruction syntax.
func instOpcode(syntax string) string {
i := strings.Index(syntax, " ")
if i == -1 {
return syntax
}
return syntax[:i]
}
// instArgs returns the arguments from an instruction syntax.
func instArgs(syntax string) []string {
i := strings.Index(syntax, " ")
if i < 0 {
return nil
}
args := strings.Split(syntax[i+1:], ",")
for i := range args {
args[i] = strings.TrimSpace(args[i])
}
return args
}
@@ -0,0 +1,108 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86csv
import (
"reflect"
"strings"
"testing"
)
// This test makes it harder to break Reader unintentionally.
//
// Deeper testing is probably not required because 99% of the job is
// done by csv.Reader.
func TestReader(t *testing.T) {
input := `# x86.csv v0.2
"ADDSUBPD xmm1, xmm2/m128","ADDSUBPD xmm2/m128, xmm1","addsubpd xmm2/m128, xmm1","66 0F D0 /r","V","V","SSE3","","rw,r","",""
"VPEXTRQ r/m64, xmm1, imm8","VPEXTRQ imm8, xmm1, r/m64","vpextrq imm8, xmm1, r/m64","VEX.128.66.0F3A.W1 16 /r ib","I","V","AVX","","w,r,r","",""
"XOR r8, r/m8","XORB r/m8, r8","xorb r/m8, r8","REX 32 /r","N.E.","V","","pseudo64","rw,r","Y","8"
`
want := []Inst{
{
Intel: "ADDSUBPD xmm1, xmm2/m128",
Go: "ADDSUBPD xmm2/m128, xmm1",
GNU: "addsubpd xmm2/m128, xmm1",
Encoding: "66 0F D0 /r",
Mode32: "V",
Mode64: "V",
CPUID: "SSE3",
Action: "rw,r",
},
{
Intel: "VPEXTRQ r/m64, xmm1, imm8",
Go: "VPEXTRQ imm8, xmm1, r/m64",
GNU: "vpextrq imm8, xmm1, r/m64",
Encoding: "VEX.128.66.0F3A.W1 16 /r ib",
Mode32: "I",
Mode64: "V",
CPUID: "AVX",
Action: "w,r,r",
},
{
Intel: "XOR r8, r/m8",
Go: "XORB r/m8, r8",
GNU: "xorb r/m8, r8",
Encoding: "REX 32 /r",
Mode32: "N.E.",
Mode64: "V",
Tags: "pseudo64",
Action: "rw,r",
Multisize: "Y",
DataSize: "8",
},
}
r := NewReader(strings.NewReader(input))
inst, err := r.Read()
if err != nil {
t.Fatalf("Read(): %v", err)
}
restInsts, err := r.ReadAll()
if err != nil {
t.Fatalf("ReadAll(): %v", err)
}
if remainder, err := r.ReadAll(); remainder != nil || err != nil {
t.Errorf("ReadAll() on exhausted r failed")
}
have := append([]*Inst{inst}, restInsts...)
if len(want) != len(have) {
t.Fatalf("len(have) is %d, want %d\n", len(have), len(want))
}
lines := strings.Split(input, "\n")
lines = lines[1:] // Drop comment line
for i := range want {
if want[i] != *have[i] {
t.Errorf("%s:\nhave: %v\nwant: %v", lines[i], have[i], want[i])
}
}
}
func TestSyntaxSplit(t *testing.T) {
tests := []struct {
syntax string
opcode string
args []string
}{
{"RET", "RET", nil},
{"CALLW* r/m16", "CALLW*", []string{"r/m16"}},
{"JMP_FAR m16:16", "JMP_FAR", []string{"m16:16"}},
{"movl CR0-CR7, rmr32", "movl", []string{"CR0-CR7", "rmr32"}},
{"VFMSUBADD132PD xmm1, xmmV, xmm2/m128", "VFMSUBADD132PD", []string{"xmm1", "xmmV", "xmm2/m128"}},
}
for _, tt := range tests {
op, args := instOpcode(tt.syntax), instArgs(tt.syntax)
if op != tt.opcode {
t.Errorf("%s: opcode mismatch (have `%s`, want `%s`)",
tt.syntax, op, tt.opcode)
}
if !reflect.DeepEqual(args, tt.args) {
t.Errorf("%s: args mismatch (have %v, want %s)",
tt.syntax, args, tt.args)
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
x86manual.pdf
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More