From patchwork Wed Sep 11 13:05:57 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Claudio Fontana X-Patchwork-Id: 274321 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AC2172C00E9 for ; Thu, 12 Sep 2013 01:12:52 +1000 (EST) Received: from localhost ([::1]:36486 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VJm66-0003Qj-11 for incoming@patchwork.ozlabs.org; Wed, 11 Sep 2013 11:12:50 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38043) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VJk87-0004b9-Rp for qemu-devel@nongnu.org; Wed, 11 Sep 2013 09:07:26 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VJk7V-0000CY-34 for qemu-devel@nongnu.org; Wed, 11 Sep 2013 09:06:47 -0400 Received: from lhrrgout.huawei.com ([194.213.3.17]:34597) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VJk7T-0000Bi-Bk for qemu-devel@nongnu.org; Wed, 11 Sep 2013 09:06:09 -0400 Received: from 172.18.7.190 (EHLO lhreml203-edg.china.huawei.com) ([172.18.7.190]) by lhrrg02-dlp.huawei.com (MOS 4.3.5-GA FastPath queued) with ESMTP id AVH50960; Wed, 11 Sep 2013 13:06:05 +0000 (GMT) Received: from LHREML403-HUB.china.huawei.com (10.201.5.217) by lhreml203-edg.huawei.com (172.18.7.221) with Microsoft SMTP Server (TLS) id 14.3.146.0; Wed, 11 Sep 2013 14:05:53 +0100 Received: from [127.0.0.1] (10.220.139.110) by lhreml403-hub.china.huawei.com (10.201.5.217) with Microsoft SMTP Server id 14.3.146.0; Wed, 11 Sep 2013 14:05:58 +0100 Message-ID: <52306AB5.3050701@huawei.com> Date: Wed, 11 Sep 2013 15:05:57 +0200 From: Claudio Fontana User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:17.0) Gecko/20130801 Thunderbird/17.0.8 MIME-Version: 1.0 To: "qemu-devel@nongnu.org" References: <52306808.4030701@huawei.com> In-Reply-To: <52306808.4030701@huawei.com> X-Originating-IP: [10.220.139.110] X-CFilter-Loop: Reflected X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 194.213.3.17 X-Mailman-Approved-At: Wed, 11 Sep 2013 11:10:54 -0400 Cc: Peter Maydell Subject: [Qemu-devel] [RFC 3/4] disas: add libvixl source code for aarch64 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org this just adds the libvixl source code verbatim from ARM, including the source code itself (src/), license and readmes. Examples, the scons build system etc, are excluded. Not yet bound to QEMU's build system. Signed-off-by: Claudio Fontana --- disas/libvixl/LICENCE | 30 + disas/libvixl/README.md | 128 ++ disas/libvixl/doc/changelog.md | 12 + disas/libvixl/doc/supported-instructions.md | 1133 ++++++++++++++ disas/libvixl/src/a64/assembler-a64.cc | 2172 ++++++++++++++++++++++++++ disas/libvixl/src/a64/assembler-a64.h | 1784 +++++++++++++++++++++ disas/libvixl/src/a64/constants-a64.h | 1104 +++++++++++++ disas/libvixl/src/a64/cpu-a64.cc | 148 ++ disas/libvixl/src/a64/cpu-a64.h | 56 + disas/libvixl/src/a64/debugger-a64.cc | 1511 ++++++++++++++++++ disas/libvixl/src/a64/debugger-a64.h | 188 +++ disas/libvixl/src/a64/decoder-a64.cc | 712 +++++++++ disas/libvixl/src/a64/decoder-a64.h | 198 +++ disas/libvixl/src/a64/disasm-a64.cc | 1678 ++++++++++++++++++++ disas/libvixl/src/a64/disasm-a64.h | 109 ++ disas/libvixl/src/a64/instructions-a64.cc | 238 +++ disas/libvixl/src/a64/instructions-a64.h | 344 ++++ disas/libvixl/src/a64/instrument-a64.cc | 638 ++++++++ disas/libvixl/src/a64/instrument-a64.h | 108 ++ disas/libvixl/src/a64/macro-assembler-a64.cc | 1108 +++++++++++++ disas/libvixl/src/a64/macro-assembler-a64.h | 1175 ++++++++++++++ disas/libvixl/src/a64/simulator-a64.cc | 2077 ++++++++++++++++++++++++ disas/libvixl/src/a64/simulator-a64.h | 576 +++++++ disas/libvixl/src/globals.h | 66 + disas/libvixl/src/platform.h | 43 + disas/libvixl/src/utils.cc | 120 ++ disas/libvixl/src/utils.h | 126 ++ 27 files changed, 17582 insertions(+) create mode 100644 disas/libvixl/LICENCE create mode 100644 disas/libvixl/README.md create mode 100644 disas/libvixl/doc/changelog.md create mode 100644 disas/libvixl/doc/supported-instructions.md create mode 100644 disas/libvixl/src/a64/assembler-a64.cc create mode 100644 disas/libvixl/src/a64/assembler-a64.h create mode 100644 disas/libvixl/src/a64/constants-a64.h create mode 100644 disas/libvixl/src/a64/cpu-a64.cc create mode 100644 disas/libvixl/src/a64/cpu-a64.h create mode 100644 disas/libvixl/src/a64/debugger-a64.cc create mode 100644 disas/libvixl/src/a64/debugger-a64.h create mode 100644 disas/libvixl/src/a64/decoder-a64.cc create mode 100644 disas/libvixl/src/a64/decoder-a64.h create mode 100644 disas/libvixl/src/a64/disasm-a64.cc create mode 100644 disas/libvixl/src/a64/disasm-a64.h create mode 100644 disas/libvixl/src/a64/instructions-a64.cc create mode 100644 disas/libvixl/src/a64/instructions-a64.h create mode 100644 disas/libvixl/src/a64/instrument-a64.cc create mode 100644 disas/libvixl/src/a64/instrument-a64.h create mode 100644 disas/libvixl/src/a64/macro-assembler-a64.cc create mode 100644 disas/libvixl/src/a64/macro-assembler-a64.h create mode 100644 disas/libvixl/src/a64/simulator-a64.cc create mode 100644 disas/libvixl/src/a64/simulator-a64.h create mode 100644 disas/libvixl/src/globals.h create mode 100644 disas/libvixl/src/platform.h create mode 100644 disas/libvixl/src/utils.cc create mode 100644 disas/libvixl/src/utils.h diff --git a/disas/libvixl/LICENCE b/disas/libvixl/LICENCE new file mode 100644 index 0000000..b7e160a --- /dev/null +++ b/disas/libvixl/LICENCE @@ -0,0 +1,30 @@ +LICENCE +======= + +The software in this repository is covered by the following licence. + +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. diff --git a/disas/libvixl/README.md b/disas/libvixl/README.md new file mode 100644 index 0000000..e3c3a0a --- /dev/null +++ b/disas/libvixl/README.md @@ -0,0 +1,128 @@ +VIXL: AArch64 Runtime Code Generation Library Version 1.1 +========================================================= + +Contents: + + * Requirements + * Overview + * Known limitations + * Usage + + +Requirements +============ + +To build VIXL the following software is required: + + 1. Python 2.7 + 2. SCons 2.0 + 3. GCC 4.4 + +A 64-bit host machine is required, implementing an LP64 data model. VIXL has +only been tested using GCC on Ubuntu systems. + +To run the linter stage of the tests, the following software is also required: + + 1. Git + 2. [Google's `cpplint.py`][cpplint] + +Refer to the 'Usage' section for details. + + +Overview +======== + +VIXL is made of three components. + + 1. A programmatic assembler to generate A64 code at runtime. The assembler + abstracts some of the constraints of the A64 ISA, for example most + instructions support any immediate. + 2. A disassembler which can print any instruction emitted by the assembler. + 3. A simulator which can simulate any instruction emitted by the assembler. + The simulator allows generated code to be run on another architecture + without the need for a full ISA model. + +The VIXL git repository can be found [on GitHub][vixl]. Changes from previous +versions of VIXL can be found in the [Changelog](doc/changelog.md). + + +Known Limitations +================= + +VIXL was developed to target JavaScript engines so a number of features from A64 +were deemed unnecessary: + + * No Advanced SIMD support. + * Limited rounding mode support for floating point. + * No support for synchronisation instructions. + * Limited support for system instructions. + * A few miscellaneous integer and floating point instructions are missing. + +The VIXL simulator supports only those instructions that the VIXL assembler can +generate. The `doc` directory contains a +[list of supported instructions](doc/supported-instructions.md). + + +Usage +===== + + +Running all Tests +----------------- + +The helper script `tools/presubmit.py` will build and run every test that is +provided with VIXL, in both release and debug mode. It is a useful script for +verifying that all of VIXL's dependencies are in place and that VIXL is working +as it should. + +By default, the `tools/presubmit.py` script runs a linter to check that the +source code conforms with the code style guide, and to detect several common +errors that the compiler may not warn about. This is most useful for VIXL +developers. The linter has the following dependencies: + + 1. Git must be installed, and the VIXL project must be in a valid Git + repository, such as one produced using `git clone`. + 2. `cpplint.py`, [as provided by Google][cpplint], must be available (and + executable) on the `PATH`. Only revision 104 has been tested with VIXL. + +It is possible to tell `tools/presubmit.py` to skip the linter stage by passing +`--nolint`. This removes the dependency on `cpplint.py` and Git. The `--nolint` +option is implied if the VIXL project is a snapshot (with no `.git` directory). + + +Building and Running Specific Tests +----------------------------------- + +The helper script `tools/test.py` will build and run all the tests for the +assembler and disassembler in release mode. Add `--mode=debug` to build and run +in debug mode. The tests can be built separately using SCons: `scons +target=cctest`. + + +Building and Running the Benchmarks +----------------------------------- + +There are two very basic benchmarks provided with VIXL: + + 1. bench\_dataop, emitting adds + 2. bench\_branch, emitting branches + +To build one benchmark: `scons target=bench_xxx`, then run it as +`./bench_xxx_sim `. The benchmarks do not report a +figure; they should be timed using the `time` command. + + +Getting Started +--------------- + +A short introduction to using VIXL can be found [here](doc/getting-started.md). +Example source code is provided in the `examples` directory. Build this using +`scons target=examples` from the root directory. + + + +[cpplint]: https://google-styleguide.googlecode.com/svn-history/r104/trunk/cpplint/cpplint.py + "Google's cpplint.py script." + +[vixl]: https://github.com/armvixl/vixl + "The VIXL repository on GitHub." diff --git a/disas/libvixl/doc/changelog.md b/disas/libvixl/doc/changelog.md new file mode 100644 index 0000000..8bab932 --- /dev/null +++ b/disas/libvixl/doc/changelog.md @@ -0,0 +1,12 @@ +VIXL Change Log +=============== + +* 1.1 + + Improved robustness of instruction decoder and disassembler. + + Added support for double-to-float conversions using `fcvt`. + + Added support for more fixed-point to floating-point conversions (`ucvtf` + and `scvtf`). + + Added instruction statistics collection class `instrument-a64.cc`. + +* 1.0 + + Initial release. diff --git a/disas/libvixl/doc/supported-instructions.md b/disas/libvixl/doc/supported-instructions.md new file mode 100644 index 0000000..90d63ec --- /dev/null +++ b/disas/libvixl/doc/supported-instructions.md @@ -0,0 +1,1133 @@ +VIXL Supported Instruction List +=============================== + +This is a list of the AArch64 instructions supported by the VIXL assembler, +disassembler and simulator. The simulator may not support all floating point +operations to the precision required by AArch64 - please check the simulator +source code for details. + +AArch64 integer instructions +---------------------------- + +### adc ### + +Add with carry bit. + + void adc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### add ### + +Add. + + void add(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### adr ### + +Calculate the address of a PC offset. + + void adr(const Register& rd, int imm21) + + +### adr ### + +Calculate the address of a label. + + void adr(const Register& rd, Label* label) + + +### asr ### + +Arithmetic shift right. + + inline void asr(const Register& rd, const Register& rn, unsigned shift) + + +### asrv ### + +Arithmetic shift right by variable. + + void asrv(const Register& rd, const Register& rn, const Register& rm) + + +### b ### + +Branch to PC offset. + + void b(int imm26, Condition cond = al) + + +### b ### + +Branch to label. + + void b(Label* label, Condition cond = al) + + +### bfi ### + +Bitfield insert. + + inline void bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) + + +### bfm ### + +Bitfield move. + + void bfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms) + + +### bfxil ### + +Bitfield extract and insert low. + + inline void bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) + + +### bic ### + +Bit clear (A & ~B). + + void bic(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### bl ### + +Branch with link to PC offset. + + void bl(int imm26) + + +### bl ### + +Branch with link to label. + + void bl(Label* label) + + +### blr ### + +Branch with link to register. + + void blr(const Register& xn) + + +### br ### + +Branch to register. + + void br(const Register& xn) + + +### brk ### + +Monitor debug-mode breakpoint. + + void brk(int code) + + +### cbnz ### + +Compare and branch to PC offset if not zero. + + void cbnz(const Register& rt, int imm19) + + +### cbnz ### + +Compare and branch to label if not zero. + + void cbnz(const Register& rt, Label* label) + + +### cbz ### + +Compare and branch to PC offset if zero. + + void cbz(const Register& rt, int imm19) + + +### cbz ### + +Compare and branch to label if zero. + + void cbz(const Register& rt, Label* label) + + +### ccmn ### + +Conditional compare negative. + + void ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond) + + +### ccmp ### + +Conditional compare. + + void ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond) + + +### cinc ### + +Conditional increment: rd = cond ? rn + 1 : rn. + + void cinc(const Register& rd, const Register& rn, Condition cond) + + +### cinv ### + +Conditional invert: rd = cond ? ~rn : rn. + + void cinv(const Register& rd, const Register& rn, Condition cond) + + +### cls ### + +Count leading sign bits. + + void cls(const Register& rd, const Register& rn) + + +### clz ### + +Count leading zeroes. + + void clz(const Register& rd, const Register& rn) + + +### cmn ### + +Compare negative. + + void cmn(const Register& rn, const Operand& operand) + + +### cmp ### + +Compare. + + void cmp(const Register& rn, const Operand& operand) + + +### cneg ### + +Conditional negate: rd = cond ? -rn : rn. + + void cneg(const Register& rd, const Register& rn, Condition cond) + + +### csel ### + +Conditional select: rd = cond ? rn : rm. + + void csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) + + +### cset ### + +Conditional set: rd = cond ? 1 : 0. + + void cset(const Register& rd, Condition cond) + + +### csetm ### + +Conditional set mask: rd = cond ? -1 : 0. + + void csetm(const Register& rd, Condition cond) + + +### csinc ### + +Conditional select increment: rd = cond ? rn : rm + 1. + + void csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) + + +### csinv ### + +Conditional select inversion: rd = cond ? rn : ~rm. + + void csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) + + +### csneg ### + +Conditional select negation: rd = cond ? rn : -rm. + + void csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) + + +### eon ### + +Bitwise enor/xnor (A ^ ~B). + + void eon(const Register& rd, const Register& rn, const Operand& operand) + + +### eor ### + +Bitwise eor/xor (A ^ B). + + void eor(const Register& rd, const Register& rn, const Operand& operand) + + +### extr ### + +Extract. + + void extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb) + + +### hint ### + +System hint. + + void hint(SystemHint code) + + +### hlt ### + +Halting debug-mode breakpoint. + + void hlt(int code) + + +### ldnp ### + +Load integer or FP register pair, non-temporal. + + void ldnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src) + + +### ldp ### + +Load integer or FP register pair. + + void ldp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src) + + +### ldpsw ### + +Load word pair with sign extension. + + void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src) + + +### ldr ### + +Load integer or FP register. + + void ldr(const CPURegister& rt, const MemOperand& src) + + +### ldr ### + +Load literal to FP register. + + void ldr(const FPRegister& ft, double imm) + + +### ldr ### + +Load literal to register. + + void ldr(const Register& rt, uint64_t imm) + + +### ldrb ### + +Load byte. + + void ldrb(const Register& rt, const MemOperand& src) + + +### ldrh ### + +Load half-word. + + void ldrh(const Register& rt, const MemOperand& src) + + +### ldrsb ### + +Load byte with sign extension. + + void ldrsb(const Register& rt, const MemOperand& src) + + +### ldrsh ### + +Load half-word with sign extension. + + void ldrsh(const Register& rt, const MemOperand& src) + + +### ldrsw ### + +Load word with sign extension. + + void ldrsw(const Register& rt, const MemOperand& src) + + +### lsl ### + +Logical shift left. + + inline void lsl(const Register& rd, const Register& rn, unsigned shift) + + +### lslv ### + +Logical shift left by variable. + + void lslv(const Register& rd, const Register& rn, const Register& rm) + + +### lsr ### + +Logical shift right. + + inline void lsr(const Register& rd, const Register& rn, unsigned shift) + + +### lsrv ### + +Logical shift right by variable. + + void lsrv(const Register& rd, const Register& rn, const Register& rm) + + +### madd ### + +Multiply and accumulate. + + void madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) + + +### mneg ### + +Negated multiply. + + void mneg(const Register& rd, const Register& rn, const Register& rm) + + +### mov ### + +Move register to register. + + void mov(const Register& rd, const Register& rn) + + +### movk ### + +Move immediate and keep. + + void movk(const Register& rd, uint64_t imm, int shift = -1) + + +### movn ### + +Move inverted immediate. + + void movn(const Register& rd, uint64_t imm, int shift = -1) + + +### movz ### + +Move immediate. + + void movz(const Register& rd, uint64_t imm, int shift = -1) + + +### mrs ### + +Move to register from system register. + + void mrs(const Register& rt, SystemRegister sysreg) + + +### msr ### + +Move from register to system register. + + void msr(SystemRegister sysreg, const Register& rt) + + +### msub ### + +Multiply and subtract. + + void msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) + + +### mul ### + +Multiply. + + void mul(const Register& rd, const Register& rn, const Register& rm) + + +### mvn ### + +Move inverted operand to register. + + void mvn(const Register& rd, const Operand& operand) + + +### neg ### + +Negate. + + void neg(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### ngc ### + +Negate with carry bit. + + void ngc(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### nop ### + +No-op. + + void nop() + + +### orn ### + +Bitwise nor (A | ~B). + + void orn(const Register& rd, const Register& rn, const Operand& operand) + + +### orr ### + +Bitwise or (A | B). + + void orr(const Register& rd, const Register& rn, const Operand& operand) + + +### rbit ### + +Bit reverse. + + void rbit(const Register& rd, const Register& rn) + + +### ret ### + +Branch to register with return hint. + + void ret(const Register& xn = lr) + + +### rev ### + +Reverse bytes. + + void rev(const Register& rd, const Register& rn) + + +### rev16 ### + +Reverse bytes in 16-bit half words. + + void rev16(const Register& rd, const Register& rn) + + +### rev32 ### + +Reverse bytes in 32-bit words. + + void rev32(const Register& rd, const Register& rn) + + +### ror ### + +Rotate right. + + inline void ror(const Register& rd, const Register& rs, unsigned shift) + + +### rorv ### + +Rotate right by variable. + + void rorv(const Register& rd, const Register& rn, const Register& rm) + + +### sbc ### + +Subtract with carry bit. + + void sbc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### sbfiz ### + +Signed bitfield insert with zero at right. + + inline void sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) + + +### sbfm ### + +Signed bitfield move. + + void sbfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms) + + +### sbfx ### + +Signed bitfield extract. + + inline void sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) + + +### scvtf ### + +Convert signed integer or fixed point to FP. + + void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0) + + +### sdiv ### + +Signed integer divide. + + void sdiv(const Register& rd, const Register& rn, const Register& rm) + + +### smaddl ### + +Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + + void smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) + + +### smsubl ### + +Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. + + void smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) + + +### smulh ### + +Signed multiply high: 64 x 64 -> 64-bit <127:64>. + + void smulh(const Register& xd, const Register& xn, const Register& xm) + + +### smull ### + +Signed long multiply: 32 x 32 -> 64-bit. + + void smull(const Register& rd, const Register& rn, const Register& rm) + + +### stnp ### + +Store integer or FP register pair, non-temporal. + + void stnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst) + + +### stp ### + +Store integer or FP register pair. + + void stp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst) + + +### str ### + +Store integer or FP register. + + void str(const CPURegister& rt, const MemOperand& dst) + + +### strb ### + +Store byte. + + void strb(const Register& rt, const MemOperand& dst) + + +### strh ### + +Store half-word. + + void strh(const Register& rt, const MemOperand& dst) + + +### sub ### + +Subtract. + + void sub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags) + + +### sxtb ### + +Signed extend byte. + + inline void sxtb(const Register& rd, const Register& rn) + + +### sxth ### + +Signed extend halfword. + + inline void sxth(const Register& rd, const Register& rn) + + +### sxtw ### + +Signed extend word. + + inline void sxtw(const Register& rd, const Register& rn) + + +### tbnz ### + +Test bit and branch to PC offset if not zero. + + void tbnz(const Register& rt, unsigned bit_pos, int imm14) + + +### tbnz ### + +Test bit and branch to label if not zero. + + void tbnz(const Register& rt, unsigned bit_pos, Label* label) + + +### tbz ### + +Test bit and branch to PC offset if zero. + + void tbz(const Register& rt, unsigned bit_pos, int imm14) + + +### tbz ### + +Test bit and branch to label if zero. + + void tbz(const Register& rt, unsigned bit_pos, Label* label) + + +### tst ### + +Bit test and set flags. + + void tst(const Register& rn, const Operand& operand) + + +### ubfiz ### + +Unsigned bitfield insert with zero at right. + + inline void ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) + + +### ubfm ### + +Unsigned bitfield move. + + void ubfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms) + + +### ubfx ### + +Unsigned bitfield extract. + + inline void ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) + + +### ucvtf ### + +Convert unsigned integer or fixed point to FP. + + void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0) + + +### udiv ### + +Unsigned integer divide. + + void udiv(const Register& rd, const Register& rn, const Register& rm) + + +### umaddl ### + +Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + + void umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) + + +### umsubl ### + +Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. + + void umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) + + +### uxtb ### + +Unsigned extend byte. + + inline void uxtb(const Register& rd, const Register& rn) + + +### uxth ### + +Unsigned extend halfword. + + inline void uxth(const Register& rd, const Register& rn) + + +### uxtw ### + +Unsigned extend word. + + inline void uxtw(const Register& rd, const Register& rn) + + + +AArch64 floating point instructions +----------------------------------- + +### fabs ### + +FP absolute. + + void fabs(const FPRegister& fd, const FPRegister& fn) + + +### fadd ### + +FP add. + + void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) + + +### fccmp ### + +FP conditional compare. + + void fccmp(const FPRegister& fn, + const FPRegister& fm, + StatusFlags nzcv, + Condition cond) + + +### fcmp ### + +FP compare immediate. + + void fcmp(const FPRegister& fn, double value) + + +### fcmp ### + +FP compare registers. + + void fcmp(const FPRegister& fn, const FPRegister& fm) + + +### fcsel ### + +FP conditional select. + + void fcsel(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + Condition cond) + + +### fcvt ### + +FP convert single to double precision. + + void fcvt(const FPRegister& fd, const FPRegister& fn) + + +### fcvtms ### + +Convert FP to signed integer (round towards -infinity). + + void fcvtms(const Register& rd, const FPRegister& fn) + + +### fcvtmu ### + +Convert FP to unsigned integer (round towards -infinity). + + void fcvtmu(const Register& rd, const FPRegister& fn) + + +### fcvtns ### + +Convert FP to signed integer (nearest with ties to even). + + void fcvtns(const Register& rd, const FPRegister& fn) + + +### fcvtnu ### + +Convert FP to unsigned integer (nearest with ties to even). + + void fcvtnu(const Register& rd, const FPRegister& fn) + + +### fcvtzs ### + +Convert FP to signed integer (round towards zero). + + void fcvtzs(const Register& rd, const FPRegister& fn) + + +### fcvtzu ### + +Convert FP to unsigned integer (round towards zero). + + void fcvtzu(const Register& rd, const FPRegister& fn) + + +### fdiv ### + +FP divide. + + void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) + + +### fmax ### + +FP maximum. + + void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) + + +### fmin ### + +FP minimum. + + void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) + + +### fmov ### + +Move FP register to FP register. + + void fmov(FPRegister fd, FPRegister fn) + + +### fmov ### + +Move FP register to register. + + void fmov(Register rd, FPRegister fn) + + +### fmov ### + +Move immediate to FP register. + + void fmov(FPRegister fd, double imm) + + +### fmov ### + +Move register to FP register. + + void fmov(FPRegister fd, Register rn) + + +### fmsub ### + +FP multiply and subtract. + + void fmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa) + + +### fmul ### + +FP multiply. + + void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) + + +### fneg ### + +FP negate. + + void fneg(const FPRegister& fd, const FPRegister& fn) + + +### frintn ### + +FP round to integer (nearest with ties to even). + + void frintn(const FPRegister& fd, const FPRegister& fn) + + +### frintz ### + +FP round to integer (towards zero). + + void frintz(const FPRegister& fd, const FPRegister& fn) + + +### fsqrt ### + +FP square root. + + void fsqrt(const FPRegister& fd, const FPRegister& fn) + + +### fsub ### + +FP subtract. + + void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) + + + +Additional or pseudo instructions +--------------------------------- + +### bind ### + +Bind a label to the current PC. + + void bind(Label* label) + + +### dc32 ### + +Emit 32 bits of data into the instruction stream. + + inline void dc32(uint32_t data) + + +### dc64 ### + +Emit 64 bits of data into the instruction stream. + + inline void dc64(uint64_t data) + + +### dci ### + +Emit raw instructions into the instruction stream. + + inline void dci(Instr raw_inst) + + +### debug ### + +Debug control pseudo instruction, only supported by the debugger. + + void debug(const char* message, uint32_t code, Instr params = BREAK) + + + diff --git a/disas/libvixl/src/a64/assembler-a64.cc b/disas/libvixl/src/a64/assembler-a64.cc new file mode 100644 index 0000000..89f7406 --- /dev/null +++ b/disas/libvixl/src/a64/assembler-a64.cc @@ -0,0 +1,2172 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + + +#include +#include "a64/assembler-a64.h" + +namespace vixl { + +// CPURegList utilities. +CPURegister CPURegList::PopLowestIndex() { + if (IsEmpty()) { + return NoCPUReg; + } + int index = CountTrailingZeros(list_, kRegListSizeInBits); + ASSERT((1 << index) & list_); + Remove(index); + return CPURegister(index, size_, type_); +} + + +CPURegister CPURegList::PopHighestIndex() { + ASSERT(IsValid()); + if (IsEmpty()) { + return NoCPUReg; + } + int index = CountLeadingZeros(list_, kRegListSizeInBits); + index = kRegListSizeInBits - 1 - index; + ASSERT((1 << index) & list_); + Remove(index); + return CPURegister(index, size_, type_); +} + + +bool CPURegList::IsValid() const { + if ((type_ == CPURegister::kRegister) || + (type_ == CPURegister::kFPRegister)) { + bool is_valid = true; + // Try to create a CPURegister for each element in the list. + for (int i = 0; i < kRegListSizeInBits; i++) { + if (((list_ >> i) & 1) != 0) { + is_valid &= CPURegister(i, size_, type_).IsValid(); + } + } + return is_valid; + } else if (type_ == CPURegister::kNoRegister) { + // We can't use IsEmpty here because that asserts IsValid(). + return list_ == 0; + } else { + return false; + } +} + + +void CPURegList::RemoveCalleeSaved() { + if (type() == CPURegister::kRegister) { + Remove(GetCalleeSaved(RegisterSizeInBits())); + } else if (type() == CPURegister::kFPRegister) { + Remove(GetCalleeSavedFP(RegisterSizeInBits())); + } else { + ASSERT(type() == CPURegister::kNoRegister); + ASSERT(IsEmpty()); + // The list must already be empty, so do nothing. + } +} + + +CPURegList CPURegList::GetCalleeSaved(unsigned size) { + return CPURegList(CPURegister::kRegister, size, 19, 29); +} + + +CPURegList CPURegList::GetCalleeSavedFP(unsigned size) { + return CPURegList(CPURegister::kFPRegister, size, 8, 15); +} + + +CPURegList CPURegList::GetCallerSaved(unsigned size) { + // Registers x0-x18 and lr (x30) are caller-saved. + CPURegList list = CPURegList(CPURegister::kRegister, size, 0, 18); + list.Combine(lr); + return list; +} + + +CPURegList CPURegList::GetCallerSavedFP(unsigned size) { + // Registers d0-d7 and d16-d31 are caller-saved. + CPURegList list = CPURegList(CPURegister::kFPRegister, size, 0, 7); + list.Combine(CPURegList(CPURegister::kFPRegister, size, 16, 31)); + return list; +} + + +const CPURegList kCalleeSaved = CPURegList::GetCalleeSaved(); +const CPURegList kCalleeSavedFP = CPURegList::GetCalleeSavedFP(); +const CPURegList kCallerSaved = CPURegList::GetCallerSaved(); +const CPURegList kCallerSavedFP = CPURegList::GetCallerSavedFP(); + + +// Registers. +#define WREG(n) w##n, +const Register Register::wregisters[] = { +REGISTER_CODE_LIST(WREG) +}; +#undef WREG + +#define XREG(n) x##n, +const Register Register::xregisters[] = { +REGISTER_CODE_LIST(XREG) +}; +#undef XREG + +#define SREG(n) s##n, +const FPRegister FPRegister::sregisters[] = { +REGISTER_CODE_LIST(SREG) +}; +#undef SREG + +#define DREG(n) d##n, +const FPRegister FPRegister::dregisters[] = { +REGISTER_CODE_LIST(DREG) +}; +#undef DREG + + +const Register& Register::WRegFromCode(unsigned code) { + // This function returns the zero register when code = 31. The stack pointer + // can not be returned. + ASSERT(code < kNumberOfRegisters); + return wregisters[code]; +} + + +const Register& Register::XRegFromCode(unsigned code) { + // This function returns the zero register when code = 31. The stack pointer + // can not be returned. + ASSERT(code < kNumberOfRegisters); + return xregisters[code]; +} + + +const FPRegister& FPRegister::SRegFromCode(unsigned code) { + ASSERT(code < kNumberOfFPRegisters); + return sregisters[code]; +} + + +const FPRegister& FPRegister::DRegFromCode(unsigned code) { + ASSERT(code < kNumberOfFPRegisters); + return dregisters[code]; +} + + +const Register& CPURegister::W() const { + ASSERT(IsValidRegister()); + ASSERT(Is64Bits()); + return Register::WRegFromCode(code_); +} + + +const Register& CPURegister::X() const { + ASSERT(IsValidRegister()); + ASSERT(Is32Bits()); + return Register::XRegFromCode(code_); +} + + +const FPRegister& CPURegister::S() const { + ASSERT(IsValidFPRegister()); + ASSERT(Is64Bits()); + return FPRegister::SRegFromCode(code_); +} + + +const FPRegister& CPURegister::D() const { + ASSERT(IsValidFPRegister()); + ASSERT(Is32Bits()); + return FPRegister::DRegFromCode(code_); +} + + +// Operand. +Operand::Operand(int64_t immediate) + : immediate_(immediate), + reg_(NoReg), + shift_(NO_SHIFT), + extend_(NO_EXTEND), + shift_amount_(0) {} + + +Operand::Operand(Register reg, Shift shift, unsigned shift_amount) + : reg_(reg), + shift_(shift), + extend_(NO_EXTEND), + shift_amount_(shift_amount) { + ASSERT(reg.Is64Bits() || (shift_amount < kWRegSize)); + ASSERT(reg.Is32Bits() || (shift_amount < kXRegSize)); + ASSERT(!reg.IsSP()); +} + + +Operand::Operand(Register reg, Extend extend, unsigned shift_amount) + : reg_(reg), + shift_(NO_SHIFT), + extend_(extend), + shift_amount_(shift_amount) { + ASSERT(reg.IsValid()); + ASSERT(shift_amount <= 4); + ASSERT(!reg.IsSP()); +} + + +bool Operand::IsImmediate() const { + return reg_.Is(NoReg); +} + + +bool Operand::IsShiftedRegister() const { + return reg_.IsValid() && (shift_ != NO_SHIFT); +} + + +bool Operand::IsExtendedRegister() const { + return reg_.IsValid() && (extend_ != NO_EXTEND); +} + + +Operand Operand::ToExtendedRegister() const { + ASSERT(IsShiftedRegister()); + ASSERT((shift_ == LSL) && (shift_amount_ <= 4)); + return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_); +} + + +// MemOperand +MemOperand::MemOperand(Register base, ptrdiff_t offset, AddrMode addrmode) + : base_(base), regoffset_(NoReg), offset_(offset), addrmode_(addrmode) { + ASSERT(base.Is64Bits() && !base.IsZero()); +} + + +MemOperand::MemOperand(Register base, + Register regoffset, + Extend extend, + unsigned shift_amount) + : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset), + shift_(NO_SHIFT), extend_(extend), shift_amount_(shift_amount) { + ASSERT(base.Is64Bits() && !base.IsZero()); + ASSERT(!regoffset.IsSP()); + ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX)); +} + + +MemOperand::MemOperand(Register base, + Register regoffset, + Shift shift, + unsigned shift_amount) + : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset), + shift_(shift), extend_(NO_EXTEND), shift_amount_(shift_amount) { + ASSERT(base.Is64Bits() && !base.IsZero()); + ASSERT(!regoffset.IsSP()); + ASSERT(shift == LSL); +} + + +MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode) + : base_(base), regoffset_(NoReg), addrmode_(addrmode) { + ASSERT(base.Is64Bits() && !base.IsZero()); + + if (offset.IsImmediate()) { + offset_ = offset.immediate(); + } else if (offset.IsShiftedRegister()) { + ASSERT(addrmode == Offset); + + regoffset_ = offset.reg(); + shift_= offset.shift(); + shift_amount_ = offset.shift_amount(); + + extend_ = NO_EXTEND; + offset_ = 0; + + // These assertions match those in the shifted-register constructor. + ASSERT(!regoffset_.IsSP()); + ASSERT(shift_ == LSL); + } else { + ASSERT(offset.IsExtendedRegister()); + ASSERT(addrmode == Offset); + + regoffset_ = offset.reg(); + extend_ = offset.extend(); + shift_amount_ = offset.shift_amount(); + + shift_= NO_SHIFT; + offset_ = 0; + + // These assertions match those in the extended-register constructor. + ASSERT(!regoffset_.IsSP()); + ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX)); + } +} + + +bool MemOperand::IsImmediateOffset() const { + return (addrmode_ == Offset) && regoffset_.Is(NoReg); +} + + +bool MemOperand::IsRegisterOffset() const { + return (addrmode_ == Offset) && !regoffset_.Is(NoReg); +} + + +bool MemOperand::IsPreIndex() const { + return addrmode_ == PreIndex; +} + + +bool MemOperand::IsPostIndex() const { + return addrmode_ == PostIndex; +} + + +// Assembler +Assembler::Assembler(byte* buffer, unsigned buffer_size) + : buffer_size_(buffer_size), literal_pool_monitor_(0) { + // Assert that this is an LP64 system. + ASSERT(sizeof(int) == sizeof(int32_t)); // NOLINT(runtime/sizeof) + ASSERT(sizeof(long) == sizeof(int64_t)); // NOLINT(runtime/int) + ASSERT(sizeof(void *) == sizeof(int64_t)); // NOLINT(runtime/sizeof) + ASSERT(sizeof(1) == sizeof(int32_t)); // NOLINT(runtime/sizeof) + ASSERT(sizeof(1L) == sizeof(int64_t)); // NOLINT(runtime/sizeof) + + buffer_ = reinterpret_cast(buffer); + pc_ = buffer_; + Reset(); +} + + +Assembler::~Assembler() { + ASSERT(finalized_ || (pc_ == buffer_)); + ASSERT(literals_.empty()); +} + + +void Assembler::Reset() { +#ifdef DEBUG + ASSERT((pc_ >= buffer_) && (pc_ < buffer_ + buffer_size_)); + ASSERT(literal_pool_monitor_ == 0); + memset(buffer_, 0, pc_ - buffer_); + finalized_ = false; +#endif + pc_ = buffer_; + literals_.clear(); + next_literal_pool_check_ = pc_ + kLiteralPoolCheckInterval; +} + + +void Assembler::FinalizeCode() { + EmitLiteralPool(); +#ifdef DEBUG + finalized_ = true; +#endif +} + + +void Assembler::bind(Label* label) { + label->is_bound_ = true; + label->target_ = pc_; + while (label->IsLinked()) { + // Get the address of the following instruction in the chain. + Instruction* next_link = label->link_->ImmPCOffsetTarget(); + // Update the instruction target. + label->link_->SetImmPCOffsetTarget(label->target_); + // Update the label's link. + // If the offset of the branch we just updated was 0 (kEndOfChain) we are + // done. + label->link_ = (label->link_ != next_link) ? next_link : NULL; + } +} + + +int Assembler::UpdateAndGetByteOffsetTo(Label* label) { + int offset; + ASSERT(sizeof(*pc_) == 1); + if (label->IsBound()) { + offset = label->target() - pc_; + } else if (label->IsLinked()) { + offset = label->link() - pc_; + } else { + offset = Label::kEndOfChain; + } + label->set_link(pc_); + return offset; +} + + +// Code generation. +void Assembler::br(const Register& xn) { + ASSERT(xn.Is64Bits()); + Emit(BR | Rn(xn)); +} + + +void Assembler::blr(const Register& xn) { + ASSERT(xn.Is64Bits()); + Emit(BLR | Rn(xn)); +} + + +void Assembler::ret(const Register& xn) { + ASSERT(xn.Is64Bits()); + Emit(RET | Rn(xn)); +} + + +void Assembler::b(int imm26) { + Emit(B | ImmUncondBranch(imm26)); +} + + +void Assembler::b(int imm19, Condition cond) { + Emit(B_cond | ImmCondBranch(imm19) | cond); +} + + +void Assembler::b(Label* label) { + b(UpdateAndGetInstructionOffsetTo(label)); +} + + +void Assembler::b(Label* label, Condition cond) { + b(UpdateAndGetInstructionOffsetTo(label), cond); +} + + +void Assembler::bl(int imm26) { + Emit(BL | ImmUncondBranch(imm26)); +} + + +void Assembler::bl(Label* label) { + bl(UpdateAndGetInstructionOffsetTo(label)); +} + + +void Assembler::cbz(const Register& rt, + int imm19) { + Emit(SF(rt) | CBZ | ImmCmpBranch(imm19) | Rt(rt)); +} + + +void Assembler::cbz(const Register& rt, + Label* label) { + cbz(rt, UpdateAndGetInstructionOffsetTo(label)); +} + + +void Assembler::cbnz(const Register& rt, + int imm19) { + Emit(SF(rt) | CBNZ | ImmCmpBranch(imm19) | Rt(rt)); +} + + +void Assembler::cbnz(const Register& rt, + Label* label) { + cbnz(rt, UpdateAndGetInstructionOffsetTo(label)); +} + + +void Assembler::tbz(const Register& rt, + unsigned bit_pos, + int imm14) { + ASSERT(rt.Is64Bits()); + Emit(TBZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt)); +} + + +void Assembler::tbz(const Register& rt, + unsigned bit_pos, + Label* label) { + tbz(rt, bit_pos, UpdateAndGetInstructionOffsetTo(label)); +} + + +void Assembler::tbnz(const Register& rt, + unsigned bit_pos, + int imm14) { + ASSERT(rt.Is64Bits()); + Emit(TBNZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt)); +} + + +void Assembler::tbnz(const Register& rt, + unsigned bit_pos, + Label* label) { + tbnz(rt, bit_pos, UpdateAndGetInstructionOffsetTo(label)); +} + + +void Assembler::adr(const Register& rd, int imm21) { + ASSERT(rd.Is64Bits()); + Emit(ADR | ImmPCRelAddress(imm21) | Rd(rd)); +} + + +void Assembler::adr(const Register& rd, Label* label) { + adr(rd, UpdateAndGetByteOffsetTo(label)); +} + + +void Assembler::add(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + AddSub(rd, rn, operand, S, ADD); +} + + +void Assembler::cmn(const Register& rn, + const Operand& operand) { + Register zr = AppropriateZeroRegFor(rn); + add(zr, rn, operand, SetFlags); +} + + +void Assembler::sub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + AddSub(rd, rn, operand, S, SUB); +} + + +void Assembler::cmp(const Register& rn, const Operand& operand) { + Register zr = AppropriateZeroRegFor(rn); + sub(zr, rn, operand, SetFlags); +} + + +void Assembler::neg(const Register& rd, const Operand& operand, FlagsUpdate S) { + Register zr = AppropriateZeroRegFor(rd); + sub(rd, zr, operand, S); +} + + +void Assembler::adc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + AddSubWithCarry(rd, rn, operand, S, ADC); +} + + +void Assembler::sbc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + AddSubWithCarry(rd, rn, operand, S, SBC); +} + + +void Assembler::ngc(const Register& rd, const Operand& operand, FlagsUpdate S) { + Register zr = AppropriateZeroRegFor(rd); + sbc(rd, zr, operand, S); +} + + +// Logical instructions. +void Assembler::and_(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + Logical(rd, rn, operand, (S == SetFlags) ? ANDS : AND); +} + + +void Assembler::tst(const Register& rn, + const Operand& operand) { + and_(AppropriateZeroRegFor(rn), rn, operand, SetFlags); +} + + +void Assembler::bic(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + Logical(rd, rn, operand, (S == SetFlags) ? BICS : BIC); +} + + +void Assembler::orr(const Register& rd, + const Register& rn, + const Operand& operand) { + Logical(rd, rn, operand, ORR); +} + + +void Assembler::orn(const Register& rd, + const Register& rn, + const Operand& operand) { + Logical(rd, rn, operand, ORN); +} + + +void Assembler::eor(const Register& rd, + const Register& rn, + const Operand& operand) { + Logical(rd, rn, operand, EOR); +} + + +void Assembler::eon(const Register& rd, + const Register& rn, + const Operand& operand) { + Logical(rd, rn, operand, EON); +} + + +void Assembler::lslv(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | LSLV | Rm(rm) | Rn(rn) | Rd(rd)); +} + + +void Assembler::lsrv(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | LSRV | Rm(rm) | Rn(rn) | Rd(rd)); +} + + +void Assembler::asrv(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | ASRV | Rm(rm) | Rn(rn) | Rd(rd)); +} + + +void Assembler::rorv(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | RORV | Rm(rm) | Rn(rn) | Rd(rd)); +} + + +// Bitfield operations. +void Assembler::bfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms) { + ASSERT(rd.size() == rn.size()); + Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); + Emit(SF(rd) | BFM | N | + ImmR(immr, rd.size()) | ImmS(imms, rd.size()) | Rn(rn) | Rd(rd)); +} + + +void Assembler::sbfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms) { + ASSERT(rd.size() == rn.size()); + Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); + Emit(SF(rd) | SBFM | N | + ImmR(immr, rd.size()) | ImmS(imms, rd.size()) | Rn(rn) | Rd(rd)); +} + + +void Assembler::ubfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms) { + ASSERT(rd.size() == rn.size()); + Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); + Emit(SF(rd) | UBFM | N | + ImmR(immr, rd.size()) | ImmS(imms, rd.size()) | Rn(rn) | Rd(rd)); +} + + +void Assembler::extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); + Emit(SF(rd) | EXTR | N | Rm(rm) | ImmS(lsb, rd.size()) | Rn(rn) | Rd(rd)); +} + + +void Assembler::csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ConditionalSelect(rd, rn, rm, cond, CSEL); +} + + +void Assembler::csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ConditionalSelect(rd, rn, rm, cond, CSINC); +} + + +void Assembler::csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ConditionalSelect(rd, rn, rm, cond, CSINV); +} + + +void Assembler::csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ConditionalSelect(rd, rn, rm, cond, CSNEG); +} + + +void Assembler::cset(const Register &rd, Condition cond) { + ASSERT((cond != al) && (cond != nv)); + Register zr = AppropriateZeroRegFor(rd); + csinc(rd, zr, zr, InvertCondition(cond)); +} + + +void Assembler::csetm(const Register &rd, Condition cond) { + ASSERT((cond != al) && (cond != nv)); + Register zr = AppropriateZeroRegFor(rd); + csinv(rd, zr, zr, InvertCondition(cond)); +} + + +void Assembler::cinc(const Register &rd, const Register &rn, Condition cond) { + ASSERT((cond != al) && (cond != nv)); + csinc(rd, rn, rn, InvertCondition(cond)); +} + + +void Assembler::cinv(const Register &rd, const Register &rn, Condition cond) { + ASSERT((cond != al) && (cond != nv)); + csinv(rd, rn, rn, InvertCondition(cond)); +} + + +void Assembler::cneg(const Register &rd, const Register &rn, Condition cond) { + ASSERT((cond != al) && (cond != nv)); + csneg(rd, rn, rn, InvertCondition(cond)); +} + + +void Assembler::ConditionalSelect(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond, + ConditionalSelectOp op) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | op | Rm(rm) | Cond(cond) | Rn(rn) | Rd(rd)); +} + + +void Assembler::ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond) { + ConditionalCompare(rn, operand, nzcv, cond, CCMN); +} + + +void Assembler::ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond) { + ConditionalCompare(rn, operand, nzcv, cond, CCMP); +} + + +void Assembler::DataProcessing3Source(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra, + DataProcessing3SourceOp op) { + Emit(SF(rd) | op | Rm(rm) | Ra(ra) | Rn(rn) | Rd(rd)); +} + + +void Assembler::mul(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(AreSameSizeAndType(rd, rn, rm)); + DataProcessing3Source(rd, rn, rm, AppropriateZeroRegFor(rd), MADD); +} + + +void Assembler::madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + DataProcessing3Source(rd, rn, rm, ra, MADD); +} + + +void Assembler::mneg(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(AreSameSizeAndType(rd, rn, rm)); + DataProcessing3Source(rd, rn, rm, AppropriateZeroRegFor(rd), MSUB); +} + + +void Assembler::msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + DataProcessing3Source(rd, rn, rm, ra, MSUB); +} + + +void Assembler::umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(rd.Is64Bits() && ra.Is64Bits()); + ASSERT(rn.Is32Bits() && rm.Is32Bits()); + DataProcessing3Source(rd, rn, rm, ra, UMADDL_x); +} + + +void Assembler::smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(rd.Is64Bits() && ra.Is64Bits()); + ASSERT(rn.Is32Bits() && rm.Is32Bits()); + DataProcessing3Source(rd, rn, rm, ra, SMADDL_x); +} + + +void Assembler::umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(rd.Is64Bits() && ra.Is64Bits()); + ASSERT(rn.Is32Bits() && rm.Is32Bits()); + DataProcessing3Source(rd, rn, rm, ra, UMSUBL_x); +} + + +void Assembler::smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(rd.Is64Bits() && ra.Is64Bits()); + ASSERT(rn.Is32Bits() && rm.Is32Bits()); + DataProcessing3Source(rd, rn, rm, ra, SMSUBL_x); +} + + +void Assembler::smull(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.Is64Bits()); + ASSERT(rn.Is32Bits() && rm.Is32Bits()); + DataProcessing3Source(rd, rn, rm, xzr, SMADDL_x); +} + + +void Assembler::sdiv(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | SDIV | Rm(rm) | Rn(rn) | Rd(rd)); +} + + +void Assembler::smulh(const Register& xd, + const Register& xn, + const Register& xm) { + ASSERT(xd.Is64Bits() && xn.Is64Bits() && xm.Is64Bits()); + DataProcessing3Source(xd, xn, xm, xzr, SMULH_x); +} + +void Assembler::udiv(const Register& rd, + const Register& rn, + const Register& rm) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == rm.size()); + Emit(SF(rd) | UDIV | Rm(rm) | Rn(rn) | Rd(rd)); +} + + +void Assembler::rbit(const Register& rd, + const Register& rn) { + DataProcessing1Source(rd, rn, RBIT); +} + + +void Assembler::rev16(const Register& rd, + const Register& rn) { + DataProcessing1Source(rd, rn, REV16); +} + + +void Assembler::rev32(const Register& rd, + const Register& rn) { + ASSERT(rd.Is64Bits()); + DataProcessing1Source(rd, rn, REV); +} + + +void Assembler::rev(const Register& rd, + const Register& rn) { + DataProcessing1Source(rd, rn, rd.Is64Bits() ? REV_x : REV_w); +} + + +void Assembler::clz(const Register& rd, + const Register& rn) { + DataProcessing1Source(rd, rn, CLZ); +} + + +void Assembler::cls(const Register& rd, + const Register& rn) { + DataProcessing1Source(rd, rn, CLS); +} + + +void Assembler::ldp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& src) { + LoadStorePair(rt, rt2, src, LoadPairOpFor(rt, rt2)); +} + + +void Assembler::stp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& dst) { + LoadStorePair(rt, rt2, dst, StorePairOpFor(rt, rt2)); +} + + +void Assembler::ldpsw(const Register& rt, + const Register& rt2, + const MemOperand& src) { + ASSERT(rt.Is64Bits()); + LoadStorePair(rt, rt2, src, LDPSW_x); +} + + +void Assembler::LoadStorePair(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairOp op) { + // 'rt' and 'rt2' can only be aliased for stores. + ASSERT(((op & LoadStorePairLBit) == 0) || !rt.Is(rt2)); + ASSERT(AreSameSizeAndType(rt, rt2)); + + Instr memop = op | Rt(rt) | Rt2(rt2) | RnSP(addr.base()) | + ImmLSPair(addr.offset(), CalcLSPairDataSize(op)); + + Instr addrmodeop; + if (addr.IsImmediateOffset()) { + addrmodeop = LoadStorePairOffsetFixed; + } else { + ASSERT(addr.offset() != 0); + if (addr.IsPreIndex()) { + addrmodeop = LoadStorePairPreIndexFixed; + } else { + ASSERT(addr.IsPostIndex()); + addrmodeop = LoadStorePairPostIndexFixed; + } + } + Emit(addrmodeop | memop); +} + + +void Assembler::ldnp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& src) { + LoadStorePairNonTemporal(rt, rt2, src, + LoadPairNonTemporalOpFor(rt, rt2)); +} + + +void Assembler::stnp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& dst) { + LoadStorePairNonTemporal(rt, rt2, dst, + StorePairNonTemporalOpFor(rt, rt2)); +} + + +void Assembler::LoadStorePairNonTemporal(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairNonTemporalOp op) { + ASSERT(!rt.Is(rt2)); + ASSERT(AreSameSizeAndType(rt, rt2)); + ASSERT(addr.IsImmediateOffset()); + + LSDataSize size = CalcLSPairDataSize( + static_cast(op & LoadStorePairMask)); + Emit(op | Rt(rt) | Rt2(rt2) | RnSP(addr.base()) | + ImmLSPair(addr.offset(), size)); +} + + +// Memory instructions. +void Assembler::ldrb(const Register& rt, const MemOperand& src) { + LoadStore(rt, src, LDRB_w); +} + + +void Assembler::strb(const Register& rt, const MemOperand& dst) { + LoadStore(rt, dst, STRB_w); +} + + +void Assembler::ldrsb(const Register& rt, const MemOperand& src) { + LoadStore(rt, src, rt.Is64Bits() ? LDRSB_x : LDRSB_w); +} + + +void Assembler::ldrh(const Register& rt, const MemOperand& src) { + LoadStore(rt, src, LDRH_w); +} + + +void Assembler::strh(const Register& rt, const MemOperand& dst) { + LoadStore(rt, dst, STRH_w); +} + + +void Assembler::ldrsh(const Register& rt, const MemOperand& src) { + LoadStore(rt, src, rt.Is64Bits() ? LDRSH_x : LDRSH_w); +} + + +void Assembler::ldr(const CPURegister& rt, const MemOperand& src) { + LoadStore(rt, src, LoadOpFor(rt)); +} + + +void Assembler::str(const CPURegister& rt, const MemOperand& src) { + LoadStore(rt, src, StoreOpFor(rt)); +} + + +void Assembler::ldrsw(const Register& rt, const MemOperand& src) { + ASSERT(rt.Is64Bits()); + LoadStore(rt, src, LDRSW_x); +} + + +void Assembler::ldr(const Register& rt, uint64_t imm) { + LoadLiteral(rt, imm, rt.Is64Bits() ? LDR_x_lit : LDR_w_lit); +} + + +void Assembler::ldr(const FPRegister& ft, double imm) { + uint64_t rawbits = 0; + LoadLiteralOp op; + + if (ft.Is64Bits()) { + rawbits = double_to_rawbits(imm); + op = LDR_d_lit; + } else { + ASSERT(ft.Is32Bits()); + float float_imm = static_cast(imm); + rawbits = float_to_rawbits(float_imm); + op = LDR_s_lit; + } + + LoadLiteral(ft, rawbits, op); +} + + +void Assembler::mov(const Register& rd, const Register& rm) { + // Moves involving the stack pointer are encoded as add immediate with + // second operand of zero. Otherwise, orr with first operand zr is + // used. + if (rd.IsSP() || rm.IsSP()) { + add(rd, rm, 0); + } else { + orr(rd, AppropriateZeroRegFor(rd), rm); + } +} + + +void Assembler::mvn(const Register& rd, const Operand& operand) { + orn(rd, AppropriateZeroRegFor(rd), operand); +} + + +void Assembler::mrs(const Register& rt, SystemRegister sysreg) { + ASSERT(rt.Is64Bits()); + Emit(MRS | ImmSystemRegister(sysreg) | Rt(rt)); +} + + +void Assembler::msr(SystemRegister sysreg, const Register& rt) { + ASSERT(rt.Is64Bits()); + Emit(MSR | Rt(rt) | ImmSystemRegister(sysreg)); +} + + +void Assembler::hint(SystemHint code) { + Emit(HINT | ImmHint(code) | Rt(xzr)); +} + + +void Assembler::fmov(FPRegister fd, double imm) { + if (fd.Is64Bits() && IsImmFP64(imm)) { + Emit(FMOV_d_imm | Rd(fd) | ImmFP64(imm)); + } else if (fd.Is32Bits() && IsImmFP32(imm)) { + Emit(FMOV_s_imm | Rd(fd) | ImmFP32(static_cast(imm))); + } else if ((imm == 0.0) && (copysign(1.0, imm) == 1.0)) { + Register zr = AppropriateZeroRegFor(fd); + fmov(fd, zr); + } else { + ldr(fd, imm); + } +} + + +void Assembler::fmov(Register rd, FPRegister fn) { + ASSERT(rd.size() == fn.size()); + FPIntegerConvertOp op = rd.Is32Bits() ? FMOV_ws : FMOV_xd; + Emit(op | Rd(rd) | Rn(fn)); +} + + +void Assembler::fmov(FPRegister fd, Register rn) { + ASSERT(fd.size() == rn.size()); + FPIntegerConvertOp op = fd.Is32Bits() ? FMOV_sw : FMOV_dx; + Emit(op | Rd(fd) | Rn(rn)); +} + + +void Assembler::fmov(FPRegister fd, FPRegister fn) { + ASSERT(fd.size() == fn.size()); + Emit(FPType(fd) | FMOV | Rd(fd) | Rn(fn)); +} + + +void Assembler::fadd(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm) { + FPDataProcessing2Source(fd, fn, fm, FADD); +} + + +void Assembler::fsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm) { + FPDataProcessing2Source(fd, fn, fm, FSUB); +} + + +void Assembler::fmul(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm) { + FPDataProcessing2Source(fd, fn, fm, FMUL); +} + + +void Assembler::fmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa) { + FPDataProcessing3Source(fd, fn, fm, fa, fd.Is32Bits() ? FMSUB_s : FMSUB_d); +} + + +void Assembler::fdiv(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm) { + FPDataProcessing2Source(fd, fn, fm, FDIV); +} + + +void Assembler::fmax(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm) { + FPDataProcessing2Source(fd, fn, fm, FMAX); +} + + +void Assembler::fmin(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm) { + FPDataProcessing2Source(fd, fn, fm, FMIN); +} + + +void Assembler::fabs(const FPRegister& fd, + const FPRegister& fn) { + ASSERT(fd.SizeInBits() == fn.SizeInBits()); + FPDataProcessing1Source(fd, fn, FABS); +} + + +void Assembler::fneg(const FPRegister& fd, + const FPRegister& fn) { + ASSERT(fd.SizeInBits() == fn.SizeInBits()); + FPDataProcessing1Source(fd, fn, FNEG); +} + + +void Assembler::fsqrt(const FPRegister& fd, + const FPRegister& fn) { + ASSERT(fd.SizeInBits() == fn.SizeInBits()); + FPDataProcessing1Source(fd, fn, FSQRT); +} + + +void Assembler::frintn(const FPRegister& fd, + const FPRegister& fn) { + ASSERT(fd.SizeInBits() == fn.SizeInBits()); + FPDataProcessing1Source(fd, fn, FRINTN); +} + + +void Assembler::frintz(const FPRegister& fd, + const FPRegister& fn) { + ASSERT(fd.SizeInBits() == fn.SizeInBits()); + FPDataProcessing1Source(fd, fn, FRINTZ); +} + + +void Assembler::fcmp(const FPRegister& fn, + const FPRegister& fm) { + ASSERT(fn.size() == fm.size()); + Emit(FPType(fn) | FCMP | Rm(fm) | Rn(fn)); +} + + +void Assembler::fcmp(const FPRegister& fn, + double value) { + USE(value); + // Although the fcmp instruction can strictly only take an immediate value of + // +0.0, we don't need to check for -0.0 because the sign of 0.0 doesn't + // affect the result of the comparison. + ASSERT(value == 0.0); + Emit(FPType(fn) | FCMP_zero | Rn(fn)); +} + + +void Assembler::fccmp(const FPRegister& fn, + const FPRegister& fm, + StatusFlags nzcv, + Condition cond) { + ASSERT(fn.size() == fm.size()); + Emit(FPType(fn) | FCCMP | Rm(fm) | Cond(cond) | Rn(fn) | Nzcv(nzcv)); +} + + +void Assembler::fcsel(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + Condition cond) { + ASSERT(fd.size() == fn.size()); + ASSERT(fd.size() == fm.size()); + Emit(FPType(fd) | FCSEL | Rm(fm) | Cond(cond) | Rn(fn) | Rd(fd)); +} + + +void Assembler::FPConvertToInt(const Register& rd, + const FPRegister& fn, + FPIntegerConvertOp op) { + Emit(SF(rd) | FPType(fn) | op | Rn(fn) | Rd(rd)); +} + + +void Assembler::fcvt(const FPRegister& fd, + const FPRegister& fn) { + if (fd.Is64Bits()) { + // Convert float to double. + ASSERT(fn.Is32Bits()); + FPDataProcessing1Source(fd, fn, FCVT_ds); + } else { + // Convert double to float. + ASSERT(fn.Is64Bits()); + FPDataProcessing1Source(fd, fn, FCVT_sd); + } +} + + +void Assembler::fcvtmu(const Register& rd, const FPRegister& fn) { + FPConvertToInt(rd, fn, FCVTMU); +} + + +void Assembler::fcvtms(const Register& rd, const FPRegister& fn) { + FPConvertToInt(rd, fn, FCVTMS); +} + + +void Assembler::fcvtnu(const Register& rd, + const FPRegister& fn) { + FPConvertToInt(rd, fn, FCVTNU); +} + + +void Assembler::fcvtns(const Register& rd, + const FPRegister& fn) { + FPConvertToInt(rd, fn, FCVTNS); +} + + +void Assembler::fcvtzu(const Register& rd, + const FPRegister& fn) { + FPConvertToInt(rd, fn, FCVTZU); +} + + +void Assembler::fcvtzs(const Register& rd, + const FPRegister& fn) { + FPConvertToInt(rd, fn, FCVTZS); +} + + +void Assembler::scvtf(const FPRegister& fd, + const Register& rn, + unsigned fbits) { + if (fbits == 0) { + Emit(SF(rn) | FPType(fd) | SCVTF | Rn(rn) | Rd(fd)); + } else { + Emit(SF(rn) | FPType(fd) | SCVTF_fixed | FPScale(64 - fbits) | Rn(rn) | + Rd(fd)); + } +} + + +void Assembler::ucvtf(const FPRegister& fd, + const Register& rn, + unsigned fbits) { + if (fbits == 0) { + Emit(SF(rn) | FPType(fd) | UCVTF | Rn(rn) | Rd(fd)); + } else { + Emit(SF(rn) | FPType(fd) | UCVTF_fixed | FPScale(64 - fbits) | Rn(rn) | + Rd(fd)); + } +} + + +// Note: +// Below, a difference in case for the same letter indicates a +// negated bit. +// If b is 1, then B is 0. +Instr Assembler::ImmFP32(float imm) { + ASSERT(IsImmFP32(imm)); + // bits: aBbb.bbbc.defg.h000.0000.0000.0000.0000 + uint32_t bits = float_to_rawbits(imm); + // bit7: a000.0000 + uint32_t bit7 = ((bits >> 31) & 0x1) << 7; + // bit6: 0b00.0000 + uint32_t bit6 = ((bits >> 29) & 0x1) << 6; + // bit5_to_0: 00cd.efgh + uint32_t bit5_to_0 = (bits >> 19) & 0x3f; + + return (bit7 | bit6 | bit5_to_0) << ImmFP_offset; +} + + +Instr Assembler::ImmFP64(double imm) { + ASSERT(IsImmFP64(imm)); + // bits: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 + uint64_t bits = double_to_rawbits(imm); + // bit7: a000.0000 + uint32_t bit7 = ((bits >> 63) & 0x1) << 7; + // bit6: 0b00.0000 + uint32_t bit6 = ((bits >> 61) & 0x1) << 6; + // bit5_to_0: 00cd.efgh + uint32_t bit5_to_0 = (bits >> 48) & 0x3f; + + return (bit7 | bit6 | bit5_to_0) << ImmFP_offset; +} + + +// Code generation helpers. +void Assembler::MoveWide(const Register& rd, + uint64_t imm, + int shift, + MoveWideImmediateOp mov_op) { + if (shift >= 0) { + // Explicit shift specified. + ASSERT((shift == 0) || (shift == 16) || (shift == 32) || (shift == 48)); + ASSERT(rd.Is64Bits() || (shift == 0) || (shift == 16)); + shift /= 16; + } else { + // Calculate a new immediate and shift combination to encode the immediate + // argument. + shift = 0; + if ((imm & ~0xffffUL) == 0) { + // Nothing to do. + } else if ((imm & ~(0xffffUL << 16)) == 0) { + imm >>= 16; + shift = 1; + } else if ((imm & ~(0xffffUL << 32)) == 0) { + ASSERT(rd.Is64Bits()); + imm >>= 32; + shift = 2; + } else if ((imm & ~(0xffffUL << 48)) == 0) { + ASSERT(rd.Is64Bits()); + imm >>= 48; + shift = 3; + } + } + + ASSERT(is_uint16(imm)); + + Emit(SF(rd) | MoveWideImmediateFixed | mov_op | + Rd(rd) | ImmMoveWide(imm) | ShiftMoveWide(shift)); +} + + +void Assembler::AddSub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op) { + ASSERT(rd.size() == rn.size()); + if (operand.IsImmediate()) { + int64_t immediate = operand.immediate(); + ASSERT(IsImmAddSub(immediate)); + Instr dest_reg = (S == SetFlags) ? Rd(rd) : RdSP(rd); + Emit(SF(rd) | AddSubImmediateFixed | op | Flags(S) | + ImmAddSub(immediate) | dest_reg | RnSP(rn)); + } else if (operand.IsShiftedRegister()) { + ASSERT(operand.reg().size() == rd.size()); + ASSERT(operand.shift() != ROR); + + // For instructions of the form: + // add/sub wsp, , [, LSL #0-3 ] + // add/sub , wsp, [, LSL #0-3 ] + // add/sub wsp, wsp, [, LSL #0-3 ] + // adds/subs , wsp, [, LSL #0-3 ] + // or their 64-bit register equivalents, convert the operand from shifted to + // extended register mode, and emit an add/sub extended instruction. + if (rn.IsSP() || rd.IsSP()) { + ASSERT(!(rd.IsSP() && (S == SetFlags))); + DataProcExtendedRegister(rd, rn, operand.ToExtendedRegister(), S, + AddSubExtendedFixed | op); + } else { + DataProcShiftedRegister(rd, rn, operand, S, AddSubShiftedFixed | op); + } + } else { + ASSERT(operand.IsExtendedRegister()); + DataProcExtendedRegister(rd, rn, operand, S, AddSubExtendedFixed | op); + } +} + + +void Assembler::AddSubWithCarry(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op) { + ASSERT(rd.size() == rn.size()); + ASSERT(rd.size() == operand.reg().size()); + ASSERT(operand.IsShiftedRegister() && (operand.shift_amount() == 0)); + Emit(SF(rd) | op | Flags(S) | Rm(operand.reg()) | Rn(rn) | Rd(rd)); +} + + +void Assembler::hlt(int code) { + ASSERT(is_uint16(code)); + Emit(HLT | ImmException(code)); +} + + +void Assembler::brk(int code) { + ASSERT(is_uint16(code)); + Emit(BRK | ImmException(code)); +} + + +void Assembler::Logical(const Register& rd, + const Register& rn, + const Operand& operand, + LogicalOp op) { + ASSERT(rd.size() == rn.size()); + if (operand.IsImmediate()) { + int64_t immediate = operand.immediate(); + unsigned reg_size = rd.size(); + + ASSERT(immediate != 0); + ASSERT(immediate != -1); + ASSERT(rd.Is64Bits() || is_uint32(immediate)); + + // If the operation is NOT, invert the operation and immediate. + if ((op & NOT) == NOT) { + op = static_cast(op & ~NOT); + immediate = rd.Is64Bits() ? ~immediate : (~immediate & kWRegMask); + } + + unsigned n, imm_s, imm_r; + if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) { + // Immediate can be encoded in the instruction. + LogicalImmediate(rd, rn, n, imm_s, imm_r, op); + } else { + // This case is handled in the macro assembler. + UNREACHABLE(); + } + } else { + ASSERT(operand.IsShiftedRegister()); + ASSERT(operand.reg().size() == rd.size()); + Instr dp_op = static_cast(op | LogicalShiftedFixed); + DataProcShiftedRegister(rd, rn, operand, LeaveFlags, dp_op); + } +} + + +void Assembler::LogicalImmediate(const Register& rd, + const Register& rn, + unsigned n, + unsigned imm_s, + unsigned imm_r, + LogicalOp op) { + unsigned reg_size = rd.size(); + Instr dest_reg = (op == ANDS) ? Rd(rd) : RdSP(rd); + Emit(SF(rd) | LogicalImmediateFixed | op | BitN(n, reg_size) | + ImmSetBits(imm_s, reg_size) | ImmRotate(imm_r, reg_size) | dest_reg | + Rn(rn)); +} + + +void Assembler::ConditionalCompare(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op) { + Instr ccmpop; + if (operand.IsImmediate()) { + int64_t immediate = operand.immediate(); + ASSERT(IsImmConditionalCompare(immediate)); + ccmpop = ConditionalCompareImmediateFixed | op | ImmCondCmp(immediate); + } else { + ASSERT(operand.IsShiftedRegister() && (operand.shift_amount() == 0)); + ccmpop = ConditionalCompareRegisterFixed | op | Rm(operand.reg()); + } + Emit(SF(rn) | ccmpop | Cond(cond) | Rn(rn) | Nzcv(nzcv)); +} + + +void Assembler::DataProcessing1Source(const Register& rd, + const Register& rn, + DataProcessing1SourceOp op) { + ASSERT(rd.size() == rn.size()); + Emit(SF(rn) | op | Rn(rn) | Rd(rd)); +} + + +void Assembler::FPDataProcessing1Source(const FPRegister& fd, + const FPRegister& fn, + FPDataProcessing1SourceOp op) { + Emit(FPType(fn) | op | Rn(fn) | Rd(fd)); +} + + +void Assembler::FPDataProcessing2Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + FPDataProcessing2SourceOp op) { + ASSERT(fd.size() == fn.size()); + ASSERT(fd.size() == fm.size()); + Emit(FPType(fd) | op | Rm(fm) | Rn(fn) | Rd(fd)); +} + + +void Assembler::FPDataProcessing3Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa, + FPDataProcessing3SourceOp op) { + ASSERT(AreSameSizeAndType(fd, fn, fm, fa)); + Emit(FPType(fd) | op | Rm(fm) | Rn(fn) | Rd(fd) | Ra(fa)); +} + + +void Assembler::EmitShift(const Register& rd, + const Register& rn, + Shift shift, + unsigned shift_amount) { + switch (shift) { + case LSL: + lsl(rd, rn, shift_amount); + break; + case LSR: + lsr(rd, rn, shift_amount); + break; + case ASR: + asr(rd, rn, shift_amount); + break; + case ROR: + ror(rd, rn, shift_amount); + break; + default: + UNREACHABLE(); + } +} + + +void Assembler::EmitExtendShift(const Register& rd, + const Register& rn, + Extend extend, + unsigned left_shift) { + ASSERT(rd.size() >= rn.size()); + unsigned reg_size = rd.size(); + // Use the correct size of register. + Register rn_ = Register(rn.code(), rd.size()); + // Bits extracted are high_bit:0. + unsigned high_bit = (8 << (extend & 0x3)) - 1; + // Number of bits left in the result that are not introduced by the shift. + unsigned non_shift_bits = (reg_size - left_shift) & (reg_size - 1); + + if ((non_shift_bits > high_bit) || (non_shift_bits == 0)) { + switch (extend) { + case UXTB: + case UXTH: + case UXTW: ubfm(rd, rn_, non_shift_bits, high_bit); break; + case SXTB: + case SXTH: + case SXTW: sbfm(rd, rn_, non_shift_bits, high_bit); break; + case UXTX: + case SXTX: { + ASSERT(rn.size() == kXRegSize); + // Nothing to extend. Just shift. + lsl(rd, rn_, left_shift); + break; + } + default: UNREACHABLE(); + } + } else { + // No need to extend as the extended bits would be shifted away. + lsl(rd, rn_, left_shift); + } +} + + +void Assembler::DataProcShiftedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op) { + ASSERT(operand.IsShiftedRegister()); + ASSERT(rn.Is64Bits() || (rn.Is32Bits() && is_uint5(operand.shift_amount()))); + Emit(SF(rd) | op | Flags(S) | + ShiftDP(operand.shift()) | ImmDPShift(operand.shift_amount()) | + Rm(operand.reg()) | Rn(rn) | Rd(rd)); +} + + +void Assembler::DataProcExtendedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op) { + Instr dest_reg = (S == SetFlags) ? Rd(rd) : RdSP(rd); + Emit(SF(rd) | op | Flags(S) | Rm(operand.reg()) | + ExtendMode(operand.extend()) | ImmExtendShift(operand.shift_amount()) | + dest_reg | RnSP(rn)); +} + + +bool Assembler::IsImmAddSub(int64_t immediate) { + return is_uint12(immediate) || + (is_uint12(immediate >> 12) && ((immediate & 0xfff) == 0)); +} + +void Assembler::LoadStore(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op) { + Instr memop = op | Rt(rt) | RnSP(addr.base()); + ptrdiff_t offset = addr.offset(); + + if (addr.IsImmediateOffset()) { + LSDataSize size = CalcLSDataSize(op); + if (IsImmLSScaled(offset, size)) { + // Use the scaled addressing mode. + Emit(LoadStoreUnsignedOffsetFixed | memop | + ImmLSUnsigned(offset >> size)); + } else if (IsImmLSUnscaled(offset)) { + // Use the unscaled addressing mode. + Emit(LoadStoreUnscaledOffsetFixed | memop | ImmLS(offset)); + } else { + // This case is handled in the macro assembler. + UNREACHABLE(); + } + } else if (addr.IsRegisterOffset()) { + Extend ext = addr.extend(); + Shift shift = addr.shift(); + unsigned shift_amount = addr.shift_amount(); + + // LSL is encoded in the option field as UXTX. + if (shift == LSL) { + ext = UXTX; + } + + // Shifts are encoded in one bit, indicating a left shift by the memory + // access size. + ASSERT((shift_amount == 0) || + (shift_amount == static_cast(CalcLSDataSize(op)))); + Emit(LoadStoreRegisterOffsetFixed | memop | Rm(addr.regoffset()) | + ExtendMode(ext) | ImmShiftLS((shift_amount > 0) ? 1 : 0)); + } else { + if (IsImmLSUnscaled(offset)) { + if (addr.IsPreIndex()) { + Emit(LoadStorePreIndexFixed | memop | ImmLS(offset)); + } else { + ASSERT(addr.IsPostIndex()); + Emit(LoadStorePostIndexFixed | memop | ImmLS(offset)); + } + } else { + // This case is handled in the macro assembler. + UNREACHABLE(); + } + } +} + + +bool Assembler::IsImmLSUnscaled(ptrdiff_t offset) { + return is_int9(offset); +} + + +bool Assembler::IsImmLSScaled(ptrdiff_t offset, LSDataSize size) { + bool offset_is_size_multiple = (((offset >> size) << size) == offset); + return offset_is_size_multiple && is_uint12(offset >> size); +} + + +void Assembler::LoadLiteral(const CPURegister& rt, + uint64_t imm, + LoadLiteralOp op) { + ASSERT(is_int32(imm) || is_uint32(imm) || (rt.Is64Bits())); + + BlockLiteralPoolScope scope(this); + RecordLiteral(imm, rt.SizeInBytes()); + Emit(op | ImmLLiteral(0) | Rt(rt)); +} + + +// Test if a given value can be encoded in the immediate field of a logical +// instruction. +// If it can be encoded, the function returns true, and values pointed to by n, +// imm_s and imm_r are updated with immediates encoded in the format required +// by the corresponding fields in the logical instruction. +// If it can not be encoded, the function returns false, and the values pointed +// to by n, imm_s and imm_r are undefined. +bool Assembler::IsImmLogical(uint64_t value, + unsigned width, + unsigned* n, + unsigned* imm_s, + unsigned* imm_r) { + ASSERT((n != NULL) && (imm_s != NULL) && (imm_r != NULL)); + ASSERT((width == kWRegSize) || (width == kXRegSize)); + + // Logical immediates are encoded using parameters n, imm_s and imm_r using + // the following table: + // + // N imms immr size S R + // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) + // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) + // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) + // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) + // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) + // 0 11110s xxxxxr 2 UInt(s) UInt(r) + // (s bits must not be all set) + // + // A pattern is constructed of size bits, where the least significant S+1 + // bits are set. The pattern is rotated right by R, and repeated across a + // 32 or 64-bit value, depending on destination register width. + // + // To test if an arbitrary immediate can be encoded using this scheme, an + // iterative algorithm is used. + // + // TODO: This code does not consider using X/W register overlap to support + // 64-bit immediates where the top 32-bits are zero, and the bottom 32-bits + // are an encodable logical immediate. + + // 1. If the value has all set or all clear bits, it can't be encoded. + if ((value == 0) || (value == 0xffffffffffffffffUL) || + ((width == kWRegSize) && (value == 0xffffffff))) { + return false; + } + + unsigned lead_zero = CountLeadingZeros(value, width); + unsigned lead_one = CountLeadingZeros(~value, width); + unsigned trail_zero = CountTrailingZeros(value, width); + unsigned trail_one = CountTrailingZeros(~value, width); + unsigned set_bits = CountSetBits(value, width); + + // The fixed bits in the immediate s field. + // If width == 64 (X reg), start at 0xFFFFFF80. + // If width == 32 (W reg), start at 0xFFFFFFC0, as the iteration for 64-bit + // widths won't be executed. + int imm_s_fixed = (width == kXRegSize) ? -128 : -64; + int imm_s_mask = 0x3F; + + for (;;) { + // 2. If the value is two bits wide, it can be encoded. + if (width == 2) { + *n = 0; + *imm_s = 0x3C; + *imm_r = (value & 3) - 1; + return true; + } + + *n = (width == 64) ? 1 : 0; + *imm_s = ((imm_s_fixed | (set_bits - 1)) & imm_s_mask); + if ((lead_zero + set_bits) == width) { + *imm_r = 0; + } else { + *imm_r = (lead_zero > 0) ? (width - trail_zero) : lead_one; + } + + // 3. If the sum of leading zeros, trailing zeros and set bits is equal to + // the bit width of the value, it can be encoded. + if (lead_zero + trail_zero + set_bits == width) { + return true; + } + + // 4. If the sum of leading ones, trailing ones and unset bits in the + // value is equal to the bit width of the value, it can be encoded. + if (lead_one + trail_one + (width - set_bits) == width) { + return true; + } + + // 5. If the most-significant half of the bitwise value is equal to the + // least-significant half, return to step 2 using the least-significant + // half of the value. + uint64_t mask = (1UL << (width >> 1)) - 1; + if ((value & mask) == ((value >> (width >> 1)) & mask)) { + width >>= 1; + set_bits >>= 1; + imm_s_fixed >>= 1; + continue; + } + + // 6. Otherwise, the value can't be encoded. + return false; + } +} + +bool Assembler::IsImmConditionalCompare(int64_t immediate) { + return is_uint5(immediate); +} + + +bool Assembler::IsImmFP32(float imm) { + // Valid values will have the form: + // aBbb.bbbc.defg.h000.0000.0000.0000.0000 + uint32_t bits = float_to_rawbits(imm); + // bits[19..0] are cleared. + if ((bits & 0x7ffff) != 0) { + return false; + } + + // bits[29..25] are all set or all cleared. + uint32_t b_pattern = (bits >> 16) & 0x3e00; + if (b_pattern != 0 && b_pattern != 0x3e00) { + return false; + } + + // bit[30] and bit[29] are opposite. + if (((bits ^ (bits << 1)) & 0x40000000) == 0) { + return false; + } + + return true; +} + + +bool Assembler::IsImmFP64(double imm) { + // Valid values will have the form: + // aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 + uint64_t bits = double_to_rawbits(imm); + // bits[47..0] are cleared. + if ((bits & 0xffffffffffffL) != 0) { + return false; + } + + // bits[61..54] are all set or all cleared. + uint32_t b_pattern = (bits >> 48) & 0x3fc0; + if (b_pattern != 0 && b_pattern != 0x3fc0) { + return false; + } + + // bit[62] and bit[61] are opposite. + if (((bits ^ (bits << 1)) & 0x4000000000000000L) == 0) { + return false; + } + + return true; +} + + +LoadStoreOp Assembler::LoadOpFor(const CPURegister& rt) { + ASSERT(rt.IsValid()); + if (rt.IsRegister()) { + return rt.Is64Bits() ? LDR_x : LDR_w; + } else { + ASSERT(rt.IsFPRegister()); + return rt.Is64Bits() ? LDR_d : LDR_s; + } +} + + +LoadStorePairOp Assembler::LoadPairOpFor(const CPURegister& rt, + const CPURegister& rt2) { + ASSERT(AreSameSizeAndType(rt, rt2)); + USE(rt2); + if (rt.IsRegister()) { + return rt.Is64Bits() ? LDP_x : LDP_w; + } else { + ASSERT(rt.IsFPRegister()); + return rt.Is64Bits() ? LDP_d : LDP_s; + } +} + + +LoadStoreOp Assembler::StoreOpFor(const CPURegister& rt) { + ASSERT(rt.IsValid()); + if (rt.IsRegister()) { + return rt.Is64Bits() ? STR_x : STR_w; + } else { + ASSERT(rt.IsFPRegister()); + return rt.Is64Bits() ? STR_d : STR_s; + } +} + + +LoadStorePairOp Assembler::StorePairOpFor(const CPURegister& rt, + const CPURegister& rt2) { + ASSERT(AreSameSizeAndType(rt, rt2)); + USE(rt2); + if (rt.IsRegister()) { + return rt.Is64Bits() ? STP_x : STP_w; + } else { + ASSERT(rt.IsFPRegister()); + return rt.Is64Bits() ? STP_d : STP_s; + } +} + + +LoadStorePairNonTemporalOp Assembler::LoadPairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2) { + ASSERT(AreSameSizeAndType(rt, rt2)); + USE(rt2); + if (rt.IsRegister()) { + return rt.Is64Bits() ? LDNP_x : LDNP_w; + } else { + ASSERT(rt.IsFPRegister()); + return rt.Is64Bits() ? LDNP_d : LDNP_s; + } +} + + +LoadStorePairNonTemporalOp Assembler::StorePairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2) { + ASSERT(AreSameSizeAndType(rt, rt2)); + USE(rt2); + if (rt.IsRegister()) { + return rt.Is64Bits() ? STNP_x : STNP_w; + } else { + ASSERT(rt.IsFPRegister()); + return rt.Is64Bits() ? STNP_d : STNP_s; + } +} + + +void Assembler::RecordLiteral(int64_t imm, unsigned size) { + literals_.push_front(new Literal(pc_, imm, size)); +} + + +// Check if a literal pool should be emitted. Currently a literal is emitted +// when: +// * the distance to the first literal load handled by this pool is greater +// than the recommended distance and the literal pool can be emitted without +// generating a jump over it. +// * the distance to the first literal load handled by this pool is greater +// than twice the recommended distance. +// TODO: refine this heuristic using real world data. +void Assembler::CheckLiteralPool(LiteralPoolEmitOption option) { + if (IsLiteralPoolBlocked()) { + // Literal pool emission is forbidden, no point in doing further checks. + return; + } + + if (literals_.empty()) { + // No literal pool to emit. + next_literal_pool_check_ += kLiteralPoolCheckInterval; + return; + } + + intptr_t distance = pc_ - literals_.back()->pc_; + if ((distance < kRecommendedLiteralPoolRange) || + ((option == JumpRequired) && + (distance < (2 * kRecommendedLiteralPoolRange)))) { + // We prefer not to have to jump over the literal pool. + next_literal_pool_check_ += kLiteralPoolCheckInterval; + return; + } + + EmitLiteralPool(option); +} + + +void Assembler::EmitLiteralPool(LiteralPoolEmitOption option) { + // Prevent recursive calls while emitting the literal pool. + BlockLiteralPoolScope scope(this); + + Label marker; + Label start_of_pool; + Label end_of_pool; + + if (option == JumpRequired) { + b(&end_of_pool); + } + + // Leave space for a literal pool marker. This is populated later, once the + // size of the pool is known. + bind(&marker); + nop(); + + // Now populate the literal pool. + bind(&start_of_pool); + std::list::iterator it; + for (it = literals_.begin(); it != literals_.end(); it++) { + // Update the load-literal instruction to point to this pool entry. + Instruction* load_literal = (*it)->pc_; + load_literal->SetImmLLiteral(pc_); + // Copy the data into the pool. + uint64_t value= (*it)->value_; + unsigned size = (*it)->size_; + ASSERT((size == kXRegSizeInBytes) || (size == kWRegSizeInBytes)); + ASSERT((pc_ + size) <= (buffer_ + buffer_size_)); + memcpy(pc_, &value, size); + pc_ += size; + delete *it; + } + literals_.clear(); + bind(&end_of_pool); + + // The pool size should always be a multiple of four bytes because that is the + // scaling applied by the LDR(literal) instruction, even for X-register loads. + ASSERT((SizeOfCodeGeneratedSince(&start_of_pool) % 4) == 0); + uint64_t pool_size = SizeOfCodeGeneratedSince(&start_of_pool) / 4; + + // Literal pool marker indicating the size in words of the literal pool. + // We use a literal load to the zero register, the offset indicating the + // size in words. This instruction can encode a large enough offset to span + // the entire pool at its maximum size. + Instr marker_instruction = LDR_x_lit | ImmLLiteral(pool_size) | Rt(xzr); + memcpy(marker.target(), &marker_instruction, kInstructionSize); + + next_literal_pool_check_ = pc_ + kLiteralPoolCheckInterval; +} + + +// Return the size in bytes, required by the literal pool entries. This does +// not include any marker or branch over the literal pool itself. +size_t Assembler::LiteralPoolSize() { + size_t size = 0; + + std::list::iterator it; + for (it = literals_.begin(); it != literals_.end(); it++) { + size += (*it)->size_; + } + + return size; +} + + +bool AreAliased(const CPURegister& reg1, const CPURegister& reg2, + const CPURegister& reg3, const CPURegister& reg4, + const CPURegister& reg5, const CPURegister& reg6, + const CPURegister& reg7, const CPURegister& reg8) { + int number_of_valid_regs = 0; + int number_of_valid_fpregs = 0; + + RegList unique_regs = 0; + RegList unique_fpregs = 0; + + const CPURegister regs[] = {reg1, reg2, reg3, reg4, reg5, reg6, reg7, reg8}; + + for (unsigned i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) { + if (regs[i].IsRegister()) { + number_of_valid_regs++; + unique_regs |= regs[i].Bit(); + } else if (regs[i].IsFPRegister()) { + number_of_valid_fpregs++; + unique_fpregs |= regs[i].Bit(); + } else { + ASSERT(!regs[i].IsValid()); + } + } + + int number_of_unique_regs = + CountSetBits(unique_regs, sizeof(unique_regs) * 8); + int number_of_unique_fpregs = + CountSetBits(unique_fpregs, sizeof(unique_fpregs) * 8); + + ASSERT(number_of_valid_regs >= number_of_unique_regs); + ASSERT(number_of_valid_fpregs >= number_of_unique_fpregs); + + return (number_of_valid_regs != number_of_unique_regs) || + (number_of_valid_fpregs != number_of_unique_fpregs); +} + + +bool AreSameSizeAndType(const CPURegister& reg1, const CPURegister& reg2, + const CPURegister& reg3, const CPURegister& reg4, + const CPURegister& reg5, const CPURegister& reg6, + const CPURegister& reg7, const CPURegister& reg8) { + ASSERT(reg1.IsValid()); + bool match = true; + match &= !reg2.IsValid() || reg2.IsSameSizeAndType(reg1); + match &= !reg3.IsValid() || reg3.IsSameSizeAndType(reg1); + match &= !reg4.IsValid() || reg4.IsSameSizeAndType(reg1); + match &= !reg5.IsValid() || reg5.IsSameSizeAndType(reg1); + match &= !reg6.IsValid() || reg6.IsSameSizeAndType(reg1); + match &= !reg7.IsValid() || reg7.IsSameSizeAndType(reg1); + match &= !reg8.IsValid() || reg8.IsSameSizeAndType(reg1); + return match; +} + + +} // namespace vixl diff --git a/disas/libvixl/src/a64/assembler-a64.h b/disas/libvixl/src/a64/assembler-a64.h new file mode 100644 index 0000000..93b3011 --- /dev/null +++ b/disas/libvixl/src/a64/assembler-a64.h @@ -0,0 +1,1784 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_ASSEMBLER_A64_H_ +#define VIXL_A64_ASSEMBLER_A64_H_ + +#include + +#include "globals.h" +#include "utils.h" +#include "a64/instructions-a64.h" + +namespace vixl { + +typedef uint64_t RegList; +static const int kRegListSizeInBits = sizeof(RegList) * 8; + +// Registers. + +// Some CPURegister methods can return Register and FPRegister types, so we +// need to declare them in advance. +class Register; +class FPRegister; + + +class CPURegister { + public: + enum RegisterType { + // The kInvalid value is used to detect uninitialized static instances, + // which are always zero-initialized before any constructors are called. + kInvalid = 0, + kRegister, + kFPRegister, + kNoRegister + }; + + CPURegister() : code_(0), size_(0), type_(kNoRegister) { + ASSERT(!IsValid()); + ASSERT(IsNone()); + } + + CPURegister(unsigned code, unsigned size, RegisterType type) + : code_(code), size_(size), type_(type) { + ASSERT(IsValidOrNone()); + } + + unsigned code() const { + ASSERT(IsValid()); + return code_; + } + + RegisterType type() const { + ASSERT(IsValidOrNone()); + return type_; + } + + RegList Bit() const { + ASSERT(code_ < (sizeof(RegList) * 8)); + return IsValid() ? (static_cast(1) << code_) : 0; + } + + unsigned size() const { + ASSERT(IsValid()); + return size_; + } + + int SizeInBytes() const { + ASSERT(IsValid()); + ASSERT(size() % 8 == 0); + return size_ / 8; + } + + int SizeInBits() const { + ASSERT(IsValid()); + return size_; + } + + bool Is32Bits() const { + ASSERT(IsValid()); + return size_ == 32; + } + + bool Is64Bits() const { + ASSERT(IsValid()); + return size_ == 64; + } + + bool IsValid() const { + if (IsValidRegister() || IsValidFPRegister()) { + ASSERT(!IsNone()); + return true; + } else { + ASSERT(IsNone()); + return false; + } + } + + bool IsValidRegister() const { + return IsRegister() && + ((size_ == kWRegSize) || (size_ == kXRegSize)) && + ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); + } + + bool IsValidFPRegister() const { + return IsFPRegister() && + ((size_ == kSRegSize) || (size_ == kDRegSize)) && + (code_ < kNumberOfFPRegisters); + } + + bool IsNone() const { + // kNoRegister types should always have size 0 and code 0. + ASSERT((type_ != kNoRegister) || (code_ == 0)); + ASSERT((type_ != kNoRegister) || (size_ == 0)); + + return type_ == kNoRegister; + } + + bool Is(const CPURegister& other) const { + ASSERT(IsValidOrNone() && other.IsValidOrNone()); + return (code_ == other.code_) && (size_ == other.size_) && + (type_ == other.type_); + } + + inline bool IsZero() const { + ASSERT(IsValid()); + return IsRegister() && (code_ == kZeroRegCode); + } + + inline bool IsSP() const { + ASSERT(IsValid()); + return IsRegister() && (code_ == kSPRegInternalCode); + } + + inline bool IsRegister() const { + return type_ == kRegister; + } + + inline bool IsFPRegister() const { + return type_ == kFPRegister; + } + + const Register& W() const; + const Register& X() const; + const FPRegister& S() const; + const FPRegister& D() const; + + inline bool IsSameSizeAndType(const CPURegister& other) const { + return (size_ == other.size_) && (type_ == other.type_); + } + + protected: + unsigned code_; + unsigned size_; + RegisterType type_; + + private: + bool IsValidOrNone() const { + return IsValid() || IsNone(); + } +}; + + +class Register : public CPURegister { + public: + explicit Register() : CPURegister() {} + inline explicit Register(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + ASSERT(IsValidRegister()); + } + explicit Register(unsigned code, unsigned size) + : CPURegister(code, size, kRegister) {} + + bool IsValid() const { + ASSERT(IsRegister() || IsNone()); + return IsValidRegister(); + } + + static const Register& WRegFromCode(unsigned code); + static const Register& XRegFromCode(unsigned code); + + // V8 compatibility. + static const int kNumRegisters = kNumberOfRegisters; + static const int kNumAllocatableRegisters = kNumberOfRegisters - 1; + + private: + static const Register wregisters[]; + static const Register xregisters[]; +}; + + +class FPRegister : public CPURegister { + public: + inline FPRegister() : CPURegister() {} + inline explicit FPRegister(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + ASSERT(IsValidFPRegister()); + } + inline FPRegister(unsigned code, unsigned size) + : CPURegister(code, size, kFPRegister) {} + + bool IsValid() const { + ASSERT(IsFPRegister() || IsNone()); + return IsValidFPRegister(); + } + + static const FPRegister& SRegFromCode(unsigned code); + static const FPRegister& DRegFromCode(unsigned code); + + // V8 compatibility. + static const int kNumRegisters = kNumberOfFPRegisters; + static const int kNumAllocatableRegisters = kNumberOfFPRegisters - 1; + + private: + static const FPRegister sregisters[]; + static const FPRegister dregisters[]; +}; + + +// No*Reg is used to indicate an unused argument, or an error case. Note that +// these all compare equal (using the Is() method). The Register and FPRegister +// variants are provided for convenience. +const Register NoReg; +const FPRegister NoFPReg; +const CPURegister NoCPUReg; + + +#define DEFINE_REGISTERS(N) \ +const Register w##N(N, kWRegSize); \ +const Register x##N(N, kXRegSize); +REGISTER_CODE_LIST(DEFINE_REGISTERS) +#undef DEFINE_REGISTERS +const Register wsp(kSPRegInternalCode, kWRegSize); +const Register sp(kSPRegInternalCode, kXRegSize); + + +#define DEFINE_FPREGISTERS(N) \ +const FPRegister s##N(N, kSRegSize); \ +const FPRegister d##N(N, kDRegSize); +REGISTER_CODE_LIST(DEFINE_FPREGISTERS) +#undef DEFINE_FPREGISTERS + + +// Registers aliases. +const Register ip0 = x16; +const Register ip1 = x17; +const Register lr = x30; +const Register xzr = x31; +const Register wzr = w31; + + +// AreAliased returns true if any of the named registers overlap. Arguments +// set to NoReg are ignored. The system stack pointer may be specified. +bool AreAliased(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoReg, + const CPURegister& reg4 = NoReg, + const CPURegister& reg5 = NoReg, + const CPURegister& reg6 = NoReg, + const CPURegister& reg7 = NoReg, + const CPURegister& reg8 = NoReg); + + +// AreSameSizeAndType returns true if all of the specified registers have the +// same size, and are of the same type. The system stack pointer may be +// specified. Arguments set to NoReg are ignored, as are any subsequent +// arguments. At least one argument (reg1) must be valid (not NoCPUReg). +bool AreSameSizeAndType(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoCPUReg, + const CPURegister& reg4 = NoCPUReg, + const CPURegister& reg5 = NoCPUReg, + const CPURegister& reg6 = NoCPUReg, + const CPURegister& reg7 = NoCPUReg, + const CPURegister& reg8 = NoCPUReg); + + +// Lists of registers. +class CPURegList { + public: + inline explicit CPURegList(CPURegister reg1, + CPURegister reg2 = NoCPUReg, + CPURegister reg3 = NoCPUReg, + CPURegister reg4 = NoCPUReg) + : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), + size_(reg1.size()), type_(reg1.type()) { + ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); + ASSERT(IsValid()); + } + + inline CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) + : list_(list), size_(size), type_(type) { + ASSERT(IsValid()); + } + + inline CPURegList(CPURegister::RegisterType type, unsigned size, + unsigned first_reg, unsigned last_reg) + : size_(size), type_(type) { + ASSERT(((type == CPURegister::kRegister) && + (last_reg < kNumberOfRegisters)) || + ((type == CPURegister::kFPRegister) && + (last_reg < kNumberOfFPRegisters))); + ASSERT(last_reg >= first_reg); + list_ = (1UL << (last_reg + 1)) - 1; + list_ &= ~((1UL << first_reg) - 1); + ASSERT(IsValid()); + } + + inline CPURegister::RegisterType type() const { + ASSERT(IsValid()); + return type_; + } + + // Combine another CPURegList into this one. Registers that already exist in + // this list are left unchanged. The type and size of the registers in the + // 'other' list must match those in this list. + void Combine(const CPURegList& other) { + ASSERT(IsValid()); + ASSERT(other.type() == type_); + ASSERT(other.RegisterSizeInBits() == size_); + list_ |= other.list(); + } + + // Remove every register in the other CPURegList from this one. Registers that + // do not exist in this list are ignored. The type and size of the registers + // in the 'other' list must match those in this list. + void Remove(const CPURegList& other) { + ASSERT(IsValid()); + ASSERT(other.type() == type_); + ASSERT(other.RegisterSizeInBits() == size_); + list_ &= ~other.list(); + } + + // Variants of Combine and Remove which take a single register. + inline void Combine(const CPURegister& other) { + ASSERT(other.type() == type_); + ASSERT(other.size() == size_); + Combine(other.code()); + } + + inline void Remove(const CPURegister& other) { + ASSERT(other.type() == type_); + ASSERT(other.size() == size_); + Remove(other.code()); + } + + // Variants of Combine and Remove which take a single register by its code; + // the type and size of the register is inferred from this list. + inline void Combine(int code) { + ASSERT(IsValid()); + ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ |= (1UL << code); + } + + inline void Remove(int code) { + ASSERT(IsValid()); + ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ &= ~(1UL << code); + } + + inline RegList list() const { + ASSERT(IsValid()); + return list_; + } + + // Remove all callee-saved registers from the list. This can be useful when + // preparing registers for an AAPCS64 function call, for example. + void RemoveCalleeSaved(); + + CPURegister PopLowestIndex(); + CPURegister PopHighestIndex(); + + // AAPCS64 callee-saved registers. + static CPURegList GetCalleeSaved(unsigned size = kXRegSize); + static CPURegList GetCalleeSavedFP(unsigned size = kDRegSize); + + // AAPCS64 caller-saved registers. Note that this includes lr. + static CPURegList GetCallerSaved(unsigned size = kXRegSize); + static CPURegList GetCallerSavedFP(unsigned size = kDRegSize); + + inline bool IsEmpty() const { + ASSERT(IsValid()); + return list_ == 0; + } + + inline bool IncludesAliasOf(const CPURegister& other) const { + ASSERT(IsValid()); + return (type_ == other.type()) && (other.Bit() & list_); + } + + inline int Count() const { + ASSERT(IsValid()); + return CountSetBits(list_, kRegListSizeInBits); + } + + inline unsigned RegisterSizeInBits() const { + ASSERT(IsValid()); + return size_; + } + + inline unsigned RegisterSizeInBytes() const { + int size_in_bits = RegisterSizeInBits(); + ASSERT((size_in_bits % 8) == 0); + return size_in_bits / 8; + } + + private: + RegList list_; + unsigned size_; + CPURegister::RegisterType type_; + + bool IsValid() const; +}; + + +// AAPCS64 callee-saved registers. +extern const CPURegList kCalleeSaved; +extern const CPURegList kCalleeSavedFP; + + +// AAPCS64 caller-saved registers. Note that this includes lr. +extern const CPURegList kCallerSaved; +extern const CPURegList kCallerSavedFP; + + +// Operand. +class Operand { + public: + // # + // where is int64_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(int64_t immediate); // NOLINT(runtime/explicit) + + // rm, { #} + // where is one of {LSL, LSR, ASR, ROR}. + // is uint6_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(Register reg, + Shift shift = LSL, + unsigned shift_amount = 0); // NOLINT(runtime/explicit) + + // rm, { {#}} + // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. + // is uint2_t. + explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); + + bool IsImmediate() const; + bool IsShiftedRegister() const; + bool IsExtendedRegister() const; + + // This returns an LSL shift (<= 4) operand as an equivalent extend operand, + // which helps in the encoding of instructions that use the stack pointer. + Operand ToExtendedRegister() const; + + int64_t immediate() const { + ASSERT(IsImmediate()); + return immediate_; + } + + Register reg() const { + ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return reg_; + } + + Shift shift() const { + ASSERT(IsShiftedRegister()); + return shift_; + } + + Extend extend() const { + ASSERT(IsExtendedRegister()); + return extend_; + } + + unsigned shift_amount() const { + ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return shift_amount_; + } + + private: + int64_t immediate_; + Register reg_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +// MemOperand represents the addressing mode of a load or store instruction. +class MemOperand { + public: + explicit MemOperand(Register base, + ptrdiff_t offset = 0, + AddrMode addrmode = Offset); + explicit MemOperand(Register base, + Register regoffset, + Shift shift = LSL, + unsigned shift_amount = 0); + explicit MemOperand(Register base, + Register regoffset, + Extend extend, + unsigned shift_amount = 0); + explicit MemOperand(Register base, + const Operand& offset, + AddrMode addrmode = Offset); + + const Register& base() const { return base_; } + const Register& regoffset() const { return regoffset_; } + ptrdiff_t offset() const { return offset_; } + AddrMode addrmode() const { return addrmode_; } + Shift shift() const { return shift_; } + Extend extend() const { return extend_; } + unsigned shift_amount() const { return shift_amount_; } + bool IsImmediateOffset() const; + bool IsRegisterOffset() const; + bool IsPreIndex() const; + bool IsPostIndex() const; + + private: + Register base_; + Register regoffset_; + ptrdiff_t offset_; + AddrMode addrmode_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +class Label { + public: + Label() : is_bound_(false), link_(NULL), target_(NULL) {} + ~Label() { + // If the label has been linked to, it needs to be bound to a target. + ASSERT(!IsLinked() || IsBound()); + } + + inline Instruction* link() const { return link_; } + inline Instruction* target() const { return target_; } + + inline bool IsBound() const { return is_bound_; } + inline bool IsLinked() const { return link_ != NULL; } + + inline void set_link(Instruction* new_link) { link_ = new_link; } + + static const int kEndOfChain = 0; + + private: + // Indicates if the label has been bound, ie its location is fixed. + bool is_bound_; + // Branches instructions branching to this label form a chained list, with + // their offset indicating where the next instruction is located. + // link_ points to the latest branch instruction generated branching to this + // branch. + // If link_ is not NULL, the label has been linked to. + Instruction* link_; + // The label location. + Instruction* target_; + + friend class Assembler; +}; + + +// TODO: Obtain better values for these, based on real-world data. +const int kLiteralPoolCheckInterval = 4 * KBytes; +const int kRecommendedLiteralPoolRange = 2 * kLiteralPoolCheckInterval; + + +// Control whether a branch over the literal pool should also be emitted. This +// is needed if the literal pool has to be emitted in the middle of the JITted +// code. +enum LiteralPoolEmitOption { + JumpRequired, + NoJumpRequired +}; + + +// Literal pool entry. +class Literal { + public: + Literal(Instruction* pc, uint64_t imm, unsigned size) + : pc_(pc), value_(imm), size_(size) {} + + private: + Instruction* pc_; + int64_t value_; + unsigned size_; + + friend class Assembler; +}; + + +// Assembler. +class Assembler { + public: + Assembler(byte* buffer, unsigned buffer_size); + + // The destructor asserts that one of the following is true: + // * The Assembler object has not been used. + // * Nothing has been emitted since the last Reset() call. + // * Nothing has been emitted since the last FinalizeCode() call. + ~Assembler(); + + // System functions. + + // Start generating code from the beginning of the buffer, discarding any code + // and data that has already been emitted into the buffer. + // + // In order to avoid any accidental transfer of state, Reset ASSERTs that the + // constant pool is not blocked. + void Reset(); + + // Finalize a code buffer of generated instructions. This function must be + // called before executing or copying code from the buffer. + void FinalizeCode(); + + // Label. + // Bind a label to the current PC. + void bind(Label* label); + int UpdateAndGetByteOffsetTo(Label* label); + inline int UpdateAndGetInstructionOffsetTo(Label* label) { + ASSERT(Label::kEndOfChain == 0); + return UpdateAndGetByteOffsetTo(label) >> kInstructionSizeLog2; + } + + + // Instruction set functions. + + // Branch / Jump instructions. + // Branch to register. + void br(const Register& xn); + + // Branch with link to register. + void blr(const Register& xn); + + // Branch to register with return hint. + void ret(const Register& xn = lr); + + // Unconditional branch to label. + void b(Label* label); + + // Conditional branch to label. + void b(Label* label, Condition cond); + + // Unconditional branch to PC offset. + void b(int imm26); + + // Conditional branch to PC offset. + void b(int imm19, Condition cond); + + // Branch with link to label. + void bl(Label* label); + + // Branch with link to PC offset. + void bl(int imm26); + + // Compare and branch to label if zero. + void cbz(const Register& rt, Label* label); + + // Compare and branch to PC offset if zero. + void cbz(const Register& rt, int imm19); + + // Compare and branch to label if not zero. + void cbnz(const Register& rt, Label* label); + + // Compare and branch to PC offset if not zero. + void cbnz(const Register& rt, int imm19); + + // Test bit and branch to label if zero. + void tbz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if zero. + void tbz(const Register& rt, unsigned bit_pos, int imm14); + + // Test bit and branch to label if not zero. + void tbnz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if not zero. + void tbnz(const Register& rt, unsigned bit_pos, int imm14); + + // Address calculation instructions. + // Calculate a PC-relative address. Unlike for branches the offset in adr is + // unscaled (i.e. the result can be unaligned). + + // Calculate the address of a label. + void adr(const Register& rd, Label* label); + + // Calculate the address of a PC offset. + void adr(const Register& rd, int imm21); + + // Data Processing instructions. + // Add. + void add(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Compare negative. + void cmn(const Register& rn, const Operand& operand); + + // Subtract. + void sub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Compare. + void cmp(const Register& rn, const Operand& operand); + + // Negate. + void neg(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Add with carry bit. + void adc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Subtract with carry bit. + void sbc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Negate with carry bit. + void ngc(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Logical instructions. + // Bitwise and (A & B). + void and_(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Bit test and set flags. + void tst(const Register& rn, const Operand& operand); + + // Bit clear (A & ~B). + void bic(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Bitwise or (A | B). + void orr(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise nor (A | ~B). + void orn(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise eor/xor (A ^ B). + void eor(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise enor/xnor (A ^ ~B). + void eon(const Register& rd, const Register& rn, const Operand& operand); + + // Logical shift left by variable. + void lslv(const Register& rd, const Register& rn, const Register& rm); + + // Logical shift right by variable. + void lsrv(const Register& rd, const Register& rn, const Register& rm); + + // Arithmetic shift right by variable. + void asrv(const Register& rd, const Register& rn, const Register& rm); + + // Rotate right by variable. + void rorv(const Register& rd, const Register& rn, const Register& rm); + + // Bitfield instructions. + // Bitfield move. + void bfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Signed bitfield move. + void sbfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Unsigned bitfield move. + void ubfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Bfm aliases. + // Bitfield insert. + inline void bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Bitfield extract and insert low. + inline void bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, lsb, lsb + width - 1); + } + + // Sbfm aliases. + // Arithmetic shift right. + inline void asr(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(shift < rd.size()); + sbfm(rd, rn, shift, rd.size() - 1); + } + + // Signed bitfield insert with zero at right. + inline void sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Signed bitfield extract. + inline void sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, lsb, lsb + width - 1); + } + + // Signed extend byte. + inline void sxtb(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 7); + } + + // Signed extend halfword. + inline void sxth(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 15); + } + + // Signed extend word. + inline void sxtw(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 31); + } + + // Ubfm aliases. + // Logical shift left. + inline void lsl(const Register& rd, const Register& rn, unsigned shift) { + unsigned reg_size = rd.size(); + ASSERT(shift < reg_size); + ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); + } + + // Logical shift right. + inline void lsr(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(shift < rd.size()); + ubfm(rd, rn, shift, rd.size() - 1); + } + + // Unsigned bitfield insert with zero at right. + inline void ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Unsigned bitfield extract. + inline void ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, lsb, lsb + width - 1); + } + + // Unsigned extend byte. + inline void uxtb(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 7); + } + + // Unsigned extend halfword. + inline void uxth(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 15); + } + + // Unsigned extend word. + inline void uxtw(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 31); + } + + // Extract. + void extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb); + + // Conditional select: rd = cond ? rn : rm. + void csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select increment: rd = cond ? rn : rm + 1. + void csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select inversion: rd = cond ? rn : ~rm. + void csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select negation: rd = cond ? rn : -rm. + void csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional set: rd = cond ? 1 : 0. + void cset(const Register& rd, Condition cond); + + // Conditional set mask: rd = cond ? -1 : 0. + void csetm(const Register& rd, Condition cond); + + // Conditional increment: rd = cond ? rn + 1 : rn. + void cinc(const Register& rd, const Register& rn, Condition cond); + + // Conditional invert: rd = cond ? ~rn : rn. + void cinv(const Register& rd, const Register& rn, Condition cond); + + // Conditional negate: rd = cond ? -rn : rn. + void cneg(const Register& rd, const Register& rn, Condition cond); + + // Rotate right. + inline void ror(const Register& rd, const Register& rs, unsigned shift) { + extr(rd, rs, rs, shift); + } + + // Conditional comparison. + // Conditional compare negative. + void ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Conditional compare. + void ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Multiply. + void mul(const Register& rd, const Register& rn, const Register& rm); + + // Negated multiply. + void mneg(const Register& rd, const Register& rn, const Register& rm); + + // Signed long multiply: 32 x 32 -> 64-bit. + void smull(const Register& rd, const Register& rn, const Register& rm); + + // Signed multiply high: 64 x 64 -> 64-bit <127:64>. + void smulh(const Register& xd, const Register& xn, const Register& xm); + + // Multiply and accumulate. + void madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Multiply and subtract. + void msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed integer divide. + void sdiv(const Register& rd, const Register& rn, const Register& rm); + + // Unsigned integer divide. + void udiv(const Register& rd, const Register& rn, const Register& rm); + + // Bit reverse. + void rbit(const Register& rd, const Register& rn); + + // Reverse bytes in 16-bit half words. + void rev16(const Register& rd, const Register& rn); + + // Reverse bytes in 32-bit words. + void rev32(const Register& rd, const Register& rn); + + // Reverse bytes. + void rev(const Register& rd, const Register& rn); + + // Count leading zeroes. + void clz(const Register& rd, const Register& rn); + + // Count leading sign bits. + void cls(const Register& rd, const Register& rn); + + // Memory instructions. + // Load integer or FP register. + void ldr(const CPURegister& rt, const MemOperand& src); + + // Store integer or FP register. + void str(const CPURegister& rt, const MemOperand& dst); + + // Load word with sign extension. + void ldrsw(const Register& rt, const MemOperand& src); + + // Load byte. + void ldrb(const Register& rt, const MemOperand& src); + + // Store byte. + void strb(const Register& rt, const MemOperand& dst); + + // Load byte with sign extension. + void ldrsb(const Register& rt, const MemOperand& src); + + // Load half-word. + void ldrh(const Register& rt, const MemOperand& src); + + // Store half-word. + void strh(const Register& rt, const MemOperand& dst); + + // Load half-word with sign extension. + void ldrsh(const Register& rt, const MemOperand& src); + + // Load integer or FP register pair. + void ldp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair. + void stp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load word pair with sign extension. + void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); + + // Load integer or FP register pair, non-temporal. + void ldnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair, non-temporal. + void stnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load literal to register. + void ldr(const Register& rt, uint64_t imm); + + // Load literal to FP register. + void ldr(const FPRegister& ft, double imm); + + // Move instructions. The default shift of -1 indicates that the move + // instruction will calculate an appropriate 16-bit immediate and left shift + // that is equal to the 64-bit immediate argument. If an explicit left shift + // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. + // + // For movk, an explicit shift can be used to indicate which half word should + // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant + // half word with zero, whereas movk(x0, 0, 48) will overwrite the + // most-significant. + + // Move immediate and keep. + void movk(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVK); + } + + // Move inverted immediate. + void movn(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVN); + } + + // Move immediate. + void movz(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVZ); + } + + // Misc instructions. + // Monitor debug-mode breakpoint. + void brk(int code); + + // Halting debug-mode breakpoint. + void hlt(int code); + + // Move register to register. + void mov(const Register& rd, const Register& rn); + + // Move inverted operand to register. + void mvn(const Register& rd, const Operand& operand); + + // System instructions. + // Move to register from system register. + void mrs(const Register& rt, SystemRegister sysreg); + + // Move from register to system register. + void msr(SystemRegister sysreg, const Register& rt); + + // System hint. + void hint(SystemHint code); + + // Alias for system instructions. + // No-op. + void nop() { + hint(NOP); + } + + // FP instructions. + // Move immediate to FP register. + void fmov(FPRegister fd, double imm); + + // Move FP register to register. + void fmov(Register rd, FPRegister fn); + + // Move register to FP register. + void fmov(FPRegister fd, Register rn); + + // Move FP register to FP register. + void fmov(FPRegister fd, FPRegister fn); + + // FP add. + void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP subtract. + void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP multiply. + void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP multiply and subtract. + void fmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa); + + // FP divide. + void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP maximum. + void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP minimum. + void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP absolute. + void fabs(const FPRegister& fd, const FPRegister& fn); + + // FP negate. + void fneg(const FPRegister& fd, const FPRegister& fn); + + // FP square root. + void fsqrt(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (nearest with ties to even). + void frintn(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (towards zero). + void frintz(const FPRegister& fd, const FPRegister& fn); + + // FP compare registers. + void fcmp(const FPRegister& fn, const FPRegister& fm); + + // FP compare immediate. + void fcmp(const FPRegister& fn, double value); + + // FP conditional compare. + void fccmp(const FPRegister& fn, + const FPRegister& fm, + StatusFlags nzcv, + Condition cond); + + // FP conditional select. + void fcsel(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + Condition cond); + + // Common FP Convert function. + void FPConvertToInt(const Register& rd, + const FPRegister& fn, + FPIntegerConvertOp op); + + // FP convert between single and double precision. + void fcvt(const FPRegister& fd, const FPRegister& fn); + + // Convert FP to unsigned integer (round towards -infinity). + void fcvtmu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (round towards -infinity). + void fcvtms(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (nearest with ties to even). + void fcvtnu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (nearest with ties to even). + void fcvtns(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (round towards zero). + void fcvtzu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (round towards zero). + void fcvtzs(const Register& rd, const FPRegister& fn); + + // Convert signed integer or fixed point to FP. + void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); + + // Convert unsigned integer or fixed point to FP. + void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); + + // Emit generic instructions. + // Emit raw instructions into the instruction stream. + inline void dci(Instr raw_inst) { Emit(raw_inst); } + + // Emit 32 bits of data into the instruction stream. + inline void dc32(uint32_t data) { EmitData(&data, sizeof(data)); } + + // Emit 64 bits of data into the instruction stream. + inline void dc64(uint64_t data) { EmitData(&data, sizeof(data)); } + + // Copy a string into the instruction stream, including the terminating NULL + // character. The instruction pointer (pc_) is then aligned correctly for + // subsequent instructions. + void EmitStringData(const char * string) { + ASSERT(string != NULL); + + size_t len = strlen(string) + 1; + EmitData(string, len); + + // Pad with NULL characters until pc_ is aligned. + const char pad[] = {'\0', '\0', '\0', '\0'}; + ASSERT(sizeof(pad) == kInstructionSize); + Instruction* next_pc = AlignUp(pc_, kInstructionSize); + EmitData(&pad, next_pc - pc_); + } + + // Code generation helpers. + + // Register encoding. + static Instr Rd(CPURegister rd) { + ASSERT(rd.code() != kSPRegInternalCode); + return rd.code() << Rd_offset; + } + + static Instr Rn(CPURegister rn) { + ASSERT(rn.code() != kSPRegInternalCode); + return rn.code() << Rn_offset; + } + + static Instr Rm(CPURegister rm) { + ASSERT(rm.code() != kSPRegInternalCode); + return rm.code() << Rm_offset; + } + + static Instr Ra(CPURegister ra) { + ASSERT(ra.code() != kSPRegInternalCode); + return ra.code() << Ra_offset; + } + + static Instr Rt(CPURegister rt) { + ASSERT(rt.code() != kSPRegInternalCode); + return rt.code() << Rt_offset; + } + + static Instr Rt2(CPURegister rt2) { + ASSERT(rt2.code() != kSPRegInternalCode); + return rt2.code() << Rt2_offset; + } + + // These encoding functions allow the stack pointer to be encoded, and + // disallow the zero register. + static Instr RdSP(Register rd) { + ASSERT(!rd.IsZero()); + return (rd.code() & kRegCodeMask) << Rd_offset; + } + + static Instr RnSP(Register rn) { + ASSERT(!rn.IsZero()); + return (rn.code() & kRegCodeMask) << Rn_offset; + } + + // Flags encoding. + static Instr Flags(FlagsUpdate S) { + if (S == SetFlags) { + return 1 << FlagsUpdate_offset; + } else if (S == LeaveFlags) { + return 0 << FlagsUpdate_offset; + } + UNREACHABLE(); + return 0; + } + + static Instr Cond(Condition cond) { + return cond << Condition_offset; + } + + // PC-relative address encoding. + static Instr ImmPCRelAddress(int imm21) { + ASSERT(is_int21(imm21)); + Instr imm = static_cast(truncate_to_int21(imm21)); + Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; + Instr immlo = imm << ImmPCRelLo_offset; + return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); + } + + // Branch encoding. + static Instr ImmUncondBranch(int imm26) { + ASSERT(is_int26(imm26)); + return truncate_to_int26(imm26) << ImmUncondBranch_offset; + } + + static Instr ImmCondBranch(int imm19) { + ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCondBranch_offset; + } + + static Instr ImmCmpBranch(int imm19) { + ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCmpBranch_offset; + } + + static Instr ImmTestBranch(int imm14) { + ASSERT(is_int14(imm14)); + return truncate_to_int14(imm14) << ImmTestBranch_offset; + } + + static Instr ImmTestBranchBit(unsigned bit_pos) { + ASSERT(is_uint6(bit_pos)); + // Subtract five from the shift offset, as we need bit 5 from bit_pos. + unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); + unsigned b40 = bit_pos << ImmTestBranchBit40_offset; + b5 &= ImmTestBranchBit5_mask; + b40 &= ImmTestBranchBit40_mask; + return b5 | b40; + } + + // Data Processing encoding. + static Instr SF(Register rd) { + return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; + } + + static Instr ImmAddSub(int64_t imm) { + ASSERT(IsImmAddSub(imm)); + if (is_uint12(imm)) { // No shift required. + return imm << ImmAddSub_offset; + } else { + return ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); + } + } + + static inline Instr ImmS(unsigned imms, unsigned reg_size) { + ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || + ((reg_size == kWRegSize) && is_uint5(imms))); + USE(reg_size); + return imms << ImmS_offset; + } + + static inline Instr ImmR(unsigned immr, unsigned reg_size) { + ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + ASSERT(is_uint6(immr)); + return immr << ImmR_offset; + } + + static inline Instr ImmSetBits(unsigned imms, unsigned reg_size) { + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + ASSERT(is_uint6(imms)); + ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); + USE(reg_size); + return imms << ImmSetBits_offset; + } + + static inline Instr ImmRotate(unsigned immr, unsigned reg_size) { + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + return immr << ImmRotate_offset; + } + + static inline Instr ImmLLiteral(int imm19) { + ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmLLiteral_offset; + } + + static inline Instr BitN(unsigned bitn, unsigned reg_size) { + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + ASSERT((reg_size == kXRegSize) || (bitn == 0)); + USE(reg_size); + return bitn << BitN_offset; + } + + static Instr ShiftDP(Shift shift) { + ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); + return shift << ShiftDP_offset; + } + + static Instr ImmDPShift(unsigned amount) { + ASSERT(is_uint6(amount)); + return amount << ImmDPShift_offset; + } + + static Instr ExtendMode(Extend extend) { + return extend << ExtendMode_offset; + } + + static Instr ImmExtendShift(unsigned left_shift) { + ASSERT(left_shift <= 4); + return left_shift << ImmExtendShift_offset; + } + + static Instr ImmCondCmp(unsigned imm) { + ASSERT(is_uint5(imm)); + return imm << ImmCondCmp_offset; + } + + static Instr Nzcv(StatusFlags nzcv) { + return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; + } + + // MemOperand offset encoding. + static Instr ImmLSUnsigned(int imm12) { + ASSERT(is_uint12(imm12)); + return imm12 << ImmLSUnsigned_offset; + } + + static Instr ImmLS(int imm9) { + ASSERT(is_int9(imm9)); + return truncate_to_int9(imm9) << ImmLS_offset; + } + + static Instr ImmLSPair(int imm7, LSDataSize size) { + ASSERT(((imm7 >> size) << size) == imm7); + int scaled_imm7 = imm7 >> size; + ASSERT(is_int7(scaled_imm7)); + return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; + } + + static Instr ImmShiftLS(unsigned shift_amount) { + ASSERT(is_uint1(shift_amount)); + return shift_amount << ImmShiftLS_offset; + } + + static Instr ImmException(int imm16) { + ASSERT(is_uint16(imm16)); + return imm16 << ImmException_offset; + } + + static Instr ImmSystemRegister(int imm15) { + ASSERT(is_uint15(imm15)); + return imm15 << ImmSystemRegister_offset; + } + + static Instr ImmHint(int imm7) { + ASSERT(is_uint7(imm7)); + return imm7 << ImmHint_offset; + } + + static LSDataSize CalcLSDataSize(LoadStoreOp op) { + ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8)); + return static_cast(op >> SizeLS_offset); + } + + // Move immediates encoding. + static Instr ImmMoveWide(uint64_t imm) { + ASSERT(is_uint16(imm)); + return imm << ImmMoveWide_offset; + } + + static Instr ShiftMoveWide(int64_t shift) { + ASSERT(is_uint2(shift)); + return shift << ShiftMoveWide_offset; + } + + // FP Immediates. + static Instr ImmFP32(float imm); + static Instr ImmFP64(double imm); + + // FP register type. + static Instr FPType(FPRegister fd) { + return fd.Is64Bits() ? FP64 : FP32; + } + + static Instr FPScale(unsigned scale) { + ASSERT(is_uint6(scale)); + return scale << FPScale_offset; + } + + // Size of the code generated in bytes + uint64_t SizeOfCodeGenerated() const { + ASSERT((pc_ >= buffer_) && (pc_ < (buffer_ + buffer_size_))); + return pc_ - buffer_; + } + + // Size of the code generated since label to the current position. + uint64_t SizeOfCodeGeneratedSince(Label* label) const { + ASSERT(label->IsBound()); + ASSERT((pc_ >= label->target()) && (pc_ < (buffer_ + buffer_size_))); + return pc_ - label->target(); + } + + + inline void BlockLiteralPool() { + literal_pool_monitor_++; + } + + inline void ReleaseLiteralPool() { + if (--literal_pool_monitor_ == 0) { + // Has the literal pool been blocked for too long? + ASSERT(literals_.empty() || + (pc_ < (literals_.back()->pc_ + kMaxLoadLiteralRange))); + } + } + + inline bool IsLiteralPoolBlocked() { + return literal_pool_monitor_ != 0; + } + + void CheckLiteralPool(LiteralPoolEmitOption option = JumpRequired); + void EmitLiteralPool(LiteralPoolEmitOption option = NoJumpRequired); + size_t LiteralPoolSize(); + + protected: + inline const Register& AppropriateZeroRegFor(const CPURegister& reg) const { + return reg.Is64Bits() ? xzr : wzr; + } + + + void LoadStore(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op); + static bool IsImmLSUnscaled(ptrdiff_t offset); + static bool IsImmLSScaled(ptrdiff_t offset, LSDataSize size); + + void Logical(const Register& rd, + const Register& rn, + const Operand& operand, + LogicalOp op); + void LogicalImmediate(const Register& rd, + const Register& rn, + unsigned n, + unsigned imm_s, + unsigned imm_r, + LogicalOp op); + static bool IsImmLogical(uint64_t value, + unsigned width, + unsigned* n, + unsigned* imm_s, + unsigned* imm_r); + + void ConditionalCompare(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op); + static bool IsImmConditionalCompare(int64_t immediate); + + void AddSubWithCarry(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op); + + // Functions for emulating operands not directly supported by the instruction + // set. + void EmitShift(const Register& rd, + const Register& rn, + Shift shift, + unsigned amount); + void EmitExtendShift(const Register& rd, + const Register& rn, + Extend extend, + unsigned left_shift); + + void AddSub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op); + static bool IsImmAddSub(int64_t immediate); + + // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified + // registers. Only simple loads are supported; sign- and zero-extension (such + // as in LDPSW_x or LDRB_w) are not supported. + static LoadStoreOp LoadOpFor(const CPURegister& rt); + static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStoreOp StoreOpFor(const CPURegister& rt); + static LoadStorePairOp StorePairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + + + private: + // Instruction helpers. + void MoveWide(const Register& rd, + uint64_t imm, + int shift, + MoveWideImmediateOp mov_op); + void DataProcShiftedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void DataProcExtendedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void LoadStorePair(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairOp op); + void LoadStorePairNonTemporal(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairNonTemporalOp op); + void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); + void ConditionalSelect(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond, + ConditionalSelectOp op); + void DataProcessing1Source(const Register& rd, + const Register& rn, + DataProcessing1SourceOp op); + void DataProcessing3Source(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra, + DataProcessing3SourceOp op); + void FPDataProcessing1Source(const FPRegister& fd, + const FPRegister& fn, + FPDataProcessing1SourceOp op); + void FPDataProcessing2Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + FPDataProcessing2SourceOp op); + void FPDataProcessing3Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa, + FPDataProcessing3SourceOp op); + + // Encoding helpers. + static bool IsImmFP32(float imm); + static bool IsImmFP64(double imm); + + void RecordLiteral(int64_t imm, unsigned size); + + // Emit the instruction at pc_. + void Emit(Instr instruction) { + ASSERT(sizeof(*pc_) == 1); + ASSERT(sizeof(instruction) == kInstructionSize); + ASSERT((pc_ + sizeof(instruction)) <= (buffer_ + buffer_size_)); + +#ifdef DEBUG + finalized_ = false; +#endif + + memcpy(pc_, &instruction, sizeof(instruction)); + pc_ += sizeof(instruction); + CheckBufferSpace(); + } + + // Emit data inline in the instruction stream. + void EmitData(void const * data, unsigned size) { + ASSERT(sizeof(*pc_) == 1); + ASSERT((pc_ + size) <= (buffer_ + buffer_size_)); + +#ifdef DEBUG + finalized_ = false; +#endif + + // TODO: Record this 'instruction' as data, so that it can be disassembled + // correctly. + memcpy(pc_, data, size); + pc_ += size; + CheckBufferSpace(); + } + + inline void CheckBufferSpace() { + ASSERT(pc_ < (buffer_ + buffer_size_)); + if (pc_ > next_literal_pool_check_) { + CheckLiteralPool(); + } + } + + // The buffer into which code and relocation info are generated. + Instruction* buffer_; + // Buffer size, in bytes. + unsigned buffer_size_; + Instruction* pc_; + std::list literals_; + Instruction* next_literal_pool_check_; + unsigned literal_pool_monitor_; + + friend class BlockLiteralPoolScope; + +#ifdef DEBUG + bool finalized_; +#endif +}; + +class BlockLiteralPoolScope { + public: + explicit BlockLiteralPoolScope(Assembler* assm) : assm_(assm) { + assm_->BlockLiteralPool(); + } + + ~BlockLiteralPoolScope() { + assm_->ReleaseLiteralPool(); + } + + private: + Assembler* assm_; +}; +} // namespace vixl + +#endif // VIXL_A64_ASSEMBLER_A64_H_ diff --git a/disas/libvixl/src/a64/constants-a64.h b/disas/libvixl/src/a64/constants-a64.h new file mode 100644 index 0000000..2e0336d --- /dev/null +++ b/disas/libvixl/src/a64/constants-a64.h @@ -0,0 +1,1104 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_CONSTANTS_A64_H_ +#define VIXL_A64_CONSTANTS_A64_H_ + +namespace vixl { + +const unsigned kNumberOfRegisters = 32; +const unsigned kNumberOfFPRegisters = 32; +// Callee saved registers are x21-x30(lr). +const int kNumberOfCalleeSavedRegisters = 10; +const int kFirstCalleeSavedRegisterIndex = 21; +// Callee saved FP registers are d8-d15. +const int kNumberOfCalleeSavedFPRegisters = 8; +const int kFirstCalleeSavedFPRegisterIndex = 8; + +#define REGISTER_CODE_LIST(R) \ +R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ +R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ +R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ +R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) + +#define INSTRUCTION_FIELDS_LIST(V_) \ +/* Register fields */ \ +V_(Rd, 4, 0, Bits) /* Destination register. */ \ +V_(Rn, 9, 5, Bits) /* First source register. */ \ +V_(Rm, 20, 16, Bits) /* Second source register. */ \ +V_(Ra, 14, 10, Bits) /* Third source register. */ \ +V_(Rt, 4, 0, Bits) /* Load dest / store source. */ \ +V_(Rt2, 14, 10, Bits) /* Load second dest / */ \ + /* store second source. */ \ +V_(PrefetchMode, 4, 0, Bits) \ + \ +/* Common bits */ \ +V_(SixtyFourBits, 31, 31, Bits) \ +V_(FlagsUpdate, 29, 29, Bits) \ + \ +/* PC relative addressing */ \ +V_(ImmPCRelHi, 23, 5, SignedBits) \ +V_(ImmPCRelLo, 30, 29, Bits) \ + \ +/* Add/subtract/logical shift register */ \ +V_(ShiftDP, 23, 22, Bits) \ +V_(ImmDPShift, 15, 10, Bits) \ + \ +/* Add/subtract immediate */ \ +V_(ImmAddSub, 21, 10, Bits) \ +V_(ShiftAddSub, 23, 22, Bits) \ + \ +/* Add/substract extend */ \ +V_(ImmExtendShift, 12, 10, Bits) \ +V_(ExtendMode, 15, 13, Bits) \ + \ +/* Move wide */ \ +V_(ImmMoveWide, 20, 5, Bits) \ +V_(ShiftMoveWide, 22, 21, Bits) \ + \ +/* Logical immediate, bitfield and extract */ \ +V_(BitN, 22, 22, Bits) \ +V_(ImmRotate, 21, 16, Bits) \ +V_(ImmSetBits, 15, 10, Bits) \ +V_(ImmR, 21, 16, Bits) \ +V_(ImmS, 15, 10, Bits) \ + \ +/* Test and branch immediate */ \ +V_(ImmTestBranch, 18, 5, SignedBits) \ +V_(ImmTestBranchBit40, 23, 19, Bits) \ +V_(ImmTestBranchBit5, 31, 31, Bits) \ + \ +/* Conditionals */ \ +V_(Condition, 15, 12, Bits) \ +V_(ConditionBranch, 3, 0, Bits) \ +V_(Nzcv, 3, 0, Bits) \ +V_(ImmCondCmp, 20, 16, Bits) \ +V_(ImmCondBranch, 23, 5, SignedBits) \ + \ +/* Floating point */ \ +V_(FPType, 23, 22, Bits) \ +V_(ImmFP, 20, 13, Bits) \ +V_(FPScale, 15, 10, Bits) \ + \ +/* Load Store */ \ +V_(ImmLS, 20, 12, SignedBits) \ +V_(ImmLSUnsigned, 21, 10, Bits) \ +V_(ImmLSPair, 21, 15, SignedBits) \ +V_(SizeLS, 31, 30, Bits) \ +V_(ImmShiftLS, 12, 12, Bits) \ + \ +/* Other immediates */ \ +V_(ImmUncondBranch, 25, 0, SignedBits) \ +V_(ImmCmpBranch, 23, 5, SignedBits) \ +V_(ImmLLiteral, 23, 5, SignedBits) \ +V_(ImmException, 20, 5, Bits) \ +V_(ImmHint, 11, 5, Bits) \ + \ +/* System (MRS, MSR) */ \ +V_(ImmSystemRegister, 19, 5, Bits) \ +V_(SysO0, 19, 19, Bits) \ +V_(SysOp1, 18, 16, Bits) \ +V_(SysOp2, 7, 5, Bits) \ +V_(CRn, 15, 12, Bits) \ +V_(CRm, 11, 8, Bits) \ + + +#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ +/* NZCV */ \ +V_(Flags, 31, 28, Bits) \ +V_(N, 31, 31, Bits) \ +V_(Z, 30, 30, Bits) \ +V_(C, 29, 29, Bits) \ +V_(V, 28, 28, Bits) \ +M_(NZCV, Flags_mask) \ + \ +/* FPCR */ \ +V_(AHP, 26, 26, Bits) \ +V_(DN, 25, 25, Bits) \ +V_(FZ, 24, 24, Bits) \ +V_(RMode, 23, 22, Bits) \ +M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) + + +// Fields offsets. +#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ +const int Name##_offset = LowBit; \ +const int Name##_width = HighBit - LowBit + 1; \ +const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; +#define NOTHING(A, B) +INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) +SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) +#undef NOTHING +#undef DECLARE_FIELDS_BITS + +// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed +// from ImmPCRelLo and ImmPCRelHi. +const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; + +// Condition codes. +enum Condition { + eq = 0, + ne = 1, + hs = 2, + lo = 3, + mi = 4, + pl = 5, + vs = 6, + vc = 7, + hi = 8, + ls = 9, + ge = 10, + lt = 11, + gt = 12, + le = 13, + al = 14, + nv = 15 // Behaves as always/al. +}; + +inline Condition InvertCondition(Condition cond) { + // Conditions al and nv behave identically, as "always true". They can't be + // inverted, because there is no "always false" condition. + ASSERT((cond != al) && (cond != nv)); + return static_cast(cond ^ 1); +} + +enum FlagsUpdate { + SetFlags = 1, + LeaveFlags = 0 +}; + +enum StatusFlags { + NoFlag = 0, + + // Derive the flag combinations from the system register bit descriptions. + NFlag = N_mask, + ZFlag = Z_mask, + CFlag = C_mask, + VFlag = V_mask, + NZFlag = NFlag | ZFlag, + NCFlag = NFlag | CFlag, + NVFlag = NFlag | VFlag, + ZCFlag = ZFlag | CFlag, + ZVFlag = ZFlag | VFlag, + CVFlag = CFlag | VFlag, + NZCFlag = NFlag | ZFlag | CFlag, + NZVFlag = NFlag | ZFlag | VFlag, + NCVFlag = NFlag | CFlag | VFlag, + ZCVFlag = ZFlag | CFlag | VFlag, + NZCVFlag = NFlag | ZFlag | CFlag | VFlag, + + // Floating-point comparison results. + FPEqualFlag = ZCFlag, + FPLessThanFlag = NFlag, + FPGreaterThanFlag = CFlag, + FPUnorderedFlag = CVFlag +}; + +enum Shift { + NO_SHIFT = -1, + LSL = 0x0, + LSR = 0x1, + ASR = 0x2, + ROR = 0x3 +}; + +enum Extend { + NO_EXTEND = -1, + UXTB = 0, + UXTH = 1, + UXTW = 2, + UXTX = 3, + SXTB = 4, + SXTH = 5, + SXTW = 6, + SXTX = 7 +}; + +enum SystemHint { + NOP = 0, + YIELD = 1, + WFE = 2, + WFI = 3, + SEV = 4, + SEVL = 5 +}; + +// System/special register names. +// This information is not encoded as one field but as the concatenation of +// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). +enum SystemRegister { + NZCV = ((0x1 << SysO0_offset) | + (0x3 << SysOp1_offset) | + (0x4 << CRn_offset) | + (0x2 << CRm_offset) | + (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, + FPCR = ((0x1 << SysO0_offset) | + (0x3 << SysOp1_offset) | + (0x4 << CRn_offset) | + (0x4 << CRm_offset) | + (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset +}; + +// Instruction enumerations. +// +// These are the masks that define a class of instructions, and the list of +// instructions within each class. Each enumeration has a Fixed, FMask and +// Mask value. +// +// Fixed: The fixed bits in this instruction class. +// FMask: The mask used to extract the fixed bits in the class. +// Mask: The mask used to identify the instructions within a class. +// +// The enumerations can be used like this: +// +// ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); +// switch(instr->Mask(PCRelAddressingMask)) { +// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; +// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; +// default: printf("Unknown instruction\n"); +// } + + +// Generic fields. +enum GenericInstrField { + SixtyFourBits = 0x80000000, + ThirtyTwoBits = 0x00000000, + FP32 = 0x00000000, + FP64 = 0x00400000 +}; + +// PC relative addressing. +enum PCRelAddressingOp { + PCRelAddressingFixed = 0x10000000, + PCRelAddressingFMask = 0x1F000000, + PCRelAddressingMask = 0x9F000000, + ADR = PCRelAddressingFixed | 0x00000000, + ADRP = PCRelAddressingFixed | 0x80000000 +}; + +// Add/sub (immediate, shifted and extended.) +const int kSFOffset = 31; +enum AddSubOp { + AddSubOpMask = 0x60000000, + AddSubSetFlagsBit = 0x20000000, + ADD = 0x00000000, + ADDS = ADD | AddSubSetFlagsBit, + SUB = 0x40000000, + SUBS = SUB | AddSubSetFlagsBit +}; + +#define ADD_SUB_OP_LIST(V) \ + V(ADD), \ + V(ADDS), \ + V(SUB), \ + V(SUBS) + +enum AddSubImmediateOp { + AddSubImmediateFixed = 0x11000000, + AddSubImmediateFMask = 0x1F000000, + AddSubImmediateMask = 0xFF000000, + #define ADD_SUB_IMMEDIATE(A) \ + A##_w_imm = AddSubImmediateFixed | A, \ + A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) + #undef ADD_SUB_IMMEDIATE +}; + +enum AddSubShiftedOp { + AddSubShiftedFixed = 0x0B000000, + AddSubShiftedFMask = 0x1F200000, + AddSubShiftedMask = 0xFF200000, + #define ADD_SUB_SHIFTED(A) \ + A##_w_shift = AddSubShiftedFixed | A, \ + A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) + #undef ADD_SUB_SHIFTED +}; + +enum AddSubExtendedOp { + AddSubExtendedFixed = 0x0B200000, + AddSubExtendedFMask = 0x1F200000, + AddSubExtendedMask = 0xFFE00000, + #define ADD_SUB_EXTENDED(A) \ + A##_w_ext = AddSubExtendedFixed | A, \ + A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits + ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) + #undef ADD_SUB_EXTENDED +}; + +// Add/sub with carry. +enum AddSubWithCarryOp { + AddSubWithCarryFixed = 0x1A000000, + AddSubWithCarryFMask = 0x1FE00000, + AddSubWithCarryMask = 0xFFE0FC00, + ADC_w = AddSubWithCarryFixed | ADD, + ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, + ADC = ADC_w, + ADCS_w = AddSubWithCarryFixed | ADDS, + ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, + SBC_w = AddSubWithCarryFixed | SUB, + SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, + SBC = SBC_w, + SBCS_w = AddSubWithCarryFixed | SUBS, + SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits +}; + + +// Logical (immediate and shifted register). +enum LogicalOp { + LogicalOpMask = 0x60200000, + NOT = 0x00200000, + AND = 0x00000000, + BIC = AND | NOT, + ORR = 0x20000000, + ORN = ORR | NOT, + EOR = 0x40000000, + EON = EOR | NOT, + ANDS = 0x60000000, + BICS = ANDS | NOT +}; + +// Logical immediate. +enum LogicalImmediateOp { + LogicalImmediateFixed = 0x12000000, + LogicalImmediateFMask = 0x1F800000, + LogicalImmediateMask = 0xFF800000, + AND_w_imm = LogicalImmediateFixed | AND, + AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, + ORR_w_imm = LogicalImmediateFixed | ORR, + ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, + EOR_w_imm = LogicalImmediateFixed | EOR, + EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, + ANDS_w_imm = LogicalImmediateFixed | ANDS, + ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits +}; + +// Logical shifted register. +enum LogicalShiftedOp { + LogicalShiftedFixed = 0x0A000000, + LogicalShiftedFMask = 0x1F000000, + LogicalShiftedMask = 0xFF200000, + AND_w = LogicalShiftedFixed | AND, + AND_x = LogicalShiftedFixed | AND | SixtyFourBits, + AND_shift = AND_w, + BIC_w = LogicalShiftedFixed | BIC, + BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, + BIC_shift = BIC_w, + ORR_w = LogicalShiftedFixed | ORR, + ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, + ORR_shift = ORR_w, + ORN_w = LogicalShiftedFixed | ORN, + ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, + ORN_shift = ORN_w, + EOR_w = LogicalShiftedFixed | EOR, + EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, + EOR_shift = EOR_w, + EON_w = LogicalShiftedFixed | EON, + EON_x = LogicalShiftedFixed | EON | SixtyFourBits, + EON_shift = EON_w, + ANDS_w = LogicalShiftedFixed | ANDS, + ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, + ANDS_shift = ANDS_w, + BICS_w = LogicalShiftedFixed | BICS, + BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, + BICS_shift = BICS_w +}; + +// Move wide immediate. +enum MoveWideImmediateOp { + MoveWideImmediateFixed = 0x12800000, + MoveWideImmediateFMask = 0x1F800000, + MoveWideImmediateMask = 0xFF800000, + MOVN = 0x00000000, + MOVZ = 0x40000000, + MOVK = 0x60000000, + MOVN_w = MoveWideImmediateFixed | MOVN, + MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, + MOVZ_w = MoveWideImmediateFixed | MOVZ, + MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, + MOVK_w = MoveWideImmediateFixed | MOVK, + MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits +}; + +// Bitfield. +const int kBitfieldNOffset = 22; +enum BitfieldOp { + BitfieldFixed = 0x13000000, + BitfieldFMask = 0x1F800000, + BitfieldMask = 0xFF800000, + SBFM_w = BitfieldFixed | 0x00000000, + SBFM_x = BitfieldFixed | 0x80000000, + SBFM = SBFM_w, + BFM_w = BitfieldFixed | 0x20000000, + BFM_x = BitfieldFixed | 0xA0000000, + BFM = BFM_w, + UBFM_w = BitfieldFixed | 0x40000000, + UBFM_x = BitfieldFixed | 0xC0000000, + UBFM = UBFM_w + // Bitfield N field. +}; + +// Extract. +enum ExtractOp { + ExtractFixed = 0x13800000, + ExtractFMask = 0x1F800000, + ExtractMask = 0xFFA00000, + EXTR_w = ExtractFixed | 0x00000000, + EXTR_x = ExtractFixed | 0x80000000, + EXTR = EXTR_w +}; + +// Unconditional branch. +enum UnconditionalBranchOp { + UnconditionalBranchFixed = 0x14000000, + UnconditionalBranchFMask = 0x7C000000, + UnconditionalBranchMask = 0xFC000000, + B = UnconditionalBranchFixed | 0x00000000, + BL = UnconditionalBranchFixed | 0x80000000 +}; + +// Unconditional branch to register. +enum UnconditionalBranchToRegisterOp { + UnconditionalBranchToRegisterFixed = 0xD6000000, + UnconditionalBranchToRegisterFMask = 0xFE000000, + UnconditionalBranchToRegisterMask = 0xFFFFFC1F, + BR = UnconditionalBranchToRegisterFixed | 0x001F0000, + BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, + RET = UnconditionalBranchToRegisterFixed | 0x005F0000 +}; + +// Compare and branch. +enum CompareBranchOp { + CompareBranchFixed = 0x34000000, + CompareBranchFMask = 0x7E000000, + CompareBranchMask = 0xFF000000, + CBZ_w = CompareBranchFixed | 0x00000000, + CBZ_x = CompareBranchFixed | 0x80000000, + CBZ = CBZ_w, + CBNZ_w = CompareBranchFixed | 0x01000000, + CBNZ_x = CompareBranchFixed | 0x81000000, + CBNZ = CBNZ_w +}; + +// Test and branch. +enum TestBranchOp { + TestBranchFixed = 0x36000000, + TestBranchFMask = 0x7E000000, + TestBranchMask = 0x7F000000, + TBZ = TestBranchFixed | 0x00000000, + TBNZ = TestBranchFixed | 0x01000000 +}; + +// Conditional branch. +enum ConditionalBranchOp { + ConditionalBranchFixed = 0x54000000, + ConditionalBranchFMask = 0xFE000000, + ConditionalBranchMask = 0xFF000010, + B_cond = ConditionalBranchFixed | 0x00000000 +}; + +// System. +// System instruction encoding is complicated because some instructions use op +// and CR fields to encode parameters. To handle this cleanly, the system +// instructions are split into more than one enum. + +enum SystemOp { + SystemFixed = 0xD5000000, + SystemFMask = 0xFFC00000 +}; + +enum SystemSysRegOp { + SystemSysRegFixed = 0xD5100000, + SystemSysRegFMask = 0xFFD00000, + SystemSysRegMask = 0xFFF00000, + MRS = SystemSysRegFixed | 0x00200000, + MSR = SystemSysRegFixed | 0x00000000 +}; + +enum SystemHintOp { + SystemHintFixed = 0xD503201F, + SystemHintFMask = 0xFFFFF01F, + SystemHintMask = 0xFFFFF01F, + HINT = SystemHintFixed | 0x00000000 +}; + +// Exception. +enum ExceptionOp { + ExceptionFixed = 0xD4000000, + ExceptionFMask = 0xFF000000, + ExceptionMask = 0xFFE0001F, + HLT = ExceptionFixed | 0x00400000, + BRK = ExceptionFixed | 0x00200000, + SVC = ExceptionFixed | 0x00000001, + HVC = ExceptionFixed | 0x00000002, + SMC = ExceptionFixed | 0x00000003, + DCPS1 = ExceptionFixed | 0x00A00001, + DCPS2 = ExceptionFixed | 0x00A00002, + DCPS3 = ExceptionFixed | 0x00A00003 +}; + +// Any load or store. +enum LoadStoreAnyOp { + LoadStoreAnyFMask = 0x0a000000, + LoadStoreAnyFixed = 0x08000000 +}; + +#define LOAD_STORE_PAIR_OP_LIST(V) \ + V(STP, w, 0x00000000), \ + V(LDP, w, 0x00400000), \ + V(LDPSW, x, 0x40400000), \ + V(STP, x, 0x80000000), \ + V(LDP, x, 0x80400000), \ + V(STP, s, 0x04000000), \ + V(LDP, s, 0x04400000), \ + V(STP, d, 0x44000000), \ + V(LDP, d, 0x44400000) + +// Load/store pair (post, pre and offset.) +enum LoadStorePairOp { + LoadStorePairMask = 0xC4400000, + LoadStorePairLBit = 1 << 22, + #define LOAD_STORE_PAIR(A, B, C) \ + A##_##B = C + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) + #undef LOAD_STORE_PAIR +}; + +enum LoadStorePairPostIndexOp { + LoadStorePairPostIndexFixed = 0x28800000, + LoadStorePairPostIndexFMask = 0x3B800000, + LoadStorePairPostIndexMask = 0xFFC00000, + #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ + A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) + #undef LOAD_STORE_PAIR_POST_INDEX +}; + +enum LoadStorePairPreIndexOp { + LoadStorePairPreIndexFixed = 0x29800000, + LoadStorePairPreIndexFMask = 0x3B800000, + LoadStorePairPreIndexMask = 0xFFC00000, + #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ + A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) + #undef LOAD_STORE_PAIR_PRE_INDEX +}; + +enum LoadStorePairOffsetOp { + LoadStorePairOffsetFixed = 0x29000000, + LoadStorePairOffsetFMask = 0x3B800000, + LoadStorePairOffsetMask = 0xFFC00000, + #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ + A##_##B##_off = LoadStorePairOffsetFixed | A##_##B + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) + #undef LOAD_STORE_PAIR_OFFSET +}; + +enum LoadStorePairNonTemporalOp { + LoadStorePairNonTemporalFixed = 0x28000000, + LoadStorePairNonTemporalFMask = 0x3B800000, + LoadStorePairNonTemporalMask = 0xFFC00000, + STNP_w = LoadStorePairNonTemporalFixed | STP_w, + LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, + STNP_x = LoadStorePairNonTemporalFixed | STP_x, + LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, + STNP_s = LoadStorePairNonTemporalFixed | STP_s, + LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, + STNP_d = LoadStorePairNonTemporalFixed | STP_d, + LDNP_d = LoadStorePairNonTemporalFixed | LDP_d +}; + +// Load literal. +enum LoadLiteralOp { + LoadLiteralFixed = 0x18000000, + LoadLiteralFMask = 0x3B000000, + LoadLiteralMask = 0xFF000000, + LDR_w_lit = LoadLiteralFixed | 0x00000000, + LDR_x_lit = LoadLiteralFixed | 0x40000000, + LDRSW_x_lit = LoadLiteralFixed | 0x80000000, + PRFM_lit = LoadLiteralFixed | 0xC0000000, + LDR_s_lit = LoadLiteralFixed | 0x04000000, + LDR_d_lit = LoadLiteralFixed | 0x44000000 +}; + +#define LOAD_STORE_OP_LIST(V) \ + V(ST, RB, w, 0x00000000), \ + V(ST, RH, w, 0x40000000), \ + V(ST, R, w, 0x80000000), \ + V(ST, R, x, 0xC0000000), \ + V(LD, RB, w, 0x00400000), \ + V(LD, RH, w, 0x40400000), \ + V(LD, R, w, 0x80400000), \ + V(LD, R, x, 0xC0400000), \ + V(LD, RSB, x, 0x00800000), \ + V(LD, RSH, x, 0x40800000), \ + V(LD, RSW, x, 0x80800000), \ + V(LD, RSB, w, 0x00C00000), \ + V(LD, RSH, w, 0x40C00000), \ + V(ST, R, s, 0x84000000), \ + V(ST, R, d, 0xC4000000), \ + V(LD, R, s, 0x84400000), \ + V(LD, R, d, 0xC4400000) + + +// Load/store unscaled offset. +enum LoadStoreUnscaledOffsetOp { + LoadStoreUnscaledOffsetFixed = 0x38000000, + LoadStoreUnscaledOffsetFMask = 0x3B200C00, + LoadStoreUnscaledOffsetMask = 0xFFE00C00, + #define LOAD_STORE_UNSCALED(A, B, C, D) \ + A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) + #undef LOAD_STORE_UNSCALED +}; + +// Load/store (post, pre, offset and unsigned.) +enum LoadStoreOp { + LoadStoreOpMask = 0xC4C00000, + #define LOAD_STORE(A, B, C, D) \ + A##B##_##C = D + LOAD_STORE_OP_LIST(LOAD_STORE), + #undef LOAD_STORE + PRFM = 0xC0800000 +}; + +// Load/store post index. +enum LoadStorePostIndex { + LoadStorePostIndexFixed = 0x38000400, + LoadStorePostIndexFMask = 0x3B200C00, + LoadStorePostIndexMask = 0xFFE00C00, + #define LOAD_STORE_POST_INDEX(A, B, C, D) \ + A##B##_##C##_post = LoadStorePostIndexFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) + #undef LOAD_STORE_POST_INDEX +}; + +// Load/store pre index. +enum LoadStorePreIndex { + LoadStorePreIndexFixed = 0x38000C00, + LoadStorePreIndexFMask = 0x3B200C00, + LoadStorePreIndexMask = 0xFFE00C00, + #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ + A##B##_##C##_pre = LoadStorePreIndexFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) + #undef LOAD_STORE_PRE_INDEX +}; + +// Load/store unsigned offset. +enum LoadStoreUnsignedOffset { + LoadStoreUnsignedOffsetFixed = 0x39000000, + LoadStoreUnsignedOffsetFMask = 0x3B000000, + LoadStoreUnsignedOffsetMask = 0xFFC00000, + PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, + #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ + A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) + #undef LOAD_STORE_UNSIGNED_OFFSET +}; + +// Load/store register offset. +enum LoadStoreRegisterOffset { + LoadStoreRegisterOffsetFixed = 0x38200800, + LoadStoreRegisterOffsetFMask = 0x3B200C00, + LoadStoreRegisterOffsetMask = 0xFFE00C00, + PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, + #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ + A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D + LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) + #undef LOAD_STORE_REGISTER_OFFSET +}; + +// Conditional compare. +enum ConditionalCompareOp { + ConditionalCompareMask = 0x60000000, + CCMN = 0x20000000, + CCMP = 0x60000000 +}; + +// Conditional compare register. +enum ConditionalCompareRegisterOp { + ConditionalCompareRegisterFixed = 0x1A400000, + ConditionalCompareRegisterFMask = 0x1FE00800, + ConditionalCompareRegisterMask = 0xFFE00C10, + CCMN_w = ConditionalCompareRegisterFixed | CCMN, + CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, + CCMP_w = ConditionalCompareRegisterFixed | CCMP, + CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP +}; + +// Conditional compare immediate. +enum ConditionalCompareImmediateOp { + ConditionalCompareImmediateFixed = 0x1A400800, + ConditionalCompareImmediateFMask = 0x1FE00800, + ConditionalCompareImmediateMask = 0xFFE00C10, + CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, + CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, + CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, + CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP +}; + +// Conditional select. +enum ConditionalSelectOp { + ConditionalSelectFixed = 0x1A800000, + ConditionalSelectFMask = 0x1FE00000, + ConditionalSelectMask = 0xFFE00C00, + CSEL_w = ConditionalSelectFixed | 0x00000000, + CSEL_x = ConditionalSelectFixed | 0x80000000, + CSEL = CSEL_w, + CSINC_w = ConditionalSelectFixed | 0x00000400, + CSINC_x = ConditionalSelectFixed | 0x80000400, + CSINC = CSINC_w, + CSINV_w = ConditionalSelectFixed | 0x40000000, + CSINV_x = ConditionalSelectFixed | 0xC0000000, + CSINV = CSINV_w, + CSNEG_w = ConditionalSelectFixed | 0x40000400, + CSNEG_x = ConditionalSelectFixed | 0xC0000400, + CSNEG = CSNEG_w +}; + +// Data processing 1 source. +enum DataProcessing1SourceOp { + DataProcessing1SourceFixed = 0x5AC00000, + DataProcessing1SourceFMask = 0x5FE00000, + DataProcessing1SourceMask = 0xFFFFFC00, + RBIT = DataProcessing1SourceFixed | 0x00000000, + RBIT_w = RBIT, + RBIT_x = RBIT | SixtyFourBits, + REV16 = DataProcessing1SourceFixed | 0x00000400, + REV16_w = REV16, + REV16_x = REV16 | SixtyFourBits, + REV = DataProcessing1SourceFixed | 0x00000800, + REV_w = REV, + REV32_x = REV | SixtyFourBits, + REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, + CLZ = DataProcessing1SourceFixed | 0x00001000, + CLZ_w = CLZ, + CLZ_x = CLZ | SixtyFourBits, + CLS = DataProcessing1SourceFixed | 0x00001400, + CLS_w = CLS, + CLS_x = CLS | SixtyFourBits +}; + +// Data processing 2 source. +enum DataProcessing2SourceOp { + DataProcessing2SourceFixed = 0x1AC00000, + DataProcessing2SourceFMask = 0x5FE00000, + DataProcessing2SourceMask = 0xFFE0FC00, + UDIV_w = DataProcessing2SourceFixed | 0x00000800, + UDIV_x = DataProcessing2SourceFixed | 0x80000800, + UDIV = UDIV_w, + SDIV_w = DataProcessing2SourceFixed | 0x00000C00, + SDIV_x = DataProcessing2SourceFixed | 0x80000C00, + SDIV = SDIV_w, + LSLV_w = DataProcessing2SourceFixed | 0x00002000, + LSLV_x = DataProcessing2SourceFixed | 0x80002000, + LSLV = LSLV_w, + LSRV_w = DataProcessing2SourceFixed | 0x00002400, + LSRV_x = DataProcessing2SourceFixed | 0x80002400, + LSRV = LSRV_w, + ASRV_w = DataProcessing2SourceFixed | 0x00002800, + ASRV_x = DataProcessing2SourceFixed | 0x80002800, + ASRV = ASRV_w, + RORV_w = DataProcessing2SourceFixed | 0x00002C00, + RORV_x = DataProcessing2SourceFixed | 0x80002C00, + RORV = RORV_w, + CRC32B = DataProcessing2SourceFixed | 0x00004000, + CRC32H = DataProcessing2SourceFixed | 0x00004400, + CRC32W = DataProcessing2SourceFixed | 0x00004800, + CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, + CRC32CB = DataProcessing2SourceFixed | 0x00005000, + CRC32CH = DataProcessing2SourceFixed | 0x00005400, + CRC32CW = DataProcessing2SourceFixed | 0x00005800, + CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 +}; + +// Data processing 3 source. +enum DataProcessing3SourceOp { + DataProcessing3SourceFixed = 0x1B000000, + DataProcessing3SourceFMask = 0x1F000000, + DataProcessing3SourceMask = 0xFFE08000, + MADD_w = DataProcessing3SourceFixed | 0x00000000, + MADD_x = DataProcessing3SourceFixed | 0x80000000, + MADD = MADD_w, + MSUB_w = DataProcessing3SourceFixed | 0x00008000, + MSUB_x = DataProcessing3SourceFixed | 0x80008000, + MSUB = MSUB_w, + SMADDL_x = DataProcessing3SourceFixed | 0x80200000, + SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, + SMULH_x = DataProcessing3SourceFixed | 0x80400000, + UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, + UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, + UMULH_x = DataProcessing3SourceFixed | 0x80C00000 +}; + +// Floating point compare. +enum FPCompareOp { + FPCompareFixed = 0x1E202000, + FPCompareFMask = 0x5F203C00, + FPCompareMask = 0xFFE0FC1F, + FCMP_s = FPCompareFixed | 0x00000000, + FCMP_d = FPCompareFixed | FP64 | 0x00000000, + FCMP = FCMP_s, + FCMP_s_zero = FPCompareFixed | 0x00000008, + FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, + FCMP_zero = FCMP_s_zero, + FCMPE_s = FPCompareFixed | 0x00000010, + FCMPE_d = FPCompareFixed | FP64 | 0x00000010, + FCMPE_s_zero = FPCompareFixed | 0x00000018, + FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018 +}; + +// Floating point conditional compare. +enum FPConditionalCompareOp { + FPConditionalCompareFixed = 0x1E200400, + FPConditionalCompareFMask = 0x5F200C00, + FPConditionalCompareMask = 0xFFE00C10, + FCCMP_s = FPConditionalCompareFixed | 0x00000000, + FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, + FCCMP = FCCMP_s, + FCCMPE_s = FPConditionalCompareFixed | 0x00000010, + FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, + FCCMPE = FCCMPE_s +}; + +// Floating point conditional select. +enum FPConditionalSelectOp { + FPConditionalSelectFixed = 0x1E200C00, + FPConditionalSelectFMask = 0x5F200C00, + FPConditionalSelectMask = 0xFFE00C00, + FCSEL_s = FPConditionalSelectFixed | 0x00000000, + FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, + FCSEL = FCSEL_s +}; + +// Floating point immediate. +enum FPImmediateOp { + FPImmediateFixed = 0x1E201000, + FPImmediateFMask = 0x5F201C00, + FPImmediateMask = 0xFFE01C00, + FMOV_s_imm = FPImmediateFixed | 0x00000000, + FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 +}; + +// Floating point data processing 1 source. +enum FPDataProcessing1SourceOp { + FPDataProcessing1SourceFixed = 0x1E204000, + FPDataProcessing1SourceFMask = 0x5F207C00, + FPDataProcessing1SourceMask = 0xFFFFFC00, + FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, + FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, + FMOV = FMOV_s, + FABS_s = FPDataProcessing1SourceFixed | 0x00008000, + FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, + FABS = FABS_s, + FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, + FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, + FNEG = FNEG_s, + FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, + FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, + FSQRT = FSQRT_s, + FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, + FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, + FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, + FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, + FRINTN = FRINTN_s, + FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, + FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, + FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, + FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, + FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, + FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, + FRINTZ = FRINTZ_s, + FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, + FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, + FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, + FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, + FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, + FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000 +}; + +// Floating point data processing 2 source. +enum FPDataProcessing2SourceOp { + FPDataProcessing2SourceFixed = 0x1E200800, + FPDataProcessing2SourceFMask = 0x5F200C00, + FPDataProcessing2SourceMask = 0xFFE0FC00, + FMUL = FPDataProcessing2SourceFixed | 0x00000000, + FMUL_s = FMUL, + FMUL_d = FMUL | FP64, + FDIV = FPDataProcessing2SourceFixed | 0x00001000, + FDIV_s = FDIV, + FDIV_d = FDIV | FP64, + FADD = FPDataProcessing2SourceFixed | 0x00002000, + FADD_s = FADD, + FADD_d = FADD | FP64, + FSUB = FPDataProcessing2SourceFixed | 0x00003000, + FSUB_s = FSUB, + FSUB_d = FSUB | FP64, + FMAX = FPDataProcessing2SourceFixed | 0x00004000, + FMAX_s = FMAX, + FMAX_d = FMAX | FP64, + FMIN = FPDataProcessing2SourceFixed | 0x00005000, + FMIN_s = FMIN, + FMIN_d = FMIN | FP64, + FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, + FMAXNM_s = FMAXNM, + FMAXNM_d = FMAXNM | FP64, + FMINNM = FPDataProcessing2SourceFixed | 0x00007000, + FMINNM_s = FMINNM, + FMINNM_d = FMINNM | FP64, + FNMUL = FPDataProcessing2SourceFixed | 0x00008000, + FNMUL_s = FNMUL, + FNMUL_d = FNMUL | FP64 +}; + +// Floating point data processing 3 source. +enum FPDataProcessing3SourceOp { + FPDataProcessing3SourceFixed = 0x1F000000, + FPDataProcessing3SourceFMask = 0x5F000000, + FPDataProcessing3SourceMask = 0xFFE08000, + FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, + FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, + FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, + FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, + FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, + FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, + FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, + FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 +}; + +// Conversion between floating point and integer. +enum FPIntegerConvertOp { + FPIntegerConvertFixed = 0x1E200000, + FPIntegerConvertFMask = 0x5F20FC00, + FPIntegerConvertMask = 0xFFFFFC00, + FCVTNS = FPIntegerConvertFixed | 0x00000000, + FCVTNS_ws = FCVTNS, + FCVTNS_xs = FCVTNS | SixtyFourBits, + FCVTNS_wd = FCVTNS | FP64, + FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, + FCVTNU = FPIntegerConvertFixed | 0x00010000, + FCVTNU_ws = FCVTNU, + FCVTNU_xs = FCVTNU | SixtyFourBits, + FCVTNU_wd = FCVTNU | FP64, + FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, + FCVTPS = FPIntegerConvertFixed | 0x00080000, + FCVTPS_ws = FCVTPS, + FCVTPS_xs = FCVTPS | SixtyFourBits, + FCVTPS_wd = FCVTPS | FP64, + FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, + FCVTPU = FPIntegerConvertFixed | 0x00090000, + FCVTPU_ws = FCVTPU, + FCVTPU_xs = FCVTPU | SixtyFourBits, + FCVTPU_wd = FCVTPU | FP64, + FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, + FCVTMS = FPIntegerConvertFixed | 0x00100000, + FCVTMS_ws = FCVTMS, + FCVTMS_xs = FCVTMS | SixtyFourBits, + FCVTMS_wd = FCVTMS | FP64, + FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, + FCVTMU = FPIntegerConvertFixed | 0x00110000, + FCVTMU_ws = FCVTMU, + FCVTMU_xs = FCVTMU | SixtyFourBits, + FCVTMU_wd = FCVTMU | FP64, + FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, + FCVTZS = FPIntegerConvertFixed | 0x00180000, + FCVTZS_ws = FCVTZS, + FCVTZS_xs = FCVTZS | SixtyFourBits, + FCVTZS_wd = FCVTZS | FP64, + FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, + FCVTZU = FPIntegerConvertFixed | 0x00190000, + FCVTZU_ws = FCVTZU, + FCVTZU_xs = FCVTZU | SixtyFourBits, + FCVTZU_wd = FCVTZU | FP64, + FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, + SCVTF = FPIntegerConvertFixed | 0x00020000, + SCVTF_sw = SCVTF, + SCVTF_sx = SCVTF | SixtyFourBits, + SCVTF_dw = SCVTF | FP64, + SCVTF_dx = SCVTF | SixtyFourBits | FP64, + UCVTF = FPIntegerConvertFixed | 0x00030000, + UCVTF_sw = UCVTF, + UCVTF_sx = UCVTF | SixtyFourBits, + UCVTF_dw = UCVTF | FP64, + UCVTF_dx = UCVTF | SixtyFourBits | FP64, + FCVTAS = FPIntegerConvertFixed | 0x00040000, + FCVTAS_ws = FCVTAS, + FCVTAS_xs = FCVTAS | SixtyFourBits, + FCVTAS_wd = FCVTAS | FP64, + FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, + FCVTAU = FPIntegerConvertFixed | 0x00050000, + FCVTAU_ws = FCVTAU, + FCVTAU_xs = FCVTAU | SixtyFourBits, + FCVTAU_wd = FCVTAU | FP64, + FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, + FMOV_ws = FPIntegerConvertFixed | 0x00060000, + FMOV_sw = FPIntegerConvertFixed | 0x00070000, + FMOV_xd = FMOV_ws | SixtyFourBits | FP64, + FMOV_dx = FMOV_sw | SixtyFourBits | FP64 +}; + +// Conversion between fixed point and floating point. +enum FPFixedPointConvertOp { + FPFixedPointConvertFixed = 0x1E000000, + FPFixedPointConvertFMask = 0x5F200000, + FPFixedPointConvertMask = 0xFFFF0000, + FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, + FCVTZS_ws_fixed = FCVTZS_fixed, + FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, + FCVTZS_wd_fixed = FCVTZS_fixed | FP64, + FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, + FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, + FCVTZU_ws_fixed = FCVTZU_fixed, + FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, + FCVTZU_wd_fixed = FCVTZU_fixed | FP64, + FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, + SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, + SCVTF_sw_fixed = SCVTF_fixed, + SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, + SCVTF_dw_fixed = SCVTF_fixed | FP64, + SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, + UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, + UCVTF_sw_fixed = UCVTF_fixed, + UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, + UCVTF_dw_fixed = UCVTF_fixed | FP64, + UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 +}; + +// Unimplemented and unallocated instructions. These are defined to make fixed +// bit assertion easier. +enum UnimplementedOp { + UnimplementedFixed = 0x00000000, + UnimplementedFMask = 0x00000000 +}; + +enum UnallocatedOp { + UnallocatedFixed = 0x00000000, + UnallocatedFMask = 0x00000000 +}; + +} // namespace vixl + +#endif // VIXL_A64_CONSTANTS_A64_H_ diff --git a/disas/libvixl/src/a64/cpu-a64.cc b/disas/libvixl/src/a64/cpu-a64.cc new file mode 100644 index 0000000..8586563 --- /dev/null +++ b/disas/libvixl/src/a64/cpu-a64.cc @@ -0,0 +1,148 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "utils.h" +#include "a64/cpu-a64.h" + +namespace vixl { + +// Initialise to smallest possible cache size. +unsigned CPU::dcache_line_size_ = 1; +unsigned CPU::icache_line_size_ = 1; + + +// Currently computes I and D cache line size. +void CPU::SetUp() { + uint32_t cache_type_register = GetCacheType(); + + // The cache type register holds information about the caches, including I + // D caches line size. + static const int kDCacheLineSizeShift = 16; + static const int kICacheLineSizeShift = 0; + static const uint32_t kDCacheLineSizeMask = 0xf << kDCacheLineSizeShift; + static const uint32_t kICacheLineSizeMask = 0xf << kICacheLineSizeShift; + + // The cache type register holds the size of the I and D caches as a power of + // two. + uint32_t dcache_line_size_power_of_two = + (cache_type_register & kDCacheLineSizeMask) >> kDCacheLineSizeShift; + uint32_t icache_line_size_power_of_two = + (cache_type_register & kICacheLineSizeMask) >> kICacheLineSizeShift; + + dcache_line_size_ = 1 << dcache_line_size_power_of_two; + icache_line_size_ = 1 << icache_line_size_power_of_two; +} + + +uint32_t CPU::GetCacheType() { +#ifdef USE_SIMULATOR + // This will lead to a cache with 1 byte long lines, which is fine since the + // simulator will not need this information. + return 0; +#else + uint32_t cache_type_register; + // Copy the content of the cache type register to a core register. + __asm__ __volatile__ ("mrs %[ctr], ctr_el0" // NOLINT + : [ctr] "=r" (cache_type_register)); + return cache_type_register; +#endif +} + + +void CPU::EnsureIAndDCacheCoherency(void *address, size_t length) { +#ifdef USE_SIMULATOR + USE(address); + USE(length); + // TODO: consider adding cache simulation to ensure every address run has been + // synchronised. +#else + // The code below assumes user space cache operations are allowed. + + uintptr_t start = reinterpret_cast(address); + // Sizes will be used to generate a mask big enough to cover a pointer. + uintptr_t dsize = static_cast(dcache_line_size_); + uintptr_t isize = static_cast(icache_line_size_); + // Cache line sizes are always a power of 2. + ASSERT(CountSetBits(dsize, 64) == 1); + ASSERT(CountSetBits(isize, 64) == 1); + uintptr_t dstart = start & ~(dsize - 1); + uintptr_t istart = start & ~(isize - 1); + uintptr_t end = start + length; + + __asm__ __volatile__ ( // NOLINT + // Clean every line of the D cache containing the target data. + "0: \n\t" + // dc : Data Cache maintenance + // c : Clean + // va : by (Virtual) Address + // u : to the point of Unification + // The point of unification for a processor is the point by which the + // instruction and data caches are guaranteed to see the same copy of a + // memory location. See ARM DDI 0406B page B2-12 for more information. + "dc cvau, %[dline] \n\t" + "add %[dline], %[dline], %[dsize] \n\t" + "cmp %[dline], %[end] \n\t" + "b.lt 0b \n\t" + // Barrier to make sure the effect of the code above is visible to the rest + // of the world. + // dsb : Data Synchronisation Barrier + // ish : Inner SHareable domain + // The point of unification for an Inner Shareable shareability domain is + // the point by which the instruction and data caches of all the processors + // in that Inner Shareable shareability domain are guaranteed to see the + // same copy of a memory location. See ARM DDI 0406B page B2-12 for more + // information. + "dsb ish \n\t" + // Invalidate every line of the I cache containing the target data. + "1: \n\t" + // ic : instruction cache maintenance + // i : invalidate + // va : by address + // u : to the point of unification + "ic ivau, %[iline] \n\t" + "add %[iline], %[iline], %[isize] \n\t" + "cmp %[iline], %[end] \n\t" + "b.lt 1b \n\t" + // Barrier to make sure the effect of the code above is visible to the rest + // of the world. + "dsb ish \n\t" + // Barrier to ensure any prefetching which happened before this code is + // discarded. + // isb : Instruction Synchronisation Barrier + "isb \n\t" + : [dline] "+r" (dstart), + [iline] "+r" (istart) + : [dsize] "r" (dsize), + [isize] "r" (isize), + [end] "r" (end) + // This code does not write to memory but without the dependency gcc might + // move this code before the code is generated. + : "cc", "memory" + ); // NOLINT +#endif +} + +} // namespace vixl diff --git a/disas/libvixl/src/a64/cpu-a64.h b/disas/libvixl/src/a64/cpu-a64.h new file mode 100644 index 0000000..dfd8f01 --- /dev/null +++ b/disas/libvixl/src/a64/cpu-a64.h @@ -0,0 +1,56 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_CPU_A64_H +#define VIXL_CPU_A64_H + +#include "globals.h" + +namespace vixl { + +class CPU { + public: + // Initialise CPU support. + static void SetUp(); + + // Ensures the data at a given address and with a given size is the same for + // the I and D caches. I and D caches are not automatically coherent on ARM + // so this operation is required before any dynamically generated code can + // safely run. + static void EnsureIAndDCacheCoherency(void *address, size_t length); + + private: + // Return the content of the cache type register. + static uint32_t GetCacheType(); + + // I and D cache line size in bytes. + static unsigned icache_line_size_; + static unsigned dcache_line_size_; +}; + +} // namespace vixl + +#endif // VIXL_CPU_A64_H diff --git a/disas/libvixl/src/a64/debugger-a64.cc b/disas/libvixl/src/a64/debugger-a64.cc new file mode 100644 index 0000000..ecdc835 --- /dev/null +++ b/disas/libvixl/src/a64/debugger-a64.cc @@ -0,0 +1,1511 @@ +// Copyright 2013 ARM Limited +// 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 ARM Limited 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 ARM LIMITED 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 ARM LIMITED 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. + +#include "a64/debugger-a64.h" + +namespace vixl { + +// List of commands supported by the debugger. +#define DEBUG_COMMAND_LIST(C) \ +C(HelpCommand) \ +C(ContinueCommand) \ +C(StepCommand) \ +C(DisasmCommand) \ +C(PrintCommand) \ +C(MemCommand) + +// Debugger command lines are broken up in token of different type to make +// processing easier later on. +class Token { + public: + virtual ~Token() {} + + // Token type. + virtual bool IsRegister() const { return false; } + virtual bool IsFPRegister() const { return false; } + virtual bool IsIdentifier() const { return false; } + virtual bool IsAddress() const { return false; } + virtual bool IsInteger() const { return false; } + virtual bool IsFormat() const { return false; } + virtual bool IsUnknown() const { return false; } + // Token properties. + virtual bool CanAddressMemory() const { return false; } + virtual uint8_t* ToAddress(Debugger* debugger) const; + virtual void Print(FILE* out = stdout) const = 0; + + static Token* Tokenize(const char* arg); +}; + +// Tokens often hold one value. +template class ValueToken : public Token { + public: + explicit ValueToken(T value) : value_(value) {} + ValueToken() {} + + T value() const { return value_; } + + protected: + T value_; +}; + +// Integer registers (X or W) and their aliases. +// Format: wn or xn with 0 <= n < 32 or a name in the aliases list. +class RegisterToken : public ValueToken { + public: + explicit RegisterToken(const Register reg) + : ValueToken(reg) {} + + virtual bool IsRegister() const { return true; } + virtual bool CanAddressMemory() const { return value().Is64Bits(); } + virtual uint8_t* ToAddress(Debugger* debugger) const; + virtual void Print(FILE* out = stdout) const ; + const char* Name() const; + + static Token* Tokenize(const char* arg); + static RegisterToken* Cast(Token* tok) { + ASSERT(tok->IsRegister()); + return reinterpret_cast(tok); + } + + private: + static const int kMaxAliasNumber = 4; + static const char* kXAliases[kNumberOfRegisters][kMaxAliasNumber]; + static const char* kWAliases[kNumberOfRegisters][kMaxAliasNumber]; +}; + +// Floating point registers (D or S). +// Format: sn or dn with 0 <= n < 32. +class FPRegisterToken : public ValueToken { + public: + explicit FPRegisterToken(const FPRegister fpreg) + : ValueToken(fpreg) {} + + virtual bool IsFPRegister() const { return true; } + virtual void Print(FILE* out = stdout) const ; + + static Token* Tokenize(const char* arg); + static FPRegisterToken* Cast(Token* tok) { + ASSERT(tok->IsFPRegister()); + return reinterpret_cast(tok); + } +}; + + +// Non-register identifiers. +// Format: Alphanumeric string starting with a letter. +class IdentifierToken : public ValueToken { + public: + explicit IdentifierToken(const char* name) { + int size = strlen(name) + 1; + value_ = new char[size]; + strncpy(value_, name, size); + } + virtual ~IdentifierToken() { delete[] value_; } + + virtual bool IsIdentifier() const { return true; } + virtual bool CanAddressMemory() const { return strcmp(value(), "pc") == 0; } + virtual uint8_t* ToAddress(Debugger* debugger) const; + virtual void Print(FILE* out = stdout) const; + + static Token* Tokenize(const char* arg); + static IdentifierToken* Cast(Token* tok) { + ASSERT(tok->IsIdentifier()); + return reinterpret_cast(tok); + } +}; + +// 64-bit address literal. +// Format: 0x... with up to 16 hexadecimal digits. +class AddressToken : public ValueToken { + public: + explicit AddressToken(uint8_t* address) : ValueToken(address) {} + + virtual bool IsAddress() const { return true; } + virtual bool CanAddressMemory() const { return true; } + virtual uint8_t* ToAddress(Debugger* debugger) const; + virtual void Print(FILE* out = stdout) const ; + + static Token* Tokenize(const char* arg); + static AddressToken* Cast(Token* tok) { + ASSERT(tok->IsAddress()); + return reinterpret_cast(tok); + } +}; + + +// 64-bit decimal integer literal. +// Format: n. +class IntegerToken : public ValueToken { + public: + explicit IntegerToken(int value) : ValueToken(value) {} + + virtual bool IsInteger() const { return true; } + virtual void Print(FILE* out = stdout) const; + + static Token* Tokenize(const char* arg); + static IntegerToken* Cast(Token* tok) { + ASSERT(tok->IsInteger()); + return reinterpret_cast(tok); + } +}; + +// Literal describing how to print a chunk of data (up to 64 bits). +// Format: %qt +// where q (qualifier) is one of +// * s: signed integer +// * u: unsigned integer +// * a: hexadecimal floating point +// and t (type) is one of +// * x: 64-bit integer +// * w: 32-bit integer +// * h: 16-bit integer +// * b: 8-bit integer +// * c: character +// * d: double +// * s: float +// When no qualifier is given for integers, they are printed in hexadecinal. +class FormatToken : public Token { + public: + FormatToken() {} + + virtual bool IsFormat() const { return true; } + virtual int SizeOf() const = 0; + virtual void PrintData(void* data, FILE* out = stdout) const = 0; + virtual void Print(FILE* out = stdout) const = 0; + + static Token* Tokenize(const char* arg); + static FormatToken* Cast(Token* tok) { + ASSERT(tok->IsFormat()); + return reinterpret_cast(tok); + } +}; + + +template class Format : public FormatToken { + public: + explicit Format(const char* fmt) : fmt_(fmt) {} + + virtual int SizeOf() const { return sizeof(T); } + virtual void PrintData(void* data, FILE* out = stdout) const { + T value; + memcpy(&value, data, sizeof(value)); + fprintf(out, fmt_, value); + } + virtual void Print(FILE* out = stdout) const; + + private: + const char* fmt_; +}; + +// Tokens which don't fit any of the above. +class UnknownToken : public Token { + public: + explicit UnknownToken(const char* arg) { + int size = strlen(arg) + 1; + unknown_ = new char[size]; + strncpy(unknown_, arg, size); + } + virtual ~UnknownToken() { delete[] unknown_; } + + virtual bool IsUnknown() const { return true; } + virtual void Print(FILE* out = stdout) const; + + private: + char* unknown_; +}; + + +// All debugger commands must subclass DebugCommand and implement Run, Print +// and Build. Commands must also define kHelp and kAliases. +class DebugCommand { + public: + explicit DebugCommand(Token* name) : name_(IdentifierToken::Cast(name)) {} + DebugCommand() : name_(NULL) {} + virtual ~DebugCommand() { delete name_; } + + const char* name() { return name_->value(); } + // Run the command on the given debugger. The command returns true if + // execution should move to the next instruction. + virtual bool Run(Debugger * debugger) = 0; + virtual void Print(FILE* out = stdout); + + static bool Match(const char* name, const char** aliases); + static DebugCommand* Parse(char* line); + static void PrintHelp(const char** aliases, + const char* args, + const char* help); + + private: + IdentifierToken* name_; +}; + +// For all commands below see their respective kHelp and kAliases in +// debugger-a64.cc +class HelpCommand : public DebugCommand { + public: + explicit HelpCommand(Token* name) : DebugCommand(name) {} + + virtual bool Run(Debugger* debugger); + + static DebugCommand* Build(std::vector args); + + static const char* kHelp; + static const char* kAliases[]; + static const char* kArguments; +}; + + +class ContinueCommand : public DebugCommand { + public: + explicit ContinueCommand(Token* name) : DebugCommand(name) {} + + virtual bool Run(Debugger* debugger); + + static DebugCommand* Build(std::vector args); + + static const char* kHelp; + static const char* kAliases[]; + static const char* kArguments; +}; + + +class StepCommand : public DebugCommand { + public: + StepCommand(Token* name, IntegerToken* count) + : DebugCommand(name), count_(count) {} + virtual ~StepCommand() { delete count_; } + + int64_t count() { return count_->value(); } + virtual bool Run(Debugger* debugger); + virtual void Print(FILE* out = stdout); + + static DebugCommand* Build(std::vector args); + + static const char* kHelp; + static const char* kAliases[]; + static const char* kArguments; + + private: + IntegerToken* count_; +}; + +class DisasmCommand : public DebugCommand { + public: + DisasmCommand(Token* name, Token* target, IntegerToken* count) + : DebugCommand(name), target_(target), count_(count) {} + virtual ~DisasmCommand() { + delete target_; + delete count_; + } + + Token* target() { return target_; } + int64_t count() { return count_->value(); } + virtual bool Run(Debugger* debugger); + virtual void Print(FILE* out = stdout); + + static DebugCommand* Build(std::vector args); + + static const char* kHelp; + static const char* kAliases[]; + static const char* kArguments; + + private: + Token* target_; + IntegerToken* count_; +}; + + +class PrintCommand : public DebugCommand { + public: + PrintCommand(Token* name, Token* target) + : DebugCommand(name), target_(target) {} + virtual ~PrintCommand() { delete target_; } + + Token* target() { return target_; } + virtual bool Run(Debugger* debugger); + virtual void Print(FILE* out = stdout); + + static DebugCommand* Build(std::vector args); + + static const char* kHelp; + static const char* kAliases[]; + static const char* kArguments; + + private: + Token* target_; +}; + +class MemCommand : public DebugCommand { + public: + MemCommand(Token* name, + Token* target, + IntegerToken* count, + FormatToken* format) + : DebugCommand(name), target_(target), count_(count), format_(format) {} + virtual ~MemCommand() { + delete target_; + delete count_; + delete format_; + } + + Token* target() { return target_; } + int64_t count() { return count_->value(); } + FormatToken* format() { return format_; } + virtual bool Run(Debugger* debugger); + virtual void Print(FILE* out = stdout); + + static DebugCommand* Build(std::vector args); + + static const char* kHelp; + static const char* kAliases[]; + static const char* kArguments; + + private: + Token* target_; + IntegerToken* count_; + FormatToken* format_; +}; + +// Commands which name does not match any of the known commnand. +class UnknownCommand : public DebugCommand { + public: + explicit UnknownCommand(std::vector args) : args_(args) {} + virtual ~UnknownCommand(); + + virtual bool Run(Debugger* debugger); + + private: + std::vector args_; +}; + +// Commands which name match a known command but the syntax is invalid. +class InvalidCommand : public DebugCommand { + public: + InvalidCommand(std::vector args, int index, const char* cause) + : args_(args), index_(index), cause_(cause) {} + virtual ~InvalidCommand(); + + virtual bool Run(Debugger* debugger); + + private: + std::vector args_; + int index_; + const char* cause_; +}; + +const char* HelpCommand::kAliases[] = { "help", NULL }; +const char* HelpCommand::kArguments = NULL; +const char* HelpCommand::kHelp = " print this help"; + +const char* ContinueCommand::kAliases[] = { "continue", "c", NULL }; +const char* ContinueCommand::kArguments = NULL; +const char* ContinueCommand::kHelp = " resume execution"; + +const char* StepCommand::kAliases[] = { "stepi", "si", NULL }; +const char* StepCommand::kArguments = "[n = 1]"; +const char* StepCommand::kHelp = " execute n next instruction(s)"; + +const char* DisasmCommand::kAliases[] = { "dis", "d", NULL }; +const char* DisasmCommand::kArguments = "[addr = pc] [n = 1]"; +const char* DisasmCommand::kHelp = + " disassemble n instruction(s) at address addr.\n" + " addr can be an immediate address, a register or the pc." +; + +const char* PrintCommand::kAliases[] = { "print", "p", NULL }; +const char* PrintCommand::kArguments = ""; +const char* PrintCommand::kHelp = + " print the given entity\n" + " entity can be 'regs' for W and X registers, 'fpregs' for S and D\n" + " registers, 'sysregs' for system registers (including NZCV) or 'pc'." +; + +const char* MemCommand::kAliases[] = { "mem", "m", NULL }; +const char* MemCommand::kArguments = " [n = 1] [format = %x]"; +const char* MemCommand::kHelp = + " print n memory item(s) at address addr according to the given format.\n" + " addr can be an immediate address, a register or the pc.\n" + " format is made of a qualifer: 's', 'u', 'a' (signed, unsigned, hexa)\n" + " and a type 'x', 'w', 'h', 'b' (64- to 8-bit integer), 'c' (character),\n" + " 's' (float) or 'd' (double). E.g 'mem sp %w' will print a 32-bit word\n" + " from the stack as an hexadecimal number." +; + +const char* RegisterToken::kXAliases[kNumberOfRegisters][kMaxAliasNumber] = { + { "x0", NULL }, + { "x1", NULL }, + { "x2", NULL }, + { "x3", NULL }, + { "x4", NULL }, + { "x5", NULL }, + { "x6", NULL }, + { "x7", NULL }, + { "x8", NULL }, + { "x9", NULL }, + { "x10", NULL }, + { "x11", NULL }, + { "x12", NULL }, + { "x13", NULL }, + { "x14", NULL }, + { "x15", NULL }, + { "ip0", "x16", NULL }, + { "ip1", "x17", NULL }, + { "x18", "pr", NULL }, + { "x19", NULL }, + { "x20", NULL }, + { "x21", NULL }, + { "x22", NULL }, + { "x23", NULL }, + { "x24", NULL }, + { "x25", NULL }, + { "x26", NULL }, + { "x27", NULL }, + { "x28", NULL }, + { "fp", "x29", NULL }, + { "lr", "x30", NULL }, + { "sp", NULL} +}; + +const char* RegisterToken::kWAliases[kNumberOfRegisters][kMaxAliasNumber] = { + { "w0", NULL }, + { "w1", NULL }, + { "w2", NULL }, + { "w3", NULL }, + { "w4", NULL }, + { "w5", NULL }, + { "w6", NULL }, + { "w7", NULL }, + { "w8", NULL }, + { "w9", NULL }, + { "w10", NULL }, + { "w11", NULL }, + { "w12", NULL }, + { "w13", NULL }, + { "w14", NULL }, + { "w15", NULL }, + { "w16", NULL }, + { "w17", NULL }, + { "w18", NULL }, + { "w19", NULL }, + { "w20", NULL }, + { "w21", NULL }, + { "w22", NULL }, + { "w23", NULL }, + { "w24", NULL }, + { "w25", NULL }, + { "w26", NULL }, + { "w27", NULL }, + { "w28", NULL }, + { "w29", NULL }, + { "w30", NULL }, + { "wsp", NULL } +}; + + +Debugger::Debugger(Decoder* decoder, FILE* stream) + : Simulator(decoder, stream), + log_parameters_(0), + debug_parameters_(0), + pending_request_(false), + steps_(0), + last_command_(NULL) { + disasm_ = new PrintDisassembler(stdout); + printer_ = new Decoder(); + printer_->AppendVisitor(disasm_); +} + + +void Debugger::Run() { + while (pc_ != kEndOfSimAddress) { + if (pending_request()) { + LogProcessorState(); + RunDebuggerShell(); + } + + ExecuteInstruction(); + } +} + + +void Debugger::PrintInstructions(void* address, int64_t count) { + if (count == 0) { + return; + } + + Instruction* from = Instruction::Cast(address); + if (count < 0) { + count = -count; + from -= (count - 1) * kInstructionSize; + } + Instruction* to = from + count * kInstructionSize; + + for (Instruction* current = from; + current < to; + current = current->NextInstruction()) { + printer_->Decode(current); + } +} + + +void Debugger::PrintMemory(const uint8_t* address, + int64_t count, + const FormatToken* format) { + if (count == 0) { + return; + } + + const uint8_t* from = address; + int size = format->SizeOf(); + if (count < 0) { + count = -count; + from -= (count - 1) * size; + } + const uint8_t* to = from + count * size; + + for (const uint8_t* current = from; current < to; current += size) { + if (((current - from) % 16) == 0) { + printf("\n%p: ", current); + } + + uint64_t data = MemoryRead(current, size); + format->PrintData(&data); + printf(" "); + } + printf("\n\n"); +} + + +void Debugger::VisitException(Instruction* instr) { + switch (instr->Mask(ExceptionMask)) { + case BRK: + DoBreakpoint(instr); + return; + case HLT: + switch (instr->ImmException()) { + case kUnreachableOpcode: + DoUnreachable(instr); + return; + case kTraceOpcode: + DoTrace(instr); + return; + case kLogOpcode: + DoLog(instr); + return; + } + // Fall through + default: Simulator::VisitException(instr); + } +} + + +void Debugger::LogSystemRegisters() { + if (log_parameters_ & LOG_SYS_REGS) PrintSystemRegisters(); +} + + +void Debugger::LogRegisters() { + if (log_parameters_ & LOG_REGS) PrintRegisters(); +} + + +void Debugger::LogFPRegisters() { + if (log_parameters_ & LOG_FP_REGS) PrintFPRegisters(); +} + + +void Debugger::LogProcessorState() { + LogSystemRegisters(); + LogRegisters(); + LogFPRegisters(); +} + + +// Read a command. A command will be at most kMaxDebugShellLine char long and +// ends with '\n\0'. +// TODO: Should this be a utility function? +char* Debugger::ReadCommandLine(const char* prompt, char* buffer, int length) { + int fgets_calls = 0; + char* end = NULL; + + printf("%s", prompt); + fflush(stdout); + + do { + if (fgets(buffer, length, stdin) == NULL) { + printf(" ** Error while reading command. **\n"); + return NULL; + } + + fgets_calls++; + end = strchr(buffer, '\n'); + } while (end == NULL); + + if (fgets_calls != 1) { + printf(" ** Command too long. **\n"); + return NULL; + } + + // Remove the newline from the end of the command. + ASSERT(end[1] == '\0'); + ASSERT((end - buffer) < (length - 1)); + end[0] = '\0'; + + return buffer; +} + + +void Debugger::RunDebuggerShell() { + if (IsDebuggerRunning()) { + if (steps_ > 0) { + // Finish stepping first. + --steps_; + return; + } + + printf("Next: "); + PrintInstructions(pc()); + bool done = false; + while (!done) { + char buffer[kMaxDebugShellLine]; + char* line = ReadCommandLine("vixl> ", buffer, kMaxDebugShellLine); + + if (line == NULL) continue; // An error occurred. + + DebugCommand* command = DebugCommand::Parse(line); + if (command != NULL) { + last_command_ = command; + } + + if (last_command_ != NULL) { + done = last_command_->Run(this); + } else { + printf("No previous command to run!\n"); + } + } + + if ((debug_parameters_ & DBG_BREAK) != 0) { + // The break request has now been handled, move to next instruction. + debug_parameters_ &= ~DBG_BREAK; + increment_pc(); + } + } +} + + +void Debugger::DoBreakpoint(Instruction* instr) { + ASSERT(instr->Mask(ExceptionMask) == BRK); + + printf("Hit breakpoint at pc=%p.\n", reinterpret_cast(instr)); + set_debug_parameters(debug_parameters() | DBG_BREAK | DBG_ACTIVE); + // Make the shell point to the brk instruction. + set_pc(instr); +} + + +void Debugger::DoUnreachable(Instruction* instr) { + ASSERT((instr->Mask(ExceptionMask) == HLT) && + (instr->ImmException() == kUnreachableOpcode)); + + fprintf(stream_, "Hit UNREACHABLE marker at pc=%p.\n", + reinterpret_cast(instr)); + abort(); +} + + +void Debugger::DoTrace(Instruction* instr) { + ASSERT((instr->Mask(ExceptionMask) == HLT) && + (instr->ImmException() == kTraceOpcode)); + + // Read the arguments encoded inline in the instruction stream. + uint32_t parameters; + uint32_t command; + + ASSERT(sizeof(*instr) == 1); + memcpy(¶meters, instr + kTraceParamsOffset, sizeof(parameters)); + memcpy(&command, instr + kTraceCommandOffset, sizeof(command)); + + switch (command) { + case TRACE_ENABLE: + set_log_parameters(log_parameters() | parameters); + break; + case TRACE_DISABLE: + set_log_parameters(log_parameters() & ~parameters); + break; + default: + UNREACHABLE(); + } + + set_pc(instr->InstructionAtOffset(kTraceLength)); +} + + +void Debugger::DoLog(Instruction* instr) { + ASSERT((instr->Mask(ExceptionMask) == HLT) && + (instr->ImmException() == kLogOpcode)); + + // Read the arguments encoded inline in the instruction stream. + uint32_t parameters; + + ASSERT(sizeof(*instr) == 1); + memcpy(¶meters, instr + kTraceParamsOffset, sizeof(parameters)); + + // We don't support a one-shot LOG_DISASM. + ASSERT((parameters & LOG_DISASM) == 0); + // Print the requested information. + if (parameters & LOG_SYS_REGS) PrintSystemRegisters(true); + if (parameters & LOG_REGS) PrintRegisters(true); + if (parameters & LOG_FP_REGS) PrintFPRegisters(true); + + set_pc(instr->InstructionAtOffset(kLogLength)); +} + + +static bool StringToUInt64(uint64_t* value, const char* line, int base = 10) { + char* endptr = NULL; + errno = 0; // Reset errors. + uint64_t parsed = strtoul(line, &endptr, base); + + if (errno == ERANGE) { + // Overflow. + return false; + } + + if (endptr == line) { + // No digits were parsed. + return false; + } + + if (*endptr != '\0') { + // Non-digit characters present at the end. + return false; + } + + *value = parsed; + return true; +} + + +static bool StringToInt64(int64_t* value, const char* line, int base = 10) { + char* endptr = NULL; + errno = 0; // Reset errors. + int64_t parsed = strtol(line, &endptr, base); + + if (errno == ERANGE) { + // Overflow, undeflow. + return false; + } + + if (endptr == line) { + // No digits were parsed. + return false; + } + + if (*endptr != '\0') { + // Non-digit characters present at the end. + return false; + } + + *value = parsed; + return true; +} + + +uint8_t* Token::ToAddress(Debugger* debugger) const { + USE(debugger); + UNREACHABLE(); + return NULL; +} + + +Token* Token::Tokenize(const char* arg) { + if ((arg == NULL) || (*arg == '\0')) { + return NULL; + } + + // The order is important. For example Identifier::Tokenize would consider + // any register to be a valid identifier. + + Token* token = RegisterToken::Tokenize(arg); + if (token != NULL) { + return token; + } + + token = FPRegisterToken::Tokenize(arg); + if (token != NULL) { + return token; + } + + token = IdentifierToken::Tokenize(arg); + if (token != NULL) { + return token; + } + + token = AddressToken::Tokenize(arg); + if (token != NULL) { + return token; + } + + token = IntegerToken::Tokenize(arg); + if (token != NULL) { + return token; + } + + token = FormatToken::Tokenize(arg); + if (token != NULL) { + return token; + } + + return new UnknownToken(arg); +} + + +uint8_t* RegisterToken::ToAddress(Debugger* debugger) const { + ASSERT(CanAddressMemory()); + uint64_t reg_value = debugger->xreg(value().code(), Reg31IsStackPointer); + uint8_t* address = NULL; + memcpy(&address, ®_value, sizeof(address)); + return address; +} + + +void RegisterToken::Print(FILE* out) const { + ASSERT(value().IsValid()); + fprintf(out, "[Register %s]", Name()); +} + + +const char* RegisterToken::Name() const { + if (value().Is32Bits()) { + return kWAliases[value().code()][0]; + } else { + return kXAliases[value().code()][0]; + } +} + + +Token* RegisterToken::Tokenize(const char* arg) { + for (unsigned i = 0; i < kNumberOfRegisters; i++) { + // Is it a X register or alias? + for (const char** current = kXAliases[i]; *current != NULL; current++) { + if (strcmp(arg, *current) == 0) { + return new RegisterToken(Register::XRegFromCode(i)); + } + } + + // Is it a W register or alias? + for (const char** current = kWAliases[i]; *current != NULL; current++) { + if (strcmp(arg, *current) == 0) { + return new RegisterToken(Register::WRegFromCode(i)); + } + } + } + + return NULL; +} + + +void FPRegisterToken::Print(FILE* out) const { + ASSERT(value().IsValid()); + char prefix = value().Is32Bits() ? 's' : 'd'; + fprintf(out, "[FPRegister %c%" PRIu32 "]", prefix, value().code()); +} + + +Token* FPRegisterToken::Tokenize(const char* arg) { + if (strlen(arg) < 2) { + return NULL; + } + + switch (*arg) { + case 's': + case 'd': + const char* cursor = arg + 1; + uint64_t code = 0; + if (!StringToUInt64(&code, cursor)) { + return NULL; + } + + if (code > kNumberOfFPRegisters) { + return NULL; + } + + FPRegister fpreg = NoFPReg; + switch (*arg) { + case 's': fpreg = FPRegister::SRegFromCode(code); break; + case 'd': fpreg = FPRegister::DRegFromCode(code); break; + default: UNREACHABLE(); + } + + return new FPRegisterToken(fpreg); + } + + return NULL; +} + + +uint8_t* IdentifierToken::ToAddress(Debugger* debugger) const { + ASSERT(CanAddressMemory()); + Instruction* pc_value = debugger->pc(); + uint8_t* address = NULL; + memcpy(&address, &pc_value, sizeof(address)); + return address; +} + +void IdentifierToken::Print(FILE* out) const { + fprintf(out, "[Identifier %s]", value()); +} + + +Token* IdentifierToken::Tokenize(const char* arg) { + if (!isalpha(arg[0])) { + return NULL; + } + + const char* cursor = arg + 1; + while ((*cursor != '\0') && isalnum(*cursor)) { + ++cursor; + } + + if (*cursor == '\0') { + return new IdentifierToken(arg); + } + + return NULL; +} + + +uint8_t* AddressToken::ToAddress(Debugger* debugger) const { + USE(debugger); + return value(); +} + + +void AddressToken::Print(FILE* out) const { + fprintf(out, "[Address %p]", value()); +} + + +Token* AddressToken::Tokenize(const char* arg) { + if ((strlen(arg) < 3) || (arg[0] != '0') || (arg[1] != 'x')) { + return NULL; + } + + uint64_t ptr = 0; + if (!StringToUInt64(&ptr, arg, 16)) { + return NULL; + } + + uint8_t* address = reinterpret_cast(ptr); + return new AddressToken(address); +} + + +void IntegerToken::Print(FILE* out) const { + fprintf(out, "[Integer %" PRId64 "]", value()); +} + + +Token* IntegerToken::Tokenize(const char* arg) { + int64_t value = 0; + if (!StringToInt64(&value, arg)) { + return NULL; + } + + return new IntegerToken(value); +} + + +Token* FormatToken::Tokenize(const char* arg) { + if (arg[0] != '%') { + return NULL; + } + + int length = strlen(arg); + if ((length < 2) || (length > 3)) { + return NULL; + } + + char type = arg[length - 1]; + if (length == 2) { + switch (type) { + case 'x': return new Format("%016" PRIx64); + case 'w': return new Format("%08" PRIx32); + case 'h': return new Format("%04" PRIx16); + case 'b': return new Format("%02" PRIx8); + case 'c': return new Format("%c"); + case 'd': return new Format("%g"); + case 's': return new Format("%g"); + default: return NULL; + } + } + + ASSERT(length == 3); + switch (arg[1]) { + case 's': + switch (type) { + case 'x': return new Format("%+20" PRId64); + case 'w': return new Format("%+11" PRId32); + case 'h': return new Format("%+6" PRId16); + case 'b': return new Format("%+4" PRId8); + default: return NULL; + } + case 'u': + switch (type) { + case 'x': return new Format("%20" PRIu64); + case 'w': return new Format("%10" PRIu32); + case 'h': return new Format("%5" PRIu16); + case 'b': return new Format("%3" PRIu8); + default: return NULL; + } + case 'a': + switch (type) { + case 'd': return new Format("%a"); + case 's': return new Format("%a"); + default: return NULL; + } + default: return NULL; + } +} + + +template +void Format::Print(FILE* out) const { + fprintf(out, "[Format %s - %lu byte(s)]", fmt_, sizeof(T)); +} + + +void UnknownToken::Print(FILE* out) const { + fprintf(out, "[Unknown %s]", unknown_); +} + + +void DebugCommand::Print(FILE* out) { + fprintf(out, "%s", name()); +} + + +bool DebugCommand::Match(const char* name, const char** aliases) { + for (const char** current = aliases; *current != NULL; current++) { + if (strcmp(name, *current) == 0) { + return true; + } + } + + return false; +} + + +DebugCommand* DebugCommand::Parse(char* line) { + std::vector args; + + for (char* chunk = strtok(line, " "); + chunk != NULL; + chunk = strtok(NULL, " ")) { + args.push_back(Token::Tokenize(chunk)); + } + + if (args.size() == 0) { + return NULL; + } + + if (!args[0]->IsIdentifier()) { + return new InvalidCommand(args, 0, "command name is not an identifier"); + } + + const char* name = IdentifierToken::Cast(args[0])->value(); + #define RETURN_IF_MATCH(Command) \ + if (Match(name, Command::kAliases)) { \ + return Command::Build(args); \ + } + DEBUG_COMMAND_LIST(RETURN_IF_MATCH); + #undef RETURN_IF_MATCH + + return new UnknownCommand(args); +} + + +void DebugCommand::PrintHelp(const char** aliases, + const char* args, + const char* help) { + ASSERT(aliases[0] != NULL); + ASSERT(help != NULL); + + printf("\n----\n\n"); + for (const char** current = aliases; *current != NULL; current++) { + if (args != NULL) { + printf("%s %s\n", *current, args); + } else { + printf("%s\n", *current); + } + } + printf("\n%s\n", help); +} + + +bool HelpCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + USE(debugger); + + #define PRINT_HELP(Command) \ + DebugCommand::PrintHelp(Command::kAliases, \ + Command::kArguments, \ + Command::kHelp); + DEBUG_COMMAND_LIST(PRINT_HELP); + #undef PRINT_HELP + printf("\n----\n\n"); + + return false; +} + + +DebugCommand* HelpCommand::Build(std::vector args) { + if (args.size() != 1) { + return new InvalidCommand(args, -1, "too many arguments"); + } + + return new HelpCommand(args[0]); +} + + +bool ContinueCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + + debugger->set_debug_parameters(debugger->debug_parameters() & ~DBG_ACTIVE); + return true; +} + + +DebugCommand* ContinueCommand::Build(std::vector args) { + if (args.size() != 1) { + return new InvalidCommand(args, -1, "too many arguments"); + } + + return new ContinueCommand(args[0]); +} + + +bool StepCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + + int64_t steps = count(); + if (steps < 0) { + printf(" ** invalid value for steps: %" PRId64 " (<0) **\n", steps); + } else if (steps > 1) { + debugger->set_steps(steps - 1); + } + + return true; +} + + +void StepCommand::Print(FILE* out) { + fprintf(out, "%s %" PRId64 "", name(), count()); +} + + +DebugCommand* StepCommand::Build(std::vector args) { + IntegerToken* count = NULL; + switch (args.size()) { + case 1: { // step [1] + count = new IntegerToken(1); + break; + } + case 2: { // step n + Token* first = args[1]; + if (!first->IsInteger()) { + return new InvalidCommand(args, 1, "expects int"); + } + count = IntegerToken::Cast(first); + break; + } + default: + return new InvalidCommand(args, -1, "too many arguments"); + } + + return new StepCommand(args[0], count); +} + + +bool DisasmCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + + uint8_t* from = target()->ToAddress(debugger); + debugger->PrintInstructions(from, count()); + + return false; +} + + +void DisasmCommand::Print(FILE* out) { + fprintf(out, "%s ", name()); + target()->Print(out); + fprintf(out, " %" PRId64 "", count()); +} + + +DebugCommand* DisasmCommand::Build(std::vector args) { + Token* address = NULL; + IntegerToken* count = NULL; + switch (args.size()) { + case 1: { // disasm [pc] [1] + address = new IdentifierToken("pc"); + count = new IntegerToken(1); + break; + } + case 2: { // disasm [pc] n or disasm address [1] + Token* first = args[1]; + if (first->IsInteger()) { + address = new IdentifierToken("pc"); + count = IntegerToken::Cast(first); + } else if (first->CanAddressMemory()) { + address = first; + count = new IntegerToken(1); + } else { + return new InvalidCommand(args, 1, "expects int or addr"); + } + break; + } + case 3: { // disasm address count + Token* first = args[1]; + Token* second = args[2]; + if (!first->CanAddressMemory() || !second->IsInteger()) { + return new InvalidCommand(args, -1, "disasm addr int"); + } + address = first; + count = IntegerToken::Cast(second); + break; + } + default: + return new InvalidCommand(args, -1, "wrong arguments number"); + } + + return new DisasmCommand(args[0], address, count); +} + + +void PrintCommand::Print(FILE* out) { + fprintf(out, "%s ", name()); + target()->Print(out); +} + + +bool PrintCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + + Token* tok = target(); + if (tok->IsIdentifier()) { + char* identifier = IdentifierToken::Cast(tok)->value(); + if (strcmp(identifier, "regs") == 0) { + debugger->PrintRegisters(true); + } else if (strcmp(identifier, "fpregs") == 0) { + debugger->PrintFPRegisters(true); + } else if (strcmp(identifier, "sysregs") == 0) { + debugger->PrintSystemRegisters(true); + } else if (strcmp(identifier, "pc") == 0) { + printf("pc = %16p\n", reinterpret_cast(debugger->pc())); + } else { + printf(" ** Unknown identifier to print: %s **\n", identifier); + } + + return false; + } + + if (tok->IsRegister()) { + RegisterToken* reg_tok = RegisterToken::Cast(tok); + Register reg = reg_tok->value(); + if (reg.Is32Bits()) { + printf("%s = %" PRId32 "\n", + reg_tok->Name(), + debugger->wreg(reg.code(), Reg31IsStackPointer)); + } else { + printf("%s = %" PRId64 "\n", + reg_tok->Name(), + debugger->xreg(reg.code(), Reg31IsStackPointer)); + } + + return false; + } + + if (tok->IsFPRegister()) { + FPRegister fpreg = FPRegisterToken::Cast(tok)->value(); + if (fpreg.Is32Bits()) { + printf("s%u = %g\n", fpreg.code(), debugger->sreg(fpreg.code())); + } else { + printf("d%u = %g\n", fpreg.code(), debugger->dreg(fpreg.code())); + } + + return false; + } + + UNREACHABLE(); + return false; +} + + +DebugCommand* PrintCommand::Build(std::vector args) { + Token* target = NULL; + switch (args.size()) { + case 2: { + target = args[1]; + if (!target->IsRegister() + && !target->IsFPRegister() + && !target->IsIdentifier()) { + return new InvalidCommand(args, 1, "expects reg or identifier"); + } + break; + } + default: + return new InvalidCommand(args, -1, "too many arguments"); + } + + return new PrintCommand(args[0], target); +} + + +bool MemCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + + uint8_t* address = target()->ToAddress(debugger); + debugger->PrintMemory(address, count(), format()); + + return false; +} + + +void MemCommand::Print(FILE* out) { + fprintf(out, "%s ", name()); + target()->Print(out); + fprintf(out, " %" PRId64 " ", count()); + format()->Print(out); +} + + +DebugCommand* MemCommand::Build(std::vector args) { + if (args.size() < 2) { + return new InvalidCommand(args, -1, "too few arguments"); + } + + Token* target = args[1]; + IntegerToken* count = NULL; + FormatToken* format = NULL; + + if (!target->CanAddressMemory()) { + return new InvalidCommand(args, 1, "expects address"); + } + + switch (args.size()) { + case 2: { // mem addressable [1] [%x] + count = new IntegerToken(1); + format = new Format("%016x"); + break; + } + case 3: { // mem addr n [%x] or mem addr [n] %f + Token* second = args[2]; + if (second->IsInteger()) { + count = IntegerToken::Cast(second); + format = new Format("%016x"); + } else if (second->IsFormat()) { + count = new IntegerToken(1); + format = FormatToken::Cast(second); + } else { + return new InvalidCommand(args, 2, "expects int or format"); + } + break; + } + case 4: { // mem addr n %f + Token* second = args[2]; + Token* third = args[3]; + if (!second->IsInteger() || !third->IsFormat()) { + return new InvalidCommand(args, -1, "mem addr >>int<< %F"); + } + + count = IntegerToken::Cast(second); + format = FormatToken::Cast(third); + break; + } + default: + return new InvalidCommand(args, -1, "too many arguments"); + } + + return new MemCommand(args[0], target, count, format); +} + + +UnknownCommand::~UnknownCommand() { + const int size = args_.size(); + for (int i = 0; i < size; ++i) { + delete args_[i]; + } +} + + +bool UnknownCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + USE(debugger); + + printf(" ** Unknown Command:"); + const int size = args_.size(); + for (int i = 0; i < size; ++i) { + printf(" "); + args_[i]->Print(stdout); + } + printf(" **\n"); + + return false; +} + + +InvalidCommand::~InvalidCommand() { + const int size = args_.size(); + for (int i = 0; i < size; ++i) { + delete args_[i]; + } +} + + +bool InvalidCommand::Run(Debugger* debugger) { + ASSERT(debugger->IsDebuggerRunning()); + USE(debugger); + + printf(" ** Invalid Command:"); + const int size = args_.size(); + for (int i = 0; i < size; ++i) { + printf(" "); + if (i == index_) { + printf(">>"); + args_[i]->Print(stdout); + printf("<<"); + } else { + args_[i]->Print(stdout); + } + } + printf(" **\n"); + printf(" ** %s\n", cause_); + + return false; +} + +} // namespace vixl diff --git a/disas/libvixl/src/a64/debugger-a64.h b/disas/libvixl/src/a64/debugger-a64.h new file mode 100644 index 0000000..542d202 --- /dev/null +++ b/disas/libvixl/src/a64/debugger-a64.h @@ -0,0 +1,188 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_DEBUGGER_A64_H_ +#define VIXL_A64_DEBUGGER_A64_H_ + +#include +#include +#include +#include + +#include "globals.h" +#include "utils.h" +#include "a64/constants-a64.h" +#include "a64/simulator-a64.h" + +namespace vixl { + +// Debug instructions. +// +// VIXL's macro-assembler and debugger support a few pseudo instructions to +// make debugging easier. These pseudo instructions do not exist on real +// hardware. +// +// Each debug pseudo instruction is represented by a HLT instruction. The HLT +// immediate field is used to identify the type of debug pseudo isntruction. +// Each pseudo instruction use a custom encoding for additional arguments, as +// described below. + +// Unreachable +// +// Instruction which should never be executed. This is used as a guard in parts +// of the code that should not be reachable, such as in data encoded inline in +// the instructions. +const Instr kUnreachableOpcode = 0xdeb0; + +// Trace +// - parameter: TraceParameter stored as a uint32_t +// - command: TraceCommand stored as a uint32_t +// +// Allow for trace management in the generated code. See the corresponding +// enums for more information on permitted actions. +const Instr kTraceOpcode = 0xdeb2; +const unsigned kTraceParamsOffset = 1 * kInstructionSize; +const unsigned kTraceCommandOffset = 2 * kInstructionSize; +const unsigned kTraceLength = 3 * kInstructionSize; + +// Log +// - parameter: TraceParameter stored as a uint32_t +// +// Output the requested information. +const Instr kLogOpcode = 0xdeb3; +const unsigned kLogParamsOffset = 1 * kInstructionSize; +const unsigned kLogLength = 2 * kInstructionSize; + +// Trace commands. +enum TraceCommand { + TRACE_ENABLE = 1, + TRACE_DISABLE = 2 +}; + +// Trace parameters. +enum TraceParameters { + LOG_DISASM = 1 << 0, // Log disassembly. + LOG_REGS = 1 << 1, // Log general purpose registers. + LOG_FP_REGS = 1 << 2, // Log floating-point registers. + LOG_SYS_REGS = 1 << 3, // Log the flags and system registers. + + LOG_STATE = LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS, + LOG_ALL = LOG_DISASM | LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS +}; + +// Debugger parameters +enum DebugParameters { + DBG_ACTIVE = 1 << 0, // The debugger is active. + DBG_BREAK = 1 << 1 // The debugger is at a breakpoint. +}; + +// Forward declarations. +class DebugCommand; +class Token; +class FormatToken; + +class Debugger : public Simulator { + public: + Debugger(Decoder* decoder, FILE* stream = stdout); + + virtual void Run(); + void VisitException(Instruction* instr); + + inline int log_parameters() { + // The simulator can control disassembly, so make sure that the Debugger's + // log parameters agree with it. + if (disasm_trace()) { + log_parameters_ |= LOG_DISASM; + } + return log_parameters_; + } + inline void set_log_parameters(int parameters) { + set_disasm_trace((parameters & LOG_DISASM) != 0); + log_parameters_ = parameters; + + update_pending_request(); + } + + inline int debug_parameters() { return debug_parameters_; } + inline void set_debug_parameters(int parameters) { + debug_parameters_ = parameters; + + update_pending_request(); + } + + // Numbers of instructions to execute before the debugger shell is given + // back control. + inline int steps() { return steps_; } + inline void set_steps(int value) { + ASSERT(value > 1); + steps_ = value; + } + + inline bool IsDebuggerRunning() { + return (debug_parameters_ & DBG_ACTIVE) != 0; + } + + inline bool pending_request() { return pending_request_; } + inline void update_pending_request() { + const int kLoggingMask = LOG_STATE; + const bool logging = (log_parameters_ & kLoggingMask) != 0; + const bool debugging = IsDebuggerRunning(); + + pending_request_ = logging || debugging; + } + + void PrintInstructions(void* address, int64_t count = 1); + void PrintMemory(const uint8_t* address, + int64_t count, + const FormatToken* format); + + private: + void LogSystemRegisters(); + void LogRegisters(); + void LogFPRegisters(); + void LogProcessorState(); + char* ReadCommandLine(const char* prompt, char* buffer, int length); + void RunDebuggerShell(); + void DoBreakpoint(Instruction* instr); + void DoUnreachable(Instruction* instr); + void DoTrace(Instruction* instr); + void DoLog(Instruction* instr); + + int log_parameters_; + int debug_parameters_; + bool pending_request_; + int steps_; + DebugCommand* last_command_; + PrintDisassembler* disasm_; + Decoder* printer_; + + // Length of the biggest command line accepted by the debugger shell. + static const int kMaxDebugShellLine = 256; +}; + +} // namespace vixl + +#endif // VIXL_A64_DEBUGGER_A64_H_ diff --git a/disas/libvixl/src/a64/decoder-a64.cc b/disas/libvixl/src/a64/decoder-a64.cc new file mode 100644 index 0000000..9e9033c --- /dev/null +++ b/disas/libvixl/src/a64/decoder-a64.cc @@ -0,0 +1,712 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "globals.h" +#include "utils.h" +#include "a64/decoder-a64.h" + +namespace vixl { +// Top-level instruction decode function. +void Decoder::Decode(Instruction *instr) { + if (instr->Bits(28, 27) == 0) { + VisitUnallocated(instr); + } else { + switch (instr->Bits(27, 24)) { + // 0: PC relative addressing. + case 0x0: DecodePCRelAddressing(instr); break; + + // 1: Add/sub immediate. + case 0x1: DecodeAddSubImmediate(instr); break; + + // A: Logical shifted register. + // Add/sub with carry. + // Conditional compare register. + // Conditional compare immediate. + // Conditional select. + // Data processing 1 source. + // Data processing 2 source. + // B: Add/sub shifted register. + // Add/sub extended register. + // Data processing 3 source. + case 0xA: + case 0xB: DecodeDataProcessing(instr); break; + + // 2: Logical immediate. + // Move wide immediate. + case 0x2: DecodeLogical(instr); break; + + // 3: Bitfield. + // Extract. + case 0x3: DecodeBitfieldExtract(instr); break; + + // 4: Unconditional branch immediate. + // Exception generation. + // Compare and branch immediate. + // 5: Compare and branch immediate. + // Conditional branch. + // System. + // 6,7: Unconditional branch. + // Test and branch immediate. + case 0x4: + case 0x5: + case 0x6: + case 0x7: DecodeBranchSystemException(instr); break; + + // 8,9: Load/store register pair post-index. + // Load register literal. + // Load/store register unscaled immediate. + // Load/store register immediate post-index. + // Load/store register immediate pre-index. + // Load/store register offset. + // Load/store exclusive. + // C,D: Load/store register pair offset. + // Load/store register pair pre-index. + // Load/store register unsigned immediate. + // Advanced SIMD. + case 0x8: + case 0x9: + case 0xC: + case 0xD: DecodeLoadStore(instr); break; + + // E: FP fixed point conversion. + // FP integer conversion. + // FP data processing 1 source. + // FP compare. + // FP immediate. + // FP data processing 2 source. + // FP conditional compare. + // FP conditional select. + // Advanced SIMD. + // F: FP data processing 3 source. + // Advanced SIMD. + case 0xE: + case 0xF: DecodeFP(instr); break; + } + } +} + +void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { + visitors_.remove(new_visitor); + visitors_.push_front(new_visitor); +} + + +void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { + visitors_.remove(new_visitor); + visitors_.push_back(new_visitor); +} + + +void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor) { + visitors_.remove(new_visitor); + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + if (*it == registered_visitor) { + visitors_.insert(it, new_visitor); + return; + } + } + // We reached the end of the list. The last element must be + // registered_visitor. + ASSERT(*it == registered_visitor); + visitors_.insert(it, new_visitor); +} + + +void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor) { + visitors_.remove(new_visitor); + std::list::iterator it; + for (it = visitors_.begin(); it != visitors_.end(); it++) { + if (*it == registered_visitor) { + it++; + visitors_.insert(it, new_visitor); + return; + } + } + // We reached the end of the list. The last element must be + // registered_visitor. + ASSERT(*it == registered_visitor); + visitors_.push_back(new_visitor); +} + + +void Decoder::RemoveVisitor(DecoderVisitor* visitor) { + visitors_.remove(visitor); +} + + +void Decoder::DecodePCRelAddressing(Instruction* instr) { + ASSERT(instr->Bits(27, 24) == 0x0); + // We know bit 28 is set, as = 0 is filtered out at the top level + // decode. + ASSERT(instr->Bit(28) == 0x1); + VisitPCRelAddressing(instr); +} + + +void Decoder::DecodeBranchSystemException(Instruction* instr) { + ASSERT((instr->Bits(27, 24) == 0x4) || + (instr->Bits(27, 24) == 0x5) || + (instr->Bits(27, 24) == 0x6) || + (instr->Bits(27, 24) == 0x7) ); + + switch (instr->Bits(31, 29)) { + case 0: + case 4: { + VisitUnconditionalBranch(instr); + break; + } + case 1: + case 5: { + if (instr->Bit(25) == 0) { + VisitCompareBranch(instr); + } else { + VisitTestBranch(instr); + } + break; + } + case 2: { + if (instr->Bit(25) == 0) { + if ((instr->Bit(24) == 0x1) || + (instr->Mask(0x01000010) == 0x00000010)) { + VisitUnallocated(instr); + } else { + VisitConditionalBranch(instr); + } + } else { + VisitUnallocated(instr); + } + break; + } + case 6: { + if (instr->Bit(25) == 0) { + if (instr->Bit(24) == 0) { + if ((instr->Bits(4, 2) != 0) || + (instr->Mask(0x00E0001D) == 0x00200001) || + (instr->Mask(0x00E0001D) == 0x00400001) || + (instr->Mask(0x00E0001E) == 0x00200002) || + (instr->Mask(0x00E0001E) == 0x00400002) || + (instr->Mask(0x00E0001C) == 0x00600000) || + (instr->Mask(0x00E0001C) == 0x00800000) || + (instr->Mask(0x00E0001F) == 0x00A00000) || + (instr->Mask(0x00C0001C) == 0x00C00000)) { + VisitUnallocated(instr); + } else { + VisitException(instr); + } + } else { + if (instr->Bits(23, 22) == 0) { + const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); + if ((instr->Bits(21, 19) == 0x4) || + (masked_003FF0E0 == 0x00033000) || + (masked_003FF0E0 == 0x003FF020) || + (masked_003FF0E0 == 0x003FF060) || + (masked_003FF0E0 == 0x003FF0E0) || + (instr->Mask(0x00388000) == 0x00008000) || + (instr->Mask(0x0038E000) == 0x00000000) || + (instr->Mask(0x0039E000) == 0x00002000) || + (instr->Mask(0x003AE000) == 0x00002000) || + (instr->Mask(0x003CE000) == 0x00042000) || + (instr->Mask(0x003FFFC0) == 0x000320C0) || + (instr->Mask(0x003FF100) == 0x00032100) || + (instr->Mask(0x003FF200) == 0x00032200) || + (instr->Mask(0x003FF400) == 0x00032400) || + (instr->Mask(0x003FF800) == 0x00032800) || + (instr->Mask(0x0038F000) == 0x00005000) || + (instr->Mask(0x0038E000) == 0x00006000)) { + VisitUnallocated(instr); + } else { + VisitSystem(instr); + } + } else { + VisitUnallocated(instr); + } + } + } else { + if ((instr->Bit(24) == 0x1) || + (instr->Bits(20, 16) != 0x1F) || + (instr->Bits(15, 10) != 0) || + (instr->Bits(4, 0) != 0) || + (instr->Bits(24, 21) == 0x3) || + (instr->Bits(24, 22) == 0x3)) { + VisitUnallocated(instr); + } else { + VisitUnconditionalBranchToRegister(instr); + } + } + break; + } + case 3: + case 7: { + VisitUnallocated(instr); + break; + } + } +} + + +void Decoder::DecodeLoadStore(Instruction* instr) { + ASSERT((instr->Bits(27, 24) == 0x8) || + (instr->Bits(27, 24) == 0x9) || + (instr->Bits(27, 24) == 0xC) || + (instr->Bits(27, 24) == 0xD) ); + + if (instr->Bit(24) == 0) { + if (instr->Bit(28) == 0) { + if (instr->Bit(29) == 0) { + if (instr->Bit(26) == 0) { + // TODO: VisitLoadStoreExclusive. + VisitUnimplemented(instr); + } else { + DecodeAdvSIMDLoadStore(instr); + } + } else { + if ((instr->Bits(31, 30) == 0x3) || + (instr->Mask(0xC4400000) == 0x40000000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + if (instr->Mask(0xC4400000) == 0xC0400000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePairNonTemporal(instr); + } + } else { + VisitLoadStorePairPostIndex(instr); + } + } + } + } else { + if (instr->Bit(29) == 0) { + if (instr->Mask(0xC4000000) == 0xC4000000) { + VisitUnallocated(instr); + } else { + VisitLoadLiteral(instr); + } + } else { + if ((instr->Mask(0x84C00000) == 0x80C00000) || + (instr->Mask(0x44800000) == 0x44800000) || + (instr->Mask(0x84800000) == 0x84800000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(21) == 0) { + switch (instr->Bits(11, 10)) { + case 0: { + VisitLoadStoreUnscaledOffset(instr); + break; + } + case 1: { + if (instr->Mask(0xC4C00000) == 0xC0800000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePostIndex(instr); + } + break; + } + case 2: { + // TODO: VisitLoadStoreRegisterOffsetUnpriv. + VisitUnimplemented(instr); + break; + } + case 3: { + if (instr->Mask(0xC4C00000) == 0xC0800000) { + VisitUnallocated(instr); + } else { + VisitLoadStorePreIndex(instr); + } + break; + } + } + } else { + if (instr->Bits(11, 10) == 0x2) { + if (instr->Bit(14) == 0) { + VisitUnallocated(instr); + } else { + VisitLoadStoreRegisterOffset(instr); + } + } else { + VisitUnallocated(instr); + } + } + } + } + } + } else { + if (instr->Bit(28) == 0) { + if (instr->Bit(29) == 0) { + VisitUnallocated(instr); + } else { + if ((instr->Bits(31, 30) == 0x3) || + (instr->Mask(0xC4400000) == 0x40000000)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + VisitLoadStorePairOffset(instr); + } else { + VisitLoadStorePairPreIndex(instr); + } + } + } + } else { + if (instr->Bit(29) == 0) { + VisitUnallocated(instr); + } else { + if ((instr->Mask(0x84C00000) == 0x80C00000) || + (instr->Mask(0x44800000) == 0x44800000) || + (instr->Mask(0x84800000) == 0x84800000)) { + VisitUnallocated(instr); + } else { + VisitLoadStoreUnsignedOffset(instr); + } + } + } + } +} + + +void Decoder::DecodeLogical(Instruction* instr) { + ASSERT(instr->Bits(27, 24) == 0x2); + + if (instr->Mask(0x80400000) == 0x00400000) { + VisitUnallocated(instr); + } else { + if (instr->Bit(23) == 0) { + VisitLogicalImmediate(instr); + } else { + if (instr->Bits(30, 29) == 0x1) { + VisitUnallocated(instr); + } else { + VisitMoveWideImmediate(instr); + } + } + } +} + + +void Decoder::DecodeBitfieldExtract(Instruction* instr) { + ASSERT(instr->Bits(27, 24) == 0x3); + + if ((instr->Mask(0x80400000) == 0x80000000) || + (instr->Mask(0x80400000) == 0x00400000) || + (instr->Mask(0x80008000) == 0x00008000)) { + VisitUnallocated(instr); + } else if (instr->Bit(23) == 0) { + if ((instr->Mask(0x80200000) == 0x00200000) || + (instr->Mask(0x60000000) == 0x60000000)) { + VisitUnallocated(instr); + } else { + VisitBitfield(instr); + } + } else { + if ((instr->Mask(0x60200000) == 0x00200000) || + (instr->Mask(0x60000000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitExtract(instr); + } + } +} + + +void Decoder::DecodeAddSubImmediate(Instruction* instr) { + ASSERT(instr->Bits(27, 24) == 0x1); + if (instr->Bit(23) == 1) { + VisitUnallocated(instr); + } else { + VisitAddSubImmediate(instr); + } +} + + +void Decoder::DecodeDataProcessing(Instruction* instr) { + ASSERT((instr->Bits(27, 24) == 0xA) || + (instr->Bits(27, 24) == 0xB) ); + + if (instr->Bit(24) == 0) { + if (instr->Bit(28) == 0) { + if (instr->Mask(0x80008000) == 0x00008000) { + VisitUnallocated(instr); + } else { + VisitLogicalShifted(instr); + } + } else { + switch (instr->Bits(23, 21)) { + case 0: { + if (instr->Mask(0x0000FC00) != 0) { + VisitUnallocated(instr); + } else { + VisitAddSubWithCarry(instr); + } + break; + } + case 2: { + if ((instr->Bit(29) == 0) || + (instr->Mask(0x00000410) != 0)) { + VisitUnallocated(instr); + } else { + if (instr->Bit(11) == 0) { + VisitConditionalCompareRegister(instr); + } else { + VisitConditionalCompareImmediate(instr); + } + } + break; + } + case 4: { + if (instr->Mask(0x20000800) != 0x00000000) { + VisitUnallocated(instr); + } else { + VisitConditionalSelect(instr); + } + break; + } + case 6: { + if (instr->Bit(29) == 0x1) { + VisitUnallocated(instr); + } else { + if (instr->Bit(30) == 0) { + if ((instr->Bit(15) == 0x1) || + (instr->Bits(15, 11) == 0) || + (instr->Bits(15, 12) == 0x1) || + (instr->Bits(15, 12) == 0x3) || + (instr->Bits(15, 13) == 0x3) || + (instr->Mask(0x8000EC00) == 0x00004C00) || + (instr->Mask(0x8000E800) == 0x80004000) || + (instr->Mask(0x8000E400) == 0x80004000)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing2Source(instr); + } + } else { + if ((instr->Bit(13) == 1) || + (instr->Bits(20, 16) != 0) || + (instr->Bits(15, 14) != 0) || + (instr->Mask(0xA01FFC00) == 0x00000C00) || + (instr->Mask(0x201FF800) == 0x00001800)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing1Source(instr); + } + } + break; + } + } + case 1: + case 3: + case 5: + case 7: VisitUnallocated(instr); break; + } + } + } else { + if (instr->Bit(28) == 0) { + if (instr->Bit(21) == 0) { + if ((instr->Bits(23, 22) == 0x3) || + (instr->Mask(0x80008000) == 0x00008000)) { + VisitUnallocated(instr); + } else { + VisitAddSubShifted(instr); + } + } else { + if ((instr->Mask(0x00C00000) != 0x00000000) || + (instr->Mask(0x00001400) == 0x00001400) || + (instr->Mask(0x00001800) == 0x00001800)) { + VisitUnallocated(instr); + } else { + VisitAddSubExtended(instr); + } + } + } else { + if ((instr->Bit(30) == 0x1) || + (instr->Bits(30, 29) == 0x1) || + (instr->Mask(0xE0600000) == 0x00200000) || + (instr->Mask(0xE0608000) == 0x00400000) || + (instr->Mask(0x60608000) == 0x00408000) || + (instr->Mask(0x60E00000) == 0x00E00000) || + (instr->Mask(0x60E00000) == 0x00800000) || + (instr->Mask(0x60E00000) == 0x00600000)) { + VisitUnallocated(instr); + } else { + VisitDataProcessing3Source(instr); + } + } + } +} + + +void Decoder::DecodeFP(Instruction* instr) { + ASSERT((instr->Bits(27, 24) == 0xE) || + (instr->Bits(27, 24) == 0xF) ); + + if (instr->Bit(28) == 0) { + DecodeAdvSIMDDataProcessing(instr); + } else { + if (instr->Bit(29) == 1) { + VisitUnallocated(instr); + } else { + if (instr->Bits(31, 30) == 0x3) { + VisitUnallocated(instr); + } else if (instr->Bits(31, 30) == 0x1) { + DecodeAdvSIMDDataProcessing(instr); + } else { + if (instr->Bit(24) == 0) { + if (instr->Bit(21) == 0) { + if ((instr->Bit(23) == 1) || + (instr->Bit(18) == 1) || + (instr->Mask(0x80008000) == 0x00000000) || + (instr->Mask(0x000E0000) == 0x00000000) || + (instr->Mask(0x000E0000) == 0x000A0000) || + (instr->Mask(0x00160000) == 0x00000000) || + (instr->Mask(0x00160000) == 0x00120000)) { + VisitUnallocated(instr); + } else { + VisitFPFixedPointConvert(instr); + } + } else { + if (instr->Bits(15, 10) == 32) { + VisitUnallocated(instr); + } else if (instr->Bits(15, 10) == 0) { + if ((instr->Bits(23, 22) == 0x3) || + (instr->Mask(0x000E0000) == 0x000A0000) || + (instr->Mask(0x000E0000) == 0x000C0000) || + (instr->Mask(0x00160000) == 0x00120000) || + (instr->Mask(0x00160000) == 0x00140000) || + (instr->Mask(0x20C40000) == 0x00800000) || + (instr->Mask(0x20C60000) == 0x00840000) || + (instr->Mask(0xA0C60000) == 0x80060000) || + (instr->Mask(0xA0C60000) == 0x00860000) || + (instr->Mask(0xA0C60000) == 0x00460000) || + (instr->Mask(0xA0CE0000) == 0x80860000) || + (instr->Mask(0xA0CE0000) == 0x804E0000) || + (instr->Mask(0xA0CE0000) == 0x000E0000) || + (instr->Mask(0xA0D60000) == 0x00160000) || + (instr->Mask(0xA0D60000) == 0x80560000) || + (instr->Mask(0xA0D60000) == 0x80960000)) { + VisitUnallocated(instr); + } else { + VisitFPIntegerConvert(instr); + } + } else if (instr->Bits(14, 10) == 16) { + const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); + if ((instr->Mask(0x80180000) != 0) || + (masked_A0DF8000 == 0x00020000) || + (masked_A0DF8000 == 0x00030000) || + (masked_A0DF8000 == 0x00068000) || + (masked_A0DF8000 == 0x00428000) || + (masked_A0DF8000 == 0x00430000) || + (masked_A0DF8000 == 0x00468000) || + (instr->Mask(0xA0D80000) == 0x00800000) || + (instr->Mask(0xA0DE0000) == 0x00C00000) || + (instr->Mask(0xA0DF0000) == 0x00C30000) || + (instr->Mask(0xA0DC0000) == 0x00C40000)) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing1Source(instr); + } + } else if (instr->Bits(13, 10) == 8) { + if ((instr->Bits(15, 14) != 0) || + (instr->Bits(2, 0) != 0) || + (instr->Mask(0x80800000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitFPCompare(instr); + } + } else if (instr->Bits(12, 10) == 4) { + if ((instr->Bits(9, 5) != 0) || + (instr->Mask(0x80800000) != 0x00000000)) { + VisitUnallocated(instr); + } else { + VisitFPImmediate(instr); + } + } else { + if (instr->Mask(0x80800000) != 0x00000000) { + VisitUnallocated(instr); + } else { + switch (instr->Bits(11, 10)) { + case 1: { + VisitFPConditionalCompare(instr); + break; + } + case 2: { + if ((instr->Bits(15, 14) == 0x3) || + (instr->Mask(0x00009000) == 0x00009000) || + (instr->Mask(0x0000A000) == 0x0000A000)) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing2Source(instr); + } + break; + } + case 3: { + VisitFPConditionalSelect(instr); + break; + } + default: UNREACHABLE(); + } + } + } + } + } else { + // Bit 30 == 1 has been handled earlier. + ASSERT(instr->Bit(30) == 0); + if (instr->Mask(0xA0800000) != 0) { + VisitUnallocated(instr); + } else { + VisitFPDataProcessing3Source(instr); + } + } + } + } + } +} + + +void Decoder::DecodeAdvSIMDLoadStore(Instruction* instr) { + // TODO: Implement Advanced SIMD load/store instruction decode. + ASSERT(instr->Bits(29, 25) == 0x6); + VisitUnimplemented(instr); +} + + +void Decoder::DecodeAdvSIMDDataProcessing(Instruction* instr) { + // TODO: Implement Advanced SIMD data processing instruction decode. + ASSERT(instr->Bits(27, 25) == 0x7); + VisitUnimplemented(instr); +} + + +#define DEFINE_VISITOR_CALLERS(A) \ + void Decoder::Visit##A(Instruction *instr) { \ + ASSERT(instr->Mask(A##FMask) == A##Fixed); \ + std::list::iterator it; \ + for (it = visitors_.begin(); it != visitors_.end(); it++) { \ + (*it)->Visit##A(instr); \ + } \ + } +VISITOR_LIST(DEFINE_VISITOR_CALLERS) +#undef DEFINE_VISITOR_CALLERS +} // namespace vixl diff --git a/disas/libvixl/src/a64/decoder-a64.h b/disas/libvixl/src/a64/decoder-a64.h new file mode 100644 index 0000000..bbbbd81 --- /dev/null +++ b/disas/libvixl/src/a64/decoder-a64.h @@ -0,0 +1,198 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_DECODER_A64_H_ +#define VIXL_A64_DECODER_A64_H_ + +#include + +#include "globals.h" +#include "a64/instructions-a64.h" + + +// List macro containing all visitors needed by the decoder class. + +#define VISITOR_LIST(V) \ + V(PCRelAddressing) \ + V(AddSubImmediate) \ + V(LogicalImmediate) \ + V(MoveWideImmediate) \ + V(Bitfield) \ + V(Extract) \ + V(UnconditionalBranch) \ + V(UnconditionalBranchToRegister) \ + V(CompareBranch) \ + V(TestBranch) \ + V(ConditionalBranch) \ + V(System) \ + V(Exception) \ + V(LoadStorePairPostIndex) \ + V(LoadStorePairOffset) \ + V(LoadStorePairPreIndex) \ + V(LoadStorePairNonTemporal) \ + V(LoadLiteral) \ + V(LoadStoreUnscaledOffset) \ + V(LoadStorePostIndex) \ + V(LoadStorePreIndex) \ + V(LoadStoreRegisterOffset) \ + V(LoadStoreUnsignedOffset) \ + V(LogicalShifted) \ + V(AddSubShifted) \ + V(AddSubExtended) \ + V(AddSubWithCarry) \ + V(ConditionalCompareRegister) \ + V(ConditionalCompareImmediate) \ + V(ConditionalSelect) \ + V(DataProcessing1Source) \ + V(DataProcessing2Source) \ + V(DataProcessing3Source) \ + V(FPCompare) \ + V(FPConditionalCompare) \ + V(FPConditionalSelect) \ + V(FPImmediate) \ + V(FPDataProcessing1Source) \ + V(FPDataProcessing2Source) \ + V(FPDataProcessing3Source) \ + V(FPIntegerConvert) \ + V(FPFixedPointConvert) \ + V(Unallocated) \ + V(Unimplemented) + +namespace vixl { + +// The Visitor interface. Disassembler and simulator (and other tools) +// must provide implementations for all of these functions. +class DecoderVisitor { + public: + #define DECLARE(A) virtual void Visit##A(Instruction* instr) = 0; + VISITOR_LIST(DECLARE) + #undef DECLARE + + virtual ~DecoderVisitor() {} + + private: + // Visitors are registered in a list. + std::list visitors_; + + friend class Decoder; +}; + + +class Decoder: public DecoderVisitor { + public: + Decoder() {} + + // Top-level instruction decoder function. Decodes an instruction and calls + // the visitor functions registered with the Decoder class. + void Decode(Instruction *instr); + + // Register a new visitor class with the decoder. + // Decode() will call the corresponding visitor method from all registered + // visitor classes when decoding reaches the leaf node of the instruction + // decode tree. + // Visitors are called in the order. + // A visitor can only be registered once. + // Registering an already registered visitor will update its position. + // + // d.AppendVisitor(V1); + // d.AppendVisitor(V2); + // d.PrependVisitor(V2); // Move V2 at the start of the list. + // d.InsertVisitorBefore(V3, V2); + // d.AppendVisitor(V4); + // d.AppendVisitor(V4); // No effect. + // + // d.Decode(i); + // + // will call in order visitor methods in V3, V2, V1, V4. + void AppendVisitor(DecoderVisitor* visitor); + void PrependVisitor(DecoderVisitor* visitor); + void InsertVisitorBefore(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor); + void InsertVisitorAfter(DecoderVisitor* new_visitor, + DecoderVisitor* registered_visitor); + + // Remove a previously registered visitor class from the list of visitors + // stored by the decoder. + void RemoveVisitor(DecoderVisitor* visitor); + + #define DECLARE(A) void Visit##A(Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + private: + // Decode the PC relative addressing instruction, and call the corresponding + // visitors. + // On entry, instruction bits 27:24 = 0x0. + void DecodePCRelAddressing(Instruction* instr); + + // Decode the add/subtract immediate instruction, and call the correspoding + // visitors. + // On entry, instruction bits 27:24 = 0x1. + void DecodeAddSubImmediate(Instruction* instr); + + // Decode the branch, system command, and exception generation parts of + // the instruction tree, and call the corresponding visitors. + // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. + void DecodeBranchSystemException(Instruction* instr); + + // Decode the load and store parts of the instruction tree, and call + // the corresponding visitors. + // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. + void DecodeLoadStore(Instruction* instr); + + // Decode the logical immediate and move wide immediate parts of the + // instruction tree, and call the corresponding visitors. + // On entry, instruction bits 27:24 = 0x2. + void DecodeLogical(Instruction* instr); + + // Decode the bitfield and extraction parts of the instruction tree, + // and call the corresponding visitors. + // On entry, instruction bits 27:24 = 0x3. + void DecodeBitfieldExtract(Instruction* instr); + + // Decode the data processing parts of the instruction tree, and call the + // corresponding visitors. + // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. + void DecodeDataProcessing(Instruction* instr); + + // Decode the floating point parts of the instruction tree, and call the + // corresponding visitors. + // On entry, instruction bits 27:24 = {0xE, 0xF}. + void DecodeFP(Instruction* instr); + + // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, + // and call the corresponding visitors. + // On entry, instruction bits 29:25 = 0x6. + void DecodeAdvSIMDLoadStore(Instruction* instr); + + // Decode the Advanced SIMD (NEON) data processing part of the instruction + // tree, and call the corresponding visitors. + // On entry, instruction bits 27:25 = 0x7. + void DecodeAdvSIMDDataProcessing(Instruction* instr); +}; +} // namespace vixl + +#endif // VIXL_A64_DECODER_A64_H_ diff --git a/disas/libvixl/src/a64/disasm-a64.cc b/disas/libvixl/src/a64/disasm-a64.cc new file mode 100644 index 0000000..4a49748 --- /dev/null +++ b/disas/libvixl/src/a64/disasm-a64.cc @@ -0,0 +1,1678 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "a64/disasm-a64.h" + +namespace vixl { + +Disassembler::Disassembler() { + buffer_size_ = 256; + buffer_ = reinterpret_cast(malloc(buffer_size_)); + buffer_pos_ = 0; + own_buffer_ = true; +} + + +Disassembler::Disassembler(char* text_buffer, int buffer_size) { + buffer_size_ = buffer_size; + buffer_ = text_buffer; + buffer_pos_ = 0; + own_buffer_ = false; +} + + +Disassembler::~Disassembler() { + if (own_buffer_) { + free(buffer_); + } +} + + +char* Disassembler::GetOutput() { + return buffer_; +} + + +void Disassembler::VisitAddSubImmediate(Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && + (instr->ImmAddSub() == 0) ? true : false; + const char *mnemonic = ""; + const char *form = "'Rds, 'Rns, 'IAddSub"; + const char *form_cmp = "'Rns, 'IAddSub"; + const char *form_mov = "'Rds, 'Rns"; + + switch (instr->Mask(AddSubImmediateMask)) { + case ADD_w_imm: + case ADD_x_imm: { + mnemonic = "add"; + if (stack_op) { + mnemonic = "mov"; + form = form_mov; + } + break; + } + case ADDS_w_imm: + case ADDS_x_imm: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_imm: + case SUB_x_imm: mnemonic = "sub"; break; + case SUBS_w_imm: + case SUBS_x_imm: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubShifted(Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm'HDP"; + const char *form_cmp = "'Rn, 'Rm'HDP"; + const char *form_neg = "'Rd, 'Rm'HDP"; + + switch (instr->Mask(AddSubShiftedMask)) { + case ADD_w_shift: + case ADD_x_shift: mnemonic = "add"; break; + case ADDS_w_shift: + case ADDS_x_shift: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_shift: + case SUB_x_shift: { + mnemonic = "sub"; + if (rn_is_zr) { + mnemonic = "neg"; + form = form_neg; + } + break; + } + case SUBS_w_shift: + case SUBS_x_shift: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } else if (rn_is_zr) { + mnemonic = "negs"; + form = form_neg; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubExtended(Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + const char *mnemonic = ""; + Extend mode = static_cast(instr->ExtendMode()); + const char *form = ((mode == UXTX) || (mode == SXTX)) ? + "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; + const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? + "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; + + switch (instr->Mask(AddSubExtendedMask)) { + case ADD_w_ext: + case ADD_x_ext: mnemonic = "add"; break; + case ADDS_w_ext: + case ADDS_x_ext: { + mnemonic = "adds"; + if (rd_is_zr) { + mnemonic = "cmn"; + form = form_cmp; + } + break; + } + case SUB_w_ext: + case SUB_x_ext: mnemonic = "sub"; break; + case SUBS_w_ext: + case SUBS_x_ext: { + mnemonic = "subs"; + if (rd_is_zr) { + mnemonic = "cmp"; + form = form_cmp; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitAddSubWithCarry(Instruction* instr) { + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm"; + const char *form_neg = "'Rd, 'Rm"; + + switch (instr->Mask(AddSubWithCarryMask)) { + case ADC_w: + case ADC_x: mnemonic = "adc"; break; + case ADCS_w: + case ADCS_x: mnemonic = "adcs"; break; + case SBC_w: + case SBC_x: { + mnemonic = "sbc"; + if (rn_is_zr) { + mnemonic = "ngc"; + form = form_neg; + } + break; + } + case SBCS_w: + case SBCS_x: { + mnemonic = "sbcs"; + if (rn_is_zr) { + mnemonic = "ngcs"; + form = form_neg; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLogicalImmediate(Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rds, 'Rn, 'ITri"; + + if (instr->ImmLogical() == 0) { + // The immediate encoded in the instruction is not in the expected format. + Format(instr, "unallocated", "(LogicalImmediate)"); + return; + } + + switch (instr->Mask(LogicalImmediateMask)) { + case AND_w_imm: + case AND_x_imm: mnemonic = "and"; break; + case ORR_w_imm: + case ORR_x_imm: { + mnemonic = "orr"; + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize + : kWRegSize; + if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { + mnemonic = "mov"; + form = "'Rds, 'ITri"; + } + break; + } + case EOR_w_imm: + case EOR_x_imm: mnemonic = "eor"; break; + case ANDS_w_imm: + case ANDS_x_imm: { + mnemonic = "ands"; + if (rd_is_zr) { + mnemonic = "tst"; + form = "'Rn, 'ITri"; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { + ASSERT((reg_size == kXRegSize) || + ((reg_size == kWRegSize) && (value <= 0xffffffff))); + + // Test for movz: 16 bits set at positions 0, 16, 32 or 48. + if (((value & 0xffffffffffff0000UL) == 0UL) || + ((value & 0xffffffff0000ffffUL) == 0UL) || + ((value & 0xffff0000ffffffffUL) == 0UL) || + ((value & 0x0000ffffffffffffUL) == 0UL)) { + return true; + } + + // Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48). + if ((reg_size == kXRegSize) && + (((value & 0xffffffffffff0000UL) == 0xffffffffffff0000UL) || + ((value & 0xffffffff0000ffffUL) == 0xffffffff0000ffffUL) || + ((value & 0xffff0000ffffffffUL) == 0xffff0000ffffffffUL) || + ((value & 0x0000ffffffffffffUL) == 0x0000ffffffffffffUL))) { + return true; + } + if ((reg_size == kWRegSize) && + (((value & 0xffff0000) == 0xffff0000) || + ((value & 0x0000ffff) == 0x0000ffff))) { + return true; + } + return false; +} + + +void Disassembler::VisitLogicalShifted(Instruction* instr) { + bool rd_is_zr = RdIsZROrSP(instr); + bool rn_is_zr = RnIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm'HLo"; + + switch (instr->Mask(LogicalShiftedMask)) { + case AND_w: + case AND_x: mnemonic = "and"; break; + case BIC_w: + case BIC_x: mnemonic = "bic"; break; + case EOR_w: + case EOR_x: mnemonic = "eor"; break; + case EON_w: + case EON_x: mnemonic = "eon"; break; + case BICS_w: + case BICS_x: mnemonic = "bics"; break; + case ANDS_w: + case ANDS_x: { + mnemonic = "ands"; + if (rd_is_zr) { + mnemonic = "tst"; + form = "'Rn, 'Rm'HLo"; + } + break; + } + case ORR_w: + case ORR_x: { + mnemonic = "orr"; + if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { + mnemonic = "mov"; + form = "'Rd, 'Rm"; + } + break; + } + case ORN_w: + case ORN_x: { + mnemonic = "orn"; + if (rn_is_zr) { + mnemonic = "mvn"; + form = "'Rd, 'Rm'HLo"; + } + break; + } + default: UNREACHABLE(); + } + + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalCompareRegister(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; + + switch (instr->Mask(ConditionalCompareRegisterMask)) { + case CCMN_w: + case CCMN_x: mnemonic = "ccmn"; break; + case CCMP_w: + case CCMP_x: mnemonic = "ccmp"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; + + switch (instr->Mask(ConditionalCompareImmediateMask)) { + case CCMN_w_imm: + case CCMN_x_imm: mnemonic = "ccmn"; break; + case CCMP_w_imm: + case CCMP_x_imm: mnemonic = "ccmp"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitConditionalSelect(Instruction* instr) { + bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); + bool rn_is_rm = (instr->Rn() == instr->Rm()); + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; + const char *form_test = "'Rd, 'CInv"; + const char *form_update = "'Rd, 'Rn, 'CInv"; + + Condition cond = static_cast(instr->Condition()); + bool invertible_cond = (cond != al) && (cond != nv); + + switch (instr->Mask(ConditionalSelectMask)) { + case CSEL_w: + case CSEL_x: mnemonic = "csel"; break; + case CSINC_w: + case CSINC_x: { + mnemonic = "csinc"; + if (rnm_is_zr && invertible_cond) { + mnemonic = "cset"; + form = form_test; + } else if (rn_is_rm && invertible_cond) { + mnemonic = "cinc"; + form = form_update; + } + break; + } + case CSINV_w: + case CSINV_x: { + mnemonic = "csinv"; + if (rnm_is_zr && invertible_cond) { + mnemonic = "csetm"; + form = form_test; + } else if (rn_is_rm && invertible_cond) { + mnemonic = "cinv"; + form = form_update; + } + break; + } + case CSNEG_w: + case CSNEG_x: { + mnemonic = "csneg"; + if (rn_is_rm && invertible_cond) { + mnemonic = "cneg"; + form = form_update; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitBitfield(Instruction* instr) { + unsigned s = instr->ImmS(); + unsigned r = instr->ImmR(); + unsigned rd_size_minus_1 = + ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; + const char *mnemonic = ""; + const char *form = ""; + const char *form_shift_right = "'Rd, 'Rn, 'IBr"; + const char *form_extend = "'Rd, 'Wn"; + const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; + const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; + const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; + + switch (instr->Mask(BitfieldMask)) { + case SBFM_w: + case SBFM_x: { + mnemonic = "sbfx"; + form = form_bfx; + if (r == 0) { + form = form_extend; + if (s == 7) { + mnemonic = "sxtb"; + } else if (s == 15) { + mnemonic = "sxth"; + } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { + mnemonic = "sxtw"; + } else { + form = form_bfx; + } + } else if (s == rd_size_minus_1) { + mnemonic = "asr"; + form = form_shift_right; + } else if (s < r) { + mnemonic = "sbfiz"; + form = form_bfiz; + } + break; + } + case UBFM_w: + case UBFM_x: { + mnemonic = "ubfx"; + form = form_bfx; + if (r == 0) { + form = form_extend; + if (s == 7) { + mnemonic = "uxtb"; + } else if (s == 15) { + mnemonic = "uxth"; + } else { + form = form_bfx; + } + } + if (s == rd_size_minus_1) { + mnemonic = "lsr"; + form = form_shift_right; + } else if (r == s + 1) { + mnemonic = "lsl"; + form = form_lsl; + } else if (s < r) { + mnemonic = "ubfiz"; + form = form_bfiz; + } + break; + } + case BFM_w: + case BFM_x: { + mnemonic = "bfxil"; + form = form_bfx; + if (s < r) { + mnemonic = "bfi"; + form = form_bfiz; + } + } + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitExtract(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; + + switch (instr->Mask(ExtractMask)) { + case EXTR_w: + case EXTR_x: { + if (instr->Rn() == instr->Rm()) { + mnemonic = "ror"; + form = "'Rd, 'Rn, 'IExtract"; + } else { + mnemonic = "extr"; + } + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitPCRelAddressing(Instruction* instr) { + switch (instr->Mask(PCRelAddressingMask)) { + case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; + // ADRP is not implemented. + default: Format(instr, "unimplemented", "(PCRelAddressing)"); + } +} + + +void Disassembler::VisitConditionalBranch(Instruction* instr) { + switch (instr->Mask(ConditionalBranchMask)) { + case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break; + default: UNREACHABLE(); + } +} + + +void Disassembler::VisitUnconditionalBranchToRegister(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Xn"; + + switch (instr->Mask(UnconditionalBranchToRegisterMask)) { + case BR: mnemonic = "br"; break; + case BLR: mnemonic = "blr"; break; + case RET: { + mnemonic = "ret"; + if (instr->Rn() == kLinkRegCode) { + form = NULL; + } + break; + } + default: form = "(UnconditionalBranchToRegister)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitUnconditionalBranch(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'BImmUncn"; + + switch (instr->Mask(UnconditionalBranchMask)) { + case B: mnemonic = "b"; break; + case BL: mnemonic = "bl"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing1Source(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Rn"; + + switch (instr->Mask(DataProcessing1SourceMask)) { + #define FORMAT(A, B) \ + case A##_w: \ + case A##_x: mnemonic = B; break; + FORMAT(RBIT, "rbit"); + FORMAT(REV16, "rev16"); + FORMAT(REV, "rev"); + FORMAT(CLZ, "clz"); + FORMAT(CLS, "cls"); + #undef FORMAT + case REV32_x: mnemonic = "rev32"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing2Source(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Rd, 'Rn, 'Rm"; + + switch (instr->Mask(DataProcessing2SourceMask)) { + #define FORMAT(A, B) \ + case A##_w: \ + case A##_x: mnemonic = B; break; + FORMAT(UDIV, "udiv"); + FORMAT(SDIV, "sdiv"); + FORMAT(LSLV, "lsl"); + FORMAT(LSRV, "lsr"); + FORMAT(ASRV, "asr"); + FORMAT(RORV, "ror"); + #undef FORMAT + default: form = "(DataProcessing2Source)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitDataProcessing3Source(Instruction* instr) { + bool ra_is_zr = RaIsZROrSP(instr); + const char *mnemonic = ""; + const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; + const char *form_rrr = "'Rd, 'Rn, 'Rm"; + const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; + const char *form_xww = "'Xd, 'Wn, 'Wm"; + const char *form_xxx = "'Xd, 'Xn, 'Xm"; + + switch (instr->Mask(DataProcessing3SourceMask)) { + case MADD_w: + case MADD_x: { + mnemonic = "madd"; + form = form_rrrr; + if (ra_is_zr) { + mnemonic = "mul"; + form = form_rrr; + } + break; + } + case MSUB_w: + case MSUB_x: { + mnemonic = "msub"; + form = form_rrrr; + if (ra_is_zr) { + mnemonic = "mneg"; + form = form_rrr; + } + break; + } + case SMADDL_x: { + mnemonic = "smaddl"; + if (ra_is_zr) { + mnemonic = "smull"; + form = form_xww; + } + break; + } + case SMSUBL_x: { + mnemonic = "smsubl"; + if (ra_is_zr) { + mnemonic = "smnegl"; + form = form_xww; + } + break; + } + case UMADDL_x: { + mnemonic = "umaddl"; + if (ra_is_zr) { + mnemonic = "umull"; + form = form_xww; + } + break; + } + case UMSUBL_x: { + mnemonic = "umsubl"; + if (ra_is_zr) { + mnemonic = "umnegl"; + form = form_xww; + } + break; + } + case SMULH_x: { + mnemonic = "smulh"; + form = form_xxx; + break; + } + case UMULH_x: { + mnemonic = "umulh"; + form = form_xxx; + break; + } + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitCompareBranch(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rt, 'BImmCmpa"; + + switch (instr->Mask(CompareBranchMask)) { + case CBZ_w: + case CBZ_x: mnemonic = "cbz"; break; + case CBNZ_w: + case CBNZ_x: mnemonic = "cbnz"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitTestBranch(Instruction* instr) { + const char *mnemonic = ""; + // If the top bit of the immediate is clear, the tested register is + // disassembled as Wt, otherwise Xt. As the top bit of the immediate is + // encoded in bit 31 of the instruction, we can reuse the Rt form, which + // uses bit 31 (normally "sf") to choose the register size. + const char *form = "'Rt, 'IS, 'BImmTest"; + + switch (instr->Mask(TestBranchMask)) { + case TBZ: mnemonic = "tbz"; break; + case TBNZ: mnemonic = "tbnz"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitMoveWideImmediate(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'IMoveImm"; + + // Print the shift separately for movk, to make it clear which half word will + // be overwritten. Movn and movz print the computed immediate, which includes + // shift calculation. + switch (instr->Mask(MoveWideImmediateMask)) { + case MOVN_w: + case MOVN_x: mnemonic = "movn"; break; + case MOVZ_w: + case MOVZ_x: mnemonic = "movz"; break; + case MOVK_w: + case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +#define LOAD_STORE_LIST(V) \ + V(STRB_w, "strb", "'Wt") \ + V(STRH_w, "strh", "'Wt") \ + V(STR_w, "str", "'Wt") \ + V(STR_x, "str", "'Xt") \ + V(LDRB_w, "ldrb", "'Wt") \ + V(LDRH_w, "ldrh", "'Wt") \ + V(LDR_w, "ldr", "'Wt") \ + V(LDR_x, "ldr", "'Xt") \ + V(LDRSB_x, "ldrsb", "'Xt") \ + V(LDRSH_x, "ldrsh", "'Xt") \ + V(LDRSW_x, "ldrsw", "'Xt") \ + V(LDRSB_w, "ldrsb", "'Wt") \ + V(LDRSH_w, "ldrsh", "'Wt") \ + V(STR_s, "str", "'St") \ + V(STR_d, "str", "'Dt") \ + V(LDR_s, "ldr", "'St") \ + V(LDR_d, "ldr", "'Dt") + +void Disassembler::VisitLoadStorePreIndex(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePreIndex)"; + + switch (instr->Mask(LoadStorePreIndexMask)) { + #define LS_PREINDEX(A, B, C) \ + case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; + LOAD_STORE_LIST(LS_PREINDEX) + #undef LS_PREINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePostIndex(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePostIndex)"; + + switch (instr->Mask(LoadStorePostIndexMask)) { + #define LS_POSTINDEX(A, B, C) \ + case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; + LOAD_STORE_LIST(LS_POSTINDEX) + #undef LS_POSTINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreUnsignedOffset(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStoreUnsignedOffset)"; + + switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { + #define LS_UNSIGNEDOFFSET(A, B, C) \ + case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; + LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) + #undef LS_UNSIGNEDOFFSET + case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xn'ILU]"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreRegisterOffset(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStoreRegisterOffset)"; + + switch (instr->Mask(LoadStoreRegisterOffsetMask)) { + #define LS_REGISTEROFFSET(A, B, C) \ + case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; + LOAD_STORE_LIST(LS_REGISTEROFFSET) + #undef LS_REGISTEROFFSET + case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStoreUnscaledOffset(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Wt, ['Xns'ILS]"; + const char *form_x = "'Xt, ['Xns'ILS]"; + const char *form_s = "'St, ['Xns'ILS]"; + const char *form_d = "'Dt, ['Xns'ILS]"; + + switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { + case STURB_w: mnemonic = "sturb"; break; + case STURH_w: mnemonic = "sturh"; break; + case STUR_w: mnemonic = "stur"; break; + case STUR_x: mnemonic = "stur"; form = form_x; break; + case STUR_s: mnemonic = "stur"; form = form_s; break; + case STUR_d: mnemonic = "stur"; form = form_d; break; + case LDURB_w: mnemonic = "ldurb"; break; + case LDURH_w: mnemonic = "ldurh"; break; + case LDUR_w: mnemonic = "ldur"; break; + case LDUR_x: mnemonic = "ldur"; form = form_x; break; + case LDUR_s: mnemonic = "ldur"; form = form_s; break; + case LDUR_d: mnemonic = "ldur"; form = form_d; break; + case LDURSB_x: form = form_x; // Fall through. + case LDURSB_w: mnemonic = "ldursb"; break; + case LDURSH_x: form = form_x; // Fall through. + case LDURSH_w: mnemonic = "ldursh"; break; + case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; + default: form = "(LoadStoreUnscaledOffset)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadLiteral(Instruction* instr) { + const char *mnemonic = "ldr"; + const char *form = "(LoadLiteral)"; + + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; + case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; + case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; + case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; + default: mnemonic = "unimplemented"; + } + Format(instr, mnemonic, form); +} + + +#define LOAD_STORE_PAIR_LIST(V) \ + V(STP_w, "stp", "'Wt, 'Wt2", "4") \ + V(LDP_w, "ldp", "'Wt, 'Wt2", "4") \ + V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "4") \ + V(STP_x, "stp", "'Xt, 'Xt2", "8") \ + V(LDP_x, "ldp", "'Xt, 'Xt2", "8") \ + V(STP_s, "stp", "'St, 'St2", "4") \ + V(LDP_s, "ldp", "'St, 'St2", "4") \ + V(STP_d, "stp", "'Dt, 'Dt2", "8") \ + V(LDP_d, "ldp", "'Dt, 'Dt2", "8") + +void Disassembler::VisitLoadStorePairPostIndex(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairPostIndex)"; + + switch (instr->Mask(LoadStorePairPostIndexMask)) { + #define LSP_POSTINDEX(A, B, C, D) \ + case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; + LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) + #undef LSP_POSTINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairPreIndex(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairPreIndex)"; + + switch (instr->Mask(LoadStorePairPreIndexMask)) { + #define LSP_PREINDEX(A, B, C, D) \ + case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; + LOAD_STORE_PAIR_LIST(LSP_PREINDEX) + #undef LSP_PREINDEX + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairOffset(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(LoadStorePairOffset)"; + + switch (instr->Mask(LoadStorePairOffsetMask)) { + #define LSP_OFFSET(A, B, C, D) \ + case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; + LOAD_STORE_PAIR_LIST(LSP_OFFSET) + #undef LSP_OFFSET + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitLoadStorePairNonTemporal(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form; + + switch (instr->Mask(LoadStorePairNonTemporalMask)) { + case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; + case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; + case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; + case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; + case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; + case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; + case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; + case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; + default: form = "(LoadStorePairNonTemporal)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPCompare(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Fn, 'Fm"; + const char *form_zero = "'Fn, #0.0"; + + switch (instr->Mask(FPCompareMask)) { + case FCMP_s_zero: + case FCMP_d_zero: form = form_zero; // Fall through. + case FCMP_s: + case FCMP_d: mnemonic = "fcmp"; break; + default: form = "(FPCompare)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPConditionalCompare(Instruction* instr) { + const char *mnemonic = "unmplemented"; + const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; + + switch (instr->Mask(FPConditionalCompareMask)) { + case FCCMP_s: + case FCCMP_d: mnemonic = "fccmp"; break; + case FCCMPE_s: + case FCCMPE_d: mnemonic = "fccmpe"; break; + default: form = "(FPConditionalCompare)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPConditionalSelect(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; + + switch (instr->Mask(FPConditionalSelectMask)) { + case FCSEL_s: + case FCSEL_d: mnemonic = "fcsel"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing1Source(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'Fd, 'Fn"; + + switch (instr->Mask(FPDataProcessing1SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMOV, "fmov"); + FORMAT(FABS, "fabs"); + FORMAT(FNEG, "fneg"); + FORMAT(FSQRT, "fsqrt"); + FORMAT(FRINTN, "frintn"); + FORMAT(FRINTP, "frintp"); + FORMAT(FRINTM, "frintm"); + FORMAT(FRINTZ, "frintz"); + FORMAT(FRINTA, "frinta"); + FORMAT(FRINTX, "frintx"); + FORMAT(FRINTI, "frinti"); + #undef FORMAT + case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; + case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; + default: form = "(FPDataProcessing1Source)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing2Source(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm"; + + switch (instr->Mask(FPDataProcessing2SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMUL, "fmul"); + FORMAT(FDIV, "fdiv"); + FORMAT(FADD, "fadd"); + FORMAT(FSUB, "fsub"); + FORMAT(FMAX, "fmax"); + FORMAT(FMIN, "fmin"); + FORMAT(FMAXNM, "fmaxnm"); + FORMAT(FMINNM, "fminnm"); + FORMAT(FNMUL, "fnmul"); + #undef FORMAT + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPDataProcessing3Source(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; + + switch (instr->Mask(FPDataProcessing3SourceMask)) { + #define FORMAT(A, B) \ + case A##_s: \ + case A##_d: mnemonic = B; break; + FORMAT(FMADD, "fmadd"); + FORMAT(FMSUB, "fmsub"); + FORMAT(FNMADD, "fnmadd"); + FORMAT(FNMSUB, "fnmsub"); + #undef FORMAT + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPImmediate(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "(FPImmediate)"; + + switch (instr->Mask(FPImmediateMask)) { + case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; + case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPIntegerConvert(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "(FPIntegerConvert)"; + const char *form_rf = "'Rd, 'Fn"; + const char *form_fr = "'Fd, 'Rn"; + + switch (instr->Mask(FPIntegerConvertMask)) { + case FMOV_ws: + case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; + case FMOV_sw: + case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; + case FCVTMS_ws: + case FCVTMS_xs: + case FCVTMS_wd: + case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; + case FCVTMU_ws: + case FCVTMU_xs: + case FCVTMU_wd: + case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; + case FCVTNS_ws: + case FCVTNS_xs: + case FCVTNS_wd: + case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; + case FCVTNU_ws: + case FCVTNU_xs: + case FCVTNU_wd: + case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; + case FCVTZU_xd: + case FCVTZU_ws: + case FCVTZU_wd: + case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; + case FCVTZS_xd: + case FCVTZS_wd: + case FCVTZS_xs: + case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; + case SCVTF_sw: + case SCVTF_sx: + case SCVTF_dw: + case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; + case UCVTF_sw: + case UCVTF_sx: + case UCVTF_dw: + case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitFPFixedPointConvert(Instruction* instr) { + const char *mnemonic = ""; + const char *form = "'Rd, 'Fn, 'IFPFBits"; + const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; + + switch (instr->Mask(FPFixedPointConvertMask)) { + case FCVTZS_ws_fixed: + case FCVTZS_xs_fixed: + case FCVTZS_wd_fixed: + case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; + case FCVTZU_ws_fixed: + case FCVTZU_xs_fixed: + case FCVTZU_wd_fixed: + case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; + case SCVTF_sw_fixed: + case SCVTF_sx_fixed: + case SCVTF_dw_fixed: + case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; + case UCVTF_sw_fixed: + case UCVTF_sx_fixed: + case UCVTF_dw_fixed: + case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; + default: UNREACHABLE(); + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitSystem(Instruction* instr) { + // Some system instructions hijack their Op and Cp fields to represent a + // range of immediates instead of indicating a different instruction. This + // makes the decoding tricky. + const char *mnemonic = "unimplemented"; + const char *form = "(System)"; + + if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { + switch (instr->Mask(SystemSysRegMask)) { + case MRS: { + mnemonic = "mrs"; + switch (instr->ImmSystemRegister()) { + case NZCV: form = "'Xt, nzcv"; break; + case FPCR: form = "'Xt, fpcr"; break; + default: form = "'Xt, (unknown)"; break; + } + break; + } + case MSR: { + mnemonic = "msr"; + switch (instr->ImmSystemRegister()) { + case NZCV: form = "nzcv, 'Xt"; break; + case FPCR: form = "fpcr, 'Xt"; break; + default: form = "(unknown), 'Xt"; break; + } + break; + } + } + } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { + ASSERT(instr->Mask(SystemHintMask) == HINT); + switch (instr->ImmHint()) { + case NOP: { + mnemonic = "nop"; + form = NULL; + break; + } + } + } + + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitException(Instruction* instr) { + const char *mnemonic = "unimplemented"; + const char *form = "'IDebug"; + + switch (instr->Mask(ExceptionMask)) { + case HLT: mnemonic = "hlt"; break; + case BRK: mnemonic = "brk"; break; + case SVC: mnemonic = "svc"; break; + case HVC: mnemonic = "hvc"; break; + case SMC: mnemonic = "smc"; break; + case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; + case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; + case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; + default: form = "(Exception)"; + } + Format(instr, mnemonic, form); +} + + +void Disassembler::VisitUnimplemented(Instruction* instr) { + Format(instr, "unimplemented", "(Unimplemented)"); +} + + +void Disassembler::VisitUnallocated(Instruction* instr) { + Format(instr, "unallocated", "(Unallocated)"); +} + + +void Disassembler::ProcessOutput(Instruction* /*instr*/) { + // The base disasm does nothing more than disassembling into a buffer. +} + + +void Disassembler::Format(Instruction* instr, const char* mnemonic, + const char* format) { + ASSERT(mnemonic != NULL); + ResetOutput(); + Substitute(instr, mnemonic); + if (format != NULL) { + buffer_[buffer_pos_++] = ' '; + Substitute(instr, format); + } + buffer_[buffer_pos_] = 0; + ProcessOutput(instr); +} + + +void Disassembler::Substitute(Instruction* instr, const char* string) { + char chr = *string++; + while (chr != '\0') { + if (chr == '\'') { + string += SubstituteField(instr, string); + } else { + buffer_[buffer_pos_++] = chr; + } + chr = *string++; + } +} + + +int Disassembler::SubstituteField(Instruction* instr, const char* format) { + switch (format[0]) { + case 'R': // Register. X or W, selected by sf bit. + case 'F': // FP Register. S or D, selected by type field. + case 'W': + case 'X': + case 'S': + case 'D': return SubstituteRegisterField(instr, format); + case 'I': return SubstituteImmediateField(instr, format); + case 'L': return SubstituteLiteralField(instr, format); + case 'H': return SubstituteShiftField(instr, format); + case 'P': return SubstitutePrefetchField(instr, format); + case 'C': return SubstituteConditionField(instr, format); + case 'E': return SubstituteExtendField(instr, format); + case 'A': return SubstitutePCRelAddressField(instr, format); + case 'B': return SubstituteBranchTargetField(instr, format); + case 'O': return SubstituteLSRegOffsetField(instr, format); + default: { + UNREACHABLE(); + return 1; + } + } +} + + +int Disassembler::SubstituteRegisterField(Instruction* instr, + const char* format) { + unsigned reg_num = 0; + unsigned field_len = 2; + switch (format[1]) { + case 'd': reg_num = instr->Rd(); break; + case 'n': reg_num = instr->Rn(); break; + case 'm': reg_num = instr->Rm(); break; + case 'a': reg_num = instr->Ra(); break; + case 't': { + if (format[2] == '2') { + reg_num = instr->Rt2(); + field_len = 3; + } else { + reg_num = instr->Rt(); + } + break; + } + default: UNREACHABLE(); + } + + // Increase field length for registers tagged as stack. + if (format[2] == 's') { + field_len = 3; + } + + char reg_type; + if (format[0] == 'R') { + // Register type is R: use sf bit to choose X and W. + reg_type = instr->SixtyFourBits() ? 'x' : 'w'; + } else if (format[0] == 'F') { + // Floating-point register: use type field to choose S or D. + reg_type = ((instr->FPType() & 1) == 0) ? 's' : 'd'; + } else { + // Register type is specified. Make it lower case. + reg_type = format[0] + 0x20; + } + + if ((reg_num != kZeroRegCode) || (reg_type == 's') || (reg_type == 'd')) { + // A normal register: w0 - w30, x0 - x30, s0 - s31, d0 - d31. + AppendToOutput("%c%d", reg_type, reg_num); + } else if (format[2] == 's') { + // Disassemble w31/x31 as stack pointer wsp/sp. + AppendToOutput("%s", (reg_type == 'w') ? "wsp" : "sp"); + } else { + // Disassemble w31/x31 as zero register wzr/xzr. + AppendToOutput("%czr", reg_type); + } + + return field_len; +} + + +int Disassembler::SubstituteImmediateField(Instruction* instr, + const char* format) { + ASSERT(format[0] == 'I'); + + switch (format[1]) { + case 'M': { // IMoveImm or IMoveLSL. + if (format[5] == 'I') { + uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); + AppendToOutput("#0x%" PRIx64, imm); + } else { + ASSERT(format[5] == 'L'); + AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); + if (instr->ShiftMoveWide() > 0) { + AppendToOutput(", lsl #%d", 16 * instr->ShiftMoveWide()); + } + } + return 8; + } + case 'L': { + switch (format[2]) { + case 'L': { // ILLiteral - Immediate Load Literal. + AppendToOutput("pc%+" PRId64, + instr->ImmLLiteral() << kLiteralEntrySizeLog2); + return 9; + } + case 'S': { // ILS - Immediate Load/Store. + if (instr->ImmLS() != 0) { + AppendToOutput(", #%" PRId64, instr->ImmLS()); + } + return 3; + } + case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. + if (instr->ImmLSPair() != 0) { + // format[3] is the scale value. Convert to a number. + int scale = format[3] - 0x30; + AppendToOutput(", #%" PRId64, instr->ImmLSPair() * scale); + } + return 4; + } + case 'U': { // ILU - Immediate Load/Store Unsigned. + if (instr->ImmLSUnsigned() != 0) { + AppendToOutput(", #%" PRIu64, + instr->ImmLSUnsigned() << instr->SizeLS()); + } + return 3; + } + } + } + case 'C': { // ICondB - Immediate Conditional Branch. + int64_t offset = instr->ImmCondBranch() << 2; + char sign = (offset >= 0) ? '+' : '-'; + AppendToOutput("#%c0x%" PRIx64, sign, offset); + return 6; + } + case 'A': { // IAddSub. + ASSERT(instr->ShiftAddSub() <= 1); + int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); + AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); + return 7; + } + case 'F': { // IFPSingle, IFPDouble or IFPFBits. + if (format[3] == 'F') { // IFPFbits. + AppendToOutput("#%d", 64 - instr->FPScale()); + return 8; + } else { + AppendToOutput("#0x%" PRIx64 " (%.4f)", instr->ImmFP(), + format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); + return 9; + } + } + case 'T': { // ITri - Immediate Triangular Encoded. + AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); + return 4; + } + case 'N': { // INzcv. + int nzcv = (instr->Nzcv() << Flags_offset); + AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', + ((nzcv & ZFlag) == 0) ? 'z' : 'Z', + ((nzcv & CFlag) == 0) ? 'c' : 'C', + ((nzcv & VFlag) == 0) ? 'v' : 'V'); + return 5; + } + case 'P': { // IP - Conditional compare. + AppendToOutput("#%d", instr->ImmCondCmp()); + return 2; + } + case 'B': { // Bitfields. + return SubstituteBitfieldImmediateField(instr, format); + } + case 'E': { // IExtract. + AppendToOutput("#%d", instr->ImmS()); + return 8; + } + case 'S': { // IS - Test and branch bit. + AppendToOutput("#%d", (instr->ImmTestBranchBit5() << 5) | + instr->ImmTestBranchBit40()); + return 2; + } + case 'D': { // IDebug - HLT and BRK instructions. + AppendToOutput("#0x%x", instr->ImmException()); + return 6; + } + default: { + UNIMPLEMENTED(); + return 0; + } + } +} + + +int Disassembler::SubstituteBitfieldImmediateField(Instruction* instr, + const char* format) { + ASSERT((format[0] == 'I') && (format[1] == 'B')); + unsigned r = instr->ImmR(); + unsigned s = instr->ImmS(); + + switch (format[2]) { + case 'r': { // IBr. + AppendToOutput("#%d", r); + return 3; + } + case 's': { // IBs+1 or IBs-r+1. + if (format[3] == '+') { + AppendToOutput("#%d", s + 1); + return 5; + } else { + ASSERT(format[3] == '-'); + AppendToOutput("#%d", s - r + 1); + return 7; + } + } + case 'Z': { // IBZ-r. + ASSERT((format[3] == '-') && (format[4] == 'r')); + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; + AppendToOutput("#%d", reg_size - r); + return 5; + } + default: { + UNREACHABLE(); + return 0; + } + } +} + + +int Disassembler::SubstituteLiteralField(Instruction* instr, + const char* format) { + ASSERT(strncmp(format, "LValue", 6) == 0); + USE(format); + + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: + case LDR_x_lit: + case LDR_s_lit: + case LDR_d_lit: AppendToOutput("(addr %p)", instr->LiteralAddress()); break; + default: UNREACHABLE(); + } + + return 6; +} + + +int Disassembler::SubstituteShiftField(Instruction* instr, const char* format) { + ASSERT(format[0] == 'H'); + ASSERT(instr->ShiftDP() <= 0x3); + + switch (format[1]) { + case 'D': { // HDP. + ASSERT(instr->ShiftDP() != ROR); + } // Fall through. + case 'L': { // HLo. + if (instr->ImmDPShift() != 0) { + const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; + AppendToOutput(", %s #%" PRId64, shift_type[instr->ShiftDP()], + instr->ImmDPShift()); + } + return 3; + } + default: + UNIMPLEMENTED(); + return 0; + } +} + + +int Disassembler::SubstituteConditionField(Instruction* instr, + const char* format) { + ASSERT(format[0] == 'C'); + const char* condition_code[] = { "eq", "ne", "hs", "lo", + "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", + "gt", "le", "al", "nv" }; + int cond; + switch (format[1]) { + case 'B': cond = instr->ConditionBranch(); break; + case 'I': { + cond = InvertCondition(static_cast(instr->Condition())); + break; + } + default: cond = instr->Condition(); + } + AppendToOutput("%s", condition_code[cond]); + return 4; +} + + +int Disassembler::SubstitutePCRelAddressField(Instruction* instr, + const char* format) { + USE(format); + ASSERT(strncmp(format, "AddrPCRel", 9) == 0); + + int offset = instr->ImmPCRel(); + + // Only ADR (AddrPCRelByte) is supported. + ASSERT(strcmp(format, "AddrPCRelByte") == 0); + + char sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + // TODO: Extend this to support printing the target address. + AppendToOutput("#%c0x%x", sign, offset); + return 13; +} + + +int Disassembler::SubstituteBranchTargetField(Instruction* instr, + const char* format) { + ASSERT(strncmp(format, "BImm", 4) == 0); + + int64_t offset = 0; + switch (format[5]) { + // BImmUncn - unconditional branch immediate. + case 'n': offset = instr->ImmUncondBranch(); break; + // BImmCond - conditional branch immediate. + case 'o': offset = instr->ImmCondBranch(); break; + // BImmCmpa - compare and branch immediate. + case 'm': offset = instr->ImmCmpBranch(); break; + // BImmTest - test and branch immediate. + case 'e': offset = instr->ImmTestBranch(); break; + default: UNIMPLEMENTED(); + } + offset <<= kInstructionSizeLog2; + char sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + AppendToOutput("#%c0x%" PRIx64, sign, offset); + return 8; +} + + +int Disassembler::SubstituteExtendField(Instruction* instr, + const char* format) { + ASSERT(strncmp(format, "Ext", 3) == 0); + ASSERT(instr->ExtendMode() <= 7); + USE(format); + + const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", + "sxtb", "sxth", "sxtw", "sxtx" }; + + // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit + // registers becomes lsl. + if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && + (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || + (instr->ExtendMode() == UXTX))) { + if (instr->ImmExtendShift() > 0) { + AppendToOutput(", lsl #%d", instr->ImmExtendShift()); + } + } else { + AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); + if (instr->ImmExtendShift() > 0) { + AppendToOutput(" #%d", instr->ImmExtendShift()); + } + } + return 3; +} + + +int Disassembler::SubstituteLSRegOffsetField(Instruction* instr, + const char* format) { + ASSERT(strncmp(format, "Offsetreg", 9) == 0); + const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", + "undefined", "undefined", "sxtw", "sxtx" }; + USE(format); + + unsigned shift = instr->ImmShiftLS(); + Extend ext = static_cast(instr->ExtendMode()); + char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; + + unsigned rm = instr->Rm(); + if (rm == kZeroRegCode) { + AppendToOutput("%czr", reg_type); + } else { + AppendToOutput("%c%d", reg_type, rm); + } + + // Extend mode UXTX is an alias for shift mode LSL here. + if (!((ext == UXTX) && (shift == 0))) { + AppendToOutput(", %s", extend_mode[ext]); + if (shift != 0) { + AppendToOutput(" #%d", instr->SizeLS()); + } + } + return 9; +} + + +int Disassembler::SubstitutePrefetchField(Instruction* instr, + const char* format) { + ASSERT(format[0] == 'P'); + USE(format); + + int prefetch_mode = instr->PrefetchMode(); + + const char* ls = (prefetch_mode & 0x10) ? "st" : "ld"; + int level = (prefetch_mode >> 1) + 1; + const char* ks = (prefetch_mode & 1) ? "strm" : "keep"; + + AppendToOutput("p%sl%d%s", ls, level, ks); + return 6; +} + + +void Disassembler::ResetOutput() { + buffer_pos_ = 0; + buffer_[buffer_pos_] = 0; +} + + +void Disassembler::AppendToOutput(const char* format, ...) { + va_list args; + va_start(args, format); + buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_, format, args); + va_end(args); +} + + +void PrintDisassembler::ProcessOutput(Instruction* instr) { + fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", + reinterpret_cast(instr), + instr->InstructionBits(), + GetOutput()); +} +} // namespace vixl diff --git a/disas/libvixl/src/a64/disasm-a64.h b/disas/libvixl/src/a64/disasm-a64.h new file mode 100644 index 0000000..857a5ac --- /dev/null +++ b/disas/libvixl/src/a64/disasm-a64.h @@ -0,0 +1,109 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_DISASM_A64_H +#define VIXL_A64_DISASM_A64_H + +#include "globals.h" +#include "utils.h" +#include "instructions-a64.h" +#include "decoder-a64.h" + +namespace vixl { + +class Disassembler: public DecoderVisitor { + public: + Disassembler(); + Disassembler(char* text_buffer, int buffer_size); + virtual ~Disassembler(); + char* GetOutput(); + + // Declare all Visitor functions. + #define DECLARE(A) void Visit##A(Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + protected: + virtual void ProcessOutput(Instruction* instr); + + private: + void Format(Instruction* instr, const char* mnemonic, const char* format); + void Substitute(Instruction* instr, const char* string); + int SubstituteField(Instruction* instr, const char* format); + int SubstituteRegisterField(Instruction* instr, const char* format); + int SubstituteImmediateField(Instruction* instr, const char* format); + int SubstituteLiteralField(Instruction* instr, const char* format); + int SubstituteBitfieldImmediateField(Instruction* instr, const char* format); + int SubstituteShiftField(Instruction* instr, const char* format); + int SubstituteExtendField(Instruction* instr, const char* format); + int SubstituteConditionField(Instruction* instr, const char* format); + int SubstitutePCRelAddressField(Instruction* instr, const char* format); + int SubstituteBranchTargetField(Instruction* instr, const char* format); + int SubstituteLSRegOffsetField(Instruction* instr, const char* format); + int SubstitutePrefetchField(Instruction* instr, const char* format); + + inline bool RdIsZROrSP(Instruction* instr) const { + return (instr->Rd() == kZeroRegCode); + } + + inline bool RnIsZROrSP(Instruction* instr) const { + return (instr->Rn() == kZeroRegCode); + } + + inline bool RmIsZROrSP(Instruction* instr) const { + return (instr->Rm() == kZeroRegCode); + } + + inline bool RaIsZROrSP(Instruction* instr) const { + return (instr->Ra() == kZeroRegCode); + } + + bool IsMovzMovnImm(unsigned reg_size, uint64_t value); + + void ResetOutput(); + void AppendToOutput(const char* string, ...); + + char* buffer_; + uint32_t buffer_pos_; + uint32_t buffer_size_; + bool own_buffer_; +}; + + +class PrintDisassembler: public Disassembler { + public: + explicit PrintDisassembler(FILE* stream) : stream_(stream) { } + ~PrintDisassembler() { } + + protected: + virtual void ProcessOutput(Instruction* instr); + + private: + FILE *stream_; +}; +} // namespace vixl + +#endif // VIXL_A64_DISASM_A64_H diff --git a/disas/libvixl/src/a64/instructions-a64.cc b/disas/libvixl/src/a64/instructions-a64.cc new file mode 100644 index 0000000..e87fa3a --- /dev/null +++ b/disas/libvixl/src/a64/instructions-a64.cc @@ -0,0 +1,238 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "a64/instructions-a64.h" +#include "a64/assembler-a64.h" + +namespace vixl { + + +static uint64_t RotateRight(uint64_t value, + unsigned int rotate, + unsigned int width) { + ASSERT(width <= 64); + rotate &= 63; + return ((value & ((1UL << rotate) - 1UL)) << (width - rotate)) | + (value >> rotate); +} + + +static uint64_t RepeatBitsAcrossReg(unsigned reg_size, + uint64_t value, + unsigned width) { + ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || + (width == 32)); + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + uint64_t result = value & ((1UL << width) - 1UL); + for (unsigned i = width; i < reg_size; i *= 2) { + result |= (result << i); + } + return result; +} + + +// Logical immediates can't encode zero, so a return value of zero is used to +// indicate a failure case. Specifically, where the constraints on imm_s are +// not met. +uint64_t Instruction::ImmLogical() { + unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t n = BitN(); + int64_t imm_s = ImmSetBits(); + int64_t imm_r = ImmRotate(); + + // An integer is constructed from the n, imm_s and imm_r bits according to + // the following table: + // + // N imms immr size S R + // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) + // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) + // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) + // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) + // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) + // 0 11110s xxxxxr 2 UInt(s) UInt(r) + // (s bits must not be all set) + // + // A pattern is constructed of size bits, where the least significant S+1 + // bits are set. The pattern is rotated right by R, and repeated across a + // 32 or 64-bit value, depending on destination register width. + // + + if (n == 1) { + if (imm_s == 0x3F) { + return 0; + } + uint64_t bits = (1UL << (imm_s + 1)) - 1; + return RotateRight(bits, imm_r, 64); + } else { + if ((imm_s >> 1) == 0x1F) { + return 0; + } + for (int width = 0x20; width >= 0x2; width >>= 1) { + if ((imm_s & width) == 0) { + int mask = width - 1; + if ((imm_s & mask) == mask) { + return 0; + } + uint64_t bits = (1UL << ((imm_s & mask) + 1)) - 1; + return RepeatBitsAcrossReg(reg_size, + RotateRight(bits, imm_r & mask, width), + width); + } + } + } + UNREACHABLE(); + return 0; +} + + +float Instruction::ImmFP32() { + // ImmFP: abcdefgh (8 bits) + // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) + // where B is b ^ 1 + uint32_t bits = ImmFP(); + uint32_t bit7 = (bits >> 7) & 0x1; + uint32_t bit6 = (bits >> 6) & 0x1; + uint32_t bit5_to_0 = bits & 0x3f; + uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); + + return rawbits_to_float(result); +} + + +double Instruction::ImmFP64() { + // ImmFP: abcdefgh (8 bits) + // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) + // where B is b ^ 1 + uint32_t bits = ImmFP(); + uint64_t bit7 = (bits >> 7) & 0x1; + uint64_t bit6 = (bits >> 6) & 0x1; + uint64_t bit5_to_0 = bits & 0x3f; + uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); + + return rawbits_to_double(result); +} + + +LSDataSize CalcLSPairDataSize(LoadStorePairOp op) { + switch (op) { + case STP_x: + case LDP_x: + case STP_d: + case LDP_d: return LSDoubleWord; + default: return LSWord; + } +} + + +Instruction* Instruction::ImmPCOffsetTarget() { + ptrdiff_t offset; + if (IsPCRelAddressing()) { + // PC-relative addressing. Only ADR is supported. + offset = ImmPCRel(); + } else { + // All PC-relative branches. + ASSERT(BranchType() != UnknownBranchType); + // Relative branch offsets are instruction-size-aligned. + offset = ImmBranch() << kInstructionSizeLog2; + } + return this + offset; +} + + +inline int Instruction::ImmBranch() const { + switch (BranchType()) { + case CondBranchType: return ImmCondBranch(); + case UncondBranchType: return ImmUncondBranch(); + case CompareBranchType: return ImmCmpBranch(); + case TestBranchType: return ImmTestBranch(); + default: UNREACHABLE(); + } + return 0; +} + + +void Instruction::SetImmPCOffsetTarget(Instruction* target) { + if (IsPCRelAddressing()) { + SetPCRelImmTarget(target); + } else { + SetBranchImmTarget(target); + } +} + + +void Instruction::SetPCRelImmTarget(Instruction* target) { + // ADRP is not supported, so 'this' must point to an ADR instruction. + ASSERT(Mask(PCRelAddressingMask) == ADR); + + Instr imm = Assembler::ImmPCRelAddress(target - this); + + SetInstructionBits(Mask(~ImmPCRel_mask) | imm); +} + + +void Instruction::SetBranchImmTarget(Instruction* target) { + ASSERT(((target - this) & 3) == 0); + Instr branch_imm = 0; + uint32_t imm_mask = 0; + int offset = (target - this) >> kInstructionSizeLog2; + switch (BranchType()) { + case CondBranchType: { + branch_imm = Assembler::ImmCondBranch(offset); + imm_mask = ImmCondBranch_mask; + break; + } + case UncondBranchType: { + branch_imm = Assembler::ImmUncondBranch(offset); + imm_mask = ImmUncondBranch_mask; + break; + } + case CompareBranchType: { + branch_imm = Assembler::ImmCmpBranch(offset); + imm_mask = ImmCmpBranch_mask; + break; + } + case TestBranchType: { + branch_imm = Assembler::ImmTestBranch(offset); + imm_mask = ImmTestBranch_mask; + break; + } + default: UNREACHABLE(); + } + SetInstructionBits(Mask(~imm_mask) | branch_imm); +} + + +void Instruction::SetImmLLiteral(Instruction* source) { + ASSERT(((source - this) & 3) == 0); + int offset = (source - this) >> kLiteralEntrySizeLog2; + Instr imm = Assembler::ImmLLiteral(offset); + Instr mask = ImmLLiteral_mask; + + SetInstructionBits(Mask(~mask) | imm); +} +} // namespace vixl + diff --git a/disas/libvixl/src/a64/instructions-a64.h b/disas/libvixl/src/a64/instructions-a64.h new file mode 100644 index 0000000..0f31fcd --- /dev/null +++ b/disas/libvixl/src/a64/instructions-a64.h @@ -0,0 +1,344 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_INSTRUCTIONS_A64_H_ +#define VIXL_A64_INSTRUCTIONS_A64_H_ + +#include "globals.h" +#include "utils.h" +#include "a64/constants-a64.h" + +namespace vixl { +// ISA constants. -------------------------------------------------------------- + +typedef uint32_t Instr; +const unsigned kInstructionSize = 4; +const unsigned kInstructionSizeLog2 = 2; +const unsigned kLiteralEntrySize = 4; +const unsigned kLiteralEntrySizeLog2 = 2; +const unsigned kMaxLoadLiteralRange = 1 * MBytes; + +const unsigned kWRegSize = 32; +const unsigned kWRegSizeLog2 = 5; +const unsigned kWRegSizeInBytes = kWRegSize / 8; +const unsigned kXRegSize = 64; +const unsigned kXRegSizeLog2 = 6; +const unsigned kXRegSizeInBytes = kXRegSize / 8; +const unsigned kSRegSize = 32; +const unsigned kSRegSizeLog2 = 5; +const unsigned kSRegSizeInBytes = kSRegSize / 8; +const unsigned kDRegSize = 64; +const unsigned kDRegSizeLog2 = 6; +const unsigned kDRegSizeInBytes = kDRegSize / 8; +const int64_t kWRegMask = 0x00000000ffffffffL; +const int64_t kXRegMask = 0xffffffffffffffffL; +const int64_t kSRegMask = 0x00000000ffffffffL; +const int64_t kDRegMask = 0xffffffffffffffffL; +const int64_t kXSignMask = 0x1L << 63; +const int64_t kWSignMask = 0x1L << 31; +const int64_t kByteMask = 0xffL; +const int64_t kHalfWordMask = 0xffffL; +const int64_t kWordMask = 0xffffffffL; +const uint64_t kXMaxUInt = 0xffffffffffffffffUL; +const uint64_t kWMaxUInt = 0xffffffffUL; +const int64_t kXMaxInt = 0x7fffffffffffffffL; +const int64_t kXMinInt = 0x8000000000000000L; +const int32_t kWMaxInt = 0x7fffffff; +const int32_t kWMinInt = 0x80000000; +const unsigned kLinkRegCode = 30; +const unsigned kZeroRegCode = 31; +const unsigned kSPRegInternalCode = 63; +const unsigned kRegCodeMask = 0x1f; + +// AArch64 floating-point specifics. These match IEEE-754. +const unsigned kDoubleMantissaBits = 52; +const unsigned kDoubleExponentBits = 11; +const unsigned kFloatMantissaBits = 23; +const unsigned kFloatExponentBits = 8; + +const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); +const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); +const double kFP64PositiveInfinity = rawbits_to_double(0x7ff0000000000000UL); +const double kFP64NegativeInfinity = rawbits_to_double(0xfff0000000000000UL); + +// This value is a signalling NaN as both a double and as a float (taking the +// least-significant word). +static const double kFP64SignallingNaN = rawbits_to_double(0x7ff000007f800001); +static const float kFP32SignallingNaN = rawbits_to_float(0x7f800001); + +// A similar value, but as a quiet NaN. +static const double kFP64QuietNaN = rawbits_to_double(0x7ff800007fc00001); +static const float kFP32QuietNaN = rawbits_to_float(0x7fc00001); + +enum LSDataSize { + LSByte = 0, + LSHalfword = 1, + LSWord = 2, + LSDoubleWord = 3 +}; + +LSDataSize CalcLSPairDataSize(LoadStorePairOp op); + +enum ImmBranchType { + UnknownBranchType = 0, + CondBranchType = 1, + UncondBranchType = 2, + CompareBranchType = 3, + TestBranchType = 4 +}; + +enum AddrMode { + Offset, + PreIndex, + PostIndex +}; + +enum FPRounding { + // The first four values are encodable directly by FPCR. + FPTieEven = 0x0, + FPPositiveInfinity = 0x1, + FPNegativeInfinity = 0x2, + FPZero = 0x3, + + // The final rounding mode is only available when explicitly specified by the + // instruction (such as with fcvta). It cannot be set in FPCR. + FPTieAway +}; + +enum Reg31Mode { + Reg31IsStackPointer, + Reg31IsZeroRegister +}; + +// Instructions. --------------------------------------------------------------- + +class Instruction { + public: + inline Instr InstructionBits() const { + return *(reinterpret_cast(this)); + } + + inline void SetInstructionBits(Instr new_instr) { + *(reinterpret_cast(this)) = new_instr; + } + + inline int Bit(int pos) const { + return (InstructionBits() >> pos) & 1; + } + + inline uint32_t Bits(int msb, int lsb) const { + return unsigned_bitextract_32(msb, lsb, InstructionBits()); + } + + inline int32_t SignedBits(int msb, int lsb) const { + int32_t bits = *(reinterpret_cast(this)); + return signed_bitextract_32(msb, lsb, bits); + } + + inline Instr Mask(uint32_t mask) const { + return InstructionBits() & mask; + } + + #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ + inline int64_t Name() const { return Func(HighBit, LowBit); } + INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) + #undef DEFINE_GETTER + + // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), + // formed from ImmPCRelLo and ImmPCRelHi. + int ImmPCRel() const { + int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); + int const width = ImmPCRelLo_width + ImmPCRelHi_width; + return signed_bitextract_32(width-1, 0, offset); + } + + uint64_t ImmLogical(); + float ImmFP32(); + double ImmFP64(); + + inline LSDataSize SizeLSPair() const { + return CalcLSPairDataSize( + static_cast(Mask(LoadStorePairMask))); + } + + // Helpers. + inline bool IsCondBranchImm() const { + return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; + } + + inline bool IsUncondBranchImm() const { + return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; + } + + inline bool IsCompareBranch() const { + return Mask(CompareBranchFMask) == CompareBranchFixed; + } + + inline bool IsTestBranch() const { + return Mask(TestBranchFMask) == TestBranchFixed; + } + + inline bool IsPCRelAddressing() const { + return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; + } + + inline bool IsLogicalImmediate() const { + return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; + } + + inline bool IsAddSubImmediate() const { + return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; + } + + inline bool IsAddSubExtended() const { + return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; + } + + inline bool IsLoadOrStore() const { + return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; + } + + inline bool IsMovn() const { + return (Mask(MoveWideImmediateMask) == MOVN_x) || + (Mask(MoveWideImmediateMask) == MOVN_w); + } + + // Indicate whether Rd can be the stack pointer or the zero register. This + // does not check that the instruction actually has an Rd field. + inline Reg31Mode RdMode() const { + // The following instructions use sp or wsp as Rd: + // Add/sub (immediate) when not setting the flags. + // Add/sub (extended) when not setting the flags. + // Logical (immediate) when not setting the flags. + // Otherwise, r31 is the zero register. + if (IsAddSubImmediate() || IsAddSubExtended()) { + if (Mask(AddSubSetFlagsBit)) { + return Reg31IsZeroRegister; + } else { + return Reg31IsStackPointer; + } + } + if (IsLogicalImmediate()) { + // Of the logical (immediate) instructions, only ANDS (and its aliases) + // can set the flags. The others can all write into sp. + // Note that some logical operations are not available to + // immediate-operand instructions, so we have to combine two masks here. + if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { + return Reg31IsZeroRegister; + } else { + return Reg31IsStackPointer; + } + } + return Reg31IsZeroRegister; + } + + // Indicate whether Rn can be the stack pointer or the zero register. This + // does not check that the instruction actually has an Rn field. + inline Reg31Mode RnMode() const { + // The following instructions use sp or wsp as Rn: + // All loads and stores. + // Add/sub (immediate). + // Add/sub (extended). + // Otherwise, r31 is the zero register. + if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { + return Reg31IsStackPointer; + } + return Reg31IsZeroRegister; + } + + inline ImmBranchType BranchType() const { + if (IsCondBranchImm()) { + return CondBranchType; + } else if (IsUncondBranchImm()) { + return UncondBranchType; + } else if (IsCompareBranch()) { + return CompareBranchType; + } else if (IsTestBranch()) { + return TestBranchType; + } else { + return UnknownBranchType; + } + } + + // Find the target of this instruction. 'this' may be a branch or a + // PC-relative addressing instruction. + Instruction* ImmPCOffsetTarget(); + + // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or + // a PC-relative addressing instruction. + void SetImmPCOffsetTarget(Instruction* target); + // Patch a literal load instruction to load from 'source'. + void SetImmLLiteral(Instruction* source); + + inline uint8_t* LiteralAddress() { + int offset = ImmLLiteral() << kLiteralEntrySizeLog2; + return reinterpret_cast(this) + offset; + } + + inline uint32_t Literal32() { + uint32_t literal; + memcpy(&literal, LiteralAddress(), sizeof(literal)); + + return literal; + } + + inline uint64_t Literal64() { + uint64_t literal; + memcpy(&literal, LiteralAddress(), sizeof(literal)); + + return literal; + } + + inline float LiteralFP32() { + return rawbits_to_float(Literal32()); + } + + inline double LiteralFP64() { + return rawbits_to_double(Literal64()); + } + + inline Instruction* NextInstruction() { + return this + kInstructionSize; + } + + inline Instruction* InstructionAtOffset(int64_t offset) { + ASSERT(IsWordAligned(this + offset)); + return this + offset; + } + + template static inline Instruction* Cast(T src) { + return reinterpret_cast(src); + } + + private: + inline int ImmBranch() const; + + void SetPCRelImmTarget(Instruction* target); + void SetBranchImmTarget(Instruction* target); +}; +} // namespace vixl + +#endif // VIXL_A64_INSTRUCTIONS_A64_H_ diff --git a/disas/libvixl/src/a64/instrument-a64.cc b/disas/libvixl/src/a64/instrument-a64.cc new file mode 100644 index 0000000..507410d --- /dev/null +++ b/disas/libvixl/src/a64/instrument-a64.cc @@ -0,0 +1,638 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "a64/instrument-a64.h" + +namespace vixl { + +Counter::Counter(const char* name, CounterType type) + : count_(0), enabled_(false), type_(type) { + ASSERT(name != NULL); + strncpy(name_, name, kCounterNameMaxLength); +} + + +void Counter::Enable() { + enabled_ = true; +} + + +void Counter::Disable() { + enabled_ = false; +} + + +bool Counter::IsEnabled() { + return enabled_; +} + + +void Counter::Increment() { + if (enabled_) { + count_++; + } +} + + +uint64_t Counter::count() { + uint64_t result = count_; + if (type_ == Gauge) { + // If the counter is a Gauge, reset the count after reading. + count_ = 0; + } + return result; +} + + +const char* Counter::name() { + return name_; +} + + +CounterType Counter::type() { + return type_; +} + + +typedef struct { + const char* name; + CounterType type; +} CounterDescriptor; + + +static const CounterDescriptor kCounterList[] = { + {"Instruction", Cumulative}, + + {"Move Immediate", Gauge}, + {"Add/Sub DP", Gauge}, + {"Logical DP", Gauge}, + {"Other Int DP", Gauge}, + {"FP DP", Gauge}, + + {"Conditional Select", Gauge}, + {"Conditional Compare", Gauge}, + + {"Unconditional Branch", Gauge}, + {"Compare and Branch", Gauge}, + {"Test and Branch", Gauge}, + {"Conditional Branch", Gauge}, + + {"Load Integer", Gauge}, + {"Load FP", Gauge}, + {"Load Pair", Gauge}, + {"Load Literal", Gauge}, + + {"Store Integer", Gauge}, + {"Store FP", Gauge}, + {"Store Pair", Gauge}, + + {"PC Addressing", Gauge}, + {"Other", Gauge}, +}; + + +Instrument::Instrument(const char* datafile, uint64_t sample_period) + : output_stream_(stdout), sample_period_(sample_period) { + + // Set up the output stream. If datafile is non-NULL, use that file. If it + // can't be opened, or datafile is NULL, use stdout. + if (datafile != NULL) { + output_stream_ = fopen(datafile, "w"); + if (output_stream_ == NULL) { + printf("Can't open output file %s. Using stdout.\n", datafile); + output_stream_ = stdout; + } + } + + static const int num_counters = + sizeof(kCounterList) / sizeof(CounterDescriptor); + + // Dump an instrumentation description comment at the top of the file. + fprintf(output_stream_, "# counters=%d\n", num_counters); + fprintf(output_stream_, "# sample_period=%" PRIu64 "\n", sample_period_); + + // Construct Counter objects from counter description array. + for (int i = 0; i < num_counters; i++) { + Counter* counter = new Counter(kCounterList[i].name, kCounterList[i].type); + counters_.push_back(counter); + } + + DumpCounterNames(); +} + + +Instrument::~Instrument() { + // Dump any remaining instruction data to the output file. + DumpCounters(); + + // Free all the counter objects. + std::list::iterator it; + for (it = counters_.begin(); it != counters_.end(); it++) { + free(*it); + } + + if (output_stream_ != stdout) { + fclose(output_stream_); + } +} + + +void Instrument::Update() { + // Increment the instruction counter, and dump all counters if a sample period + // has elapsed. + static Counter* counter = GetCounter("Instruction"); + ASSERT(counter->type() == Cumulative); + counter->Increment(); + + if (counter->IsEnabled() && (counter->count() % sample_period_) == 0) { + DumpCounters(); + } +} + + +void Instrument::DumpCounters() { + // Iterate through the counter objects, dumping their values to the output + // stream. + std::list::const_iterator it; + for (it = counters_.begin(); it != counters_.end(); it++) { + fprintf(output_stream_, "%" PRIu64 ",", (*it)->count()); + } + fprintf(output_stream_, "\n"); + fflush(output_stream_); +} + + +void Instrument::DumpCounterNames() { + // Iterate through the counter objects, dumping the counter names to the + // output stream. + std::list::const_iterator it; + for (it = counters_.begin(); it != counters_.end(); it++) { + fprintf(output_stream_, "%s,", (*it)->name()); + } + fprintf(output_stream_, "\n"); + fflush(output_stream_); +} + + +void Instrument::HandleInstrumentationEvent(unsigned event) { + switch (event) { + case InstrumentStateEnable: Enable(); break; + case InstrumentStateDisable: Disable(); break; + default: DumpEventMarker(event); + } +} + + +void Instrument::DumpEventMarker(unsigned marker) { + // Dumpan event marker to the output stream as a specially formatted comment + // line. + static Counter* counter = GetCounter("Instruction"); + + fprintf(output_stream_, "# %c%c @ %" PRId64 "\n", marker & 0xff, + (marker >> 8) & 0xff, counter->count()); +} + + +Counter* Instrument::GetCounter(const char* name) { + // Get a Counter object by name from the counter list. + std::list::const_iterator it; + for (it = counters_.begin(); it != counters_.end(); it++) { + if (strcmp((*it)->name(), name) == 0) { + return *it; + } + } + + // A Counter by that name does not exist: print an error message to stderr + // and the output file, and exit. + static const char* error_message = + "# Error: Unknown counter \"%s\". Exiting.\n"; + fprintf(stderr, error_message, name); + fprintf(output_stream_, error_message, name); + exit(1); +} + + +void Instrument::Enable() { + std::list::iterator it; + for (it = counters_.begin(); it != counters_.end(); it++) { + (*it)->Enable(); + } +} + + +void Instrument::Disable() { + std::list::iterator it; + for (it = counters_.begin(); it != counters_.end(); it++) { + (*it)->Disable(); + } +} + + +void Instrument::VisitPCRelAddressing(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("PC Addressing"); + counter->Increment(); +} + + +void Instrument::VisitAddSubImmediate(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Add/Sub DP"); + counter->Increment(); +} + + +void Instrument::VisitLogicalImmediate(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Logical DP"); + counter->Increment(); +} + + +void Instrument::VisitMoveWideImmediate(Instruction* instr) { + Update(); + static Counter* counter = GetCounter("Move Immediate"); + + if (instr->IsMovn() && (instr->Rd() == kZeroRegCode)) { + unsigned imm = instr->ImmMoveWide(); + HandleInstrumentationEvent(imm); + } else { + counter->Increment(); + } +} + + +void Instrument::VisitBitfield(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other Int DP"); + counter->Increment(); +} + + +void Instrument::VisitExtract(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other Int DP"); + counter->Increment(); +} + + +void Instrument::VisitUnconditionalBranch(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Unconditional Branch"); + counter->Increment(); +} + + +void Instrument::VisitUnconditionalBranchToRegister(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Unconditional Branch"); + counter->Increment(); +} + + +void Instrument::VisitCompareBranch(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Compare and Branch"); + counter->Increment(); +} + + +void Instrument::VisitTestBranch(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Test and Branch"); + counter->Increment(); +} + + +void Instrument::VisitConditionalBranch(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Conditional Branch"); + counter->Increment(); +} + + +void Instrument::VisitSystem(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other"); + counter->Increment(); +} + + +void Instrument::VisitException(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other"); + counter->Increment(); +} + + +void Instrument::InstrumentLoadStorePair(Instruction* instr) { + static Counter* load_pair_counter = GetCounter("Load Pair"); + static Counter* store_pair_counter = GetCounter("Store Pair"); + + if (instr->Mask(LoadStorePairLBit) != 0) { + load_pair_counter->Increment(); + } else { + store_pair_counter->Increment(); + } +} + + +void Instrument::VisitLoadStorePairPostIndex(Instruction* instr) { + USE(instr); + Update(); + InstrumentLoadStorePair(instr); +} + + +void Instrument::VisitLoadStorePairOffset(Instruction* instr) { + USE(instr); + Update(); + InstrumentLoadStorePair(instr); +} + + +void Instrument::VisitLoadStorePairPreIndex(Instruction* instr) { + USE(instr); + Update(); + InstrumentLoadStorePair(instr); +} + + +void Instrument::VisitLoadStorePairNonTemporal(Instruction* instr) { + USE(instr); + Update(); + InstrumentLoadStorePair(instr); +} + + +void Instrument::VisitLoadLiteral(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Load Literal"); + counter->Increment(); +} + + +void Instrument::InstrumentLoadStore(Instruction* instr) { + static Counter* load_int_counter = GetCounter("Load Integer"); + static Counter* store_int_counter = GetCounter("Store Integer"); + static Counter* load_fp_counter = GetCounter("Load FP"); + static Counter* store_fp_counter = GetCounter("Store FP"); + + switch (instr->Mask(LoadStoreOpMask)) { + case STRB_w: // Fall through. + case STRH_w: // Fall through. + case STR_w: // Fall through. + case STR_x: store_int_counter->Increment(); break; + case STR_s: // Fall through. + case STR_d: store_fp_counter->Increment(); break; + case LDRB_w: // Fall through. + case LDRH_w: // Fall through. + case LDR_w: // Fall through. + case LDR_x: // Fall through. + case LDRSB_x: // Fall through. + case LDRSH_x: // Fall through. + case LDRSW_x: // Fall through. + case LDRSB_w: // Fall through. + case LDRSH_w: load_int_counter->Increment(); break; + case LDR_s: // Fall through. + case LDR_d: load_fp_counter->Increment(); break; + } +} + + +void Instrument::VisitLoadStoreUnscaledOffset(Instruction* instr) { + Update(); + InstrumentLoadStore(instr); +} + + +void Instrument::VisitLoadStorePostIndex(Instruction* instr) { + USE(instr); + Update(); + InstrumentLoadStore(instr); +} + + +void Instrument::VisitLoadStorePreIndex(Instruction* instr) { + Update(); + InstrumentLoadStore(instr); +} + + +void Instrument::VisitLoadStoreRegisterOffset(Instruction* instr) { + Update(); + InstrumentLoadStore(instr); +} + + +void Instrument::VisitLoadStoreUnsignedOffset(Instruction* instr) { + Update(); + InstrumentLoadStore(instr); +} + + +void Instrument::VisitLogicalShifted(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Logical DP"); + counter->Increment(); +} + + +void Instrument::VisitAddSubShifted(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Add/Sub DP"); + counter->Increment(); +} + + +void Instrument::VisitAddSubExtended(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Add/Sub DP"); + counter->Increment(); +} + + +void Instrument::VisitAddSubWithCarry(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Add/Sub DP"); + counter->Increment(); +} + + +void Instrument::VisitConditionalCompareRegister(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Conditional Compare"); + counter->Increment(); +} + + +void Instrument::VisitConditionalCompareImmediate(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Conditional Compare"); + counter->Increment(); +} + + +void Instrument::VisitConditionalSelect(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Conditional Select"); + counter->Increment(); +} + + +void Instrument::VisitDataProcessing1Source(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other Int DP"); + counter->Increment(); +} + + +void Instrument::VisitDataProcessing2Source(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other Int DP"); + counter->Increment(); +} + + +void Instrument::VisitDataProcessing3Source(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other Int DP"); + counter->Increment(); +} + + +void Instrument::VisitFPCompare(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitFPConditionalCompare(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Conditional Compare"); + counter->Increment(); +} + + +void Instrument::VisitFPConditionalSelect(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Conditional Select"); + counter->Increment(); +} + + +void Instrument::VisitFPImmediate(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitFPDataProcessing1Source(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitFPDataProcessing2Source(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitFPDataProcessing3Source(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitFPIntegerConvert(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitFPFixedPointConvert(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("FP DP"); + counter->Increment(); +} + + +void Instrument::VisitUnallocated(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other"); + counter->Increment(); +} + + +void Instrument::VisitUnimplemented(Instruction* instr) { + USE(instr); + Update(); + static Counter* counter = GetCounter("Other"); + counter->Increment(); +} + + +} // namespace v8::internal diff --git a/disas/libvixl/src/a64/instrument-a64.h b/disas/libvixl/src/a64/instrument-a64.h new file mode 100644 index 0000000..bee965b --- /dev/null +++ b/disas/libvixl/src/a64/instrument-a64.h @@ -0,0 +1,108 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_INSTRUMENT_A64_H_ +#define VIXL_A64_INSTRUMENT_A64_H_ + +#include "globals.h" +#include "utils.h" +#include "a64/decoder-a64.h" +#include "a64/constants-a64.h" +#include "a64/instrument-a64.h" + +namespace vixl { + +const int kCounterNameMaxLength = 256; +const uint64_t kDefaultInstrumentationSamplingPeriod = 1 << 22; + + +enum InstrumentState { + InstrumentStateDisable = 0, + InstrumentStateEnable = 1 +}; + + +enum CounterType { + Gauge = 0, // Gauge counters reset themselves after reading. + Cumulative = 1 // Cumulative counters keep their value after reading. +}; + + +class Counter { + public: + Counter(const char* name, CounterType type = Gauge); + ~Counter(); + + void Increment(); + void Enable(); + void Disable(); + bool IsEnabled(); + uint64_t count(); + const char* name(); + CounterType type(); + + private: + char name_[kCounterNameMaxLength]; + uint64_t count_; + bool enabled_; + CounterType type_; +}; + + +class Instrument: public DecoderVisitor { + public: + explicit Instrument(const char* datafile = NULL, + uint64_t sample_period = kDefaultInstrumentationSamplingPeriod); + ~Instrument(); + + void Enable(); + void Disable(); + + // Declare all Visitor functions. + #define DECLARE(A) void Visit##A(Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + private: + void Update(); + void DumpCounters(); + void DumpCounterNames(); + void DumpEventMarker(unsigned marker); + void HandleInstrumentationEvent(unsigned event); + Counter* GetCounter(const char* name); + + void InstrumentLoadStore(Instruction* instr); + void InstrumentLoadStorePair(Instruction* instr); + + std::list counters_; + + FILE *output_stream_; + uint64_t sample_period_; +}; + +} // namespace vixl + +#endif // VIXL_A64_INSTRUMENT_A64_H_ diff --git a/disas/libvixl/src/a64/macro-assembler-a64.cc b/disas/libvixl/src/a64/macro-assembler-a64.cc new file mode 100644 index 0000000..39a925c --- /dev/null +++ b/disas/libvixl/src/a64/macro-assembler-a64.cc @@ -0,0 +1,1108 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "a64/macro-assembler-a64.h" +namespace vixl { + +void MacroAssembler::And(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + LogicalMacro(rd, rn, operand, (S == SetFlags) ? ANDS : AND); +} + + +void MacroAssembler::Tst(const Register& rn, + const Operand& operand) { + ASSERT(allow_macro_instructions_); + And(AppropriateZeroRegFor(rn), rn, operand, SetFlags); +} + + +void MacroAssembler::Bic(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + LogicalMacro(rd, rn, operand, (S == SetFlags) ? BICS : BIC); +} + + +void MacroAssembler::Orr(const Register& rd, + const Register& rn, + const Operand& operand) { + ASSERT(allow_macro_instructions_); + LogicalMacro(rd, rn, operand, ORR); +} + + +void MacroAssembler::Orn(const Register& rd, + const Register& rn, + const Operand& operand) { + ASSERT(allow_macro_instructions_); + LogicalMacro(rd, rn, operand, ORN); +} + + +void MacroAssembler::Eor(const Register& rd, + const Register& rn, + const Operand& operand) { + ASSERT(allow_macro_instructions_); + LogicalMacro(rd, rn, operand, EOR); +} + + +void MacroAssembler::Eon(const Register& rd, + const Register& rn, + const Operand& operand) { + ASSERT(allow_macro_instructions_); + LogicalMacro(rd, rn, operand, EON); +} + + +void MacroAssembler::LogicalMacro(const Register& rd, + const Register& rn, + const Operand& operand, + LogicalOp op) { + if (operand.IsImmediate()) { + int64_t immediate = operand.immediate(); + unsigned reg_size = rd.size(); + ASSERT(rd.Is64Bits() || is_uint32(immediate)); + + // If the operation is NOT, invert the operation and immediate. + if ((op & NOT) == NOT) { + op = static_cast(op & ~NOT); + immediate = ~immediate; + if (rd.Is32Bits()) { + immediate &= kWRegMask; + } + } + + // Special cases for all set or all clear immediates. + if (immediate == 0) { + switch (op) { + case AND: + Mov(rd, 0); + return; + case ORR: // Fall through. + case EOR: + Mov(rd, rn); + return; + case ANDS: // Fall through. + case BICS: + break; + default: + UNREACHABLE(); + } + } else if ((rd.Is64Bits() && (immediate == -1L)) || + (rd.Is32Bits() && (immediate == 0xffffffffL))) { + switch (op) { + case AND: + Mov(rd, rn); + return; + case ORR: + Mov(rd, immediate); + return; + case EOR: + Mvn(rd, rn); + return; + case ANDS: // Fall through. + case BICS: + break; + default: + UNREACHABLE(); + } + } + + unsigned n, imm_s, imm_r; + if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) { + // Immediate can be encoded in the instruction. + LogicalImmediate(rd, rn, n, imm_s, imm_r, op); + } else { + // Immediate can't be encoded: synthesize using move immediate. + Register temp = AppropriateTempFor(rn); + Mov(temp, immediate); + if (rd.Is(sp)) { + // If rd is the stack pointer we cannot use it as the destination + // register so we use the temp register as an intermediate again. + Logical(temp, rn, Operand(temp), op); + Mov(sp, temp); + } else { + Logical(rd, rn, Operand(temp), op); + } + } + } else if (operand.IsExtendedRegister()) { + ASSERT(operand.reg().size() <= rd.size()); + // Add/sub extended supports shift <= 4. We want to support exactly the + // same modes here. + ASSERT(operand.shift_amount() <= 4); + ASSERT(operand.reg().Is64Bits() || + ((operand.extend() != UXTX) && (operand.extend() != SXTX))); + Register temp = AppropriateTempFor(rn, operand.reg()); + EmitExtendShift(temp, operand.reg(), operand.extend(), + operand.shift_amount()); + Logical(rd, rn, Operand(temp), op); + } else { + // The operand can be encoded in the instruction. + ASSERT(operand.IsShiftedRegister()); + Logical(rd, rn, operand, op); + } +} + + +void MacroAssembler::Mov(const Register& rd, const Operand& operand) { + ASSERT(allow_macro_instructions_); + if (operand.IsImmediate()) { + // Call the macro assembler for generic immediates. + Mov(rd, operand.immediate()); + } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { + // Emit a shift instruction if moving a shifted register. This operation + // could also be achieved using an orr instruction (like orn used by Mvn), + // but using a shift instruction makes the disassembly clearer. + EmitShift(rd, operand.reg(), operand.shift(), operand.shift_amount()); + } else if (operand.IsExtendedRegister()) { + // Emit an extend instruction if moving an extended register. This handles + // extend with post-shift operations, too. + EmitExtendShift(rd, operand.reg(), operand.extend(), + operand.shift_amount()); + } else { + // Otherwise, emit a register move only if the registers are distinct, or + // if they are not X registers. Note that mov(w0, w0) is not a no-op + // because it clears the top word of x0. + // If the sp is an operand, add #0 is emitted, otherwise, orr #0. + if (!rd.Is(operand.reg()) || !rd.Is64Bits()) { + mov(rd, operand.reg()); + } + } +} + + +void MacroAssembler::Mvn(const Register& rd, const Operand& operand) { + ASSERT(allow_macro_instructions_); + if (operand.IsImmediate()) { + // Call the macro assembler for generic immediates. + Mvn(rd, operand.immediate()); + } else if (operand.IsExtendedRegister()) { + // Emit two instructions for the extend case. This differs from Mov, as + // the extend and invert can't be achieved in one instruction. + Register temp = AppropriateTempFor(rd, operand.reg()); + EmitExtendShift(temp, operand.reg(), operand.extend(), + operand.shift_amount()); + mvn(rd, Operand(temp)); + } else { + // Otherwise, register and shifted register cases can be handled by the + // assembler directly, using orn. + mvn(rd, operand); + } +} + + +void MacroAssembler::Mov(const Register& rd, uint64_t imm) { + ASSERT(allow_macro_instructions_); + ASSERT(is_uint32(imm) || is_int32(imm) || rd.Is64Bits()); + + // Immediates on Aarch64 can be produced using an initial value, and zero to + // three move keep operations. + // + // Initial values can be generated with: + // 1. 64-bit move zero (movz). + // 2. 32-bit move negative (movn). + // 3. 64-bit move negative. + // 4. 32-bit orr immediate. + // 5. 64-bit orr immediate. + // Move-keep may then be used to modify each of the 16-bit nybbles. + // + // The code below supports all five initial value generators, and + // applying move-keep operations to move-zero initial values only. + + unsigned reg_size = rd.size(); + unsigned n, imm_s, imm_r; + if (IsImmMovz(imm, reg_size) && !rd.IsSP()) { + // Immediate can be represented in a move zero instruction. + movz(rd, imm); + } else if (IsImmMovn(imm, reg_size) && !rd.IsSP()) { + // Immediate can be represented in a move negative instruction. Movn can't + // write to the stack pointer. + movn(rd, rd.Is64Bits() ? ~imm : (~imm & kWRegMask)); + } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) { + // Immediate can be represented in a logical orr instruction. + ASSERT(!rd.IsZero()); + LogicalImmediate(rd, AppropriateZeroRegFor(rd), n, imm_s, imm_r, ORR); + } else { + // Generic immediate case. Imm will be represented by + // [imm3, imm2, imm1, imm0], where each imm is 16 bits. + // A move-zero is generated for the first non-zero immX, and a move-keep + // for subsequent non-zero immX. + + // Use a temporary register when moving to the stack pointer. + Register temp = rd.IsSP() ? AppropriateTempFor(rd) : rd; + + ASSERT((reg_size % 16) == 0); + bool first_mov_done = false; + for (unsigned i = 0; i < (temp.size() / 16); i++) { + uint64_t imm16 = (imm >> (16 * i)) & 0xffffL; + if (imm16 != 0) { + if (!first_mov_done) { + // Move the first non-zero 16-bit chunk into the destination register. + movz(temp, imm16, 16 * i); + first_mov_done = true; + } else { + // Construct a wider constant. + movk(temp, imm16, 16 * i); + } + } + } + + if (rd.IsSP()) { + mov(rd, temp); + } + + ASSERT(first_mov_done); + } +} + + +// The movz instruction can generate immediates containing an arbitrary 16-bit +// value, with remaining bits set, eg. 0x00001234, 0x0000123400000000. +bool MacroAssembler::IsImmMovz(uint64_t imm, unsigned reg_size) { + if (reg_size == kXRegSize) { + if (((imm & 0xffffffffffff0000UL) == 0UL) || + ((imm & 0xffffffff0000ffffUL) == 0UL) || + ((imm & 0xffff0000ffffffffUL) == 0UL) || + ((imm & 0x0000ffffffffffffUL) == 0UL)) { + return true; + } + } else { + ASSERT(reg_size == kWRegSize); + imm &= kWRegMask; + if (((imm & 0xffff0000) == 0) || + ((imm & 0x0000ffff) == 0)) { + return true; + } + } + return false; +} + + +// The movn instruction can generate immediates containing an arbitrary 16-bit +// value, with remaining bits set, eg. 0xffff1234, 0xffff1234ffffffff. +bool MacroAssembler::IsImmMovn(uint64_t imm, unsigned reg_size) { + return IsImmMovz(~imm, reg_size); +} + + +void MacroAssembler::Ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond) { + ASSERT(allow_macro_instructions_); + ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP); +} + + +void MacroAssembler::Ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond) { + ASSERT(allow_macro_instructions_); + ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN); +} + + +void MacroAssembler::ConditionalCompareMacro(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op) { + ASSERT((cond != al) && (cond != nv)); + if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) || + (operand.IsImmediate() && IsImmConditionalCompare(operand.immediate()))) { + // The immediate can be encoded in the instruction, or the operand is an + // unshifted register: call the assembler. + ConditionalCompare(rn, operand, nzcv, cond, op); + } else { + // The operand isn't directly supported by the instruction: perform the + // operation on a temporary register. + Register temp(NoReg); + if (operand.IsImmediate()) { + temp = AppropriateTempFor(rn); + Mov(temp, operand.immediate()); + } else if (operand.IsShiftedRegister()) { + ASSERT(operand.shift() != ROR); + ASSERT(is_uintn(rn.size() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2, + operand.shift_amount())); + temp = AppropriateTempFor(rn, operand.reg()); + EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount()); + } else { + ASSERT(operand.IsExtendedRegister()); + ASSERT(operand.reg().size() <= rn.size()); + // Add/sub extended support a shift <= 4. We want to support exactly the + // same modes. + ASSERT(operand.shift_amount() <= 4); + ASSERT(operand.reg().Is64Bits() || + ((operand.extend() != UXTX) && (operand.extend() != SXTX))); + temp = AppropriateTempFor(rn, operand.reg()); + EmitExtendShift(temp, operand.reg(), operand.extend(), + operand.shift_amount()); + } + ConditionalCompare(rn, Operand(temp), nzcv, cond, op); + } +} + + +void MacroAssembler::Add(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + if (operand.IsImmediate() && (operand.immediate() < 0)) { + AddSubMacro(rd, rn, -operand.immediate(), S, SUB); + } else { + AddSubMacro(rd, rn, operand, S, ADD); + } +} + + +void MacroAssembler::Sub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + if (operand.IsImmediate() && (operand.immediate() < 0)) { + AddSubMacro(rd, rn, -operand.immediate(), S, ADD); + } else { + AddSubMacro(rd, rn, operand, S, SUB); + } +} + + +void MacroAssembler::Cmn(const Register& rn, const Operand& operand) { + ASSERT(allow_macro_instructions_); + Add(AppropriateZeroRegFor(rn), rn, operand, SetFlags); +} + + +void MacroAssembler::Cmp(const Register& rn, const Operand& operand) { + ASSERT(allow_macro_instructions_); + Sub(AppropriateZeroRegFor(rn), rn, operand, SetFlags); +} + + +void MacroAssembler::Neg(const Register& rd, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + if (operand.IsImmediate()) { + Mov(rd, -operand.immediate()); + } else { + Sub(rd, AppropriateZeroRegFor(rd), operand, S); + } +} + + +void MacroAssembler::AddSubMacro(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op) { + if ((operand.IsImmediate() && !IsImmAddSub(operand.immediate())) || + (rn.IsZero() && !operand.IsShiftedRegister()) || + (operand.IsShiftedRegister() && (operand.shift() == ROR))) { + Register temp = AppropriateTempFor(rn); + Mov(temp, operand); + AddSub(rd, rn, temp, S, op); + } else { + AddSub(rd, rn, operand, S, op); + } +} + + +void MacroAssembler::Adc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + AddSubWithCarryMacro(rd, rn, operand, S, ADC); +} + + +void MacroAssembler::Sbc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + AddSubWithCarryMacro(rd, rn, operand, S, SBC); +} + + +void MacroAssembler::Ngc(const Register& rd, + const Operand& operand, + FlagsUpdate S) { + ASSERT(allow_macro_instructions_); + Register zr = AppropriateZeroRegFor(rd); + Sbc(rd, zr, operand, S); +} + + +void MacroAssembler::AddSubWithCarryMacro(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op) { + ASSERT(rd.size() == rn.size()); + + if (operand.IsImmediate() || + (operand.IsShiftedRegister() && (operand.shift() == ROR))) { + // Add/sub with carry (immediate or ROR shifted register.) + Register temp = AppropriateTempFor(rn); + Mov(temp, operand); + AddSubWithCarry(rd, rn, Operand(temp), S, op); + } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { + // Add/sub with carry (shifted register). + ASSERT(operand.reg().size() == rd.size()); + ASSERT(operand.shift() != ROR); + ASSERT(is_uintn(rd.size() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2, + operand.shift_amount())); + Register temp = AppropriateTempFor(rn, operand.reg()); + EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount()); + AddSubWithCarry(rd, rn, Operand(temp), S, op); + } else if (operand.IsExtendedRegister()) { + // Add/sub with carry (extended register). + ASSERT(operand.reg().size() <= rd.size()); + // Add/sub extended supports a shift <= 4. We want to support exactly the + // same modes. + ASSERT(operand.shift_amount() <= 4); + ASSERT(operand.reg().Is64Bits() || + ((operand.extend() != UXTX) && (operand.extend() != SXTX))); + Register temp = AppropriateTempFor(rn, operand.reg()); + EmitExtendShift(temp, operand.reg(), operand.extend(), + operand.shift_amount()); + AddSubWithCarry(rd, rn, Operand(temp), S, op); + } else { + // The addressing mode is directly supported by the instruction. + AddSubWithCarry(rd, rn, operand, S, op); + } +} + + +#define DEFINE_FUNCTION(FN, REGTYPE, REG, OP) \ +void MacroAssembler::FN(const REGTYPE REG, const MemOperand& addr) { \ + LoadStoreMacro(REG, addr, OP); \ +} +LS_MACRO_LIST(DEFINE_FUNCTION) +#undef DEFINE_FUNCTION + +void MacroAssembler::LoadStoreMacro(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op) { + int64_t offset = addr.offset(); + LSDataSize size = CalcLSDataSize(op); + + // Check if an immediate offset fits in the immediate field of the + // appropriate instruction. If not, emit two instructions to perform + // the operation. + if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, size) && + !IsImmLSUnscaled(offset)) { + // Immediate offset that can't be encoded using unsigned or unscaled + // addressing modes. + Register temp = AppropriateTempFor(addr.base()); + Mov(temp, addr.offset()); + LoadStore(rt, MemOperand(addr.base(), temp), op); + } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) { + // Post-index beyond unscaled addressing range. + LoadStore(rt, MemOperand(addr.base()), op); + Add(addr.base(), addr.base(), Operand(offset)); + } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) { + // Pre-index beyond unscaled addressing range. + Add(addr.base(), addr.base(), Operand(offset)); + LoadStore(rt, MemOperand(addr.base()), op); + } else { + // Encodable in one load/store instruction. + LoadStore(rt, addr, op); + } +} + + +void MacroAssembler::Push(const CPURegister& src0, const CPURegister& src1, + const CPURegister& src2, const CPURegister& src3) { + ASSERT(allow_macro_instructions_); + ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); + ASSERT(src0.IsValid()); + + int count = 1 + src1.IsValid() + src2.IsValid() + src3.IsValid(); + int size = src0.SizeInBytes(); + + PrepareForPush(count, size); + PushHelper(count, size, src0, src1, src2, src3); +} + + +void MacroAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1, + const CPURegister& dst2, const CPURegister& dst3) { + // It is not valid to pop into the same register more than once in one + // instruction, not even into the zero register. + ASSERT(allow_macro_instructions_); + ASSERT(!AreAliased(dst0, dst1, dst2, dst3)); + ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); + ASSERT(dst0.IsValid()); + + int count = 1 + dst1.IsValid() + dst2.IsValid() + dst3.IsValid(); + int size = dst0.SizeInBytes(); + + PrepareForPop(count, size); + PopHelper(count, size, dst0, dst1, dst2, dst3); +} + + +void MacroAssembler::PushCPURegList(CPURegList registers) { + int size = registers.RegisterSizeInBytes(); + + PrepareForPush(registers.Count(), size); + // Push up to four registers at a time because if the current stack pointer is + // sp and reg_size is 32, registers must be pushed in blocks of four in order + // to maintain the 16-byte alignment for sp. + ASSERT(allow_macro_instructions_); + while (!registers.IsEmpty()) { + int count_before = registers.Count(); + const CPURegister& src0 = registers.PopHighestIndex(); + const CPURegister& src1 = registers.PopHighestIndex(); + const CPURegister& src2 = registers.PopHighestIndex(); + const CPURegister& src3 = registers.PopHighestIndex(); + int count = count_before - registers.Count(); + PushHelper(count, size, src0, src1, src2, src3); + } +} + + +void MacroAssembler::PopCPURegList(CPURegList registers) { + int size = registers.RegisterSizeInBytes(); + + PrepareForPop(registers.Count(), size); + // Pop up to four registers at a time because if the current stack pointer is + // sp and reg_size is 32, registers must be pushed in blocks of four in order + // to maintain the 16-byte alignment for sp. + ASSERT(allow_macro_instructions_); + while (!registers.IsEmpty()) { + int count_before = registers.Count(); + const CPURegister& dst0 = registers.PopLowestIndex(); + const CPURegister& dst1 = registers.PopLowestIndex(); + const CPURegister& dst2 = registers.PopLowestIndex(); + const CPURegister& dst3 = registers.PopLowestIndex(); + int count = count_before - registers.Count(); + PopHelper(count, size, dst0, dst1, dst2, dst3); + } +} + + +void MacroAssembler::PushMultipleTimes(int count, Register src) { + ASSERT(allow_macro_instructions_); + int size = src.SizeInBytes(); + + PrepareForPush(count, size); + // Push up to four registers at a time if possible because if the current + // stack pointer is sp and the register size is 32, registers must be pushed + // in blocks of four in order to maintain the 16-byte alignment for sp. + while (count >= 4) { + PushHelper(4, size, src, src, src, src); + count -= 4; + } + if (count >= 2) { + PushHelper(2, size, src, src, NoReg, NoReg); + count -= 2; + } + if (count == 1) { + PushHelper(1, size, src, NoReg, NoReg, NoReg); + count -= 1; + } + ASSERT(count == 0); +} + + +void MacroAssembler::PushHelper(int count, int size, + const CPURegister& src0, + const CPURegister& src1, + const CPURegister& src2, + const CPURegister& src3) { + // Ensure that we don't unintentionally modify scratch or debug registers. + InstructionAccurateScope scope(this); + + ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); + ASSERT(size == src0.SizeInBytes()); + + // When pushing multiple registers, the store order is chosen such that + // Push(a, b) is equivalent to Push(a) followed by Push(b). + switch (count) { + case 1: + ASSERT(src1.IsNone() && src2.IsNone() && src3.IsNone()); + str(src0, MemOperand(StackPointer(), -1 * size, PreIndex)); + break; + case 2: + ASSERT(src2.IsNone() && src3.IsNone()); + stp(src1, src0, MemOperand(StackPointer(), -2 * size, PreIndex)); + break; + case 3: + ASSERT(src3.IsNone()); + stp(src2, src1, MemOperand(StackPointer(), -3 * size, PreIndex)); + str(src0, MemOperand(StackPointer(), 2 * size)); + break; + case 4: + // Skip over 4 * size, then fill in the gap. This allows four W registers + // to be pushed using sp, whilst maintaining 16-byte alignment for sp at + // all times. + stp(src3, src2, MemOperand(StackPointer(), -4 * size, PreIndex)); + stp(src1, src0, MemOperand(StackPointer(), 2 * size)); + break; + default: + UNREACHABLE(); + } +} + + +void MacroAssembler::PopHelper(int count, int size, + const CPURegister& dst0, + const CPURegister& dst1, + const CPURegister& dst2, + const CPURegister& dst3) { + // Ensure that we don't unintentionally modify scratch or debug registers. + InstructionAccurateScope scope(this); + + ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); + ASSERT(size == dst0.SizeInBytes()); + + // When popping multiple registers, the load order is chosen such that + // Pop(a, b) is equivalent to Pop(a) followed by Pop(b). + switch (count) { + case 1: + ASSERT(dst1.IsNone() && dst2.IsNone() && dst3.IsNone()); + ldr(dst0, MemOperand(StackPointer(), 1 * size, PostIndex)); + break; + case 2: + ASSERT(dst2.IsNone() && dst3.IsNone()); + ldp(dst0, dst1, MemOperand(StackPointer(), 2 * size, PostIndex)); + break; + case 3: + ASSERT(dst3.IsNone()); + ldr(dst2, MemOperand(StackPointer(), 2 * size)); + ldp(dst0, dst1, MemOperand(StackPointer(), 3 * size, PostIndex)); + break; + case 4: + // Load the higher addresses first, then load the lower addresses and skip + // the whole block in the second instruction. This allows four W registers + // to be popped using sp, whilst maintaining 16-byte alignment for sp at + // all times. + ldp(dst2, dst3, MemOperand(StackPointer(), 2 * size)); + ldp(dst0, dst1, MemOperand(StackPointer(), 4 * size, PostIndex)); + break; + default: + UNREACHABLE(); + } +} + + +void MacroAssembler::PrepareForPush(int count, int size) { + if (sp.Is(StackPointer())) { + // If the current stack pointer is sp, then it must be aligned to 16 bytes + // on entry and the total size of the specified registers must also be a + // multiple of 16 bytes. + ASSERT((count * size) % 16 == 0); + } else { + // Even if the current stack pointer is not the system stack pointer (sp), + // the system stack pointer will still be modified in order to comply with + // ABI rules about accessing memory below the system stack pointer. + BumpSystemStackPointer(count * size); + } +} + + +void MacroAssembler::PrepareForPop(int count, int size) { + USE(count); + USE(size); + if (sp.Is(StackPointer())) { + // If the current stack pointer is sp, then it must be aligned to 16 bytes + // on entry and the total size of the specified registers must also be a + // multiple of 16 bytes. + ASSERT((count * size) % 16 == 0); + } +} + +void MacroAssembler::Poke(const Register& src, const Operand& offset) { + ASSERT(allow_macro_instructions_); + if (offset.IsImmediate()) { + ASSERT(offset.immediate() >= 0); + } + + Str(src, MemOperand(StackPointer(), offset)); +} + + +void MacroAssembler::Peek(const Register& dst, const Operand& offset) { + ASSERT(allow_macro_instructions_); + if (offset.IsImmediate()) { + ASSERT(offset.immediate() >= 0); + } + + Ldr(dst, MemOperand(StackPointer(), offset)); +} + + +void MacroAssembler::Claim(const Operand& size) { + ASSERT(allow_macro_instructions_); + if (size.IsImmediate()) { + ASSERT(size.immediate() >= 0); + if (sp.Is(StackPointer())) { + ASSERT((size.immediate() % 16) == 0); + } + } + + if (!sp.Is(StackPointer())) { + BumpSystemStackPointer(size); + } + + Sub(StackPointer(), StackPointer(), size); +} + + +void MacroAssembler::Drop(const Operand& size) { + ASSERT(allow_macro_instructions_); + if (size.IsImmediate()) { + ASSERT(size.immediate() >= 0); + if (sp.Is(StackPointer())) { + ASSERT((size.immediate() % 16) == 0); + } + } + + Add(StackPointer(), StackPointer(), size); +} + + +void MacroAssembler::PushCalleeSavedRegisters() { + // Ensure that the macro-assembler doesn't use any scratch registers. + InstructionAccurateScope scope(this); + + // This method must not be called unless the current stack pointer is sp. + ASSERT(sp.Is(StackPointer())); + + MemOperand tos(sp, -2 * kXRegSizeInBytes, PreIndex); + + stp(d14, d15, tos); + stp(d12, d13, tos); + stp(d10, d11, tos); + stp(d8, d9, tos); + + stp(x29, x30, tos); + stp(x27, x28, tos); + stp(x25, x26, tos); + stp(x23, x24, tos); + stp(x21, x22, tos); + stp(x19, x20, tos); +} + + +void MacroAssembler::PopCalleeSavedRegisters() { + // Ensure that the macro-assembler doesn't use any scratch registers. + InstructionAccurateScope scope(this); + + // This method must not be called unless the current stack pointer is sp. + ASSERT(sp.Is(StackPointer())); + + MemOperand tos(sp, 2 * kXRegSizeInBytes, PostIndex); + + ldp(x19, x20, tos); + ldp(x21, x22, tos); + ldp(x23, x24, tos); + ldp(x25, x26, tos); + ldp(x27, x28, tos); + ldp(x29, x30, tos); + + ldp(d8, d9, tos); + ldp(d10, d11, tos); + ldp(d12, d13, tos); + ldp(d14, d15, tos); +} + +void MacroAssembler::BumpSystemStackPointer(const Operand& space) { + ASSERT(!sp.Is(StackPointer())); + // TODO: Several callers rely on this not using scratch registers, so we use + // the assembler directly here. However, this means that large immediate + // values of 'space' cannot be handled. + InstructionAccurateScope scope(this); + sub(sp, StackPointer(), space); +} + + +// This is the main Printf implementation. All callee-saved registers are +// preserved, but NZCV and the caller-saved registers may be clobbered. +void MacroAssembler::PrintfNoPreserve(const char * format, + const CPURegister& arg0, + const CPURegister& arg1, + const CPURegister& arg2, + const CPURegister& arg3) { + // We cannot handle a caller-saved stack pointer. It doesn't make much sense + // in most cases anyway, so this restriction shouldn't be too serious. + ASSERT(!kCallerSaved.IncludesAliasOf(StackPointer())); + + // We cannot print Tmp0() or Tmp1() as they're used internally by the macro + // assembler. We cannot print the stack pointer because it is typically used + // to preserve caller-saved registers (using other Printf variants which + // depend on this helper). + ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg0)); + ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg1)); + ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg2)); + ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg3)); + + static const int kMaxArgCount = 4; + // Assume that we have the maximum number of arguments until we know + // otherwise. + int arg_count = kMaxArgCount; + + // The provided arguments. + CPURegister args[kMaxArgCount] = {arg0, arg1, arg2, arg3}; + + // The PCS registers where the arguments need to end up. + CPURegister pcs[kMaxArgCount]; + + // Promote FP arguments to doubles, and integer arguments to X registers. + // Note that FP and integer arguments cannot be mixed, but we'll check + // AreSameSizeAndType once we've processed these promotions. + for (int i = 0; i < kMaxArgCount; i++) { + if (args[i].IsRegister()) { + // Note that we use x1 onwards, because x0 will hold the format string. + pcs[i] = Register::XRegFromCode(i + 1); + // For simplicity, we handle all integer arguments as X registers. An X + // register argument takes the same space as a W register argument in the + // PCS anyway. The only limitation is that we must explicitly clear the + // top word for W register arguments as the callee will expect it to be + // clear. + if (!args[i].Is64Bits()) { + const Register& as_x = args[i].X(); + And(as_x, as_x, 0x00000000ffffffff); + args[i] = as_x; + } + } else if (args[i].IsFPRegister()) { + pcs[i] = FPRegister::DRegFromCode(i); + // C and C++ varargs functions (such as printf) implicitly promote float + // arguments to doubles. + if (!args[i].Is64Bits()) { + FPRegister s(args[i]); + const FPRegister& as_d = args[i].D(); + Fcvt(as_d, s); + args[i] = as_d; + } + } else { + // This is the first empty (NoCPUReg) argument, so use it to set the + // argument count and bail out. + arg_count = i; + break; + } + } + ASSERT((arg_count >= 0) && (arg_count <= kMaxArgCount)); + // Check that every remaining argument is NoCPUReg. + for (int i = arg_count; i < kMaxArgCount; i++) { + ASSERT(args[i].IsNone()); + } + ASSERT((arg_count == 0) || AreSameSizeAndType(args[0], args[1], + args[2], args[3], + pcs[0], pcs[1], + pcs[2], pcs[3])); + + // Move the arguments into the appropriate PCS registers. + // + // Arranging an arbitrary list of registers into x1-x4 (or d0-d3) is + // surprisingly complicated. + // + // * For even numbers of registers, we push the arguments and then pop them + // into their final registers. This maintains 16-byte stack alignment in + // case sp is the stack pointer, since we're only handling X or D registers + // at this point. + // + // * For odd numbers of registers, we push and pop all but one register in + // the same way, but the left-over register is moved directly, since we + // can always safely move one register without clobbering any source. + if (arg_count >= 4) { + Push(args[3], args[2], args[1], args[0]); + } else if (arg_count >= 2) { + Push(args[1], args[0]); + } + + if ((arg_count % 2) != 0) { + // Move the left-over register directly. + const CPURegister& leftover_arg = args[arg_count - 1]; + const CPURegister& leftover_pcs = pcs[arg_count - 1]; + if (leftover_arg.IsRegister()) { + Mov(Register(leftover_pcs), Register(leftover_arg)); + } else { + Fmov(FPRegister(leftover_pcs), FPRegister(leftover_arg)); + } + } + + if (arg_count >= 4) { + Pop(pcs[0], pcs[1], pcs[2], pcs[3]); + } else if (arg_count >= 2) { + Pop(pcs[0], pcs[1]); + } + + // Load the format string into x0, as per the procedure-call standard. + // + // To make the code as portable as possible, the format string is encoded + // directly in the instruction stream. It might be cleaner to encode it in a + // literal pool, but since Printf is usually used for debugging, it is + // beneficial for it to be minimally dependent on other features. + Label format_address; + Adr(x0, &format_address); + + // Emit the format string directly in the instruction stream. + { BlockLiteralPoolScope scope(this); + Label after_data; + B(&after_data); + Bind(&format_address); + EmitStringData(format); + Unreachable(); + Bind(&after_data); + } + + // We don't pass any arguments on the stack, but we still need to align the C + // stack pointer to a 16-byte boundary for PCS compliance. + if (!sp.Is(StackPointer())) { + Bic(sp, StackPointer(), 0xf); + } + + // Actually call printf. This part needs special handling for the simulator, + // since the system printf function will use a different instruction set and + // the procedure-call standard will not be compatible. +#ifdef USE_SIMULATOR + { InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize); + hlt(kPrintfOpcode); + dc32(pcs[0].type()); + } +#else + Mov(Tmp0(), reinterpret_cast(printf)); + Blr(Tmp0()); +#endif +} + + +void MacroAssembler::Printf(const char * format, + const CPURegister& arg0, + const CPURegister& arg1, + const CPURegister& arg2, + const CPURegister& arg3) { + // Preserve all caller-saved registers as well as NZCV. + // If sp is the stack pointer, PushCPURegList asserts that the size of each + // list is a multiple of 16 bytes. + PushCPURegList(kCallerSaved); + PushCPURegList(kCallerSavedFP); + // Use Tmp0() as a scratch register. It is not accepted by Printf so it will + // never overlap an argument register. + Mrs(Tmp0(), NZCV); + Push(Tmp0(), xzr); + + PrintfNoPreserve(format, arg0, arg1, arg2, arg3); + + Pop(xzr, Tmp0()); + Msr(NZCV, Tmp0()); + PopCPURegList(kCallerSavedFP); + PopCPURegList(kCallerSaved); +} + +void MacroAssembler::Trace(TraceParameters parameters, TraceCommand command) { + ASSERT(allow_macro_instructions_); + +#ifdef USE_SIMULATOR + // The arguments to the trace pseudo instruction need to be contiguous in + // memory, so make sure we don't try to emit a literal pool. + InstructionAccurateScope scope(this, kTraceLength / kInstructionSize); + + Label start; + bind(&start); + + // Refer to instructions-a64.h for a description of the marker and its + // arguments. + hlt(kTraceOpcode); + + ASSERT(SizeOfCodeGeneratedSince(&start) == kTraceParamsOffset); + dc32(parameters); + + ASSERT(SizeOfCodeGeneratedSince(&start) == kTraceCommandOffset); + dc32(command); +#else + // Emit nothing on real hardware. + USE(parameters); + USE(command); +#endif +} + + +void MacroAssembler::Log(TraceParameters parameters) { + ASSERT(allow_macro_instructions_); + +#ifdef USE_SIMULATOR + // The arguments to the log pseudo instruction need to be contiguous in + // memory, so make sure we don't try to emit a literal pool. + InstructionAccurateScope scope(this, kLogLength / kInstructionSize); + + Label start; + bind(&start); + + // Refer to instructions-a64.h for a description of the marker and its + // arguments. + hlt(kLogOpcode); + + ASSERT(SizeOfCodeGeneratedSince(&start) == kLogParamsOffset); + dc32(parameters); +#else + // Emit nothing on real hardware. + USE(parameters); +#endif +} + + +void MacroAssembler::EnableInstrumentation() { + ASSERT(!isprint(InstrumentStateEnable)); + InstructionAccurateScope scope(this, 1); + movn(xzr, InstrumentStateEnable); +} + + +void MacroAssembler::DisableInstrumentation() { + ASSERT(!isprint(InstrumentStateDisable)); + InstructionAccurateScope scope(this, 1); + movn(xzr, InstrumentStateDisable); +} + + +void MacroAssembler::AnnotateInstrumentation(const char* marker_name) { + ASSERT(strlen(marker_name) == 2); + + // We allow only printable characters in the marker names. Unprintable + // characters are reserved for controlling features of the instrumentation. + ASSERT(isprint(marker_name[0]) && isprint(marker_name[1])); + + InstructionAccurateScope scope(this, 1); + movn(xzr, (marker_name[1] << 8) | marker_name[0]); +} + +} // namespace vixl diff --git a/disas/libvixl/src/a64/macro-assembler-a64.h b/disas/libvixl/src/a64/macro-assembler-a64.h new file mode 100644 index 0000000..f266063 --- /dev/null +++ b/disas/libvixl/src/a64/macro-assembler-a64.h @@ -0,0 +1,1175 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_MACRO_ASSEMBLER_A64_H_ +#define VIXL_A64_MACRO_ASSEMBLER_A64_H_ + +#include "globals.h" +#include "a64/assembler-a64.h" +#include "a64/debugger-a64.h" + + +#define LS_MACRO_LIST(V) \ + V(Ldrb, Register&, rt, LDRB_w) \ + V(Strb, Register&, rt, STRB_w) \ + V(Ldrsb, Register&, rt, rt.Is64Bits() ? LDRSB_x : LDRSB_w) \ + V(Ldrh, Register&, rt, LDRH_w) \ + V(Strh, Register&, rt, STRH_w) \ + V(Ldrsh, Register&, rt, rt.Is64Bits() ? LDRSH_x : LDRSH_w) \ + V(Ldr, CPURegister&, rt, LoadOpFor(rt)) \ + V(Str, CPURegister&, rt, StoreOpFor(rt)) \ + V(Ldrsw, Register&, rt, LDRSW_x) + +namespace vixl { + +class MacroAssembler : public Assembler { + public: + MacroAssembler(byte * buffer, unsigned buffer_size) + : Assembler(buffer, buffer_size), +#ifdef DEBUG + allow_macro_instructions_(true), +#endif + sp_(sp), tmp0_(ip0), tmp1_(ip1), fptmp0_(d31) {} + + // Logical macros. + void And(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void Bic(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void Orr(const Register& rd, + const Register& rn, + const Operand& operand); + void Orn(const Register& rd, + const Register& rn, + const Operand& operand); + void Eor(const Register& rd, + const Register& rn, + const Operand& operand); + void Eon(const Register& rd, + const Register& rn, + const Operand& operand); + void Tst(const Register& rn, const Operand& operand); + void LogicalMacro(const Register& rd, + const Register& rn, + const Operand& operand, + LogicalOp op); + + // Add and sub macros. + void Add(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void Sub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void Cmn(const Register& rn, const Operand& operand); + void Cmp(const Register& rn, const Operand& operand); + void Neg(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void AddSubMacro(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op); + + // Add/sub with carry macros. + void Adc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void Sbc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void Ngc(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + void AddSubWithCarryMacro(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op); + + // Move macros. + void Mov(const Register& rd, uint64_t imm); + void Mov(const Register& rd, const Operand& operand); + void Mvn(const Register& rd, uint64_t imm) { + Mov(rd, ~imm); + }; + void Mvn(const Register& rd, const Operand& operand); + bool IsImmMovn(uint64_t imm, unsigned reg_size); + bool IsImmMovz(uint64_t imm, unsigned reg_size); + + // Conditional compare macros. + void Ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + void Ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + void ConditionalCompareMacro(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op); + + // Load/store macros. +#define DECLARE_FUNCTION(FN, REGTYPE, REG, OP) \ + void FN(const REGTYPE REG, const MemOperand& addr); + LS_MACRO_LIST(DECLARE_FUNCTION) +#undef DECLARE_FUNCTION + + void LoadStoreMacro(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op); + + // Push or pop up to 4 registers of the same width to or from the stack, + // using the current stack pointer as set by SetStackPointer. + // + // If an argument register is 'NoReg', all further arguments are also assumed + // to be 'NoReg', and are thus not pushed or popped. + // + // Arguments are ordered such that "Push(a, b);" is functionally equivalent + // to "Push(a); Push(b);". + // + // It is valid to push the same register more than once, and there is no + // restriction on the order in which registers are specified. + // + // It is not valid to pop into the same register more than once in one + // operation, not even into the zero register. + // + // If the current stack pointer (as set by SetStackPointer) is sp, then it + // must be aligned to 16 bytes on entry and the total size of the specified + // registers must also be a multiple of 16 bytes. + // + // Even if the current stack pointer is not the system stack pointer (sp), + // Push (and derived methods) will still modify the system stack pointer in + // order to comply with ABI rules about accessing memory below the system + // stack pointer. + // + // Other than the registers passed into Pop, the stack pointer and (possibly) + // the system stack pointer, these methods do not modify any other registers. + // Scratch registers such as Tmp0() and Tmp1() are preserved. + void Push(const CPURegister& src0, const CPURegister& src1 = NoReg, + const CPURegister& src2 = NoReg, const CPURegister& src3 = NoReg); + void Pop(const CPURegister& dst0, const CPURegister& dst1 = NoReg, + const CPURegister& dst2 = NoReg, const CPURegister& dst3 = NoReg); + + // Alternative forms of Push and Pop, taking a RegList or CPURegList that + // specifies the registers that are to be pushed or popped. Higher-numbered + // registers are associated with higher memory addresses (as in the A32 push + // and pop instructions). + // + // (Push|Pop)SizeRegList allow you to specify the register size as a + // parameter. Only kXRegSize, kWRegSize, kDRegSize and kSRegSize are + // supported. + // + // Otherwise, (Push|Pop)(CPU|X|W|D|S)RegList is preferred. + void PushCPURegList(CPURegList registers); + void PopCPURegList(CPURegList registers); + + void PushSizeRegList(RegList registers, unsigned reg_size, + CPURegister::RegisterType type = CPURegister::kRegister) { + PushCPURegList(CPURegList(type, reg_size, registers)); + } + void PopSizeRegList(RegList registers, unsigned reg_size, + CPURegister::RegisterType type = CPURegister::kRegister) { + PopCPURegList(CPURegList(type, reg_size, registers)); + } + void PushXRegList(RegList regs) { + PushSizeRegList(regs, kXRegSize); + } + void PopXRegList(RegList regs) { + PopSizeRegList(regs, kXRegSize); + } + void PushWRegList(RegList regs) { + PushSizeRegList(regs, kWRegSize); + } + void PopWRegList(RegList regs) { + PopSizeRegList(regs, kWRegSize); + } + inline void PushDRegList(RegList regs) { + PushSizeRegList(regs, kDRegSize, CPURegister::kFPRegister); + } + inline void PopDRegList(RegList regs) { + PopSizeRegList(regs, kDRegSize, CPURegister::kFPRegister); + } + inline void PushSRegList(RegList regs) { + PushSizeRegList(regs, kSRegSize, CPURegister::kFPRegister); + } + inline void PopSRegList(RegList regs) { + PopSizeRegList(regs, kSRegSize, CPURegister::kFPRegister); + } + + // Push the specified register 'count' times. + void PushMultipleTimes(int count, Register src); + + // Poke 'src' onto the stack. The offset is in bytes. + // + // If the current stack pointer (as set by SetStackPointer) is sp, then sp + // must be aligned to 16 bytes. + void Poke(const Register& src, const Operand& offset); + + // Peek at a value on the stack, and put it in 'dst'. The offset is in bytes. + // + // If the current stack pointer (as set by SetStackPointer) is sp, then sp + // must be aligned to 16 bytes. + void Peek(const Register& dst, const Operand& offset); + + // Claim or drop stack space without actually accessing memory. + // + // If the current stack pointer (as set by SetStackPointer) is sp, then it + // must be aligned to 16 bytes and the size claimed or dropped must be a + // multiple of 16 bytes. + void Claim(const Operand& size); + void Drop(const Operand& size); + + // Preserve the callee-saved registers (as defined by AAPCS64). + // + // Higher-numbered registers are pushed before lower-numbered registers, and + // thus get higher addresses. + // Floating-point registers are pushed before general-purpose registers, and + // thus get higher addresses. + // + // This method must not be called unless StackPointer() is sp, and it is + // aligned to 16 bytes. + void PushCalleeSavedRegisters(); + + // Restore the callee-saved registers (as defined by AAPCS64). + // + // Higher-numbered registers are popped after lower-numbered registers, and + // thus come from higher addresses. + // Floating-point registers are popped after general-purpose registers, and + // thus come from higher addresses. + // + // This method must not be called unless StackPointer() is sp, and it is + // aligned to 16 bytes. + void PopCalleeSavedRegisters(); + + // Remaining instructions are simple pass-through calls to the assembler. + void Adr(const Register& rd, Label* label) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + adr(rd, label); + } + void Asr(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + asr(rd, rn, shift); + } + void Asr(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + asrv(rd, rn, rm); + } + void B(Label* label) { + b(label); + } + void B(Label* label, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT((cond != al) && (cond != nv)); + b(label, cond); + } + void Bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + bfi(rd, rn, lsb, width); + } + void Bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + bfxil(rd, rn, lsb, width); + } + void Bind(Label* label) { + ASSERT(allow_macro_instructions_); + bind(label); + } + void Bl(Label* label) { + ASSERT(allow_macro_instructions_); + bl(label); + } + void Blr(const Register& xn) { + ASSERT(allow_macro_instructions_); + ASSERT(!xn.IsZero()); + blr(xn); + } + void Br(const Register& xn) { + ASSERT(allow_macro_instructions_); + ASSERT(!xn.IsZero()); + br(xn); + } + void Brk(int code = 0) { + ASSERT(allow_macro_instructions_); + brk(code); + } + void Cbnz(const Register& rt, Label* label) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + cbnz(rt, label); + } + void Cbz(const Register& rt, Label* label) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + cbz(rt, label); + } + void Cinc(const Register& rd, const Register& rn, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + cinc(rd, rn, cond); + } + void Cinv(const Register& rd, const Register& rn, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + cinv(rd, rn, cond); + } + void Cls(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + cls(rd, rn); + } + void Clz(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + clz(rd, rn); + } + void Cneg(const Register& rd, const Register& rn, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + cneg(rd, rn, cond); + } + void Csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT((cond != al) && (cond != nv)); + csel(rd, rn, rm, cond); + } + void Cset(const Register& rd, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + cset(rd, cond); + } + void Csetm(const Register& rd, Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + csetm(rd, cond); + } + void Csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT((cond != al) && (cond != nv)); + csinc(rd, rn, rm, cond); + } + void Csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT((cond != al) && (cond != nv)); + csinv(rd, rn, rm, cond); + } + void Csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT((cond != al) && (cond != nv)); + csneg(rd, rn, rm, cond); + } + void Extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + extr(rd, rn, rm, lsb); + } + void Fabs(const FPRegister& fd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + fabs(fd, fn); + } + void Fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fadd(fd, fn, fm); + } + void Fccmp(const FPRegister& fn, + const FPRegister& fm, + StatusFlags nzcv, + Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT((cond != al) && (cond != nv)); + fccmp(fn, fm, nzcv, cond); + } + void Fcmp(const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fcmp(fn, fm); + } + void Fcmp(const FPRegister& fn, double value) { + ASSERT(allow_macro_instructions_); + if (value != 0.0) { + FPRegister tmp = AppropriateTempFor(fn); + Fmov(tmp, value); + fcmp(fn, tmp); + } else { + fcmp(fn, value); + } + } + void Fcsel(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + Condition cond) { + ASSERT(allow_macro_instructions_); + ASSERT((cond != al) && (cond != nv)); + fcsel(fd, fn, fm, cond); + } + void Fcvt(const FPRegister& fd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + fcvt(fd, fn); + } + void Fcvtms(const Register& rd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fcvtms(rd, fn); + } + void Fcvtmu(const Register& rd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fcvtmu(rd, fn); + } + void Fcvtns(const Register& rd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fcvtns(rd, fn); + } + void Fcvtnu(const Register& rd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fcvtnu(rd, fn); + } + void Fcvtzs(const Register& rd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fcvtzs(rd, fn); + } + void Fcvtzu(const Register& rd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fcvtzu(rd, fn); + } + void Fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fdiv(fd, fn, fm); + } + void Fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fmax(fd, fn, fm); + } + void Fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fmin(fd, fn, fm); + } + void Fmov(FPRegister fd, FPRegister fn) { + ASSERT(allow_macro_instructions_); + // Only emit an instruction if fd and fn are different, and they are both D + // registers. fmov(s0, s0) is not a no-op because it clears the top word of + // d0. Technically, fmov(d0, d0) is not a no-op either because it clears + // the top of q0, but FPRegister does not currently support Q registers. + if (!fd.Is(fn) || !fd.Is64Bits()) { + fmov(fd, fn); + } + } + void Fmov(FPRegister fd, Register rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rn.IsZero()); + fmov(fd, rn); + } + void Fmov(FPRegister fd, double imm) { + ASSERT(allow_macro_instructions_); + fmov(fd, imm); + } + void Fmov(Register rd, FPRegister fn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + fmov(rd, fn); + } + void Fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fmul(fd, fn, fm); + } + void Fmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa) { + ASSERT(allow_macro_instructions_); + fmsub(fd, fn, fm, fa); + } + void Fneg(const FPRegister& fd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + fneg(fd, fn); + } + void Frintn(const FPRegister& fd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + frintn(fd, fn); + } + void Frintz(const FPRegister& fd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + frintz(fd, fn); + } + void Fsqrt(const FPRegister& fd, const FPRegister& fn) { + ASSERT(allow_macro_instructions_); + fsqrt(fd, fn); + } + void Fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { + ASSERT(allow_macro_instructions_); + fsub(fd, fn, fm); + } + void Hint(SystemHint code) { + ASSERT(allow_macro_instructions_); + hint(code); + } + void Hlt(int code) { + ASSERT(allow_macro_instructions_); + hlt(code); + } + void Ldnp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& src) { + ASSERT(allow_macro_instructions_); + ldnp(rt, rt2, src); + } + void Ldp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& src) { + ASSERT(allow_macro_instructions_); + ldp(rt, rt2, src); + } + void Ldpsw(const Register& rt, const Register& rt2, const MemOperand& src) { + ASSERT(allow_macro_instructions_); + ldpsw(rt, rt2, src); + } + void Ldr(const FPRegister& ft, double imm) { + ASSERT(allow_macro_instructions_); + ldr(ft, imm); + } + void Ldr(const Register& rt, uint64_t imm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + ldr(rt, imm); + } + void Lsl(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + lsl(rd, rn, shift); + } + void Lsl(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + lslv(rd, rn, rm); + } + void Lsr(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + lsr(rd, rn, shift); + } + void Lsr(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + lsrv(rd, rn, rm); + } + void Madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT(!ra.IsZero()); + madd(rd, rn, rm, ra); + } + void Mneg(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + mneg(rd, rn, rm); + } + void Mov(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + mov(rd, rn); + } + void Mrs(const Register& rt, SystemRegister sysreg) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + mrs(rt, sysreg); + } + void Msr(SystemRegister sysreg, const Register& rt) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + msr(sysreg, rt); + } + void Msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT(!ra.IsZero()); + msub(rd, rn, rm, ra); + } + void Mul(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + mul(rd, rn, rm); + } + void Nop() { + ASSERT(allow_macro_instructions_); + nop(); + } + void Rbit(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + rbit(rd, rn); + } + void Ret(const Register& xn = lr) { + ASSERT(allow_macro_instructions_); + ASSERT(!xn.IsZero()); + ret(xn); + } + void Rev(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + rev(rd, rn); + } + void Rev16(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + rev16(rd, rn); + } + void Rev32(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + rev32(rd, rn); + } + void Ror(const Register& rd, const Register& rs, unsigned shift) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rs.IsZero()); + ror(rd, rs, shift); + } + void Ror(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + rorv(rd, rn, rm); + } + void Sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + sbfiz(rd, rn, lsb, width); + } + void Sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + sbfx(rd, rn, lsb, width); + } + void Scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0) { + ASSERT(allow_macro_instructions_); + ASSERT(!rn.IsZero()); + scvtf(fd, rn, fbits); + } + void Sdiv(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + sdiv(rd, rn, rm); + } + void Smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT(!ra.IsZero()); + smaddl(rd, rn, rm, ra); + } + void Smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT(!ra.IsZero()); + smsubl(rd, rn, rm, ra); + } + void Smull(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + smull(rd, rn, rm); + } + void Smulh(const Register& xd, const Register& xn, const Register& xm) { + ASSERT(allow_macro_instructions_); + ASSERT(!xd.IsZero()); + ASSERT(!xn.IsZero()); + ASSERT(!xm.IsZero()); + smulh(xd, xn, xm); + } + void Stnp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& dst) { + ASSERT(allow_macro_instructions_); + stnp(rt, rt2, dst); + } + void Stp(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& dst) { + ASSERT(allow_macro_instructions_); + stp(rt, rt2, dst); + } + void Sxtb(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + sxtb(rd, rn); + } + void Sxth(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + sxth(rd, rn); + } + void Sxtw(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + sxtw(rd, rn); + } + void Tbnz(const Register& rt, unsigned bit_pos, Label* label) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + tbnz(rt, bit_pos, label); + } + void Tbz(const Register& rt, unsigned bit_pos, Label* label) { + ASSERT(allow_macro_instructions_); + ASSERT(!rt.IsZero()); + tbz(rt, bit_pos, label); + } + void Ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ubfiz(rd, rn, lsb, width); + } + void Ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ubfx(rd, rn, lsb, width); + } + void Ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0) { + ASSERT(allow_macro_instructions_); + ASSERT(!rn.IsZero()); + ucvtf(fd, rn, fbits); + } + void Udiv(const Register& rd, const Register& rn, const Register& rm) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + udiv(rd, rn, rm); + } + void Umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT(!ra.IsZero()); + umaddl(rd, rn, rm, ra); + } + void Umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + ASSERT(!rm.IsZero()); + ASSERT(!ra.IsZero()); + umsubl(rd, rn, rm, ra); + } + void Unreachable() { + ASSERT(allow_macro_instructions_); +#ifdef USE_SIMULATOR + hlt(kUnreachableOpcode); +#else + // Branch to 0 to generate a segfault. + // lr - kInstructionSize is the address of the offending instruction. + blr(xzr); +#endif + } + void Uxtb(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + uxtb(rd, rn); + } + void Uxth(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + uxth(rd, rn); + } + void Uxtw(const Register& rd, const Register& rn) { + ASSERT(allow_macro_instructions_); + ASSERT(!rd.IsZero()); + ASSERT(!rn.IsZero()); + uxtw(rd, rn); + } + + // Push the system stack pointer (sp) down to allow the same to be done to + // the current stack pointer (according to StackPointer()). This must be + // called _before_ accessing the memory. + // + // This is necessary when pushing or otherwise adding things to the stack, to + // satisfy the AAPCS64 constraint that the memory below the system stack + // pointer is not accessed. + // + // This method asserts that StackPointer() is not sp, since the call does + // not make sense in that context. + // + // TODO: This method can only accept values of 'space' that can be encoded in + // one instruction. Refer to the implementation for details. + void BumpSystemStackPointer(const Operand& space); + +#if DEBUG + void SetAllowMacroInstructions(bool value) { + allow_macro_instructions_ = value; + } + + bool AllowMacroInstructions() const { + return allow_macro_instructions_; + } +#endif + + // Set the current stack pointer, but don't generate any code. + // Note that this does not directly affect LastStackPointer(). + void SetStackPointer(const Register& stack_pointer) { + ASSERT(!AreAliased(stack_pointer, Tmp0(), Tmp1())); + sp_ = stack_pointer; + } + + // Return the current stack pointer, as set by SetStackPointer. + const Register& StackPointer() const { + return sp_; + } + + // Set the registers used internally by the MacroAssembler as scratch + // registers. These registers are used to implement behaviours which are not + // directly supported by A64, and where an intermediate result is required. + // + // Both tmp0 and tmp1 may be set to any X register except for xzr, sp, + // and StackPointer(). Also, they must not be the same register (though they + // may both be NoReg). + // + // It is valid to set either or both of these registers to NoReg if you don't + // want the MacroAssembler to use any scratch registers. In a debug build, the + // Assembler will assert that any registers it uses are valid. Be aware that + // this check is not present in release builds. If this is a problem, use the + // Assembler directly. + void SetScratchRegisters(const Register& tmp0, const Register& tmp1) { + ASSERT(!AreAliased(xzr, sp, tmp0, tmp1)); + ASSERT(!AreAliased(StackPointer(), tmp0, tmp1)); + tmp0_ = tmp0; + tmp1_ = tmp1; + } + + const Register& Tmp0() const { + return tmp0_; + } + + const Register& Tmp1() const { + return tmp1_; + } + + void SetFPScratchRegister(const FPRegister& fptmp0) { + fptmp0_ = fptmp0; + } + + const FPRegister& FPTmp0() const { + return fptmp0_; + } + + const Register AppropriateTempFor( + const Register& target, + const CPURegister& forbidden = NoCPUReg) const { + Register candidate = forbidden.Is(Tmp0()) ? Tmp1() : Tmp0(); + ASSERT(!candidate.Is(target)); + return Register(candidate.code(), target.size()); + } + + const FPRegister AppropriateTempFor( + const FPRegister& target, + const CPURegister& forbidden = NoCPUReg) const { + USE(forbidden); + FPRegister candidate = FPTmp0(); + ASSERT(!candidate.Is(forbidden)); + ASSERT(!candidate.Is(target)); + return FPRegister(candidate.code(), target.size()); + } + + // Like printf, but print at run-time from generated code. + // + // The caller must ensure that arguments for floating-point placeholders + // (such as %e, %f or %g) are FPRegisters, and that arguments for integer + // placeholders are Registers. + // + // A maximum of four arguments may be given to any single Printf call. The + // arguments must be of the same type, but they do not need to have the same + // size. + // + // The following registers cannot be printed: + // Tmp0(), Tmp1(), StackPointer(), sp. + // + // This function automatically preserves caller-saved registers so that + // calling code can use Printf at any point without having to worry about + // corruption. The preservation mechanism generates a lot of code. If this is + // a problem, preserve the important registers manually and then call + // PrintfNoPreserve. Callee-saved registers are not used by Printf, and are + // implicitly preserved. + // + // This function assumes (and asserts) that the current stack pointer is + // callee-saved, not caller-saved. This is most likely the case anyway, as a + // caller-saved stack pointer doesn't make a lot of sense. + void Printf(const char * format, + const CPURegister& arg0 = NoCPUReg, + const CPURegister& arg1 = NoCPUReg, + const CPURegister& arg2 = NoCPUReg, + const CPURegister& arg3 = NoCPUReg); + + // Like Printf, but don't preserve any caller-saved registers, not even 'lr'. + // + // The return code from the system printf call will be returned in x0. + void PrintfNoPreserve(const char * format, + const CPURegister& arg0 = NoCPUReg, + const CPURegister& arg1 = NoCPUReg, + const CPURegister& arg2 = NoCPUReg, + const CPURegister& arg3 = NoCPUReg); + + // Trace control when running the debug simulator. + // + // For example: + // + // __ Trace(LOG_REGS, TRACE_ENABLE); + // Will add registers to the trace if it wasn't already the case. + // + // __ Trace(LOG_DISASM, TRACE_DISABLE); + // Will stop logging disassembly. It has no effect if the disassembly wasn't + // already being logged. + void Trace(TraceParameters parameters, TraceCommand command); + + // Log the requested data independently of what is being traced. + // + // For example: + // + // __ Log(LOG_FLAGS) + // Will output the flags. + void Log(TraceParameters parameters); + + // Enable or disable instrumentation when an Instrument visitor is attached to + // the simulator. + void EnableInstrumentation(); + void DisableInstrumentation(); + + // Add a marker to the instrumentation data produced by an Instrument visitor. + // The name is a two character string that will be attached to the marker in + // the output data. + void AnnotateInstrumentation(const char* marker_name); + + private: + // The actual Push and Pop implementations. These don't generate any code + // other than that required for the push or pop. This allows + // (Push|Pop)CPURegList to bundle together setup code for a large block of + // registers. + // + // Note that size is per register, and is specified in bytes. + void PushHelper(int count, int size, + const CPURegister& src0, const CPURegister& src1, + const CPURegister& src2, const CPURegister& src3); + void PopHelper(int count, int size, + const CPURegister& dst0, const CPURegister& dst1, + const CPURegister& dst2, const CPURegister& dst3); + + // Perform necessary maintenance operations before a push or pop. + // + // Note that size is per register, and is specified in bytes. + void PrepareForPush(int count, int size); + void PrepareForPop(int count, int size); + +#if DEBUG + // Tell whether any of the macro instruction can be used. When false the + // MacroAssembler will assert if a method which can emit a variable number + // of instructions is called. + bool allow_macro_instructions_; +#endif + + // The register to use as a stack pointer for stack operations. + Register sp_; + + // Scratch registers used internally by the MacroAssembler. + Register tmp0_; + Register tmp1_; + FPRegister fptmp0_; +}; + + +// Use this scope when you need a one-to-one mapping between methods and +// instructions. This scope prevents the MacroAssembler from being called and +// literal pools from being emitted. It also asserts the number of instructions +// emitted is what you specified when creating the scope. +class InstructionAccurateScope { + public: + explicit InstructionAccurateScope(MacroAssembler* masm) + : masm_(masm), size_(0) { + masm_->BlockLiteralPool(); +#ifdef DEBUG + old_allow_macro_instructions_ = masm_->AllowMacroInstructions(); + masm_->SetAllowMacroInstructions(false); +#endif + } + + InstructionAccurateScope(MacroAssembler* masm, int count) + : masm_(masm), size_(count * kInstructionSize) { + masm_->BlockLiteralPool(); +#ifdef DEBUG + masm_->bind(&start_); + old_allow_macro_instructions_ = masm_->AllowMacroInstructions(); + masm_->SetAllowMacroInstructions(false); +#endif + } + + ~InstructionAccurateScope() { + masm_->ReleaseLiteralPool(); +#ifdef DEBUG + if (start_.IsBound()) { + ASSERT(masm_->SizeOfCodeGeneratedSince(&start_) == size_); + } + masm_->SetAllowMacroInstructions(old_allow_macro_instructions_); +#endif + } + + private: + MacroAssembler* masm_; + uint64_t size_; +#ifdef DEBUG + Label start_; + bool old_allow_macro_instructions_; +#endif +}; + + +} // namespace vixl + +#endif // VIXL_A64_MACRO_ASSEMBLER_A64_H_ diff --git a/disas/libvixl/src/a64/simulator-a64.cc b/disas/libvixl/src/a64/simulator-a64.cc new file mode 100644 index 0000000..f08e0ed --- /dev/null +++ b/disas/libvixl/src/a64/simulator-a64.cc @@ -0,0 +1,2077 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include +#include "a64/simulator-a64.h" + +namespace vixl { + +const Instruction* Simulator::kEndOfSimAddress = NULL; + +void SimSystemRegister::SetBits(int msb, int lsb, uint32_t bits) { + int width = msb - lsb + 1; + ASSERT(is_uintn(width, bits) || is_intn(width, bits)); + + bits <<= lsb; + uint32_t mask = ((1 << width) - 1) << lsb; + ASSERT((mask & write_ignore_mask_) == 0); + + value_ = (value_ & ~mask) | (bits & mask); +} + + +SimSystemRegister SimSystemRegister::DefaultValueFor(SystemRegister id) { + switch (id) { + case NZCV: + return SimSystemRegister(0x00000000, NZCVWriteIgnoreMask); + case FPCR: + return SimSystemRegister(0x00000000, FPCRWriteIgnoreMask); + default: + UNREACHABLE(); + return SimSystemRegister(); + } +} + + +Simulator::Simulator(Decoder* decoder, FILE* stream) { + // Ensure shift operations act as the simulator expects. + ASSERT((static_cast(-1) >> 1) == -1); + ASSERT((static_cast(-1) >> 1) == 0x7FFFFFFF); + + // Setup the decoder. + decoder_ = decoder; + decoder_->AppendVisitor(this); + + ResetState(); + + // Allocate and setup the simulator stack. + stack_ = reinterpret_cast(malloc(stack_size_)); + stack_limit_ = stack_ + stack_protection_size_; + byte* tos = stack_ + stack_size_ - stack_protection_size_; + // The stack pointer must be 16 bytes aligned. + set_sp(reinterpret_cast(tos) & ~0xfUL); + + stream_ = stream; + print_disasm_ = new PrintDisassembler(stream_); + coloured_trace_ = false; + disasm_trace_ = false; + + // Set the sample period to 10, as the VIXL examples and tests are short. + instrumentation_ = new Instrument("vixl_stats.csv", 10); +} + + +void Simulator::ResetState() { + // Reset the system registers. + nzcv_ = SimSystemRegister::DefaultValueFor(NZCV); + fpcr_ = SimSystemRegister::DefaultValueFor(FPCR); + + // Reset registers to 0. + pc_ = NULL; + pc_modified_ = false; + for (unsigned i = 0; i < kNumberOfRegisters; i++) { + set_xreg(i, 0xbadbeef); + } + for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { + // Set FP registers to a value that is NaN in both 32-bit and 64-bit FP. + set_dreg_bits(i, 0x7ff000007f800001UL); + } + // Returning to address 0 exits the Simulator. + set_lr(reinterpret_cast(kEndOfSimAddress)); +} + + +Simulator::~Simulator() { + free(stack_); + // The decoder may outlive the simulator. + decoder_->RemoveVisitor(print_disasm_); + delete print_disasm_; + + decoder_->RemoveVisitor(instrumentation_); + delete instrumentation_; +} + + +void Simulator::Run() { + while (pc_ != kEndOfSimAddress) { + ExecuteInstruction(); + } +} + + +void Simulator::RunFrom(Instruction* first) { + pc_ = first; + pc_modified_ = false; + Run(); +} + + +const char* Simulator::xreg_names[] = { +"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", +"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", +"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", +"x24", "x25", "x26", "x27", "x28", "x29", "lr", "xzr", "sp"}; + + +const char* Simulator::wreg_names[] = { +"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", +"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", +"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", +"w24", "w25", "w26", "w27", "w28", "w29", "w30", "wzr", "wsp"}; + +const char* Simulator::sreg_names[] = { +"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"}; + +const char* Simulator::dreg_names[] = { +"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"}; + +const char* Simulator::vreg_names[] = { +"v0", "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"}; + + + +const char* Simulator::WRegNameForCode(unsigned code, Reg31Mode mode) { + ASSERT(code < kNumberOfRegisters); + // If the code represents the stack pointer, index the name after zr. + if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) { + code = kZeroRegCode + 1; + } + return wreg_names[code]; +} + + +const char* Simulator::XRegNameForCode(unsigned code, Reg31Mode mode) { + ASSERT(code < kNumberOfRegisters); + // If the code represents the stack pointer, index the name after zr. + if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) { + code = kZeroRegCode + 1; + } + return xreg_names[code]; +} + + +const char* Simulator::SRegNameForCode(unsigned code) { + ASSERT(code < kNumberOfFPRegisters); + return sreg_names[code]; +} + + +const char* Simulator::DRegNameForCode(unsigned code) { + ASSERT(code < kNumberOfFPRegisters); + return dreg_names[code]; +} + + +const char* Simulator::VRegNameForCode(unsigned code) { + ASSERT(code < kNumberOfFPRegisters); + return vreg_names[code]; +} + + +// Helpers --------------------------------------------------------------------- +int64_t Simulator::AddWithCarry(unsigned reg_size, + bool set_flags, + int64_t src1, + int64_t src2, + int64_t carry_in) { + ASSERT((carry_in == 0) || (carry_in == 1)); + ASSERT((reg_size == kXRegSize) || (reg_size == kWRegSize)); + + uint64_t u1, u2; + int64_t result; + int64_t signed_sum = src1 + src2 + carry_in; + + uint32_t N, Z, C, V; + + if (reg_size == kWRegSize) { + u1 = static_cast(src1) & kWRegMask; + u2 = static_cast(src2) & kWRegMask; + + result = signed_sum & kWRegMask; + // Compute the C flag by comparing the sum to the max unsigned integer. + C = ((kWMaxUInt - u1) < (u2 + carry_in)) || + ((kWMaxUInt - u1 - carry_in) < u2); + // Overflow iff the sign bit is the same for the two inputs and different + // for the result. + int64_t s_src1 = src1 << (kXRegSize - kWRegSize); + int64_t s_src2 = src2 << (kXRegSize - kWRegSize); + int64_t s_result = result << (kXRegSize - kWRegSize); + V = ((s_src1 ^ s_src2) >= 0) && ((s_src1 ^ s_result) < 0); + + } else { + u1 = static_cast(src1); + u2 = static_cast(src2); + + result = signed_sum; + // Compute the C flag by comparing the sum to the max unsigned integer. + C = ((kXMaxUInt - u1) < (u2 + carry_in)) || + ((kXMaxUInt - u1 - carry_in) < u2); + // Overflow iff the sign bit is the same for the two inputs and different + // for the result. + V = ((src1 ^ src2) >= 0) && ((src1 ^ result) < 0); + } + + N = CalcNFlag(result, reg_size); + Z = CalcZFlag(result); + + if (set_flags) { + nzcv().SetN(N); + nzcv().SetZ(Z); + nzcv().SetC(C); + nzcv().SetV(V); + } + return result; +} + + +int64_t Simulator::ShiftOperand(unsigned reg_size, + int64_t value, + Shift shift_type, + unsigned amount) { + if (amount == 0) { + return value; + } + int64_t mask = reg_size == kXRegSize ? kXRegMask : kWRegMask; + switch (shift_type) { + case LSL: + return (value << amount) & mask; + case LSR: + return static_cast(value) >> amount; + case ASR: { + // Shift used to restore the sign. + unsigned s_shift = kXRegSize - reg_size; + // Value with its sign restored. + int64_t s_value = (value << s_shift) >> s_shift; + return (s_value >> amount) & mask; + } + case ROR: { + if (reg_size == kWRegSize) { + value &= kWRegMask; + } + return (static_cast(value) >> amount) | + ((value & ((1L << amount) - 1L)) << (reg_size - amount)); + } + default: + UNIMPLEMENTED(); + return 0; + } +} + + +int64_t Simulator::ExtendValue(unsigned reg_size, + int64_t value, + Extend extend_type, + unsigned left_shift) { + switch (extend_type) { + case UXTB: + value &= kByteMask; + break; + case UXTH: + value &= kHalfWordMask; + break; + case UXTW: + value &= kWordMask; + break; + case SXTB: + value = (value << 56) >> 56; + break; + case SXTH: + value = (value << 48) >> 48; + break; + case SXTW: + value = (value << 32) >> 32; + break; + case UXTX: + case SXTX: + break; + default: + UNREACHABLE(); + } + int64_t mask = (reg_size == kXRegSize) ? kXRegMask : kWRegMask; + return (value << left_shift) & mask; +} + + +void Simulator::FPCompare(double val0, double val1) { + AssertSupportedFPCR(); + + // TODO: This assumes that the C++ implementation handles comparisons in the + // way that we expect (as per AssertSupportedFPCR()). + if ((isnan(val0) != 0) || (isnan(val1) != 0)) { + nzcv().SetRawValue(FPUnorderedFlag); + } else if (val0 < val1) { + nzcv().SetRawValue(FPLessThanFlag); + } else if (val0 > val1) { + nzcv().SetRawValue(FPGreaterThanFlag); + } else if (val0 == val1) { + nzcv().SetRawValue(FPEqualFlag); + } else { + UNREACHABLE(); + } +} + + +void Simulator::PrintSystemRegisters(bool print_all) { + static bool first_run = true; + + // Define some colour codes to use for the register dump. + // TODO: Find a more elegant way of defining these. + char const * const clr_normal = (coloured_trace_) ? ("\033[m") : (""); + char const * const clr_flag_name = (coloured_trace_) ? ("\033[1;30m") : (""); + char const * const clr_flag_value = (coloured_trace_) ? ("\033[1;37m") : (""); + + static SimSystemRegister last_nzcv; + if (print_all || first_run || (last_nzcv.RawValue() != nzcv().RawValue())) { + fprintf(stream_, "# %sFLAGS: %sN:%d Z:%d C:%d V:%d%s\n", + clr_flag_name, + clr_flag_value, + N(), Z(), C(), V(), + clr_normal); + } + last_nzcv = nzcv(); + + static SimSystemRegister last_fpcr; + if (print_all || first_run || (last_fpcr.RawValue() != fpcr().RawValue())) { + static const char * rmode[] = { + "0b00 (Round to Nearest)", + "0b01 (Round towards Plus Infinity)", + "0b10 (Round towards Minus Infinity)", + "0b11 (Round towards Zero)" + }; + ASSERT(fpcr().RMode() <= (sizeof(rmode) / sizeof(rmode[0]))); + fprintf(stream_, "# %sFPCR: %sAHP:%d DN:%d FZ:%d RMode:%s%s\n", + clr_flag_name, + clr_flag_value, + fpcr().AHP(), fpcr().DN(), fpcr().FZ(), rmode[fpcr().RMode()], + clr_normal); + } + last_fpcr = fpcr(); + + first_run = false; +} + + +void Simulator::PrintRegisters(bool print_all_regs) { + static bool first_run = true; + static int64_t last_regs[kNumberOfRegisters]; + + // Define some colour codes to use for the register dump. + // TODO: Find a more elegant way of defining these. + char const * const clr_normal = (coloured_trace_) ? ("\033[m") : (""); + char const * const clr_reg_name = (coloured_trace_) ? ("\033[1;34m") : (""); + char const * const clr_reg_value = (coloured_trace_) ? ("\033[1;36m") : (""); + + for (unsigned i = 0; i < kNumberOfRegisters; i++) { + if (print_all_regs || first_run || (last_regs[i] != registers_[i].x)) { + fprintf(stream_, + "# %s%4s:%s 0x%016" PRIx64 "%s\n", + clr_reg_name, + XRegNameForCode(i, Reg31IsStackPointer), + clr_reg_value, + registers_[i].x, + clr_normal); + } + // Cache the new register value so the next run can detect any changes. + last_regs[i] = registers_[i].x; + } + first_run = false; +} + + +void Simulator::PrintFPRegisters(bool print_all_regs) { + static bool first_run = true; + static uint64_t last_regs[kNumberOfFPRegisters]; + + // Define some colour codes to use for the register dump. + // TODO: Find a more elegant way of defining these. + char const * const clr_normal = (coloured_trace_) ? ("\033[m") : (""); + char const * const clr_reg_name = (coloured_trace_) ? ("\033[1;33m") : (""); + char const * const clr_reg_value = (coloured_trace_) ? ("\033[1;35m") : (""); + + // Print as many rows of registers as necessary, keeping each individual + // register in the same column each time (to make it easy to visually scan + // for changes). + for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { + if (print_all_regs || first_run || + (last_regs[i] != double_to_rawbits(fpregisters_[i].d))) { + fprintf(stream_, + "# %s %4s:%s 0x%016" PRIx64 "%s (%s%s:%s %g%s %s:%s %g%s)\n", + clr_reg_name, + VRegNameForCode(i), + clr_reg_value, + double_to_rawbits(fpregisters_[i].d), + clr_normal, + clr_reg_name, + DRegNameForCode(i), + clr_reg_value, + fpregisters_[i].d, + clr_reg_name, + SRegNameForCode(i), + clr_reg_value, + fpregisters_[i].s, + clr_normal); + } + // Cache the new register value so the next run can detect any changes. + last_regs[i] = double_to_rawbits(fpregisters_[i].d); + } + first_run = false; +} + + +void Simulator::PrintProcessorState() { + PrintSystemRegisters(); + PrintRegisters(); + PrintFPRegisters(); +} + + +// Visitors--------------------------------------------------------------------- + +void Simulator::VisitUnimplemented(Instruction* instr) { + printf("Unimplemented instruction at 0x%p: 0x%08" PRIx32 "\n", + reinterpret_cast(instr), instr->InstructionBits()); + UNIMPLEMENTED(); +} + + +void Simulator::VisitUnallocated(Instruction* instr) { + printf("Unallocated instruction at 0x%p: 0x%08" PRIx32 "\n", + reinterpret_cast(instr), instr->InstructionBits()); + UNIMPLEMENTED(); +} + + +void Simulator::VisitPCRelAddressing(Instruction* instr) { + switch (instr->Mask(PCRelAddressingMask)) { + case ADR: + set_reg(kXRegSize, + instr->Rd(), + reinterpret_cast(instr->ImmPCOffsetTarget())); + break; + case ADRP: // Not implemented in the assembler. + UNIMPLEMENTED(); + break; + default: + UNREACHABLE(); + } +} + + +void Simulator::VisitUnconditionalBranch(Instruction* instr) { + switch (instr->Mask(UnconditionalBranchMask)) { + case BL: + set_lr(reinterpret_cast(instr->NextInstruction())); + // Fall through. + case B: + set_pc(instr->ImmPCOffsetTarget()); + break; + default: UNREACHABLE(); + } +} + + +void Simulator::VisitConditionalBranch(Instruction* instr) { + ASSERT(instr->Mask(ConditionalBranchMask) == B_cond); + if (ConditionPassed(static_cast(instr->ConditionBranch()))) { + set_pc(instr->ImmPCOffsetTarget()); + } +} + + +void Simulator::VisitUnconditionalBranchToRegister(Instruction* instr) { + Instruction* target = Instruction::Cast(xreg(instr->Rn())); + + switch (instr->Mask(UnconditionalBranchToRegisterMask)) { + case BLR: + set_lr(reinterpret_cast(instr->NextInstruction())); + // Fall through. + case BR: + case RET: set_pc(target); break; + default: UNREACHABLE(); + } +} + + +void Simulator::VisitTestBranch(Instruction* instr) { + unsigned bit_pos = (instr->ImmTestBranchBit5() << 5) | + instr->ImmTestBranchBit40(); + bool take_branch = ((xreg(instr->Rt()) & (1UL << bit_pos)) == 0); + switch (instr->Mask(TestBranchMask)) { + case TBZ: break; + case TBNZ: take_branch = !take_branch; break; + default: UNIMPLEMENTED(); + } + if (take_branch) { + set_pc(instr->ImmPCOffsetTarget()); + } +} + + +void Simulator::VisitCompareBranch(Instruction* instr) { + unsigned rt = instr->Rt(); + bool take_branch = false; + switch (instr->Mask(CompareBranchMask)) { + case CBZ_w: take_branch = (wreg(rt) == 0); break; + case CBZ_x: take_branch = (xreg(rt) == 0); break; + case CBNZ_w: take_branch = (wreg(rt) != 0); break; + case CBNZ_x: take_branch = (xreg(rt) != 0); break; + default: UNIMPLEMENTED(); + } + if (take_branch) { + set_pc(instr->ImmPCOffsetTarget()); + } +} + + +void Simulator::AddSubHelper(Instruction* instr, int64_t op2) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + bool set_flags = instr->FlagsUpdate(); + int64_t new_val = 0; + Instr operation = instr->Mask(AddSubOpMask); + + switch (operation) { + case ADD: + case ADDS: { + new_val = AddWithCarry(reg_size, + set_flags, + reg(reg_size, instr->Rn(), instr->RnMode()), + op2); + break; + } + case SUB: + case SUBS: { + new_val = AddWithCarry(reg_size, + set_flags, + reg(reg_size, instr->Rn(), instr->RnMode()), + ~op2, + 1); + break; + } + default: UNREACHABLE(); + } + + set_reg(reg_size, instr->Rd(), new_val, instr->RdMode()); +} + + +void Simulator::VisitAddSubShifted(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t op2 = ShiftOperand(reg_size, + reg(reg_size, instr->Rm()), + static_cast(instr->ShiftDP()), + instr->ImmDPShift()); + AddSubHelper(instr, op2); +} + + +void Simulator::VisitAddSubImmediate(Instruction* instr) { + int64_t op2 = instr->ImmAddSub() << ((instr->ShiftAddSub() == 1) ? 12 : 0); + AddSubHelper(instr, op2); +} + + +void Simulator::VisitAddSubExtended(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t op2 = ExtendValue(reg_size, + reg(reg_size, instr->Rm()), + static_cast(instr->ExtendMode()), + instr->ImmExtendShift()); + AddSubHelper(instr, op2); +} + + +void Simulator::VisitAddSubWithCarry(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t op2 = reg(reg_size, instr->Rm()); + int64_t new_val; + + if ((instr->Mask(AddSubOpMask) == SUB) || instr->Mask(AddSubOpMask) == SUBS) { + op2 = ~op2; + } + + new_val = AddWithCarry(reg_size, + instr->FlagsUpdate(), + reg(reg_size, instr->Rn()), + op2, + C()); + + set_reg(reg_size, instr->Rd(), new_val); +} + + +void Simulator::VisitLogicalShifted(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + Shift shift_type = static_cast(instr->ShiftDP()); + unsigned shift_amount = instr->ImmDPShift(); + int64_t op2 = ShiftOperand(reg_size, reg(reg_size, instr->Rm()), shift_type, + shift_amount); + if (instr->Mask(NOT) == NOT) { + op2 = ~op2; + } + LogicalHelper(instr, op2); +} + + +void Simulator::VisitLogicalImmediate(Instruction* instr) { + LogicalHelper(instr, instr->ImmLogical()); +} + + +void Simulator::LogicalHelper(Instruction* instr, int64_t op2) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t op1 = reg(reg_size, instr->Rn()); + int64_t result = 0; + bool update_flags = false; + + // Switch on the logical operation, stripping out the NOT bit, as it has a + // different meaning for logical immediate instructions. + switch (instr->Mask(LogicalOpMask & ~NOT)) { + case ANDS: update_flags = true; // Fall through. + case AND: result = op1 & op2; break; + case ORR: result = op1 | op2; break; + case EOR: result = op1 ^ op2; break; + default: + UNIMPLEMENTED(); + } + + if (update_flags) { + nzcv().SetN(CalcNFlag(result, reg_size)); + nzcv().SetZ(CalcZFlag(result)); + nzcv().SetC(0); + nzcv().SetV(0); + } + + set_reg(reg_size, instr->Rd(), result, instr->RdMode()); +} + + +void Simulator::VisitConditionalCompareRegister(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + ConditionalCompareHelper(instr, reg(reg_size, instr->Rm())); +} + + +void Simulator::VisitConditionalCompareImmediate(Instruction* instr) { + ConditionalCompareHelper(instr, instr->ImmCondCmp()); +} + + +void Simulator::ConditionalCompareHelper(Instruction* instr, int64_t op2) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t op1 = reg(reg_size, instr->Rn()); + + if (ConditionPassed(static_cast(instr->Condition()))) { + // If the condition passes, set the status flags to the result of comparing + // the operands. + if (instr->Mask(ConditionalCompareMask) == CCMP) { + AddWithCarry(reg_size, true, op1, ~op2, 1); + } else { + ASSERT(instr->Mask(ConditionalCompareMask) == CCMN); + AddWithCarry(reg_size, true, op1, op2, 0); + } + } else { + // If the condition fails, set the status flags to the nzcv immediate. + nzcv().SetFlags(instr->Nzcv()); + } +} + + +void Simulator::VisitLoadStoreUnsignedOffset(Instruction* instr) { + int offset = instr->ImmLSUnsigned() << instr->SizeLS(); + LoadStoreHelper(instr, offset, Offset); +} + + +void Simulator::VisitLoadStoreUnscaledOffset(Instruction* instr) { + LoadStoreHelper(instr, instr->ImmLS(), Offset); +} + + +void Simulator::VisitLoadStorePreIndex(Instruction* instr) { + LoadStoreHelper(instr, instr->ImmLS(), PreIndex); +} + + +void Simulator::VisitLoadStorePostIndex(Instruction* instr) { + LoadStoreHelper(instr, instr->ImmLS(), PostIndex); +} + + +void Simulator::VisitLoadStoreRegisterOffset(Instruction* instr) { + Extend ext = static_cast(instr->ExtendMode()); + ASSERT((ext == UXTW) || (ext == UXTX) || (ext == SXTW) || (ext == SXTX)); + unsigned shift_amount = instr->ImmShiftLS() * instr->SizeLS(); + + int64_t offset = ExtendValue(kXRegSize, xreg(instr->Rm()), ext, + shift_amount); + LoadStoreHelper(instr, offset, Offset); +} + + +void Simulator::LoadStoreHelper(Instruction* instr, + int64_t offset, + AddrMode addrmode) { + unsigned srcdst = instr->Rt(); + uint8_t* address = AddressModeHelper(instr->Rn(), offset, addrmode); + int num_bytes = 1 << instr->SizeLS(); + + LoadStoreOp op = static_cast(instr->Mask(LoadStoreOpMask)); + switch (op) { + case LDRB_w: + case LDRH_w: + case LDR_w: + case LDR_x: set_xreg(srcdst, MemoryRead(address, num_bytes)); break; + case STRB_w: + case STRH_w: + case STR_w: + case STR_x: MemoryWrite(address, xreg(srcdst), num_bytes); break; + case LDRSB_w: { + set_wreg(srcdst, ExtendValue(kWRegSize, MemoryRead8(address), SXTB)); + break; + } + case LDRSB_x: { + set_xreg(srcdst, ExtendValue(kXRegSize, MemoryRead8(address), SXTB)); + break; + } + case LDRSH_w: { + set_wreg(srcdst, ExtendValue(kWRegSize, MemoryRead16(address), SXTH)); + break; + } + case LDRSH_x: { + set_xreg(srcdst, ExtendValue(kXRegSize, MemoryRead16(address), SXTH)); + break; + } + case LDRSW_x: { + set_xreg(srcdst, ExtendValue(kXRegSize, MemoryRead32(address), SXTW)); + break; + } + case LDR_s: set_sreg(srcdst, MemoryReadFP32(address)); break; + case LDR_d: set_dreg(srcdst, MemoryReadFP64(address)); break; + case STR_s: MemoryWriteFP32(address, sreg(srcdst)); break; + case STR_d: MemoryWriteFP64(address, dreg(srcdst)); break; + default: UNIMPLEMENTED(); + } +} + + +void Simulator::VisitLoadStorePairOffset(Instruction* instr) { + LoadStorePairHelper(instr, Offset); +} + + +void Simulator::VisitLoadStorePairPreIndex(Instruction* instr) { + LoadStorePairHelper(instr, PreIndex); +} + + +void Simulator::VisitLoadStorePairPostIndex(Instruction* instr) { + LoadStorePairHelper(instr, PostIndex); +} + + +void Simulator::VisitLoadStorePairNonTemporal(Instruction* instr) { + LoadStorePairHelper(instr, Offset); +} + + +void Simulator::LoadStorePairHelper(Instruction* instr, + AddrMode addrmode) { + unsigned rt = instr->Rt(); + unsigned rt2 = instr->Rt2(); + int offset = instr->ImmLSPair() << instr->SizeLSPair(); + uint8_t* address = AddressModeHelper(instr->Rn(), offset, addrmode); + + LoadStorePairOp op = + static_cast(instr->Mask(LoadStorePairMask)); + + // 'rt' and 'rt2' can only be aliased for stores. + ASSERT(((op & LoadStorePairLBit) == 0) || (rt != rt2)); + + switch (op) { + case LDP_w: { + set_wreg(rt, MemoryRead32(address)); + set_wreg(rt2, MemoryRead32(address + kWRegSizeInBytes)); + break; + } + case LDP_s: { + set_sreg(rt, MemoryReadFP32(address)); + set_sreg(rt2, MemoryReadFP32(address + kSRegSizeInBytes)); + break; + } + case LDP_x: { + set_xreg(rt, MemoryRead64(address)); + set_xreg(rt2, MemoryRead64(address + kXRegSizeInBytes)); + break; + } + case LDP_d: { + set_dreg(rt, MemoryReadFP64(address)); + set_dreg(rt2, MemoryReadFP64(address + kDRegSizeInBytes)); + break; + } + case LDPSW_x: { + set_xreg(rt, ExtendValue(kXRegSize, MemoryRead32(address), SXTW)); + set_xreg(rt2, ExtendValue(kXRegSize, + MemoryRead32(address + kWRegSizeInBytes), SXTW)); + break; + } + case STP_w: { + MemoryWrite32(address, wreg(rt)); + MemoryWrite32(address + kWRegSizeInBytes, wreg(rt2)); + break; + } + case STP_s: { + MemoryWriteFP32(address, sreg(rt)); + MemoryWriteFP32(address + kSRegSizeInBytes, sreg(rt2)); + break; + } + case STP_x: { + MemoryWrite64(address, xreg(rt)); + MemoryWrite64(address + kXRegSizeInBytes, xreg(rt2)); + break; + } + case STP_d: { + MemoryWriteFP64(address, dreg(rt)); + MemoryWriteFP64(address + kDRegSizeInBytes, dreg(rt2)); + break; + } + default: UNREACHABLE(); + } +} + + +void Simulator::VisitLoadLiteral(Instruction* instr) { + uint8_t* address = instr->LiteralAddress(); + unsigned rt = instr->Rt(); + + switch (instr->Mask(LoadLiteralMask)) { + case LDR_w_lit: set_wreg(rt, MemoryRead32(address)); break; + case LDR_x_lit: set_xreg(rt, MemoryRead64(address)); break; + case LDR_s_lit: set_sreg(rt, MemoryReadFP32(address)); break; + case LDR_d_lit: set_dreg(rt, MemoryReadFP64(address)); break; + default: UNREACHABLE(); + } +} + + +uint8_t* Simulator::AddressModeHelper(unsigned addr_reg, + int64_t offset, + AddrMode addrmode) { + uint64_t address = xreg(addr_reg, Reg31IsStackPointer); + ASSERT((sizeof(uintptr_t) == kXRegSizeInBytes) || + (address < 0x100000000UL)); + if ((addr_reg == 31) && ((address % 16) != 0)) { + // When the base register is SP the stack pointer is required to be + // quadword aligned prior to the address calculation and write-backs. + // Misalignment will cause a stack alignment fault. + ALIGNMENT_EXCEPTION(); + } + if ((addrmode == PreIndex) || (addrmode == PostIndex)) { + ASSERT(offset != 0); + set_xreg(addr_reg, address + offset, Reg31IsStackPointer); + } + + if ((addrmode == Offset) || (addrmode == PreIndex)) { + address += offset; + } + + return reinterpret_cast(address); +} + + +uint64_t Simulator::MemoryRead(const uint8_t* address, unsigned num_bytes) { + ASSERT(address != NULL); + ASSERT((num_bytes > 0) && (num_bytes <= sizeof(uint64_t))); + uint64_t read = 0; + memcpy(&read, address, num_bytes); + return read; +} + + +uint8_t Simulator::MemoryRead8(uint8_t* address) { + return MemoryRead(address, sizeof(uint8_t)); +} + + +uint16_t Simulator::MemoryRead16(uint8_t* address) { + return MemoryRead(address, sizeof(uint16_t)); +} + + +uint32_t Simulator::MemoryRead32(uint8_t* address) { + return MemoryRead(address, sizeof(uint32_t)); +} + + +float Simulator::MemoryReadFP32(uint8_t* address) { + return rawbits_to_float(MemoryRead32(address)); +} + + +uint64_t Simulator::MemoryRead64(uint8_t* address) { + return MemoryRead(address, sizeof(uint64_t)); +} + + +double Simulator::MemoryReadFP64(uint8_t* address) { + return rawbits_to_double(MemoryRead64(address)); +} + + +void Simulator::MemoryWrite(uint8_t* address, + uint64_t value, + unsigned num_bytes) { + ASSERT(address != NULL); + ASSERT((num_bytes > 0) && (num_bytes <= sizeof(uint64_t))); + memcpy(address, &value, num_bytes); +} + + +void Simulator::MemoryWrite32(uint8_t* address, uint32_t value) { + MemoryWrite(address, value, sizeof(uint32_t)); +} + + +void Simulator::MemoryWriteFP32(uint8_t* address, float value) { + MemoryWrite32(address, float_to_rawbits(value)); +} + + +void Simulator::MemoryWrite64(uint8_t* address, uint64_t value) { + MemoryWrite(address, value, sizeof(uint64_t)); +} + + +void Simulator::MemoryWriteFP64(uint8_t* address, double value) { + MemoryWrite64(address, double_to_rawbits(value)); +} + + +void Simulator::VisitMoveWideImmediate(Instruction* instr) { + MoveWideImmediateOp mov_op = + static_cast(instr->Mask(MoveWideImmediateMask)); + int64_t new_xn_val = 0; + + bool is_64_bits = instr->SixtyFourBits() == 1; + // Shift is limited for W operations. + ASSERT(is_64_bits || (instr->ShiftMoveWide() < 2)); + + // Get the shifted immediate. + int64_t shift = instr->ShiftMoveWide() * 16; + int64_t shifted_imm16 = instr->ImmMoveWide() << shift; + + // Compute the new value. + switch (mov_op) { + case MOVN_w: + case MOVN_x: { + new_xn_val = ~shifted_imm16; + if (!is_64_bits) new_xn_val &= kWRegMask; + break; + } + case MOVK_w: + case MOVK_x: { + unsigned reg_code = instr->Rd(); + int64_t prev_xn_val = is_64_bits ? xreg(reg_code) + : wreg(reg_code); + new_xn_val = (prev_xn_val & ~(0xffffL << shift)) | shifted_imm16; + break; + } + case MOVZ_w: + case MOVZ_x: { + new_xn_val = shifted_imm16; + break; + } + default: + UNREACHABLE(); + } + + // Update the destination register. + set_xreg(instr->Rd(), new_xn_val); +} + + +void Simulator::VisitConditionalSelect(Instruction* instr) { + uint64_t new_val = xreg(instr->Rn()); + + if (ConditionFailed(static_cast(instr->Condition()))) { + new_val = xreg(instr->Rm()); + switch (instr->Mask(ConditionalSelectMask)) { + case CSEL_w: + case CSEL_x: break; + case CSINC_w: + case CSINC_x: new_val++; break; + case CSINV_w: + case CSINV_x: new_val = ~new_val; break; + case CSNEG_w: + case CSNEG_x: new_val = -new_val; break; + default: UNIMPLEMENTED(); + } + } + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + set_reg(reg_size, instr->Rd(), new_val); +} + + +void Simulator::VisitDataProcessing1Source(Instruction* instr) { + unsigned dst = instr->Rd(); + unsigned src = instr->Rn(); + + switch (instr->Mask(DataProcessing1SourceMask)) { + case RBIT_w: set_wreg(dst, ReverseBits(wreg(src), kWRegSize)); break; + case RBIT_x: set_xreg(dst, ReverseBits(xreg(src), kXRegSize)); break; + case REV16_w: set_wreg(dst, ReverseBytes(wreg(src), Reverse16)); break; + case REV16_x: set_xreg(dst, ReverseBytes(xreg(src), Reverse16)); break; + case REV_w: set_wreg(dst, ReverseBytes(wreg(src), Reverse32)); break; + case REV32_x: set_xreg(dst, ReverseBytes(xreg(src), Reverse32)); break; + case REV_x: set_xreg(dst, ReverseBytes(xreg(src), Reverse64)); break; + case CLZ_w: set_wreg(dst, CountLeadingZeros(wreg(src), kWRegSize)); break; + case CLZ_x: set_xreg(dst, CountLeadingZeros(xreg(src), kXRegSize)); break; + case CLS_w: { + set_wreg(dst, CountLeadingSignBits(wreg(src), kWRegSize)); + break; + } + case CLS_x: { + set_xreg(dst, CountLeadingSignBits(xreg(src), kXRegSize)); + break; + } + default: UNIMPLEMENTED(); + } +} + + +uint64_t Simulator::ReverseBits(uint64_t value, unsigned num_bits) { + ASSERT((num_bits == kWRegSize) || (num_bits == kXRegSize)); + uint64_t result = 0; + for (unsigned i = 0; i < num_bits; i++) { + result = (result << 1) | (value & 1); + value >>= 1; + } + return result; +} + + +uint64_t Simulator::ReverseBytes(uint64_t value, ReverseByteMode mode) { + // Split the 64-bit value into an 8-bit array, where b[0] is the least + // significant byte, and b[7] is the most significant. + uint8_t bytes[8]; + uint64_t mask = 0xff00000000000000UL; + for (int i = 7; i >= 0; i--) { + bytes[i] = (value & mask) >> (i * 8); + mask >>= 8; + } + + // Permutation tables for REV instructions. + // permute_table[Reverse16] is used by REV16_x, REV16_w + // permute_table[Reverse32] is used by REV32_x, REV_w + // permute_table[Reverse64] is used by REV_x + ASSERT((Reverse16 == 0) && (Reverse32 == 1) && (Reverse64 == 2)); + static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1}, + {4, 5, 6, 7, 0, 1, 2, 3}, + {0, 1, 2, 3, 4, 5, 6, 7} }; + uint64_t result = 0; + for (int i = 0; i < 8; i++) { + result <<= 8; + result |= bytes[permute_table[mode][i]]; + } + return result; +} + + +void Simulator::VisitDataProcessing2Source(Instruction* instr) { + Shift shift_op = NO_SHIFT; + int64_t result = 0; + switch (instr->Mask(DataProcessing2SourceMask)) { + case SDIV_w: result = wreg(instr->Rn()) / wreg(instr->Rm()); break; + case SDIV_x: result = xreg(instr->Rn()) / xreg(instr->Rm()); break; + case UDIV_w: { + uint32_t rn = static_cast(wreg(instr->Rn())); + uint32_t rm = static_cast(wreg(instr->Rm())); + result = rn / rm; + break; + } + case UDIV_x: { + uint64_t rn = static_cast(xreg(instr->Rn())); + uint64_t rm = static_cast(xreg(instr->Rm())); + result = rn / rm; + break; + } + case LSLV_w: + case LSLV_x: shift_op = LSL; break; + case LSRV_w: + case LSRV_x: shift_op = LSR; break; + case ASRV_w: + case ASRV_x: shift_op = ASR; break; + case RORV_w: + case RORV_x: shift_op = ROR; break; + default: UNIMPLEMENTED(); + } + + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + if (shift_op != NO_SHIFT) { + // Shift distance encoded in the least-significant five/six bits of the + // register. + int mask = (instr->SixtyFourBits() == 1) ? 0x3f : 0x1f; + unsigned shift = wreg(instr->Rm()) & mask; + result = ShiftOperand(reg_size, reg(reg_size, instr->Rn()), shift_op, + shift); + } + set_reg(reg_size, instr->Rd(), result); +} + + +// The algorithm used is adapted from the one described in section 8.2 of +// Hacker's Delight, by Henry S. Warren, Jr. +// It assumes that a right shift on a signed integer is an arithmetic shift. +static int64_t MultiplyHighSigned(int64_t u, int64_t v) { + uint64_t u0, v0, w0; + int64_t u1, v1, w1, w2, t; + + u0 = u & 0xffffffffL; + u1 = u >> 32; + v0 = v & 0xffffffffL; + v1 = v >> 32; + + w0 = u0 * v0; + t = u1 * v0 + (w0 >> 32); + w1 = t & 0xffffffffL; + w2 = t >> 32; + w1 = u0 * v1 + w1; + + return u1 * v1 + w2 + (w1 >> 32); +} + + +void Simulator::VisitDataProcessing3Source(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + + int64_t result = 0; + uint64_t rn; + uint64_t rm; + switch (instr->Mask(DataProcessing3SourceMask)) { + case MADD_w: + case MADD_x: + result = xreg(instr->Ra()) + (xreg(instr->Rn()) * xreg(instr->Rm())); + break; + case MSUB_w: + case MSUB_x: + result = xreg(instr->Ra()) - (xreg(instr->Rn()) * xreg(instr->Rm())); + break; + case SMADDL_x: + result = xreg(instr->Ra()) + (wreg(instr->Rn()) * wreg(instr->Rm())); + break; + case SMSUBL_x: + result = xreg(instr->Ra()) - (wreg(instr->Rn()) * wreg(instr->Rm())); + break; + case UMADDL_x: + rn = static_cast(wreg(instr->Rn())); + rm = static_cast(wreg(instr->Rm())); + result = xreg(instr->Ra()) + (rn * rm); + break; + case UMSUBL_x: + rn = static_cast(wreg(instr->Rn())); + rm = static_cast(wreg(instr->Rm())); + result = xreg(instr->Ra()) - (rn * rm); + break; + case SMULH_x: + result = MultiplyHighSigned(xreg(instr->Rn()), xreg(instr->Rm())); + break; + default: UNIMPLEMENTED(); + } + set_reg(reg_size, instr->Rd(), result); +} + + +void Simulator::VisitBitfield(Instruction* instr) { + unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; + int64_t reg_mask = instr->SixtyFourBits() ? kXRegMask : kWRegMask; + int64_t R = instr->ImmR(); + int64_t S = instr->ImmS(); + int64_t diff = S - R; + int64_t mask; + if (diff >= 0) { + mask = diff < reg_size - 1 ? (1L << (diff + 1)) - 1 + : reg_mask; + } else { + mask = ((1L << (S + 1)) - 1); + mask = (static_cast(mask) >> R) | (mask << (reg_size - R)); + diff += reg_size; + } + + // inzero indicates if the extracted bitfield is inserted into the + // destination register value or in zero. + // If extend is true, extend the sign of the extracted bitfield. + bool inzero = false; + bool extend = false; + switch (instr->Mask(BitfieldMask)) { + case BFM_x: + case BFM_w: + break; + case SBFM_x: + case SBFM_w: + inzero = true; + extend = true; + break; + case UBFM_x: + case UBFM_w: + inzero = true; + break; + default: + UNIMPLEMENTED(); + } + + int64_t dst = inzero ? 0 : reg(reg_size, instr->Rd()); + int64_t src = reg(reg_size, instr->Rn()); + // Rotate source bitfield into place. + int64_t result = (static_cast(src) >> R) | (src << (reg_size - R)); + // Determine the sign extension. + int64_t topbits = ((1L << (reg_size - diff - 1)) - 1) << (diff + 1); + int64_t signbits = extend && ((src >> S) & 1) ? topbits : 0; + + // Merge sign extension, dest/zero and bitfield. + result = signbits | (result & mask) | (dst & ~mask); + + set_reg(reg_size, instr->Rd(), result); +} + + +void Simulator::VisitExtract(Instruction* instr) { + unsigned lsb = instr->ImmS(); + unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize + : kWRegSize; + set_reg(reg_size, + instr->Rd(), + (static_cast(reg(reg_size, instr->Rm())) >> lsb) | + (reg(reg_size, instr->Rn()) << (reg_size - lsb))); +} + + +void Simulator::VisitFPImmediate(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned dest = instr->Rd(); + switch (instr->Mask(FPImmediateMask)) { + case FMOV_s_imm: set_sreg(dest, instr->ImmFP32()); break; + case FMOV_d_imm: set_dreg(dest, instr->ImmFP64()); break; + default: UNREACHABLE(); + } +} + + +void Simulator::VisitFPIntegerConvert(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned dst = instr->Rd(); + unsigned src = instr->Rn(); + + FPRounding round = RMode(); + + switch (instr->Mask(FPIntegerConvertMask)) { + case FCVTMS_ws: + set_wreg(dst, FPToInt32(sreg(src), FPNegativeInfinity)); + break; + case FCVTMS_xs: + set_xreg(dst, FPToInt64(sreg(src), FPNegativeInfinity)); + break; + case FCVTMS_wd: + set_wreg(dst, FPToInt32(dreg(src), FPNegativeInfinity)); + break; + case FCVTMS_xd: + set_xreg(dst, FPToInt64(dreg(src), FPNegativeInfinity)); + break; + case FCVTMU_ws: + set_wreg(dst, FPToUInt32(sreg(src), FPNegativeInfinity)); + break; + case FCVTMU_xs: + set_xreg(dst, FPToUInt64(sreg(src), FPNegativeInfinity)); + break; + case FCVTMU_wd: + set_wreg(dst, FPToUInt32(dreg(src), FPNegativeInfinity)); + break; + case FCVTMU_xd: + set_xreg(dst, FPToUInt64(dreg(src), FPNegativeInfinity)); + break; + case FCVTNS_ws: set_wreg(dst, FPToInt32(sreg(src), FPTieEven)); break; + case FCVTNS_xs: set_xreg(dst, FPToInt64(sreg(src), FPTieEven)); break; + case FCVTNS_wd: set_wreg(dst, FPToInt32(dreg(src), FPTieEven)); break; + case FCVTNS_xd: set_xreg(dst, FPToInt64(dreg(src), FPTieEven)); break; + case FCVTNU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPTieEven)); break; + case FCVTNU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPTieEven)); break; + case FCVTNU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPTieEven)); break; + case FCVTNU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPTieEven)); break; + case FCVTZS_ws: set_wreg(dst, FPToInt32(sreg(src), FPZero)); break; + case FCVTZS_xs: set_xreg(dst, FPToInt64(sreg(src), FPZero)); break; + case FCVTZS_wd: set_wreg(dst, FPToInt32(dreg(src), FPZero)); break; + case FCVTZS_xd: set_xreg(dst, FPToInt64(dreg(src), FPZero)); break; + case FCVTZU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPZero)); break; + case FCVTZU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPZero)); break; + case FCVTZU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPZero)); break; + case FCVTZU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPZero)); break; + case FMOV_ws: set_wreg(dst, sreg_bits(src)); break; + case FMOV_xd: set_xreg(dst, dreg_bits(src)); break; + case FMOV_sw: set_sreg_bits(dst, wreg(src)); break; + case FMOV_dx: set_dreg_bits(dst, xreg(src)); break; + + // A 32-bit input can be handled in the same way as a 64-bit input, since + // the sign- or zero-extension will not affect the conversion. + case SCVTF_dx: set_dreg(dst, FixedToDouble(xreg(src), 0, round)); break; + case SCVTF_dw: set_dreg(dst, FixedToDouble(wreg(src), 0, round)); break; + case UCVTF_dx: set_dreg(dst, UFixedToDouble(xreg(src), 0, round)); break; + case UCVTF_dw: { + set_dreg(dst, UFixedToDouble(static_cast(wreg(src)), 0, round)); + break; + } + case SCVTF_sx: set_sreg(dst, FixedToFloat(xreg(src), 0, round)); break; + case SCVTF_sw: set_sreg(dst, FixedToFloat(wreg(src), 0, round)); break; + case UCVTF_sx: set_sreg(dst, UFixedToFloat(xreg(src), 0, round)); break; + case UCVTF_sw: { + set_sreg(dst, UFixedToFloat(static_cast(wreg(src)), 0, round)); + break; + } + + default: UNREACHABLE(); + } +} + + +void Simulator::VisitFPFixedPointConvert(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned dst = instr->Rd(); + unsigned src = instr->Rn(); + int fbits = 64 - instr->FPScale(); + + FPRounding round = RMode(); + + switch (instr->Mask(FPFixedPointConvertMask)) { + // A 32-bit input can be handled in the same way as a 64-bit input, since + // the sign- or zero-extension will not affect the conversion. + case SCVTF_dx_fixed: + set_dreg(dst, FixedToDouble(xreg(src), fbits, round)); + break; + case SCVTF_dw_fixed: + set_dreg(dst, FixedToDouble(wreg(src), fbits, round)); + break; + case UCVTF_dx_fixed: + set_dreg(dst, UFixedToDouble(xreg(src), fbits, round)); + break; + case UCVTF_dw_fixed: { + set_dreg(dst, + UFixedToDouble(static_cast(wreg(src)), fbits, round)); + break; + } + case SCVTF_sx_fixed: + set_sreg(dst, FixedToFloat(xreg(src), fbits, round)); + break; + case SCVTF_sw_fixed: + set_sreg(dst, FixedToFloat(wreg(src), fbits, round)); + break; + case UCVTF_sx_fixed: + set_sreg(dst, UFixedToFloat(xreg(src), fbits, round)); + break; + case UCVTF_sw_fixed: { + set_sreg(dst, + UFixedToFloat(static_cast(wreg(src)), fbits, round)); + break; + } + default: UNREACHABLE(); + } +} + + +int32_t Simulator::FPToInt32(double value, FPRounding rmode) { + value = FPRoundInt(value, rmode); + if (value >= kWMaxInt) { + return kWMaxInt; + } else if (value < kWMinInt) { + return kWMinInt; + } + return isnan(value) ? 0 : static_cast(value); +} + + +int64_t Simulator::FPToInt64(double value, FPRounding rmode) { + value = FPRoundInt(value, rmode); + if (value >= kXMaxInt) { + return kXMaxInt; + } else if (value < kXMinInt) { + return kXMinInt; + } + return isnan(value) ? 0 : static_cast(value); +} + + +uint32_t Simulator::FPToUInt32(double value, FPRounding rmode) { + value = FPRoundInt(value, rmode); + if (value >= kWMaxUInt) { + return kWMaxUInt; + } else if (value < 0.0) { + return 0; + } + return isnan(value) ? 0 : static_cast(value); +} + + +uint64_t Simulator::FPToUInt64(double value, FPRounding rmode) { + value = FPRoundInt(value, rmode); + if (value >= kXMaxUInt) { + return kXMaxUInt; + } else if (value < 0.0) { + return 0; + } + return isnan(value) ? 0 : static_cast(value); +} + + +void Simulator::VisitFPCompare(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize; + double fn_val = fpreg(reg_size, instr->Rn()); + + switch (instr->Mask(FPCompareMask)) { + case FCMP_s: + case FCMP_d: FPCompare(fn_val, fpreg(reg_size, instr->Rm())); break; + case FCMP_s_zero: + case FCMP_d_zero: FPCompare(fn_val, 0.0); break; + default: UNIMPLEMENTED(); + } +} + + +void Simulator::VisitFPConditionalCompare(Instruction* instr) { + AssertSupportedFPCR(); + + switch (instr->Mask(FPConditionalCompareMask)) { + case FCCMP_s: + case FCCMP_d: { + if (ConditionPassed(static_cast(instr->Condition()))) { + // If the condition passes, set the status flags to the result of + // comparing the operands. + unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize; + FPCompare(fpreg(reg_size, instr->Rn()), fpreg(reg_size, instr->Rm())); + } else { + // If the condition fails, set the status flags to the nzcv immediate. + nzcv().SetFlags(instr->Nzcv()); + } + break; + } + default: UNIMPLEMENTED(); + } +} + + +void Simulator::VisitFPConditionalSelect(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize; + + double selected_val; + if (ConditionPassed(static_cast(instr->Condition()))) { + selected_val = fpreg(reg_size, instr->Rn()); + } else { + selected_val = fpreg(reg_size, instr->Rm()); + } + + switch (instr->Mask(FPConditionalSelectMask)) { + case FCSEL_s: + case FCSEL_d: set_fpreg(reg_size, instr->Rd(), selected_val); break; + default: UNIMPLEMENTED(); + } +} + + +void Simulator::VisitFPDataProcessing1Source(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned fd = instr->Rd(); + unsigned fn = instr->Rn(); + + switch (instr->Mask(FPDataProcessing1SourceMask)) { + case FMOV_s: set_sreg(fd, sreg(fn)); break; + case FMOV_d: set_dreg(fd, dreg(fn)); break; + case FABS_s: set_sreg(fd, fabs(sreg(fn))); break; + case FABS_d: set_dreg(fd, fabs(dreg(fn))); break; + case FNEG_s: set_sreg(fd, -sreg(fn)); break; + case FNEG_d: set_dreg(fd, -dreg(fn)); break; + case FSQRT_s: set_sreg(fd, sqrt(sreg(fn))); break; + case FSQRT_d: set_dreg(fd, sqrt(dreg(fn))); break; + case FRINTN_s: set_sreg(fd, FPRoundInt(sreg(fn), FPTieEven)); break; + case FRINTN_d: set_dreg(fd, FPRoundInt(dreg(fn), FPTieEven)); break; + case FRINTZ_s: set_sreg(fd, FPRoundInt(sreg(fn), FPZero)); break; + case FRINTZ_d: set_dreg(fd, FPRoundInt(dreg(fn), FPZero)); break; + case FCVT_ds: set_dreg(fd, FPToDouble(sreg(fn))); break; + case FCVT_sd: set_sreg(fd, FPToFloat(dreg(fn), FPTieEven)); break; + default: UNIMPLEMENTED(); + } +} + + +// Assemble the specified IEEE-754 components into the target type and apply +// appropriate rounding. +// sign: 0 = positive, 1 = negative +// exponent: Unbiased IEEE-754 exponent. +// mantissa: The mantissa of the input. The top bit (which is not encoded for +// normal IEEE-754 values) must not be omitted. This bit has the +// value 'pow(2, exponent)'. +// +// The input value is assumed to be a normalized value. That is, the input may +// not be infinity or NaN. If the source value is subnormal, it must be +// normalized before calling this function such that the highest set bit in the +// mantissa has the value 'pow(2, exponent)'. +// +// Callers should use FPRoundToFloat or FPRoundToDouble directly, rather than +// calling a templated FPRound. +template +static T FPRound(int64_t sign, int64_t exponent, uint64_t mantissa, + FPRounding round_mode) { + ASSERT((sign == 0) || (sign == 1)); + + // Only the FPTieEven rounding mode is implemented. + ASSERT(round_mode == FPTieEven); + USE(round_mode); + + // Rounding can promote subnormals to normals, and normals to infinities. For + // example, a double with exponent 127 (FLT_MAX_EXP) would appear to be + // encodable as a float, but rounding based on the low-order mantissa bits + // could make it overflow. With ties-to-even rounding, this value would become + // an infinity. + + // ---- Rounding Method ---- + // + // The exponent is irrelevant in the rounding operation, so we treat the + // lowest-order bit that will fit into the result ('onebit') as having + // the value '1'. Similarly, the highest-order bit that won't fit into + // the result ('halfbit') has the value '0.5'. The 'point' sits between + // 'onebit' and 'halfbit': + // + // These bits fit into the result. + // |---------------------| + // mantissa = 0bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + // || + // / | + // / halfbit + // onebit + // + // For subnormal outputs, the range of representable bits is smaller and + // the position of onebit and halfbit depends on the exponent of the + // input, but the method is otherwise similar. + // + // onebit(frac) + // | + // | halfbit(frac) halfbit(adjusted) + // | / / + // | | | + // 0b00.0 (exact) -> 0b00.0 (exact) -> 0b00 + // 0b00.0... -> 0b00.0... -> 0b00 + // 0b00.1 (exact) -> 0b00.0111..111 -> 0b00 + // 0b00.1... -> 0b00.1... -> 0b01 + // 0b01.0 (exact) -> 0b01.0 (exact) -> 0b01 + // 0b01.0... -> 0b01.0... -> 0b01 + // 0b01.1 (exact) -> 0b01.1 (exact) -> 0b10 + // 0b01.1... -> 0b01.1... -> 0b10 + // 0b10.0 (exact) -> 0b10.0 (exact) -> 0b10 + // 0b10.0... -> 0b10.0... -> 0b10 + // 0b10.1 (exact) -> 0b10.0111..111 -> 0b10 + // 0b10.1... -> 0b10.1... -> 0b11 + // 0b11.0 (exact) -> 0b11.0 (exact) -> 0b11 + // ... / | / | + // / | / | + // / | + // adjusted = frac - (halfbit(mantissa) & ~onebit(frac)); / | + // + // mantissa = (mantissa >> shift) + halfbit(adjusted); + + static const int mantissa_offset = 0; + static const int exponent_offset = mantissa_offset + mbits; + static const int sign_offset = exponent_offset + ebits; + ASSERT(sign_offset == (sizeof(T) * 8 - 1)); + + // Bail out early for zero inputs. + if (mantissa == 0) { + return sign << sign_offset; + } + + // If all bits in the exponent are set, the value is infinite or NaN. + // This is true for all binary IEEE-754 formats. + static const int infinite_exponent = (1 << ebits) - 1; + static const int max_normal_exponent = infinite_exponent - 1; + + // Apply the exponent bias to encode it for the result. Doing this early makes + // it easy to detect values that will be infinite or subnormal. + exponent += max_normal_exponent >> 1; + + if (exponent > max_normal_exponent) { + // Overflow: The input is too large for the result type to represent. The + // FPTieEven rounding mode handles overflows using infinities. + exponent = infinite_exponent; + mantissa = 0; + return (sign << sign_offset) | + (exponent << exponent_offset) | + (mantissa << mantissa_offset); + } + + // Calculate the shift required to move the top mantissa bit to the proper + // place in the destination type. + const int highest_significant_bit = 63 - CountLeadingZeros(mantissa, 64); + int shift = highest_significant_bit - mbits; + + if (exponent <= 0) { + // The output will be subnormal (before rounding). + + // For subnormal outputs, the shift must be adjusted by the exponent. The +1 + // is necessary because the exponent of a subnormal value (encoded as 0) is + // the same as the exponent of the smallest normal value (encoded as 1). + shift += -exponent + 1; + + // Handle inputs that would produce a zero output. + // + // Shifts higher than highest_significant_bit+1 will always produce a zero + // result. A shift of exactly highest_significant_bit+1 might produce a + // non-zero result after rounding. + if (shift > (highest_significant_bit + 1)) { + // The result will always be +/-0.0. + return sign << sign_offset; + } + + // Properly encode the exponent for a subnormal output. + exponent = 0; + } else { + // Clear the topmost mantissa bit, since this is not encoded in IEEE-754 + // normal values. + mantissa &= ~(1UL << highest_significant_bit); + } + + if (shift > 0) { + // We have to shift the mantissa to the right. Some precision is lost, so we + // need to apply rounding. + uint64_t onebit_mantissa = (mantissa >> (shift)) & 1; + uint64_t halfbit_mantissa = (mantissa >> (shift-1)) & 1; + uint64_t adjusted = mantissa - (halfbit_mantissa & ~onebit_mantissa); + T halfbit_adjusted = (adjusted >> (shift-1)) & 1; + + T result = (sign << sign_offset) | + (exponent << exponent_offset) | + ((mantissa >> shift) << mantissa_offset); + + // A very large mantissa can overflow during rounding. If this happens, the + // exponent should be incremented and the mantissa set to 1.0 (encoded as + // 0). Applying halfbit_adjusted after assembling the float has the nice + // side-effect that this case is handled for free. + // + // This also handles cases where a very large finite value overflows to + // infinity, or where a very large subnormal value overflows to become + // normal. + return result + halfbit_adjusted; + } else { + // We have to shift the mantissa to the left (or not at all). The input + // mantissa is exactly representable in the output mantissa, so apply no + // rounding correction. + return (sign << sign_offset) | + (exponent << exponent_offset) | + ((mantissa << -shift) << mantissa_offset); + } +} + + +// See FPRound for a description of this function. +static inline double FPRoundToDouble(int64_t sign, int64_t exponent, + uint64_t mantissa, FPRounding round_mode) { + int64_t bits = + FPRound(sign, + exponent, + mantissa, + round_mode); + return rawbits_to_double(bits); +} + + +// See FPRound for a description of this function. +static inline float FPRoundToFloat(int64_t sign, int64_t exponent, + uint64_t mantissa, FPRounding round_mode) { + int32_t bits = + FPRound(sign, + exponent, + mantissa, + round_mode); + return rawbits_to_float(bits); +} + + +double Simulator::FixedToDouble(int64_t src, int fbits, FPRounding round) { + if (src >= 0) { + return UFixedToDouble(src, fbits, round); + } else { + // This works for all negative values, including INT64_MIN. + return -UFixedToDouble(-src, fbits, round); + } +} + + +double Simulator::UFixedToDouble(uint64_t src, int fbits, FPRounding round) { + // An input of 0 is a special case because the result is effectively + // subnormal: The exponent is encoded as 0 and there is no implicit 1 bit. + if (src == 0) { + return 0.0; + } + + // Calculate the exponent. The highest significant bit will have the value + // 2^exponent. + const int highest_significant_bit = 63 - CountLeadingZeros(src, 64); + const int64_t exponent = highest_significant_bit - fbits; + + return FPRoundToDouble(0, exponent, src, round); +} + + +float Simulator::FixedToFloat(int64_t src, int fbits, FPRounding round) { + if (src >= 0) { + return UFixedToFloat(src, fbits, round); + } else { + // This works for all negative values, including INT64_MIN. + return -UFixedToFloat(-src, fbits, round); + } +} + + +float Simulator::UFixedToFloat(uint64_t src, int fbits, FPRounding round) { + // An input of 0 is a special case because the result is effectively + // subnormal: The exponent is encoded as 0 and there is no implicit 1 bit. + if (src == 0) { + return 0.0f; + } + + // Calculate the exponent. The highest significant bit will have the value + // 2^exponent. + const int highest_significant_bit = 63 - CountLeadingZeros(src, 64); + const int32_t exponent = highest_significant_bit - fbits; + + return FPRoundToFloat(0, exponent, src, round); +} + + +double Simulator::FPRoundInt(double value, FPRounding round_mode) { + if ((value == 0.0) || (value == kFP64PositiveInfinity) || + (value == kFP64NegativeInfinity) || isnan(value)) { + return value; + } + + double int_result = floor(value); + double error = value - int_result; + switch (round_mode) { + case FPTieEven: { + // If the error is greater than 0.5, or is equal to 0.5 and the integer + // result is odd, round up. + if ((error > 0.5) || + ((error == 0.5) && (fmod(int_result, 2) != 0))) { + int_result++; + } + break; + } + case FPZero: { + // If value>0 then we take floor(value) + // otherwise, ceil(value). + if (value < 0) { + int_result = ceil(value); + } + break; + } + case FPNegativeInfinity: { + // We always use floor(value). + break; + } + default: UNIMPLEMENTED(); + } + return int_result; +} + + +double Simulator::FPToDouble(float value) { + switch (fpclassify(value)) { + case FP_NAN: { + // Convert NaNs as the processor would, assuming that FPCR.DN (default + // NaN) is not set: + // - The sign is propagated. + // - The payload (mantissa) is transferred entirely, except that the top + // bit is forced to '1', making the result a quiet NaN. The unused + // (low-order) payload bits are set to 0. + uint32_t raw = float_to_rawbits(value); + + uint64_t sign = raw >> 31; + uint64_t exponent = (1 << 11) - 1; + uint64_t payload = unsigned_bitextract_64(21, 0, raw); + payload <<= (52 - 23); // The unused low-order bits should be 0. + payload |= (1L << 51); // Force a quiet NaN. + + return rawbits_to_double((sign << 63) | (exponent << 52) | payload); + } + + case FP_ZERO: + case FP_NORMAL: + case FP_SUBNORMAL: + case FP_INFINITE: { + // All other inputs are preserved in a standard cast, because every value + // representable using an IEEE-754 float is also representable using an + // IEEE-754 double. + return static_cast(value); + } + } + + UNREACHABLE(); + return static_cast(value); +} + + +float Simulator::FPToFloat(double value, FPRounding round_mode) { + // Only the FPTieEven rounding mode is implemented. + ASSERT(round_mode == FPTieEven); + USE(round_mode); + + switch (fpclassify(value)) { + case FP_NAN: { + // Convert NaNs as the processor would, assuming that FPCR.DN (default + // NaN) is not set: + // - The sign is propagated. + // - The payload (mantissa) is transferred as much as possible, except + // that the top bit is forced to '1', making the result a quiet NaN. + uint64_t raw = double_to_rawbits(value); + + uint32_t sign = raw >> 63; + uint32_t exponent = (1 << 8) - 1; + uint32_t payload = unsigned_bitextract_64(50, 52 - 23, raw); + payload |= (1 << 22); // Force a quiet NaN. + + return rawbits_to_float((sign << 31) | (exponent << 23) | payload); + } + + case FP_ZERO: + case FP_INFINITE: { + // In a C++ cast, any value representable in the target type will be + // unchanged. This is always the case for +/-0.0 and infinities. + return static_cast(value); + } + + case FP_NORMAL: + case FP_SUBNORMAL: { + // Convert double-to-float as the processor would, assuming that FPCR.FZ + // (flush-to-zero) is not set. + uint64_t raw = double_to_rawbits(value); + // Extract the IEEE-754 double components. + uint32_t sign = raw >> 63; + // Extract the exponent and remove the IEEE-754 encoding bias. + int32_t exponent = unsigned_bitextract_64(62, 52, raw) - 1023; + // Extract the mantissa and add the implicit '1' bit. + uint64_t mantissa = unsigned_bitextract_64(51, 0, raw); + if (fpclassify(value) == FP_NORMAL) { + mantissa |= (1UL << 52); + } + return FPRoundToFloat(sign, exponent, mantissa, round_mode); + } + } + + UNREACHABLE(); + return value; +} + + +void Simulator::VisitFPDataProcessing2Source(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned fd = instr->Rd(); + unsigned fn = instr->Rn(); + unsigned fm = instr->Rm(); + + switch (instr->Mask(FPDataProcessing2SourceMask)) { + case FADD_s: set_sreg(fd, sreg(fn) + sreg(fm)); break; + case FADD_d: set_dreg(fd, dreg(fn) + dreg(fm)); break; + case FSUB_s: set_sreg(fd, sreg(fn) - sreg(fm)); break; + case FSUB_d: set_dreg(fd, dreg(fn) - dreg(fm)); break; + case FMUL_s: set_sreg(fd, sreg(fn) * sreg(fm)); break; + case FMUL_d: set_dreg(fd, dreg(fn) * dreg(fm)); break; + case FDIV_s: set_sreg(fd, sreg(fn) / sreg(fm)); break; + case FDIV_d: set_dreg(fd, dreg(fn) / dreg(fm)); break; + case FMAX_s: set_sreg(fd, FPMax(sreg(fn), sreg(fm))); break; + case FMAX_d: set_dreg(fd, FPMax(dreg(fn), dreg(fm))); break; + case FMIN_s: set_sreg(fd, FPMin(sreg(fn), sreg(fm))); break; + case FMIN_d: set_dreg(fd, FPMin(dreg(fn), dreg(fm))); break; + default: UNIMPLEMENTED(); + } +} + + +void Simulator::VisitFPDataProcessing3Source(Instruction* instr) { + AssertSupportedFPCR(); + + unsigned fd = instr->Rd(); + unsigned fn = instr->Rn(); + unsigned fm = instr->Rm(); + unsigned fa = instr->Ra(); + + // Note: The FMSUB implementation here is not precisely the same as the + // instruction definition. In full implementation rounding of results would + // occur once at the end, here rounding will occur after the first multiply + // and then after the subsequent addition. A full implementation here would + // be possible but would require an effort isn't immediately justified given + // the small differences we expect to see in most cases. + + switch (instr->Mask(FPDataProcessing3SourceMask)) { + case FMSUB_s: set_sreg(fd, sreg(fa) + (-sreg(fn))*sreg(fm)); break; + case FMSUB_d: set_dreg(fd, dreg(fa) + (-dreg(fn))*dreg(fm)); break; + default: UNIMPLEMENTED(); + } +} + + +double Simulator::FPMax(double a, double b) { + if (isnan(a)) { + return a; + } else if (isnan(b)) { + return b; + } + + if ((a == 0.0) && (b == 0.0) && + (copysign(1.0, a) != copysign(1.0, b))) { + // a and b are zero, and the sign differs: return +0.0. + return 0.0; + } else { + return (a > b) ? a : b; + } +} + + +double Simulator::FPMin(double a, double b) { + if (isnan(a)) { + return a; + } else if (isnan(b)) { + return b; + } + + if ((a == 0.0) && (b == 0.0) && + (copysign(1.0, a) != copysign(1.0, b))) { + // a and b are zero, and the sign differs: return -0.0. + return -0.0; + } else { + return (a < b) ? a : b; + } +} + + +void Simulator::VisitSystem(Instruction* instr) { + // Some system instructions hijack their Op and Cp fields to represent a + // range of immediates instead of indicating a different instruction. This + // makes the decoding tricky. + if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { + switch (instr->Mask(SystemSysRegMask)) { + case MRS: { + switch (instr->ImmSystemRegister()) { + case NZCV: set_xreg(instr->Rt(), nzcv().RawValue()); break; + case FPCR: set_xreg(instr->Rt(), fpcr().RawValue()); break; + default: UNIMPLEMENTED(); + } + break; + } + case MSR: { + switch (instr->ImmSystemRegister()) { + case NZCV: nzcv().SetRawValue(xreg(instr->Rt())); break; + case FPCR: fpcr().SetRawValue(xreg(instr->Rt())); break; + default: UNIMPLEMENTED(); + } + break; + } + } + } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { + ASSERT(instr->Mask(SystemHintMask) == HINT); + switch (instr->ImmHint()) { + case NOP: break; + default: UNIMPLEMENTED(); + } + } else { + UNIMPLEMENTED(); + } +} + + +void Simulator::VisitException(Instruction* instr) { + switch (instr->Mask(ExceptionMask)) { + case BRK: HostBreakpoint(); break; + case HLT: + // The Printf pseudo instruction is so useful, we include it in the + // default simulator. + if (instr->ImmException() == kPrintfOpcode) { + DoPrintf(instr); + } else { + HostBreakpoint(); + } + break; + default: + UNIMPLEMENTED(); + } +} + + +void Simulator::DoPrintf(Instruction* instr) { + ASSERT((instr->Mask(ExceptionMask) == HLT) && + (instr->ImmException() == kPrintfOpcode)); + + // Read the argument encoded inline in the instruction stream. + uint32_t type; + ASSERT(sizeof(*instr) == 1); + memcpy(&type, instr + kPrintfTypeOffset, sizeof(type)); + + const char * format = reinterpret_cast(x0()); + ASSERT(format != NULL); + + // Pass all of the relevant PCS registers onto printf. It doesn't matter + // if we pass too many as the extra ones won't be read. + int result = 0; + if (type == CPURegister::kRegister) { + result = printf(format, x1(), x2(), x3(), x4(), x5(), x6(), x7()); + } else if (type == CPURegister::kFPRegister) { + result = printf(format, d0(), d1(), d2(), d3(), d4(), d5(), d6(), d7()); + } else { + ASSERT(type == CPURegister::kNoRegister); + result = printf("%s", format); + } + set_x0(result); + + // TODO: Clobber all caller-saved registers here, to ensure no assumptions + // are made about preserved state. + + // The printf parameters are inlined in the code, so skip them. + set_pc(instr->InstructionAtOffset(kPrintfLength)); + + // Set LR as if we'd just called a native printf function. + set_lr(reinterpret_cast(pc())); +} + +} // namespace vixl diff --git a/disas/libvixl/src/a64/simulator-a64.h b/disas/libvixl/src/a64/simulator-a64.h new file mode 100644 index 0000000..0c22f9e --- /dev/null +++ b/disas/libvixl/src/a64/simulator-a64.h @@ -0,0 +1,576 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_A64_SIMULATOR_A64_H_ +#define VIXL_A64_SIMULATOR_A64_H_ + +#include "globals.h" +#include "utils.h" +#include "a64/instructions-a64.h" +#include "a64/assembler-a64.h" +#include "a64/disasm-a64.h" +#include "a64/instrument-a64.h" + +namespace vixl { + +enum ReverseByteMode { + Reverse16 = 0, + Reverse32 = 1, + Reverse64 = 2 +}; + +// Printf. See debugger-a64.h for more informations on pseudo instructions. +// - type: CPURegister::RegisterType stored as a uint32_t. +// +// Simulate a call to printf. +// +// Floating-point and integer arguments are passed in separate sets of +// registers in AAPCS64 (even for varargs functions), so it is not possible to +// determine the type of location of each argument without some information +// about the values that were passed in. This information could be retrieved +// from the printf format string, but the format string is not trivial to +// parse so we encode the relevant information with the HLT instruction under +// the type argument. Therefore the interface is: +// x0: The format string +// x1-x7: Optional arguments, if type == CPURegister::kRegister +// d0-d7: Optional arguments, if type == CPURegister::kFPRegister +const Instr kPrintfOpcode = 0xdeb1; +const unsigned kPrintfTypeOffset = 1 * kInstructionSize; +const unsigned kPrintfLength = 2 * kInstructionSize; + + +// The proper way to initialize a simulated system register (such as NZCV) is as +// follows: +// SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV); +class SimSystemRegister { + public: + // The default constructor represents a register which has no writable bits. + // It is not possible to set its value to anything other than 0. + SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { } + + inline uint32_t RawValue() const { + return value_; + } + + inline void SetRawValue(uint32_t new_value) { + value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_); + } + + inline uint32_t Bits(int msb, int lsb) const { + return unsigned_bitextract_32(msb, lsb, value_); + } + + inline int32_t SignedBits(int msb, int lsb) const { + return signed_bitextract_32(msb, lsb, value_); + } + + void SetBits(int msb, int lsb, uint32_t bits); + + // Default system register values. + static SimSystemRegister DefaultValueFor(SystemRegister id); + +#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ + inline uint32_t Name() const { return Func(HighBit, LowBit); } \ + inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); } +#define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \ + static const uint32_t Name##WriteIgnoreMask = ~static_cast(Mask); + + SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK) + +#undef DEFINE_ZERO_BITS +#undef DEFINE_GETTER + + protected: + // Most system registers only implement a few of the bits in the word. Other + // bits are "read-as-zero, write-ignored". The write_ignore_mask argument + // describes the bits which are not modifiable. + SimSystemRegister(uint32_t value, uint32_t write_ignore_mask) + : value_(value), write_ignore_mask_(write_ignore_mask) { } + + uint32_t value_; + uint32_t write_ignore_mask_; +}; + + +class Simulator : public DecoderVisitor { + public: + explicit Simulator(Decoder* decoder, FILE* stream = stdout); + ~Simulator(); + + void ResetState(); + + // TODO: We assume little endianness, and the way in which the members of this + // union overlay. Add tests to ensure this, or fix accessors to no longer + // require this assumption. + union SimRegister { + int64_t x; + int32_t w; + }; + + union SimFPRegister { + double d; + float s; + }; + + // Run the simulator. + virtual void Run(); + void RunFrom(Instruction* first); + + // Simulation helpers. + inline Instruction* pc() { return pc_; } + inline void set_pc(Instruction* new_pc) { + pc_ = new_pc; + pc_modified_ = true; + } + + inline void increment_pc() { + if (!pc_modified_) { + pc_ = pc_->NextInstruction(); + } + + pc_modified_ = false; + } + + inline void ExecuteInstruction() { + // The program counter should always be aligned. + ASSERT(IsWordAligned(pc_)); + decoder_->Decode(pc_); + increment_pc(); + } + + // Declare all Visitor functions. + #define DECLARE(A) void Visit##A(Instruction* instr); + VISITOR_LIST(DECLARE) + #undef DECLARE + + // Register accessors. + inline int32_t wreg(unsigned code, + Reg31Mode r31mode = Reg31IsZeroRegister) const { + ASSERT(code < kNumberOfRegisters); + if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { + return 0; + } + return registers_[code].w; + } + + inline int64_t xreg(unsigned code, + Reg31Mode r31mode = Reg31IsZeroRegister) const { + ASSERT(code < kNumberOfRegisters); + if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { + return 0; + } + return registers_[code].x; + } + + inline int64_t reg(unsigned size, + unsigned code, + Reg31Mode r31mode = Reg31IsZeroRegister) const { + switch (size) { + case kWRegSize: return wreg(code, r31mode) & kWRegMask; + case kXRegSize: return xreg(code, r31mode); + default: + UNREACHABLE(); + return 0; + } + } + + inline void set_wreg(unsigned code, int32_t value, + Reg31Mode r31mode = Reg31IsZeroRegister) { + ASSERT(code < kNumberOfRegisters); + if ((code == kZeroRegCode) && (r31mode == Reg31IsZeroRegister)) { + return; + } + registers_[code].x = 0; // First clear the register top bits. + registers_[code].w = value; + } + + inline void set_xreg(unsigned code, int64_t value, + Reg31Mode r31mode = Reg31IsZeroRegister) { + ASSERT(code < kNumberOfRegisters); + if ((code == kZeroRegCode) && (r31mode == Reg31IsZeroRegister)) { + return; + } + registers_[code].x = value; + } + + inline void set_reg(unsigned size, unsigned code, int64_t value, + Reg31Mode r31mode = Reg31IsZeroRegister) { + switch (size) { + case kWRegSize: + return set_wreg(code, static_cast(value & 0xffffffff), + r31mode); + case kXRegSize: + return set_xreg(code, value, r31mode); + default: + UNREACHABLE(); + break; + } + } + + #define REG_ACCESSORS(N) \ + inline int32_t w##N() { return wreg(N); } \ + inline int64_t x##N() { return xreg(N); } \ + inline void set_w##N(int32_t val) { set_wreg(N, val); } \ + inline void set_x##N(int64_t val) { set_xreg(N, val); } + REGISTER_CODE_LIST(REG_ACCESSORS) + #undef REG_ACCESSORS + + // Aliases. + #define REG_ALIAS_ACCESSORS(N, wname, xname) \ + inline int32_t wname() { return wreg(N); } \ + inline int64_t xname() { return xreg(N); } \ + inline void set_##wname(int32_t val) { set_wreg(N, val); } \ + inline void set_##xname(int64_t val) { set_xreg(N, val); } + REG_ALIAS_ACCESSORS(30, wlr, lr); + #undef REG_ALIAS_ACCESSORS + + // The stack is a special case in aarch64. + inline int32_t wsp() { return wreg(31, Reg31IsStackPointer); } + inline int64_t sp() { return xreg(31, Reg31IsStackPointer); } + inline void set_wsp(int32_t val) { + set_wreg(31, val, Reg31IsStackPointer); + } + inline void set_sp(int64_t val) { + set_xreg(31, val, Reg31IsStackPointer); + } + + // FPRegister accessors. + inline float sreg(unsigned code) const { + ASSERT(code < kNumberOfFPRegisters); + return fpregisters_[code].s; + } + + inline uint32_t sreg_bits(unsigned code) const { + return float_to_rawbits(sreg(code)); + } + + inline double dreg(unsigned code) const { + ASSERT(code < kNumberOfFPRegisters); + return fpregisters_[code].d; + } + + inline uint64_t dreg_bits(unsigned code) const { + return double_to_rawbits(dreg(code)); + } + + inline double fpreg(unsigned size, unsigned code) const { + switch (size) { + case kSRegSize: return sreg(code); + case kDRegSize: return dreg(code); + default: { + UNREACHABLE(); + return 0.0; + } + } + } + + inline void set_sreg(unsigned code, float val) { + ASSERT(code < kNumberOfFPRegisters); + // Ensure that the upper word is set to 0. + set_dreg_bits(code, 0); + + fpregisters_[code].s = val; + } + + inline void set_sreg_bits(unsigned code, uint32_t rawbits) { + ASSERT(code < kNumberOfFPRegisters); + // Ensure that the upper word is set to 0. + set_dreg_bits(code, 0); + + set_sreg(code, rawbits_to_float(rawbits)); + } + + inline void set_dreg(unsigned code, double val) { + ASSERT(code < kNumberOfFPRegisters); + fpregisters_[code].d = val; + } + + inline void set_dreg_bits(unsigned code, uint64_t rawbits) { + ASSERT(code < kNumberOfFPRegisters); + set_dreg(code, rawbits_to_double(rawbits)); + } + + inline void set_fpreg(unsigned size, unsigned code, double value) { + switch (size) { + case kSRegSize: + return set_sreg(code, value); + case kDRegSize: + return set_dreg(code, value); + default: + UNREACHABLE(); + break; + } + } + + #define FPREG_ACCESSORS(N) \ + inline float s##N() { return sreg(N); } \ + inline double d##N() { return dreg(N); } \ + inline void set_s##N(float val) { set_sreg(N, val); } \ + inline void set_d##N(double val) { set_dreg(N, val); } + REGISTER_CODE_LIST(FPREG_ACCESSORS) + #undef FPREG_ACCESSORS + + bool N() { return nzcv_.N() != 0; } + bool Z() { return nzcv_.Z() != 0; } + bool C() { return nzcv_.C() != 0; } + bool V() { return nzcv_.V() != 0; } + SimSystemRegister& nzcv() { return nzcv_; } + + // TODO(jbramley): Find a way to make the fpcr_ members return the proper + // types, so this accessor is not necessary. + FPRounding RMode() { return static_cast(fpcr_.RMode()); } + SimSystemRegister& fpcr() { return fpcr_; } + + // Debug helpers + void PrintSystemRegisters(bool print_all = false); + void PrintRegisters(bool print_all_regs = false); + void PrintFPRegisters(bool print_all_regs = false); + void PrintProcessorState(); + + static const char* WRegNameForCode(unsigned code, + Reg31Mode mode = Reg31IsZeroRegister); + static const char* XRegNameForCode(unsigned code, + Reg31Mode mode = Reg31IsZeroRegister); + static const char* SRegNameForCode(unsigned code); + static const char* DRegNameForCode(unsigned code); + static const char* VRegNameForCode(unsigned code); + + inline bool coloured_trace() { return coloured_trace_; } + inline void set_coloured_trace(bool value) { coloured_trace_ = value; } + + inline bool disasm_trace() { return disasm_trace_; } + inline void set_disasm_trace(bool value) { + if (value != disasm_trace_) { + if (value) { + decoder_->InsertVisitorBefore(print_disasm_, this); + } else { + decoder_->RemoveVisitor(print_disasm_); + } + disasm_trace_ = value; + } + } + inline void set_instruction_stats(bool value) { + if (value != instruction_stats_) { + if (value) { + decoder_->AppendVisitor(instrumentation_); + } else { + decoder_->RemoveVisitor(instrumentation_); + } + instruction_stats_ = value; + } + } + + protected: + // Simulation helpers ------------------------------------ + bool ConditionPassed(Condition cond) { + switch (cond) { + case eq: + return Z(); + case ne: + return !Z(); + case hs: + return C(); + case lo: + return !C(); + case mi: + return N(); + case pl: + return !N(); + case vs: + return V(); + case vc: + return !V(); + case hi: + return C() && !Z(); + case ls: + return !(C() && !Z()); + case ge: + return N() == V(); + case lt: + return N() != V(); + case gt: + return !Z() && (N() == V()); + case le: + return !(!Z() && (N() == V())); + case nv: // Fall through. + case al: + return true; + default: + UNREACHABLE(); + return false; + } + } + + bool ConditionFailed(Condition cond) { + return !ConditionPassed(cond); + } + + void AddSubHelper(Instruction* instr, int64_t op2); + int64_t AddWithCarry(unsigned reg_size, + bool set_flags, + int64_t src1, + int64_t src2, + int64_t carry_in = 0); + void LogicalHelper(Instruction* instr, int64_t op2); + void ConditionalCompareHelper(Instruction* instr, int64_t op2); + void LoadStoreHelper(Instruction* instr, + int64_t offset, + AddrMode addrmode); + void LoadStorePairHelper(Instruction* instr, AddrMode addrmode); + uint8_t* AddressModeHelper(unsigned addr_reg, + int64_t offset, + AddrMode addrmode); + + uint64_t MemoryRead(const uint8_t* address, unsigned num_bytes); + uint8_t MemoryRead8(uint8_t* address); + uint16_t MemoryRead16(uint8_t* address); + uint32_t MemoryRead32(uint8_t* address); + float MemoryReadFP32(uint8_t* address); + uint64_t MemoryRead64(uint8_t* address); + double MemoryReadFP64(uint8_t* address); + + void MemoryWrite(uint8_t* address, uint64_t value, unsigned num_bytes); + void MemoryWrite32(uint8_t* address, uint32_t value); + void MemoryWriteFP32(uint8_t* address, float value); + void MemoryWrite64(uint8_t* address, uint64_t value); + void MemoryWriteFP64(uint8_t* address, double value); + + int64_t ShiftOperand(unsigned reg_size, + int64_t value, + Shift shift_type, + unsigned amount); + int64_t Rotate(unsigned reg_width, + int64_t value, + Shift shift_type, + unsigned amount); + int64_t ExtendValue(unsigned reg_width, + int64_t value, + Extend extend_type, + unsigned left_shift = 0); + + uint64_t ReverseBits(uint64_t value, unsigned num_bits); + uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode); + + void FPCompare(double val0, double val1); + double FPRoundInt(double value, FPRounding round_mode); + double FPToDouble(float value); + float FPToFloat(double value, FPRounding round_mode); + double FixedToDouble(int64_t src, int fbits, FPRounding round_mode); + double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode); + float FixedToFloat(int64_t src, int fbits, FPRounding round_mode); + float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode); + int32_t FPToInt32(double value, FPRounding rmode); + int64_t FPToInt64(double value, FPRounding rmode); + uint32_t FPToUInt32(double value, FPRounding rmode); + uint64_t FPToUInt64(double value, FPRounding rmode); + double FPMax(double a, double b); + double FPMin(double a, double b); + + // Pseudo Printf instruction + void DoPrintf(Instruction* instr); + + // Processor state --------------------------------------- + + // Output stream. + FILE* stream_; + PrintDisassembler* print_disasm_; + + // Instruction statistics instrumentation. + Instrument* instrumentation_; + + // General purpose registers. Register 31 is the stack pointer. + SimRegister registers_[kNumberOfRegisters]; + + // Floating point registers + SimFPRegister fpregisters_[kNumberOfFPRegisters]; + + // Program Status Register. + // bits[31, 27]: Condition flags N, Z, C, and V. + // (Negative, Zero, Carry, Overflow) + SimSystemRegister nzcv_; + + // Floating-Point Control Register + SimSystemRegister fpcr_; + + // Only a subset of FPCR features are supported by the simulator. This helper + // checks that the FPCR settings are supported. + // + // This is checked when floating-point instructions are executed, not when + // FPCR is set. This allows generated code to modify FPCR for external + // functions, or to save and restore it when entering and leaving generated + // code. + void AssertSupportedFPCR() { + ASSERT(fpcr().DN() == 0); // No default-NaN support. + ASSERT(fpcr().FZ() == 0); // No flush-to-zero support. + ASSERT(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only. + + // The simulator does not support half-precision operations so fpcr().AHP() + // is irrelevant, and is not checked here. + } + + static inline int CalcNFlag(uint64_t result, unsigned reg_size) { + return (result >> (reg_size - 1)) & 1; + } + + static inline int CalcZFlag(uint64_t result) { + return result == 0; + } + + static const uint32_t kConditionFlagsMask = 0xf0000000; + + // Stack + byte* stack_; + static const int stack_protection_size_ = 256; + // 2 KB stack. + static const int stack_size_ = 2 * 1024 + 2 * stack_protection_size_; + byte* stack_limit_; + + Decoder* decoder_; + // Indicates if the pc has been modified by the instruction and should not be + // automatically incremented. + bool pc_modified_; + Instruction* pc_; + + static const char* xreg_names[]; + static const char* wreg_names[]; + static const char* sreg_names[]; + static const char* dreg_names[]; + static const char* vreg_names[]; + + static const Instruction* kEndOfSimAddress; + + private: + bool coloured_trace_; + + // Indicates whether the disassembly trace is active. + bool disasm_trace_; + + // Indicates whether the instruction instrumentation is active. + bool instruction_stats_; +}; +} // namespace vixl + +#endif // VIXL_A64_SIMULATOR_A64_H_ diff --git a/disas/libvixl/src/globals.h b/disas/libvixl/src/globals.h new file mode 100644 index 0000000..859ea69 --- /dev/null +++ b/disas/libvixl/src/globals.h @@ -0,0 +1,66 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_GLOBALS_H +#define VIXL_GLOBALS_H + +// Get the standard printf format macros for C99 stdint types. +#define __STDC_FORMAT_MACROS +#include + +#include +#include +#include +#include +#include +#include +#include "platform.h" + + +typedef uint8_t byte; + +const int KBytes = 1024; +const int MBytes = 1024 * KBytes; +const int GBytes = 1024 * MBytes; + + #define ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort() +#ifdef DEBUG + #define ASSERT(condition) assert(condition) + #define CHECK(condition) ASSERT(condition) + #define UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); ABORT() + #define UNREACHABLE() printf("UNREACHABLE\t"); ABORT() +#else + #define ASSERT(condition) ((void) 0) + #define CHECK(condition) assert(condition) + #define UNIMPLEMENTED() ((void) 0) + #define UNREACHABLE() ((void) 0) +#endif + +template inline void USE(T) {} + +#define ALIGNMENT_EXCEPTION() printf("ALIGNMENT EXCEPTION\t"); ABORT() + +#endif // VIXL_GLOBALS_H diff --git a/disas/libvixl/src/platform.h b/disas/libvixl/src/platform.h new file mode 100644 index 0000000..a2600f3 --- /dev/null +++ b/disas/libvixl/src/platform.h @@ -0,0 +1,43 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef PLATFORM_H +#define PLATFORM_H + +// Define platform specific functionalities. + +namespace vixl { +#ifdef USE_SIMULATOR +// Currently we assume running the simulator implies running on x86 hardware. +inline void HostBreakpoint() { asm("int3"); } +#else +inline void HostBreakpoint() { + // TODO: Implement HostBreakpoint on a64. +} +#endif +} // namespace vixl + +#endif diff --git a/disas/libvixl/src/utils.cc b/disas/libvixl/src/utils.cc new file mode 100644 index 0000000..6f85e61 --- /dev/null +++ b/disas/libvixl/src/utils.cc @@ -0,0 +1,120 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#include "utils.h" +#include + +namespace vixl { + +uint32_t float_to_rawbits(float value) { + uint32_t bits = 0; + memcpy(&bits, &value, 4); + return bits; +} + + +uint64_t double_to_rawbits(double value) { + uint64_t bits = 0; + memcpy(&bits, &value, 8); + return bits; +} + + +float rawbits_to_float(uint32_t bits) { + float value = 0.0; + memcpy(&value, &bits, 4); + return value; +} + + +double rawbits_to_double(uint64_t bits) { + double value = 0.0; + memcpy(&value, &bits, 8); + return value; +} + + +int CountLeadingZeros(uint64_t value, int width) { + ASSERT((width == 32) || (width == 64)); + int count = 0; + uint64_t bit_test = 1UL << (width - 1); + while ((count < width) && ((bit_test & value) == 0)) { + count++; + bit_test >>= 1; + } + return count; +} + + +int CountLeadingSignBits(int64_t value, int width) { + ASSERT((width == 32) || (width == 64)); + if (value >= 0) { + return CountLeadingZeros(value, width) - 1; + } else { + return CountLeadingZeros(~value, width) - 1; + } +} + + +int CountTrailingZeros(uint64_t value, int width) { + ASSERT((width == 32) || (width == 64)); + int count = 0; + while ((count < width) && (((value >> count) & 1) == 0)) { + count++; + } + return count; +} + + +int CountSetBits(uint64_t value, int width) { + // TODO: Other widths could be added here, as the implementation already + // supports them. + ASSERT((width == 32) || (width == 64)); + + // Mask out unused bits to ensure that they are not counted. + value &= (0xffffffffffffffffUL >> (64-width)); + + // Add up the set bits. + // The algorithm works by adding pairs of bit fields together iteratively, + // where the size of each bit field doubles each time. + // An example for an 8-bit value: + // Bits: h g f e d c b a + // \ | \ | \ | \ | + // value = h+g f+e d+c b+a + // \ | \ | + // value = h+g+f+e d+c+b+a + // \ | + // value = h+g+f+e+d+c+b+a + value = ((value >> 1) & 0x5555555555555555) + (value & 0x5555555555555555); + value = ((value >> 2) & 0x3333333333333333) + (value & 0x3333333333333333); + value = ((value >> 4) & 0x0f0f0f0f0f0f0f0f) + (value & 0x0f0f0f0f0f0f0f0f); + value = ((value >> 8) & 0x00ff00ff00ff00ff) + (value & 0x00ff00ff00ff00ff); + value = ((value >> 16) & 0x0000ffff0000ffff) + (value & 0x0000ffff0000ffff); + value = ((value >> 32) & 0x00000000ffffffff) + (value & 0x00000000ffffffff); + + return value; +} +} // namespace vixl diff --git a/disas/libvixl/src/utils.h b/disas/libvixl/src/utils.h new file mode 100644 index 0000000..4e0b367 --- /dev/null +++ b/disas/libvixl/src/utils.h @@ -0,0 +1,126 @@ +// Copyright 2013, ARM Limited +// 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 ARM Limited 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 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. + +#ifndef VIXL_UTILS_H +#define VIXL_UTILS_H + + +#include +#include "globals.h" + +namespace vixl { + +// Check number width. +inline bool is_intn(unsigned n, int64_t x) { + ASSERT((0 < n) && (n < 64)); + int64_t limit = 1L << (n - 1); + return (-limit <= x) && (x < limit); +} + +inline bool is_uintn(unsigned n, int64_t x) { + ASSERT((0 < n) && (n < 64)); + return !(x >> n); +} + +inline unsigned truncate_to_intn(unsigned n, int64_t x) { + ASSERT((0 < n) && (n < 64)); + return (x & ((1L << n) - 1)); +} + +#define INT_1_TO_63_LIST(V) \ +V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ +V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ +V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ +V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ +V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ +V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ +V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ +V(57) V(58) V(59) V(60) V(61) V(62) V(63) + +#define DECLARE_IS_INT_N(N) \ +inline bool is_int##N(int64_t x) { return is_intn(N, x); } +#define DECLARE_IS_UINT_N(N) \ +inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } +#define DECLARE_TRUNCATE_TO_INT_N(N) \ +inline int truncate_to_int##N(int x) { return truncate_to_intn(N, x); } +INT_1_TO_63_LIST(DECLARE_IS_INT_N) +INT_1_TO_63_LIST(DECLARE_IS_UINT_N) +INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) +#undef DECLARE_IS_INT_N +#undef DECLARE_IS_UINT_N +#undef DECLARE_TRUNCATE_TO_INT_N + +// Bit field extraction. +inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { + return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); +} + +inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { + return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); +} + +inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { + return (x << (31 - msb)) >> (lsb + 31 - msb); +} + +inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { + return (x << (63 - msb)) >> (lsb + 63 - msb); +} + +// floating point representation +uint32_t float_to_rawbits(float value); +uint64_t double_to_rawbits(double value); +float rawbits_to_float(uint32_t bits); +double rawbits_to_double(uint64_t bits); + +// Bits counting. +int CountLeadingZeros(uint64_t value, int width); +int CountLeadingSignBits(int64_t value, int width); +int CountTrailingZeros(uint64_t value, int width); +int CountSetBits(uint64_t value, int width); + +// Pointer alignment +// TODO: rename/refactor to make it specific to instructions. +template +bool IsWordAligned(T pointer) { + ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) + return (reinterpret_cast(pointer) & 3) == 0; +} + +// Increment a pointer until it has the specified alignment. +template +T AlignUp(T pointer, size_t alignment) { + ASSERT(sizeof(pointer) == sizeof(uintptr_t)); + uintptr_t pointer_raw = reinterpret_cast(pointer); + size_t align_step = (alignment - pointer_raw) % alignment; + ASSERT((pointer_raw + align_step) % alignment == 0); + return reinterpret_cast(pointer_raw + align_step); +} + + +} // namespace vixl + +#endif // VIXL_UTILS_H