whatcanGOwrong
This commit is contained in:
@@ -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
|
||||
|
||||
[](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",""
|
||||
|
@@ -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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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, ®page)
|
||||
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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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
|
||||
+2816
File diff suppressed because it is too large
Load Diff
+1418
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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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,
|
||||
}}
|
||||
Vendored
+57893
File diff suppressed because it is too large
Load Diff
+69
@@ -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
|
||||
|
||||
+341
@@ -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
|
||||
+224
@@ -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
Reference in New Issue
Block a user