mirror of
https://github.com/openharmony/third_party_vixl.git
synced 2026-07-01 20:54:04 -04:00
Upgrade VIXL version from 6.3.0 to 7.0.0
+ add CODEOWNERS file Issue: https://gitee.com/openharmony/third_party_vixl/issues/IAXCEZ Signed-off-by: Aleksei Sidorov <aleksei.sidorov@huawei.com> Change-Id: I17900177ef59b7891419ccde5a92b13995b2221f
This commit is contained in:
@@ -104,6 +104,7 @@ ohos_static_library("libvixl") {
|
||||
"src/aarch64/assembler-sve-aarch64.cc",
|
||||
"src/aarch64/cpu-aarch64.cc",
|
||||
"src/aarch64/cpu-features-auditor-aarch64.cc",
|
||||
"src/aarch64/debugger-aarch64.cc",
|
||||
"src/aarch64/decoder-aarch64.cc",
|
||||
"src/aarch64/disasm-aarch64.cc",
|
||||
"src/aarch64/instructions-aarch64.cc",
|
||||
|
||||
@@ -70,6 +70,7 @@ if (NOT PANDA_MINIMAL_VIXL)
|
||||
src/aarch64/pointer-auth-aarch64.cc
|
||||
src/aarch64/cpu-features-auditor-aarch64.cc
|
||||
#src/aarch64/macro-assembler-sve-aarch64.cc
|
||||
src/aarch64/debugger-aarch64.cc
|
||||
src/aarch64/simulator-aarch64.cc )
|
||||
endif()
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
* @Prof1983
|
||||
.* @Prof1983
|
||||
@@ -1,4 +1,4 @@
|
||||
VIXL: ARMv8 Runtime Code Generation Library 6.3.0
|
||||
VIXL: ARMv8 Runtime Code Generation Library 7.0.0
|
||||
=================================================
|
||||
|
||||
Contents:
|
||||
@@ -48,7 +48,7 @@ Requirements
|
||||
|
||||
To build VIXL the following software is required:
|
||||
|
||||
1. Python 2.7
|
||||
1. Python 3.5+
|
||||
2. SCons 2.0
|
||||
3. GCC 4.8+ or Clang 4.0+
|
||||
|
||||
@@ -61,8 +61,8 @@ software is also required:
|
||||
|
||||
1. Git
|
||||
2. [Google's `cpplint.py`][cpplint]
|
||||
3. clang-format-4.0
|
||||
4. clang-tidy-4.0
|
||||
3. clang-format 11+
|
||||
4. clang-tidy 11+
|
||||
|
||||
Refer to the 'Usage' section for details.
|
||||
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@ def simulator_validator(env):
|
||||
'AArch64. Set `target` to include `aarch64` or `a64`.')
|
||||
|
||||
|
||||
# Default variables may depend on each other, therefore we need this dictionnary
|
||||
# Default variables may depend on each other, therefore we need this dictionary
|
||||
# to be ordered.
|
||||
vars_default_handlers = OrderedDict({
|
||||
# variable_name : [ 'default val', 'handler', 'validator']
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
||||
@@ -24,14 +24,13 @@
|
||||
// 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 "bench-utils.h"
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/instructions-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
#include "aarch64/simulator-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
using namespace vixl;
|
||||
|
||||
@@ -24,12 +24,13 @@
|
||||
// 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 "bench-utils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "globals-vixl.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
#include "bench-utils.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
@@ -306,7 +307,7 @@ void BenchCodeGenerator::BindPendingLabels(uint64_t bind_mask) {
|
||||
|
||||
void BenchCodeGenerator::BindAllPendingLabels() {
|
||||
while (!labels_.empty()) {
|
||||
// BindPendingLables generates a branch over each block of bound labels.
|
||||
// BindPendingLabels generates a branch over each block of bound labels.
|
||||
// This will be repeated for each call here, but the effect is minimal and
|
||||
// (empirically) we rarely accumulate more than 64 pending labels anyway.
|
||||
BindPendingLabels(UINT64_MAX);
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
#ifndef VIXL_AARCH64_BENCH_UTILS_H_
|
||||
#define VIXL_AARCH64_BENCH_UTILS_H_
|
||||
|
||||
#include <list>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "globals-vixl.h"
|
||||
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
|
||||
class BenchTimer {
|
||||
@@ -90,7 +90,8 @@ class BenchCLI {
|
||||
}
|
||||
|
||||
char* end;
|
||||
unsigned long run_time = strtoul(argv[1], &end, 0); // NOLINT(runtime/int)
|
||||
unsigned long run_time = // NOLINT(google-runtime-int)
|
||||
strtoul(argv[1], &end, 0);
|
||||
if ((end == argv[1]) || (run_time > UINT32_MAX)) {
|
||||
PrintUsage(argv[0]);
|
||||
status_ = kExitFailure;
|
||||
@@ -242,7 +243,7 @@ class BenchCodeGenerator {
|
||||
vixl::aarch64::MacroAssembler* masm_;
|
||||
|
||||
// State for *rand48(), used to randomise code generation.
|
||||
unsigned short rand_state_[3]; // NOLINT(runtime/int)
|
||||
unsigned short rand_state_[3]; // NOLINT(google-runtime-int)
|
||||
|
||||
uint32_t rnd_;
|
||||
int rnd_bits_;
|
||||
|
||||
@@ -6,3 +6,4 @@ You can also have a look at the ['getting started' page](../getting-started-aarc
|
||||
|
||||
* [Extending and customizing the disassembler](extending-the-disassembler.md)
|
||||
* [Using VIM YouCompleteMe with VIXL](ycm.md)
|
||||
* [Debugging with the VIXL Simulator](simulator-debugger.md)
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
Debugging with the VIXL Simulator
|
||||
=================================
|
||||
|
||||
The VIXL AArch64 simulator contains a basic debugger which can be used to debug
|
||||
simulated applications. The debugger supports basic debugging features such as
|
||||
setting breakpoints, stepping through simulated instructions and printing
|
||||
simulator specific information, for example: printing the values of a register
|
||||
or printing instructions at specified addresses.
|
||||
|
||||
Using the Debugger
|
||||
------------------
|
||||
|
||||
In order to use the debugger it first needs to be enabled in the simulator.
|
||||
|
||||
```C++
|
||||
Decoder decoder;
|
||||
Simulator simulator(&decoder);
|
||||
simulator.SetDebuggerEnabled(true);
|
||||
```
|
||||
|
||||
Once enabled, the debugger will be activated whenever a breakpoint (brk) is
|
||||
encountered by the simulator. For example:
|
||||
|
||||
```asm
|
||||
add x1, x0, #5
|
||||
mov x2, #2
|
||||
|
||||
brk 0 // Debugger activated here.
|
||||
|
||||
sub x3, x1, x2
|
||||
```
|
||||
|
||||
Further breakpoints can be set either programmatically or interactively in the
|
||||
debugger itself. For example, to set breakpoints programmatically:
|
||||
|
||||
```C++
|
||||
// 'func' is an AARCH64 assembly function.
|
||||
extern "C" void func();
|
||||
|
||||
Debugger* debugger = simulator.GetDebugger();
|
||||
|
||||
// Register a breakpoint at a fixed (absolute) address.
|
||||
debugger->RegisterBreakpoint(0x00007ffbc6d38000);
|
||||
|
||||
// Register a breakpoint to an already existing assembly function.
|
||||
debugger->RegisterBreakpoint(reinterpret_cast<uint64_t>(&func));
|
||||
```
|
||||
|
||||
Or to set breakpoints interactively once the debugger has been activated:
|
||||
|
||||
```sh
|
||||
sim> break 0x00007ffbc6d38000
|
||||
```
|
||||
|
||||
The debugger has a variety of useful commands to control program flow (e.g:
|
||||
step, next, continue) and inspect features of the running simulator (e.g:
|
||||
print, trace). To view a list of all supported commands
|
||||
use "help" at the debugger prompt.
|
||||
|
||||
```sh
|
||||
sim> help
|
||||
```
|
||||
|
||||
Extending the Debugger
|
||||
----------------------
|
||||
|
||||
The debugger can be extended with custom commands to allow for greater
|
||||
flexibility in debugging individual applications. This could be used for a
|
||||
variety of applications, for example printing out object specific information
|
||||
from an address.
|
||||
|
||||
To create a custom debugger command, extend the DebuggerCmd class located in
|
||||
debugger-aarch64.h and implement its methods.
|
||||
|
||||
```C++
|
||||
class PrintObjectCmd : public DebuggerCmd {
|
||||
public:
|
||||
PrintObjectCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"printobject",
|
||||
"po",
|
||||
"<address>",
|
||||
"Print a custom object located at the given address.")
|
||||
{}
|
||||
|
||||
// Called when the command word is given to the interactive debugger.
|
||||
DebugReturn Action(const std::vector<std::string>& args) override {
|
||||
// We want exactly 1 argument (an address) given to the printobject
|
||||
// command.
|
||||
if (args.size() != 1) {
|
||||
fprintf(ostream_, "Error: incorrect command format.");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
auto addr = Debugger::ParseUint64String(args.front());
|
||||
if (addr) {
|
||||
fprintf(ostream_, "Error: could not get address from string.");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
// Convert the address given to a custom object and then print it.
|
||||
CustomObject object = reinterpret_cast<CustomObject>(*addr);
|
||||
object.print();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Then simply register the new command with the debugger.
|
||||
|
||||
```C++
|
||||
Debugger* debugger = simulator.GetDebugger();
|
||||
|
||||
debugger->RegisterCmd<PrintObjectCmd>();
|
||||
```
|
||||
@@ -84,6 +84,8 @@ void GenerateAdd2Vectors(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
#ifndef TEST_EXAMPLES
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
void PrintVector(const uint8_t* vec, unsigned num) {
|
||||
unsigned i;
|
||||
printf("( ");
|
||||
@@ -95,9 +97,9 @@ void PrintVector(const uint8_t* vec, unsigned num) {
|
||||
}
|
||||
printf(" )\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef TEST_EXAMPLES
|
||||
int main(void) {
|
||||
MacroAssembler masm;
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
// 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 "custom-disassembler.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "custom-disassembler.h"
|
||||
#include "examples.h"
|
||||
|
||||
using namespace vixl;
|
||||
@@ -106,7 +107,7 @@ void CustomDisassembler::AppendCodeRelativeCodeAddressToOutput(
|
||||
|
||||
// We override this method to add a comment to some instructions. Helpers from
|
||||
// the vixl::Instruction class can be used to analyse the instruction being
|
||||
// disasssembled.
|
||||
// disassembled.
|
||||
void CustomDisassembler::Visit(Metadata* metadata, const Instruction* instr) {
|
||||
vixl::aarch64::Disassembler::Visit(metadata, instr);
|
||||
const std::string& form = (*metadata)["form"];
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2023, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of 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 "examples.h"
|
||||
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
#include "aarch64/simulator-aarch64.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
#define __ masm->
|
||||
|
||||
void GenerateDebugExample(MacroAssembler* masm) {
|
||||
// Create a breakpoint here to break into the debugger.
|
||||
__ Brk(0);
|
||||
|
||||
// Do some arithmetic.
|
||||
__ Add(x1, x0, 5);
|
||||
__ Mov(x2, 2);
|
||||
__ Sub(x3, x1, x2);
|
||||
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
#ifndef TEST_EXAMPLES
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
int main(void) {
|
||||
MacroAssembler masm;
|
||||
|
||||
// Generate the code for the example function.
|
||||
Label debug_example;
|
||||
masm.Bind(&debug_example);
|
||||
GenerateDebugExample(&masm);
|
||||
masm.FinalizeCode();
|
||||
|
||||
Instruction* start = masm.GetLabelAddress<Instruction*>(&debug_example);
|
||||
|
||||
// Disassemble the generated code.
|
||||
PrintDisassembler disassembler(stdout);
|
||||
disassembler.DisassembleBuffer(start, masm.GetSizeOfCodeGenerated());
|
||||
|
||||
Decoder decoder;
|
||||
Simulator simulator(&decoder);
|
||||
|
||||
simulator.SetColouredTrace(true);
|
||||
simulator.SetDebuggerEnabled(true);
|
||||
|
||||
int32_t input_a = 1;
|
||||
int32_t input_b = 2;
|
||||
simulator.WriteWRegister(0, input_a);
|
||||
simulator.WriteWRegister(1, input_b);
|
||||
simulator.RunFrom(start);
|
||||
printf("The final result is %ld\n", simulator.ReadXRegister(3));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int main(void) { return 0; }
|
||||
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
#endif // TEST_EXAMPLES
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "code-buffer-vixl.h"
|
||||
|
||||
#include "aarch64/decoder-aarch64.h"
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
// 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 "executable-memory.h"
|
||||
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
#include "aarch64/simulator-aarch64.h"
|
||||
|
||||
#include "executable-memory.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
@@ -59,7 +59,7 @@ int main() {
|
||||
|
||||
simulator.WriteXRegister(0, 0x8899aabbccddeeff);
|
||||
simulator.RunFrom(masm.GetLabelAddress<Instruction *>(&demo));
|
||||
printf("x0 = %" PRIx64 "\n", simulator.ReadXRegister(0));
|
||||
printf("x0 = 0x%" PRIx64 "\n", simulator.ReadXRegister(0));
|
||||
|
||||
#else
|
||||
byte* code = masm.GetBuffer()->GetStartAddress<byte*>();
|
||||
@@ -70,7 +70,9 @@ int main() {
|
||||
memory.GetEntryPoint<uint64_t (*)(uint64_t)>(demo);
|
||||
uint64_t input_value = 0x8899aabbccddeeff;
|
||||
uint64_t output_value = (*demo_function)(input_value);
|
||||
printf("native: demo(0x%016lx) = 0x%016lx\n", input_value, output_value);
|
||||
printf("native: demo(0x%" PRIx64 ") = 0x%" PRIx64 "\n",
|
||||
input_value,
|
||||
output_value);
|
||||
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -84,6 +84,8 @@ int64_t LiteralExample(int64_t a, int64_t b) {
|
||||
b,
|
||||
simulator.ReadXRegister(0));
|
||||
|
||||
free(code);
|
||||
|
||||
return simulator.ReadXRegister(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -75,7 +75,7 @@ void GenerateNEONMatrixMultiply(MacroAssembler* masm) {
|
||||
__ Ld1(v16.V4S(), v17.V4S(), v18.V4S(), v19.V4S(), MemOperand(x2));
|
||||
|
||||
// Initialise vectors of the output matrix with zeros.
|
||||
// This is only for the purposes of showing how this can be achived
|
||||
// This is only for the purposes of showing how this can be achieved
|
||||
// but technically this is not required because we overwrite all lanes
|
||||
// of the output vectors.
|
||||
__ Movi(v0.V16B(), 0);
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
// 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 "non-const-visitor.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "examples.h"
|
||||
#include "non-const-visitor.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright 2023, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of 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 "examples.h"
|
||||
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
#include "aarch64/macro-assembler-aarch64.h"
|
||||
#include "aarch64/simulator-aarch64.h"
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
#define __ masm->
|
||||
|
||||
enum Result { FAILURE, SUCCESS };
|
||||
|
||||
// This will be called via a runtime call.
|
||||
extern "C" int example_1() { return SUCCESS; }
|
||||
|
||||
// This will never be called, instead it will be intercepted and 'callback'
|
||||
// will be called.
|
||||
uint32_t example_2() { return FAILURE; }
|
||||
|
||||
uint32_t example_3(uint32_t num, float f) {
|
||||
USE(f);
|
||||
return num;
|
||||
}
|
||||
|
||||
// This will be called instead of example_2.
|
||||
uint32_t callback(uint64_t original_target) {
|
||||
USE(original_target);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void GenerateInterceptionExamples(MacroAssembler* masm) {
|
||||
// Preserve lr, since the calls will overwrite it.
|
||||
__ Push(xzr, lr);
|
||||
|
||||
// example_1 will be intercepted and called through a runtime call.
|
||||
__ Mov(x16, reinterpret_cast<uint64_t>(example_1));
|
||||
__ Blr(x16);
|
||||
__ Mov(w1, w0);
|
||||
|
||||
// example_2 will be intercepted and callback will be called instead.
|
||||
__ Mov(x16, reinterpret_cast<uint64_t>(example_2));
|
||||
__ Blr(x16);
|
||||
__ Mov(w2, w0);
|
||||
|
||||
// Pass FAILURE as a parameter.
|
||||
__ Mov(x0, FAILURE);
|
||||
__ Fmov(s0, 3.5);
|
||||
// example_3 will be intercepted and lambda callback will be called instead.
|
||||
__ Mov(x16, reinterpret_cast<uint64_t>(example_3));
|
||||
__ Blr(x16);
|
||||
__ Mov(w3, w0);
|
||||
|
||||
// Restore lr and return.
|
||||
__ Pop(lr, xzr);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
#ifndef TEST_EXAMPLES
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
int main(void) {
|
||||
MacroAssembler masm;
|
||||
|
||||
// Generate the code for the example function.
|
||||
Label call_simulator_interception;
|
||||
masm.Bind(&call_simulator_interception);
|
||||
GenerateInterceptionExamples(&masm);
|
||||
masm.FinalizeCode();
|
||||
|
||||
Instruction* start =
|
||||
masm.GetLabelAddress<Instruction*>(&call_simulator_interception);
|
||||
|
||||
// Disassemble the generated code.
|
||||
PrintDisassembler disassembler(stdout);
|
||||
disassembler.DisassembleBuffer(start, masm.GetSizeOfCodeGenerated());
|
||||
|
||||
Decoder decoder;
|
||||
Simulator simulator(&decoder);
|
||||
|
||||
// Register interceptions to the branches, example_1 will be called via a
|
||||
// runtime call and callback will be called instead of example_2.
|
||||
simulator.RegisterBranchInterception(example_1);
|
||||
simulator.RegisterBranchInterception(example_2, callback);
|
||||
|
||||
// Lambda callbacks can be used to arbitrarily modify the simulator.
|
||||
simulator.RegisterBranchInterception(
|
||||
example_3, [&simulator](uint64_t original_target) {
|
||||
USE(original_target);
|
||||
ABI abi;
|
||||
|
||||
uint32_t param1 = simulator.ReadGenericOperand<uint32_t>(
|
||||
abi.GetNextParameterGenericOperand<uint32_t>());
|
||||
float param2 = simulator.ReadGenericOperand<float>(
|
||||
abi.GetNextParameterGenericOperand<float>());
|
||||
|
||||
if (param1 == FAILURE && param2 == 3.5) {
|
||||
simulator.WriteWRegister(0, SUCCESS);
|
||||
} else {
|
||||
simulator.WriteWRegister(0, FAILURE);
|
||||
}
|
||||
});
|
||||
|
||||
simulator.RunFrom(start);
|
||||
|
||||
uint32_t result_1 = simulator.ReadWRegister(1);
|
||||
if (result_1 == SUCCESS) {
|
||||
printf("SUCCESS: example_1 was called via a runtime call.\n");
|
||||
} else {
|
||||
printf("ERROR: example_1 was not called.\n");
|
||||
}
|
||||
|
||||
uint32_t result_2 = simulator.ReadWRegister(2);
|
||||
if (result_2 == SUCCESS) {
|
||||
printf("SUCCESS: callback was called instead of example_2.\n");
|
||||
} else {
|
||||
printf("ERROR: example_2 was called incorrectly.\n");
|
||||
}
|
||||
|
||||
uint32_t result_3 = simulator.ReadWRegister(0);
|
||||
if (result_3 == SUCCESS) {
|
||||
printf("SUCCESS: Lambda callback called instead of example_3.\n");
|
||||
} else {
|
||||
printf("ERROR: example_3 was called instead of the lambda.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
// TODO: Support running natively.
|
||||
int main(void) { return 0; }
|
||||
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
#endif // TEST_EXAMPLES
|
||||
@@ -348,7 +348,7 @@ DataTypeValue Dt_U_opc1_opc2_1_Decode(uint32_t value, unsigned* lane) {
|
||||
*lane = (value >> 2) & 1;
|
||||
return Untyped32;
|
||||
}
|
||||
*lane = -1;
|
||||
*lane = ~0U;
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ DataTypeValue Dt_opc1_opc2_1_Decode(uint32_t value, unsigned* lane) {
|
||||
*lane = (value >> 2) & 1;
|
||||
return Untyped32;
|
||||
}
|
||||
*lane = -1;
|
||||
*lane = ~0U;
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ DataTypeValue Dt_imm4_1_Decode(uint32_t value, unsigned* lane) {
|
||||
*lane = (value >> 3) & 1;
|
||||
return Untyped32;
|
||||
}
|
||||
*lane = -1;
|
||||
*lane = ~0U;
|
||||
return kDataTypeValueInvalid;
|
||||
}
|
||||
|
||||
@@ -60977,7 +60977,7 @@ void Disassembler::DecodeA32(uint32_t instr) {
|
||||
Condition condition((instr >> 28) & 0xf);
|
||||
unsigned rd = (instr >> 12) & 0xf;
|
||||
uint32_t imm = ImmediateA32::Decode(instr & 0xfff);
|
||||
Location location(-imm, kA32PcDelta);
|
||||
Location location(UnsignedNegate(imm), kA32PcDelta);
|
||||
// ADR{<c>}{<q>} <Rd>, <label> ; A2
|
||||
adr(condition, Best, Register(rd), &location);
|
||||
break;
|
||||
|
||||
@@ -36,6 +36,12 @@ extern "C" {
|
||||
#include "aarch32/constants-aarch32.h"
|
||||
#include "aarch32/operands-aarch32.h"
|
||||
|
||||
// Microsoft Visual C++ defines a `mvn` macro that conflicts with our own
|
||||
// definition.
|
||||
#if defined(_MSC_VER) && defined(mvn)
|
||||
#undef mvn
|
||||
#endif
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch32 {
|
||||
|
||||
|
||||
@@ -651,7 +651,7 @@ bool ImmediateT32::IsImmediateT32(uint32_t imm) {
|
||||
(((imm & 0xff00) == 0) || ((imm & 0xff) == 0)))
|
||||
return true;
|
||||
/* isolate least-significant set bit */
|
||||
uint32_t lsb = imm & -imm;
|
||||
uint32_t lsb = imm & UnsignedNegate(imm);
|
||||
/* if imm is less than lsb*256 then it fits, but instead we test imm/256 to
|
||||
* avoid overflow (underflow is always a successful case) */
|
||||
return ((imm >> 8) < lsb);
|
||||
@@ -702,7 +702,7 @@ bool ImmediateA32::IsImmediateA32(uint32_t imm) {
|
||||
* that the least-significant set bit is always an even bit */
|
||||
imm = imm | ((imm >> 1) & 0x55555555);
|
||||
/* isolate least-significant set bit (always even) */
|
||||
uint32_t lsb = imm & -imm;
|
||||
uint32_t lsb = imm & UnsignedNegate(imm);
|
||||
/* if imm is less than lsb*256 then it fits, but instead we test imm/256 to
|
||||
* avoid overflow (underflow is always a successful case) */
|
||||
return ((imm >> 8) < lsb);
|
||||
|
||||
@@ -40,6 +40,8 @@ extern "C" {
|
||||
|
||||
#if !defined(__linux__) && defined(__arm__)
|
||||
#define HARDFLOAT gnu__attribute__((noinline, pcs("aapcs-vfp")))
|
||||
#elif defined(_MSC_VER)
|
||||
#define HARDFLOAT __declspec(noinline)
|
||||
#else
|
||||
#define HARDFLOAT __attribute__((noinline))
|
||||
#endif
|
||||
@@ -1041,7 +1043,9 @@ class Sign {
|
||||
const char* GetName() const { return (IsPlus() ? "" : "-"); }
|
||||
bool IsPlus() const { return sign_ == plus; }
|
||||
bool IsMinus() const { return sign_ == minus; }
|
||||
int32_t ApplyTo(uint32_t value) { return IsPlus() ? value : -value; }
|
||||
int32_t ApplyTo(uint32_t value) {
|
||||
return IsPlus() ? value : UnsignedNegate(value);
|
||||
}
|
||||
|
||||
private:
|
||||
SignType sign_;
|
||||
|
||||
@@ -230,7 +230,7 @@ class Location : public LocationBase<int32_t> {
|
||||
|
||||
protected:
|
||||
// Types passed to LocationBase. Must be distinct for unbound Locations (not
|
||||
// relevant for bound locations, as they don't have a correspoding
|
||||
// relevant for bound locations, as they don't have a corresponding
|
||||
// PoolObject).
|
||||
static const int kRawLocation = 0; // Will not be used by the pool manager.
|
||||
static const int kVeneerType = 1;
|
||||
|
||||
@@ -270,8 +270,8 @@ MemOperand MacroAssembler::MemOperandComputationHelper(
|
||||
|
||||
uint32_t load_store_offset = offset & extra_offset_mask;
|
||||
uint32_t add_offset = offset & ~extra_offset_mask;
|
||||
if ((add_offset != 0) &&
|
||||
(IsModifiedImmediate(offset) || IsModifiedImmediate(-offset))) {
|
||||
if ((add_offset != 0) && (IsModifiedImmediate(offset) ||
|
||||
IsModifiedImmediate(UnsignedNegate(offset)))) {
|
||||
load_store_offset = 0;
|
||||
add_offset = offset;
|
||||
}
|
||||
@@ -292,7 +292,7 @@ MemOperand MacroAssembler::MemOperandComputationHelper(
|
||||
// of ADR -- to get behaviour like loads and stores. This ADR can handle
|
||||
// at least as much offset as the load_store_offset so it can replace it.
|
||||
|
||||
uint32_t sub_pc_offset = (-offset) & 0xfff;
|
||||
uint32_t sub_pc_offset = UnsignedNegate(offset) & 0xfff;
|
||||
load_store_offset = (offset + sub_pc_offset) & extra_offset_mask;
|
||||
add_offset = (offset + sub_pc_offset) & ~extra_offset_mask;
|
||||
|
||||
@@ -614,7 +614,7 @@ void MacroAssembler::Printf(const char* format,
|
||||
Vmsr(FPSCR, tmp);
|
||||
Pop(tmp);
|
||||
Msr(APSR_nzcvqg, tmp);
|
||||
// Restore the regsisters.
|
||||
// Restore the registers.
|
||||
if (Has32DRegs()) Vpop(Untyped64, DRegisterList(d16, 16));
|
||||
Vpop(Untyped64, DRegisterList(d0, 8));
|
||||
Pop(RegisterList(saved_registers_mask));
|
||||
|
||||
@@ -190,7 +190,7 @@ class Operand {
|
||||
}
|
||||
|
||||
private:
|
||||
// Forbid implicitely creating operands around types that cannot be encoded
|
||||
// Forbid implicitly creating operands around types that cannot be encoded
|
||||
// into a uint32_t without loss.
|
||||
#if __cplusplus >= 201103L
|
||||
Operand(int64_t) = delete; // NOLINT(runtime/explicit)
|
||||
@@ -615,7 +615,7 @@ class ImmediateVorn : public ImmediateVorr {
|
||||
// - a shifted index register <Rm>, <shift> #<amount>
|
||||
//
|
||||
// The index register may have an associated {+/-} sign,
|
||||
// which if ommitted, defaults to + .
|
||||
// which if omitted, defaults to + .
|
||||
//
|
||||
// We have two constructors for the offset:
|
||||
//
|
||||
|
||||
@@ -159,8 +159,8 @@ template <>
|
||||
inline GenericOperand ABI::GetReturnGenericOperand<void>() const {
|
||||
return GenericOperand();
|
||||
}
|
||||
}
|
||||
} // namespace vixl::aarch64
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_AARCH64_ABI_AARCH64_H_
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include "assembler-aarch64.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "assembler-aarch64.h"
|
||||
#include "macro-assembler-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
@@ -1939,6 +1940,542 @@ void Assembler::hint(int imm7) {
|
||||
}
|
||||
|
||||
|
||||
// MTE.
|
||||
|
||||
void Assembler::addg(const Register& xd,
|
||||
const Register& xn,
|
||||
int offset,
|
||||
int tag_offset) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
VIXL_ASSERT(IsMultiple(offset, kMTETagGranuleInBytes));
|
||||
|
||||
Emit(0x91800000 | RdSP(xd) | RnSP(xn) |
|
||||
ImmUnsignedField<21, 16>(offset / kMTETagGranuleInBytes) |
|
||||
ImmUnsignedField<13, 10>(tag_offset));
|
||||
}
|
||||
|
||||
void Assembler::gmi(const Register& xd,
|
||||
const Register& xn,
|
||||
const Register& xm) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
|
||||
Emit(0x9ac01400 | Rd(xd) | RnSP(xn) | Rm(xm));
|
||||
}
|
||||
|
||||
void Assembler::irg(const Register& xd,
|
||||
const Register& xn,
|
||||
const Register& xm) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
|
||||
Emit(0x9ac01000 | RdSP(xd) | RnSP(xn) | Rm(xm));
|
||||
}
|
||||
|
||||
void Assembler::ldg(const Register& xt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
VIXL_ASSERT(addr.IsImmediateOffset());
|
||||
int offset = static_cast<int>(addr.GetOffset());
|
||||
VIXL_ASSERT(IsMultiple(offset, kMTETagGranuleInBytes));
|
||||
|
||||
Emit(0xd9600000 | Rt(xt) | RnSP(addr.GetBaseRegister()) |
|
||||
ImmField<20, 12>(offset / static_cast<int>(kMTETagGranuleInBytes)));
|
||||
}
|
||||
|
||||
void Assembler::StoreTagHelper(const Register& xt,
|
||||
const MemOperand& addr,
|
||||
Instr op) {
|
||||
int offset = static_cast<int>(addr.GetOffset());
|
||||
VIXL_ASSERT(IsMultiple(offset, kMTETagGranuleInBytes));
|
||||
|
||||
Instr addr_mode;
|
||||
if (addr.IsImmediateOffset()) {
|
||||
addr_mode = 2;
|
||||
} else if (addr.IsImmediatePreIndex()) {
|
||||
addr_mode = 3;
|
||||
} else {
|
||||
VIXL_ASSERT(addr.IsImmediatePostIndex());
|
||||
addr_mode = 1;
|
||||
}
|
||||
|
||||
Emit(op | RdSP(xt) | RnSP(addr.GetBaseRegister()) | (addr_mode << 10) |
|
||||
ImmField<20, 12>(offset / static_cast<int>(kMTETagGranuleInBytes)));
|
||||
}
|
||||
|
||||
void Assembler::st2g(const Register& xt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
StoreTagHelper(xt, addr, 0xd9a00000);
|
||||
}
|
||||
|
||||
void Assembler::stg(const Register& xt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
StoreTagHelper(xt, addr, 0xd9200000);
|
||||
}
|
||||
|
||||
void Assembler::stgp(const Register& xt1,
|
||||
const Register& xt2,
|
||||
const MemOperand& addr) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
int offset = static_cast<int>(addr.GetOffset());
|
||||
VIXL_ASSERT(IsMultiple(offset, kMTETagGranuleInBytes));
|
||||
|
||||
Instr addr_mode;
|
||||
if (addr.IsImmediateOffset()) {
|
||||
addr_mode = 2;
|
||||
} else if (addr.IsImmediatePreIndex()) {
|
||||
addr_mode = 3;
|
||||
} else {
|
||||
VIXL_ASSERT(addr.IsImmediatePostIndex());
|
||||
addr_mode = 1;
|
||||
}
|
||||
|
||||
Emit(0x68000000 | RnSP(addr.GetBaseRegister()) | (addr_mode << 23) |
|
||||
ImmField<21, 15>(offset / static_cast<int>(kMTETagGranuleInBytes)) |
|
||||
Rt2(xt2) | Rt(xt1));
|
||||
}
|
||||
|
||||
void Assembler::stz2g(const Register& xt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
StoreTagHelper(xt, addr, 0xd9e00000);
|
||||
}
|
||||
|
||||
void Assembler::stzg(const Register& xt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
StoreTagHelper(xt, addr, 0xd9600000);
|
||||
}
|
||||
|
||||
void Assembler::subg(const Register& xd,
|
||||
const Register& xn,
|
||||
int offset,
|
||||
int tag_offset) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
VIXL_ASSERT(IsMultiple(offset, kMTETagGranuleInBytes));
|
||||
|
||||
Emit(0xd1800000 | RdSP(xd) | RnSP(xn) |
|
||||
ImmUnsignedField<21, 16>(offset / kMTETagGranuleInBytes) |
|
||||
ImmUnsignedField<13, 10>(tag_offset));
|
||||
}
|
||||
|
||||
void Assembler::subp(const Register& xd,
|
||||
const Register& xn,
|
||||
const Register& xm) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
|
||||
Emit(0x9ac00000 | Rd(xd) | RnSP(xn) | RmSP(xm));
|
||||
}
|
||||
|
||||
void Assembler::subps(const Register& xd,
|
||||
const Register& xn,
|
||||
const Register& xm) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMTE));
|
||||
|
||||
Emit(0xbac00000 | Rd(xd) | RnSP(xn) | RmSP(xm));
|
||||
}
|
||||
|
||||
void Assembler::cpye(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d800400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyen(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d80c400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyern(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d808400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyewn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d804400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfe(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19800400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfen(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1980c400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfern(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19808400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfewn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19804400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfm(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19400400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfmn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1940c400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfmrn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19408400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfmwn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19404400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfp(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19000400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfpn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1900c400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfprn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19008400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyfpwn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x19004400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpym(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d400400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpymn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d40c400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpymrn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d408400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpymwn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d404400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyp(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d000400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpypn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d00c400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpyprn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d008400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::cpypwn(const Register& rd,
|
||||
const Register& rs,
|
||||
const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
|
||||
|
||||
Emit(0x1d004400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::sete(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x19c08400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::seten(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x19c0a400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setge(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x1dc08400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setgen(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x1dc0a400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setgm(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x1dc04400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setgmn(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x1dc06400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setgp(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x1dc00400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setgpn(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x1dc02400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setm(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x19c04400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setmn(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x19c06400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setp(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x19c00400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::setpn(const Register& rd,
|
||||
const Register& rn,
|
||||
const Register& rs) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
|
||||
VIXL_ASSERT(!AreAliased(rd, rn, rs));
|
||||
VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
|
||||
|
||||
Emit(0x19c02400 | Rd(rd) | Rn(rn) | Rs(rs));
|
||||
}
|
||||
|
||||
void Assembler::abs(const Register& rd, const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kCSSC));
|
||||
VIXL_ASSERT(rd.IsSameSizeAndType(rn));
|
||||
|
||||
Emit(0x5ac02000 | SF(rd) | Rd(rd) | Rn(rn));
|
||||
}
|
||||
|
||||
void Assembler::cnt(const Register& rd, const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kCSSC));
|
||||
VIXL_ASSERT(rd.IsSameSizeAndType(rn));
|
||||
|
||||
Emit(0x5ac01c00 | SF(rd) | Rd(rd) | Rn(rn));
|
||||
}
|
||||
|
||||
void Assembler::ctz(const Register& rd, const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kCSSC));
|
||||
VIXL_ASSERT(rd.IsSameSizeAndType(rn));
|
||||
|
||||
Emit(0x5ac01800 | SF(rd) | Rd(rd) | Rn(rn));
|
||||
}
|
||||
|
||||
#define MINMAX(V) \
|
||||
V(smax, 0x11c00000, 0x1ac06000, true) \
|
||||
V(smin, 0x11c80000, 0x1ac06800, true) \
|
||||
V(umax, 0x11c40000, 0x1ac06400, false) \
|
||||
V(umin, 0x11cc0000, 0x1ac06c00, false)
|
||||
|
||||
#define VIXL_DEFINE_ASM_FUNC(FN, IMMOP, REGOP, SIGNED) \
|
||||
void Assembler::FN(const Register& rd, \
|
||||
const Register& rn, \
|
||||
const Operand& op) { \
|
||||
VIXL_ASSERT(rd.IsSameSizeAndType(rn)); \
|
||||
Instr i = SF(rd) | Rd(rd) | Rn(rn); \
|
||||
if (op.IsImmediate()) { \
|
||||
int64_t imm = op.GetImmediate(); \
|
||||
i |= SIGNED ? ImmField<17, 10>(imm) : ImmUnsignedField<17, 10>(imm); \
|
||||
Emit(IMMOP | i); \
|
||||
} else { \
|
||||
VIXL_ASSERT(op.IsPlainRegister()); \
|
||||
VIXL_ASSERT(op.GetRegister().IsSameSizeAndType(rd)); \
|
||||
Emit(REGOP | i | Rm(op.GetRegister())); \
|
||||
} \
|
||||
}
|
||||
MINMAX(VIXL_DEFINE_ASM_FUNC)
|
||||
#undef VIXL_DEFINE_ASM_FUNC
|
||||
|
||||
// NEON structure loads and stores.
|
||||
Instr Assembler::LoadStoreStructAddrModeField(const MemOperand& addr) {
|
||||
Instr addr_field = RnSP(addr.GetBaseRegister());
|
||||
@@ -2752,7 +3289,7 @@ void Assembler::fmov(const VRegister& vd, Float16 imm) {
|
||||
Emit(FMOV_h_imm | Rd(vd) | ImmFP16(imm));
|
||||
} else {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kNEON, CPUFeatures::kNEONHalf));
|
||||
VIXL_ASSERT(vd.Is4H() | vd.Is8H());
|
||||
VIXL_ASSERT(vd.Is4H() || vd.Is8H());
|
||||
Instr q = vd.Is8H() ? NEON_Q : 0;
|
||||
uint32_t encoded_imm = FP16ToImm8(imm);
|
||||
Emit(q | NEONModifiedImmediate_FMOV | ImmNEONabcdefgh(encoded_imm) |
|
||||
@@ -2782,7 +3319,8 @@ void Assembler::fmov(const Register& rd, const VRegister& vn) {
|
||||
|
||||
|
||||
void Assembler::fmov(const VRegister& vd, const Register& rn) {
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kFP));
|
||||
VIXL_ASSERT(CPUHas(CPUFeatures::kFP) ||
|
||||
(vd.Is1D() && CPUHas(CPUFeatures::kNEON)));
|
||||
VIXL_ASSERT(vd.Is1H() || vd.Is1S() || vd.Is1D());
|
||||
VIXL_ASSERT((vd.GetSizeInBits() == rn.GetSizeInBits()) || vd.Is1H());
|
||||
FPIntegerConvertOp op;
|
||||
@@ -5311,9 +5849,9 @@ Instr Assembler::ImmFP16(Float16 imm) {
|
||||
|
||||
|
||||
uint32_t Assembler::FP32ToImm8(float imm) {
|
||||
VIXL_ASSERT(IsImmFP32(imm));
|
||||
// bits: aBbb.bbbc.defg.h000.0000.0000.0000.0000
|
||||
uint32_t bits = FloatToRawbits(imm);
|
||||
VIXL_ASSERT(IsImmFP32(bits));
|
||||
// bit7: a000.0000
|
||||
uint32_t bit7 = ((bits >> 31) & 0x1) << 7;
|
||||
// bit6: 0b00.0000
|
||||
@@ -5329,10 +5867,10 @@ Instr Assembler::ImmFP32(float imm) { return FP32ToImm8(imm) << ImmFP_offset; }
|
||||
|
||||
|
||||
uint32_t Assembler::FP64ToImm8(double imm) {
|
||||
VIXL_ASSERT(IsImmFP64(imm));
|
||||
// bits: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
|
||||
// 0000.0000.0000.0000.0000.0000.0000.0000
|
||||
uint64_t bits = DoubleToRawbits(imm);
|
||||
VIXL_ASSERT(IsImmFP64(bits));
|
||||
// bit7: a000.0000
|
||||
uint64_t bit7 = ((bits >> 63) & 0x1) << 7;
|
||||
// bit6: 0b00.0000
|
||||
@@ -5886,10 +6424,9 @@ bool Assembler::IsImmFP16(Float16 imm) {
|
||||
}
|
||||
|
||||
|
||||
bool Assembler::IsImmFP32(float imm) {
|
||||
bool Assembler::IsImmFP32(uint32_t bits) {
|
||||
// Valid values will have the form:
|
||||
// aBbb.bbbc.defg.h000.0000.0000.0000.0000
|
||||
uint32_t bits = FloatToRawbits(imm);
|
||||
// bits[19..0] are cleared.
|
||||
if ((bits & 0x7ffff) != 0) {
|
||||
return false;
|
||||
@@ -5910,11 +6447,10 @@ bool Assembler::IsImmFP32(float imm) {
|
||||
}
|
||||
|
||||
|
||||
bool Assembler::IsImmFP64(double imm) {
|
||||
bool Assembler::IsImmFP64(uint64_t bits) {
|
||||
// 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 = DoubleToRawbits(imm);
|
||||
// bits[47..0] are cleared.
|
||||
if ((bits & 0x0000ffffffffffff) != 0) {
|
||||
return false;
|
||||
@@ -5936,16 +6472,18 @@ bool Assembler::IsImmFP64(double imm) {
|
||||
|
||||
|
||||
bool Assembler::IsImmLSPair(int64_t offset, unsigned access_size_in_bytes_log2) {
|
||||
const auto access_size_in_bytes = 1U << access_size_in_bytes_log2;
|
||||
VIXL_ASSERT(access_size_in_bytes_log2 <= kQRegSizeInBytesLog2);
|
||||
return IsMultiple(offset, 1 << access_size_in_bytes_log2) &&
|
||||
IsInt7(offset / (1 << access_size_in_bytes_log2));
|
||||
return IsMultiple(offset, access_size_in_bytes) &&
|
||||
IsInt7(offset / access_size_in_bytes);
|
||||
}
|
||||
|
||||
|
||||
bool Assembler::IsImmLSScaled(int64_t offset, unsigned access_size_in_bytes_log2) {
|
||||
const auto access_size_in_bytes = 1U << access_size_in_bytes_log2;
|
||||
VIXL_ASSERT(access_size_in_bytes_log2 <= kQRegSizeInBytesLog2);
|
||||
return IsMultiple(offset, 1 << access_size_in_bytes_log2) &&
|
||||
IsUint12(offset / (1 << access_size_in_bytes_log2));
|
||||
return IsMultiple(offset, access_size_in_bytes) &&
|
||||
IsUint12(offset / access_size_in_bytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "../globals-vixl.h"
|
||||
#include "../invalset-vixl.h"
|
||||
#include "../utils-vixl.h"
|
||||
|
||||
#include "operands-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
@@ -6926,6 +6927,176 @@ class Assembler : public vixl::internal::AssemblerBase {
|
||||
const ZRegister& zm,
|
||||
int index);
|
||||
|
||||
// Add with Tag.
|
||||
void addg(const Register& xd, const Register& xn, int offset, int tag_offset);
|
||||
|
||||
// Tag Mask Insert.
|
||||
void gmi(const Register& xd, const Register& xn, const Register& xm);
|
||||
|
||||
// Insert Random Tag.
|
||||
void irg(const Register& xd, const Register& xn, const Register& xm = xzr);
|
||||
|
||||
// Load Allocation Tag.
|
||||
void ldg(const Register& xt, const MemOperand& addr);
|
||||
|
||||
void StoreTagHelper(const Register& xt, const MemOperand& addr, Instr op);
|
||||
|
||||
// Store Allocation Tags.
|
||||
void st2g(const Register& xt, const MemOperand& addr);
|
||||
|
||||
// Store Allocation Tag.
|
||||
void stg(const Register& xt, const MemOperand& addr);
|
||||
|
||||
// Store Allocation Tag and Pair of registers.
|
||||
void stgp(const Register& xt1, const Register& xt2, const MemOperand& addr);
|
||||
|
||||
// Store Allocation Tags, Zeroing.
|
||||
void stz2g(const Register& xt, const MemOperand& addr);
|
||||
|
||||
// Store Allocation Tag, Zeroing.
|
||||
void stzg(const Register& xt, const MemOperand& addr);
|
||||
|
||||
// Subtract with Tag.
|
||||
void subg(const Register& xd, const Register& xn, int offset, int tag_offset);
|
||||
|
||||
// Subtract Pointer.
|
||||
void subp(const Register& xd, const Register& xn, const Register& xm);
|
||||
|
||||
// Subtract Pointer, setting Flags.
|
||||
void subps(const Register& xd, const Register& xn, const Register& xm);
|
||||
|
||||
// Compare with Tag.
|
||||
void cmpp(const Register& xn, const Register& xm) { subps(xzr, xn, xm); }
|
||||
|
||||
// Memory Copy.
|
||||
void cpye(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, reads and writes non-temporal.
|
||||
void cpyen(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, reads non-temporal.
|
||||
void cpyern(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, writes non-temporal.
|
||||
void cpyewn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only.
|
||||
void cpyfe(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, reads and writes non-temporal.
|
||||
void cpyfen(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, reads non-temporal.
|
||||
void cpyfern(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, writes non-temporal.
|
||||
void cpyfewn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only.
|
||||
void cpyfm(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, reads and writes non-temporal.
|
||||
void cpyfmn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, reads non-temporal.
|
||||
void cpyfmrn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, writes non-temporal.
|
||||
void cpyfmwn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only.
|
||||
void cpyfp(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, reads and writes non-temporal.
|
||||
void cpyfpn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, reads non-temporal.
|
||||
void cpyfprn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy Forward-only, writes non-temporal.
|
||||
void cpyfpwn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy.
|
||||
void cpym(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, reads and writes non-temporal.
|
||||
void cpymn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, reads non-temporal.
|
||||
void cpymrn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, writes non-temporal.
|
||||
void cpymwn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy.
|
||||
void cpyp(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, reads and writes non-temporal.
|
||||
void cpypn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, reads non-temporal.
|
||||
void cpyprn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Copy, writes non-temporal.
|
||||
void cpypwn(const Register& rd, const Register& rs, const Register& rn);
|
||||
|
||||
// Memory Set.
|
||||
void sete(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set, non-temporal.
|
||||
void seten(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set with tag setting.
|
||||
void setge(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set with tag setting, non-temporal.
|
||||
void setgen(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set with tag setting.
|
||||
void setgm(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set with tag setting, non-temporal.
|
||||
void setgmn(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set with tag setting.
|
||||
void setgp(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set with tag setting, non-temporal.
|
||||
void setgpn(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set.
|
||||
void setm(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set, non-temporal.
|
||||
void setmn(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set.
|
||||
void setp(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Memory Set, non-temporal.
|
||||
void setpn(const Register& rd, const Register& rn, const Register& rs);
|
||||
|
||||
// Absolute value.
|
||||
void abs(const Register& rd, const Register& rn);
|
||||
|
||||
// Count bits.
|
||||
void cnt(const Register& rd, const Register& rn);
|
||||
|
||||
// Count Trailing Zeros.
|
||||
void ctz(const Register& rd, const Register& rn);
|
||||
|
||||
// Signed Maximum.
|
||||
void smax(const Register& rd, const Register& rn, const Operand& op);
|
||||
|
||||
// Signed Minimum.
|
||||
void smin(const Register& rd, const Register& rn, const Operand& op);
|
||||
|
||||
// Unsigned Maximum.
|
||||
void umax(const Register& rd, const Register& rn, const Operand& op);
|
||||
|
||||
// Unsigned Minimum.
|
||||
void umin(const Register& rd, const Register& rn, const Operand& op);
|
||||
|
||||
// Emit generic instructions.
|
||||
|
||||
// Emit raw instructions into the instruction stream.
|
||||
@@ -7244,8 +7415,9 @@ class Assembler : public vixl::internal::AssemblerBase {
|
||||
}
|
||||
|
||||
static Instr ImmLSPair(int64_t imm7, unsigned access_size_in_bytes_log2) {
|
||||
VIXL_ASSERT(IsMultiple(imm7, 1 << access_size_in_bytes_log2));
|
||||
int64_t scaled_imm7 = imm7 / (1 << access_size_in_bytes_log2);
|
||||
const auto access_size_in_bytes = 1U << access_size_in_bytes_log2;
|
||||
VIXL_ASSERT(IsMultiple(imm7, access_size_in_bytes));
|
||||
int64_t scaled_imm7 = imm7 / access_size_in_bytes;
|
||||
VIXL_ASSERT(IsInt7(scaled_imm7));
|
||||
return TruncateToUint7(scaled_imm7) << ImmLSPair_offset;
|
||||
}
|
||||
@@ -7370,8 +7542,14 @@ class Assembler : public vixl::internal::AssemblerBase {
|
||||
static bool IsImmAddSub(int64_t immediate);
|
||||
static bool IsImmConditionalCompare(int64_t immediate);
|
||||
static bool IsImmFP16(Float16 imm);
|
||||
static bool IsImmFP32(float imm);
|
||||
static bool IsImmFP64(double imm);
|
||||
|
||||
static bool IsImmFP32(float imm) { return IsImmFP32(FloatToRawbits(imm)); }
|
||||
|
||||
static bool IsImmFP32(uint32_t bits);
|
||||
|
||||
static bool IsImmFP64(double imm) { return IsImmFP64(DoubleToRawbits(imm)); }
|
||||
|
||||
static bool IsImmFP64(uint64_t bits);
|
||||
static bool IsImmLogical(uint64_t value,
|
||||
unsigned width,
|
||||
unsigned* n = NULL,
|
||||
|
||||
@@ -6505,7 +6505,7 @@ void Assembler::fmov(const ZRegister& zd, const PRegisterM& pg, double imm) {
|
||||
|
||||
void Assembler::fmov(const ZRegister& zd, double imm) {
|
||||
if (IsPositiveZero(imm)) {
|
||||
dup(zd, imm);
|
||||
dup(zd, 0);
|
||||
} else {
|
||||
fdup(zd, imm);
|
||||
}
|
||||
|
||||
@@ -523,7 +523,15 @@ enum DataCacheOp {
|
||||
CVAP = CacheOpEncoder<3, 7, 12, 1>::value,
|
||||
CVADP = CacheOpEncoder<3, 7, 13, 1>::value,
|
||||
CIVAC = CacheOpEncoder<3, 7, 14, 1>::value,
|
||||
ZVA = CacheOpEncoder<3, 7, 4, 1>::value
|
||||
ZVA = CacheOpEncoder<3, 7, 4, 1>::value,
|
||||
GVA = CacheOpEncoder<3, 7, 4, 3>::value,
|
||||
GZVA = CacheOpEncoder<3, 7, 4, 4>::value,
|
||||
CGVAC = CacheOpEncoder<3, 7, 10, 3>::value,
|
||||
CGDVAC = CacheOpEncoder<3, 7, 10, 5>::value,
|
||||
CGVAP = CacheOpEncoder<3, 7, 12, 3>::value,
|
||||
CGDVAP = CacheOpEncoder<3, 7, 12, 5>::value,
|
||||
CIGVAC = CacheOpEncoder<3, 7, 14, 3>::value,
|
||||
CIGDVAC = CacheOpEncoder<3, 7, 14, 5>::value
|
||||
};
|
||||
|
||||
// Some SVE instructions support a predicate constraint pattern. This is
|
||||
|
||||
+71
-17
@@ -48,6 +48,7 @@ const IDRegister::Field AA64PFR0::kCSV3(60);
|
||||
const IDRegister::Field AA64PFR1::kBT(0);
|
||||
const IDRegister::Field AA64PFR1::kSSBS(4);
|
||||
const IDRegister::Field AA64PFR1::kMTE(8);
|
||||
const IDRegister::Field AA64PFR1::kSME(24);
|
||||
|
||||
const IDRegister::Field AA64ISAR0::kAES(4);
|
||||
const IDRegister::Field AA64ISAR0::kSHA1(8);
|
||||
@@ -78,7 +79,10 @@ const IDRegister::Field AA64ISAR1::kBF16(44);
|
||||
const IDRegister::Field AA64ISAR1::kDGH(48);
|
||||
const IDRegister::Field AA64ISAR1::kI8MM(52);
|
||||
|
||||
const IDRegister::Field AA64ISAR2::kWFXT(0);
|
||||
const IDRegister::Field AA64ISAR2::kRPRES(4);
|
||||
const IDRegister::Field AA64ISAR2::kMOPS(16);
|
||||
const IDRegister::Field AA64ISAR2::kCSSC(52);
|
||||
|
||||
const IDRegister::Field AA64MMFR0::kECV(60);
|
||||
|
||||
@@ -97,6 +101,14 @@ const IDRegister::Field AA64ZFR0::kI8MM(44);
|
||||
const IDRegister::Field AA64ZFR0::kF32MM(52);
|
||||
const IDRegister::Field AA64ZFR0::kF64MM(56);
|
||||
|
||||
const IDRegister::Field AA64SMFR0::kSMEf32f32(32, 1);
|
||||
const IDRegister::Field AA64SMFR0::kSMEb16f32(34, 1);
|
||||
const IDRegister::Field AA64SMFR0::kSMEf16f32(35, 1);
|
||||
const IDRegister::Field AA64SMFR0::kSMEi8i32(36);
|
||||
const IDRegister::Field AA64SMFR0::kSMEf64f64(48, 1);
|
||||
const IDRegister::Field AA64SMFR0::kSMEi16i64(52);
|
||||
const IDRegister::Field AA64SMFR0::kSMEfa64(63, 1);
|
||||
|
||||
CPUFeatures AA64PFR0::GetCPUFeatures() const {
|
||||
CPUFeatures f;
|
||||
if (Get(kFP) >= 0) f.Combine(CPUFeatures::kFP);
|
||||
@@ -119,6 +131,8 @@ CPUFeatures AA64PFR1::GetCPUFeatures() const {
|
||||
if (Get(kSSBS) >= 2) f.Combine(CPUFeatures::kSSBSControl);
|
||||
if (Get(kMTE) >= 1) f.Combine(CPUFeatures::kMTEInstructions);
|
||||
if (Get(kMTE) >= 2) f.Combine(CPUFeatures::kMTE);
|
||||
if (Get(kMTE) >= 3) f.Combine(CPUFeatures::kMTE3);
|
||||
if (Get(kSME) >= 1) f.Combine(CPUFeatures::kSME);
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -155,6 +169,7 @@ CPUFeatures AA64ISAR1::GetCPUFeatures() const {
|
||||
if (Get(kSB) >= 1) f.Combine(CPUFeatures::kSB);
|
||||
if (Get(kSPECRES) >= 1) f.Combine(CPUFeatures::kSPECRES);
|
||||
if (Get(kBF16) >= 1) f.Combine(CPUFeatures::kBF16);
|
||||
if (Get(kBF16) >= 2) f.Combine(CPUFeatures::kEBF16);
|
||||
if (Get(kDGH) >= 1) f.Combine(CPUFeatures::kDGH);
|
||||
if (Get(kI8MM) >= 1) f.Combine(CPUFeatures::kI8MM);
|
||||
|
||||
@@ -180,7 +195,10 @@ CPUFeatures AA64ISAR1::GetCPUFeatures() const {
|
||||
|
||||
CPUFeatures AA64ISAR2::GetCPUFeatures() const {
|
||||
CPUFeatures f;
|
||||
if (Get(kWFXT) >= 2) f.Combine(CPUFeatures::kWFXT);
|
||||
if (Get(kRPRES) >= 1) f.Combine(CPUFeatures::kRPRES);
|
||||
if (Get(kMOPS) >= 1) f.Combine(CPUFeatures::kMOPS);
|
||||
if (Get(kCSSC) >= 1) f.Combine(CPUFeatures::kCSSC);
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -213,6 +231,7 @@ CPUFeatures AA64ZFR0::GetCPUFeatures() const {
|
||||
if (Get(kSM4) >= 1) f.Combine(CPUFeatures::kSVESM4);
|
||||
if (Get(kSHA3) >= 1) f.Combine(CPUFeatures::kSVESHA3);
|
||||
if (Get(kBF16) >= 1) f.Combine(CPUFeatures::kSVEBF16);
|
||||
if (Get(kBF16) >= 2) f.Combine(CPUFeatures::kSVE_EBF16);
|
||||
if (Get(kBitPerm) >= 1) f.Combine(CPUFeatures::kSVEBitPerm);
|
||||
if (Get(kAES) >= 1) f.Combine(CPUFeatures::kSVEAES);
|
||||
if (Get(kAES) >= 2) f.Combine(CPUFeatures::kSVEPmull128);
|
||||
@@ -220,6 +239,18 @@ CPUFeatures AA64ZFR0::GetCPUFeatures() const {
|
||||
return f;
|
||||
}
|
||||
|
||||
CPUFeatures AA64SMFR0::GetCPUFeatures() const {
|
||||
CPUFeatures f;
|
||||
if (Get(kSMEf32f32) >= 1) f.Combine(CPUFeatures::kSMEf32f32);
|
||||
if (Get(kSMEb16f32) >= 1) f.Combine(CPUFeatures::kSMEb16f32);
|
||||
if (Get(kSMEf16f32) >= 1) f.Combine(CPUFeatures::kSMEf16f32);
|
||||
if (Get(kSMEi8i32) >= 15) f.Combine(CPUFeatures::kSMEi8i32);
|
||||
if (Get(kSMEf64f64) >= 1) f.Combine(CPUFeatures::kSMEf64f64);
|
||||
if (Get(kSMEi16i64) >= 15) f.Combine(CPUFeatures::kSMEi16i64);
|
||||
if (Get(kSMEfa64) >= 1) f.Combine(CPUFeatures::kSMEfa64);
|
||||
return f;
|
||||
}
|
||||
|
||||
int IDRegister::Get(IDRegister::Field field) const {
|
||||
int msb = field.GetMsb();
|
||||
int lsb = field.GetLsb();
|
||||
@@ -248,11 +279,11 @@ CPUFeatures CPU::InferCPUFeaturesFromOS(
|
||||
CPUFeatures::QueryIDRegistersOption option) {
|
||||
CPUFeatures features;
|
||||
|
||||
#if VIXL_USE_LINUX_HWCAP
|
||||
#ifdef VIXL_USE_LINUX_HWCAP
|
||||
// Map each set bit onto a feature. Ideally, we'd use HWCAP_* macros rather
|
||||
// than explicit bits, but explicit bits allow us to identify features that
|
||||
// the toolchain doesn't know about.
|
||||
static const CPUFeatures::Feature kFeatureBits[] =
|
||||
static const CPUFeatures::Feature kFeatureBitsLow[] =
|
||||
{// Bits 0-7
|
||||
CPUFeatures::kFP,
|
||||
CPUFeatures::kNEON,
|
||||
@@ -288,8 +319,11 @@ CPUFeatures CPU::InferCPUFeaturesFromOS(
|
||||
CPUFeatures::kSSBSControl,
|
||||
CPUFeatures::kSB,
|
||||
CPUFeatures::kPAuth,
|
||||
CPUFeatures::kPAuthGeneric,
|
||||
// Bits 32-39
|
||||
CPUFeatures::kPAuthGeneric};
|
||||
VIXL_STATIC_ASSERT(ArrayLength(kFeatureBitsLow) < 64);
|
||||
|
||||
static const CPUFeatures::Feature kFeatureBitsHigh[] =
|
||||
{// Bits 0-7
|
||||
CPUFeatures::kDCCVADP,
|
||||
CPUFeatures::kSVE2,
|
||||
CPUFeatures::kSVEAES,
|
||||
@@ -298,7 +332,7 @@ CPUFeatures CPU::InferCPUFeaturesFromOS(
|
||||
CPUFeatures::kSVESHA3,
|
||||
CPUFeatures::kSVESM4,
|
||||
CPUFeatures::kAXFlag,
|
||||
// Bits 40-47
|
||||
// Bits 8-15
|
||||
CPUFeatures::kFrintToFixedSizedInt,
|
||||
CPUFeatures::kSVEI8MM,
|
||||
CPUFeatures::kSVEF32MM,
|
||||
@@ -307,24 +341,43 @@ CPUFeatures CPU::InferCPUFeaturesFromOS(
|
||||
CPUFeatures::kI8MM,
|
||||
CPUFeatures::kBF16,
|
||||
CPUFeatures::kDGH,
|
||||
// Bits 48+
|
||||
// Bits 16-23
|
||||
CPUFeatures::kRNG,
|
||||
CPUFeatures::kBTI,
|
||||
CPUFeatures::kMTE,
|
||||
CPUFeatures::kECV,
|
||||
CPUFeatures::kAFP,
|
||||
CPUFeatures::kRPRES};
|
||||
CPUFeatures::kRPRES,
|
||||
CPUFeatures::kMTE3,
|
||||
CPUFeatures::kSME,
|
||||
// Bits 24-31
|
||||
CPUFeatures::kSMEi16i64,
|
||||
CPUFeatures::kSMEf64f64,
|
||||
CPUFeatures::kSMEi8i32,
|
||||
CPUFeatures::kSMEf16f32,
|
||||
CPUFeatures::kSMEb16f32,
|
||||
CPUFeatures::kSMEf32f32,
|
||||
CPUFeatures::kSMEfa64,
|
||||
CPUFeatures::kWFXT,
|
||||
// Bits 32-39
|
||||
CPUFeatures::kEBF16,
|
||||
CPUFeatures::kSVE_EBF16};
|
||||
VIXL_STATIC_ASSERT(ArrayLength(kFeatureBitsHigh) < 64);
|
||||
|
||||
uint64_t hwcap_low32 = getauxval(AT_HWCAP);
|
||||
uint64_t hwcap_high32 = getauxval(AT_HWCAP2);
|
||||
VIXL_ASSERT(IsUint32(hwcap_low32));
|
||||
VIXL_ASSERT(IsUint32(hwcap_high32));
|
||||
uint64_t hwcap = hwcap_low32 | (hwcap_high32 << 32);
|
||||
auto combine_features = [&features](uint64_t hwcap,
|
||||
const CPUFeatures::Feature* feature_array,
|
||||
size_t features_size) {
|
||||
for (size_t i = 0; i < features_size; i++) {
|
||||
if (hwcap & (UINT64_C(1) << i)) features.Combine(feature_array[i]);
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t hwcap_low = getauxval(AT_HWCAP);
|
||||
uint64_t hwcap_high = getauxval(AT_HWCAP2);
|
||||
|
||||
combine_features(hwcap_low, kFeatureBitsLow, ArrayLength(kFeatureBitsLow));
|
||||
combine_features(hwcap_high, kFeatureBitsHigh, ArrayLength(kFeatureBitsHigh));
|
||||
|
||||
VIXL_STATIC_ASSERT(ArrayLength(kFeatureBits) < 64);
|
||||
for (size_t i = 0; i < ArrayLength(kFeatureBits); i++) {
|
||||
if (hwcap & (UINT64_C(1) << i)) features.Combine(kFeatureBits[i]);
|
||||
}
|
||||
// MTE support from HWCAP2 signifies FEAT_MTE1 and FEAT_MTE2 support
|
||||
if (features.Has(CPUFeatures::kMTE)) {
|
||||
features.Combine(CPUFeatures::kMTEInstructions);
|
||||
@@ -425,7 +478,7 @@ int CPU::ReadSVEVectorLengthInBits() {
|
||||
}
|
||||
|
||||
|
||||
void CPU::EnsureIAndDCacheCoherency(void *address, size_t length) {
|
||||
void CPU::EnsureIAndDCacheCoherency(void* address, size_t length) {
|
||||
#ifdef __aarch64__
|
||||
// Implement the cache synchronisation for all targets where AArch64 is the
|
||||
// host, even if we're building the simulator for an AAarch64 host. This
|
||||
@@ -523,5 +576,6 @@ void CPU::EnsureIAndDCacheCoherency(void *address, size_t length) {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "../globals-vixl.h"
|
||||
|
||||
#include "instructions-aarch64.h"
|
||||
#include "simulator-aarch64.h"
|
||||
|
||||
#ifndef VIXL_INCLUDE_TARGET_AARCH64
|
||||
// The supporting .cc file is only compiled when the A64 target is selected.
|
||||
@@ -56,24 +57,24 @@ class IDRegister {
|
||||
public:
|
||||
enum Type { kUnsigned, kSigned };
|
||||
|
||||
static const int kMaxWidthInBits = 4;
|
||||
|
||||
// This needs to be constexpr so that fields have "constant initialisation".
|
||||
// This avoids initialisation order problems when these values are used to
|
||||
// (dynamically) initialise static variables, etc.
|
||||
explicit constexpr Field(int lsb, Type type = kUnsigned)
|
||||
: lsb_(lsb), type_(type) {}
|
||||
explicit constexpr Field(int lsb,
|
||||
int bitWidth = kMaxWidthInBits,
|
||||
Type type = kUnsigned)
|
||||
: lsb_(lsb), bitWidth_(bitWidth), type_(type) {}
|
||||
|
||||
static const int kMaxWidthInBits = 4;
|
||||
|
||||
int GetWidthInBits() const {
|
||||
// All current ID fields have four bits.
|
||||
return kMaxWidthInBits;
|
||||
}
|
||||
int GetWidthInBits() const { return bitWidth_; }
|
||||
int GetLsb() const { return lsb_; }
|
||||
int GetMsb() const { return lsb_ + GetWidthInBits() - 1; }
|
||||
Type GetType() const { return type_; }
|
||||
|
||||
private:
|
||||
int lsb_;
|
||||
int bitWidth_;
|
||||
Type type_;
|
||||
};
|
||||
|
||||
@@ -113,6 +114,7 @@ class AA64PFR1 : public IDRegister {
|
||||
static const Field kBT;
|
||||
static const Field kSSBS;
|
||||
static const Field kMTE;
|
||||
static const Field kSME;
|
||||
};
|
||||
|
||||
class AA64ISAR0 : public IDRegister {
|
||||
@@ -167,7 +169,10 @@ class AA64ISAR2 : public IDRegister {
|
||||
CPUFeatures GetCPUFeatures() const;
|
||||
|
||||
private:
|
||||
static const Field kWFXT;
|
||||
static const Field kRPRES;
|
||||
static const Field kMOPS;
|
||||
static const Field kCSSC;
|
||||
};
|
||||
|
||||
class AA64MMFR0 : public IDRegister {
|
||||
@@ -219,6 +224,22 @@ class AA64ZFR0 : public IDRegister {
|
||||
static const Field kF64MM;
|
||||
};
|
||||
|
||||
class AA64SMFR0 : public IDRegister {
|
||||
public:
|
||||
explicit AA64SMFR0(uint64_t value) : IDRegister(value) {}
|
||||
|
||||
CPUFeatures GetCPUFeatures() const;
|
||||
|
||||
private:
|
||||
static const Field kSMEf32f32;
|
||||
static const Field kSMEb16f32;
|
||||
static const Field kSMEf16f32;
|
||||
static const Field kSMEi8i32;
|
||||
static const Field kSMEf64f64;
|
||||
static const Field kSMEi16i64;
|
||||
static const Field kSMEfa64;
|
||||
};
|
||||
|
||||
class CPU {
|
||||
public:
|
||||
// Initialise CPU support.
|
||||
@@ -285,6 +306,7 @@ class CPU {
|
||||
V(AA64MMFR1, "ID_AA64MMFR1_EL1") \
|
||||
/* These registers are RES0 in the baseline Arm8.0. We can always safely */ \
|
||||
/* read them, but some compilers don't accept the symbolic names. */ \
|
||||
V(AA64SMFR0, "S3_0_C0_C4_5") \
|
||||
V(AA64ISAR2, "S3_0_C0_C6_2") \
|
||||
V(AA64MMFR2, "S3_0_C0_C7_2") \
|
||||
V(AA64ZFR0, "S3_0_C0_C4_4")
|
||||
|
||||
@@ -24,12 +24,13 @@
|
||||
// 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 "cpu-features-auditor-aarch64.h"
|
||||
|
||||
#include "cpu-features.h"
|
||||
#include "globals-vixl.h"
|
||||
#include "utils-vixl.h"
|
||||
#include "decoder-aarch64.h"
|
||||
|
||||
#include "cpu-features-auditor-aarch64.h"
|
||||
#include "decoder-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
@@ -505,8 +506,6 @@ void CPUFeaturesAuditor::VisitFPImmediate(const Instruction* instr) {
|
||||
|
||||
void CPUFeaturesAuditor::VisitFPIntegerConvert(const Instruction* instr) {
|
||||
RecordInstructionFeaturesScope scope(this);
|
||||
// All of these instructions require FP.
|
||||
scope.Record(CPUFeatures::kFP);
|
||||
switch (instr->Mask(FPIntegerConvertMask)) {
|
||||
case FCVTAS_wh:
|
||||
case FCVTAS_xh:
|
||||
@@ -536,17 +535,23 @@ void CPUFeaturesAuditor::VisitFPIntegerConvert(const Instruction* instr) {
|
||||
case SCVTF_hx:
|
||||
case UCVTF_hw:
|
||||
case UCVTF_hx:
|
||||
scope.Record(CPUFeatures::kFP);
|
||||
scope.Record(CPUFeatures::kFPHalf);
|
||||
return;
|
||||
case FMOV_dx:
|
||||
scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON);
|
||||
return;
|
||||
case FMOV_d1_x:
|
||||
case FMOV_x_d1:
|
||||
scope.Record(CPUFeatures::kFP);
|
||||
scope.Record(CPUFeatures::kNEON);
|
||||
return;
|
||||
case FJCVTZS:
|
||||
scope.Record(CPUFeatures::kFP);
|
||||
scope.Record(CPUFeatures::kJSCVT);
|
||||
return;
|
||||
default:
|
||||
// No special CPU features.
|
||||
scope.Record(CPUFeatures::kFP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1305,6 +1310,16 @@ void CPUFeaturesAuditor::VisitSystem(const Instruction* instr) {
|
||||
} else if (instr->Mask(SystemSysMask) == SYS) {
|
||||
switch (instr->GetSysOp()) {
|
||||
// DC instruction variants.
|
||||
case CGVAC:
|
||||
case CGDVAC:
|
||||
case CGVAP:
|
||||
case CGDVAP:
|
||||
case CIGVAC:
|
||||
case CIGDVAC:
|
||||
case GVA:
|
||||
case GZVA:
|
||||
scope.Record(CPUFeatures::kMTE);
|
||||
break;
|
||||
case CVAP:
|
||||
scope.Record(CPUFeatures::kDCPoP);
|
||||
break;
|
||||
@@ -1315,6 +1330,7 @@ void CPUFeaturesAuditor::VisitSystem(const Instruction* instr) {
|
||||
case CVAC:
|
||||
case CVAU:
|
||||
case CIVAC:
|
||||
case ZVA:
|
||||
// No special CPU features.
|
||||
break;
|
||||
}
|
||||
@@ -1723,6 +1739,92 @@ void CPUFeaturesAuditor::Visit(Metadata* metadata, const Instruction* instr) {
|
||||
CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)},
|
||||
{"sudot_z_zzzi_s"_h,
|
||||
CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)},
|
||||
{"addg_64_addsub_immtags"_h, CPUFeatures::kMTE},
|
||||
{"gmi_64g_dp_2src"_h, CPUFeatures::kMTE},
|
||||
{"irg_64i_dp_2src"_h, CPUFeatures::kMTE},
|
||||
{"ldg_64loffset_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"st2g_64soffset_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"st2g_64spost_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"st2g_64spre_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stgp_64_ldstpair_off"_h, CPUFeatures::kMTE},
|
||||
{"stgp_64_ldstpair_post"_h, CPUFeatures::kMTE},
|
||||
{"stgp_64_ldstpair_pre"_h, CPUFeatures::kMTE},
|
||||
{"stg_64soffset_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stg_64spost_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stg_64spre_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stz2g_64soffset_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stz2g_64spost_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stz2g_64spre_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stzg_64soffset_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stzg_64spost_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"stzg_64spre_ldsttags"_h, CPUFeatures::kMTE},
|
||||
{"subg_64_addsub_immtags"_h, CPUFeatures::kMTE},
|
||||
{"subps_64s_dp_2src"_h, CPUFeatures::kMTE},
|
||||
{"subp_64s_dp_2src"_h, CPUFeatures::kMTE},
|
||||
{"cpyen_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyern_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyewn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpye_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfen_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfern_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfewn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfe_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfmn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfmrn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfmwn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfm_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfpn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfprn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfpwn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyfp_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpymn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpymrn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpymwn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpym_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpypn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyprn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpypwn_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"cpyp_cpy_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"seten_set_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"sete_set_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"setgen_set_memcms"_h,
|
||||
CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
|
||||
{"setge_set_memcms"_h,
|
||||
CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
|
||||
{"setgmn_set_memcms"_h,
|
||||
CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
|
||||
{"setgm_set_memcms"_h,
|
||||
CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
|
||||
{"setgpn_set_memcms"_h,
|
||||
CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
|
||||
{"setgp_set_memcms"_h,
|
||||
CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
|
||||
{"setmn_set_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"setm_set_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"setpn_set_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"setp_set_memcms"_h, CPUFeatures::kMOPS},
|
||||
{"abs_32_dp_1src"_h, CPUFeatures::kCSSC},
|
||||
{"abs_64_dp_1src"_h, CPUFeatures::kCSSC},
|
||||
{"cnt_32_dp_1src"_h, CPUFeatures::kCSSC},
|
||||
{"cnt_64_dp_1src"_h, CPUFeatures::kCSSC},
|
||||
{"ctz_32_dp_1src"_h, CPUFeatures::kCSSC},
|
||||
{"ctz_64_dp_1src"_h, CPUFeatures::kCSSC},
|
||||
{"smax_32_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"smax_64_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"smin_32_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"smin_64_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"umax_32_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"umax_64_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"umin_32_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"umin_64_dp_2src"_h, CPUFeatures::kCSSC},
|
||||
{"smax_32_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"smax_64_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"smin_32_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"smin_64_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"umax_32u_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"umax_64u_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"umin_32u_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
{"umin_64u_minmax_imm"_h, CPUFeatures::kCSSC},
|
||||
};
|
||||
|
||||
if (features.count(form_hash) > 0) {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cpu-features.h"
|
||||
|
||||
#include "decoder-aarch64.h"
|
||||
#include "decoder-visitor-map-aarch64.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
// Copyright 2023, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of 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.
|
||||
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
#include "debugger-aarch64.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <limits>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
|
||||
Debugger::Debugger(Simulator* sim)
|
||||
: sim_(sim)
|
||||
#ifdef VIXL_USE_PANDA_ALLOC
|
||||
, debugger_cmds_(sim->GetAllocator().Adapter())
|
||||
, breakpoints_(sim->GetAllocator().Adapter())
|
||||
#endif
|
||||
, input_stream_(&std::cin)
|
||||
, ostream_(sim->GetOutputStream()) {
|
||||
// Register all basic debugger commands.
|
||||
RegisterCmd<HelpCmd>();
|
||||
RegisterCmd<BreakCmd>();
|
||||
RegisterCmd<StepCmd>();
|
||||
RegisterCmd<ContinueCmd>();
|
||||
RegisterCmd<PrintCmd>();
|
||||
RegisterCmd<TraceCmd>();
|
||||
RegisterCmd<GdbCmd>();
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
void Debugger::RegisterCmd() {
|
||||
#ifndef VIXL_USE_PANDA_ALLOC
|
||||
auto new_command = std::make_unique<T>(sim_);
|
||||
#else
|
||||
auto* new_command = sim_->GetAllocator().New<T>(sim_);
|
||||
#endif
|
||||
|
||||
// Check that the new command word and alias, don't already exist.
|
||||
std::string_view new_cmd_word = new_command->GetCommandWord();
|
||||
std::string_view new_cmd_alias = new_command->GetCommandAlias();
|
||||
for (const auto& cmd : debugger_cmds_) {
|
||||
std::string_view cmd_word = cmd->GetCommandWord();
|
||||
std::string_view cmd_alias = cmd->GetCommandAlias();
|
||||
|
||||
if (new_cmd_word == cmd_word) {
|
||||
VIXL_ABORT_WITH_MSG("Command word matches an existing command word.");
|
||||
} else if (new_cmd_word == cmd_alias) {
|
||||
VIXL_ABORT_WITH_MSG("Command word matches an existing command alias.");
|
||||
}
|
||||
|
||||
if (new_cmd_alias != "") {
|
||||
if (new_cmd_alias == cmd_word) {
|
||||
VIXL_ABORT_WITH_MSG("Command alias matches an existing command word.");
|
||||
} else if (new_cmd_alias == cmd_alias) {
|
||||
VIXL_ABORT_WITH_MSG("Command alias matches an existing command alias.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef VIXL_USE_PANDA_ALLOC
|
||||
debugger_cmds_.push_back(std::move(new_command));
|
||||
#else
|
||||
debugger_cmds_.push_back(new_command);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool Debugger::IsAtBreakpoint() const {
|
||||
return IsBreakpoint(reinterpret_cast<uint64_t>(sim_->ReadPc()));
|
||||
}
|
||||
|
||||
|
||||
void Debugger::Debug() {
|
||||
DebugReturn done = DebugContinue;
|
||||
while (done == DebugContinue) {
|
||||
// Disassemble the next instruction to execute.
|
||||
#ifndef PANDA_BUILD
|
||||
PrintDisassembler print_disasm = PrintDisassembler(ostream_);
|
||||
#else
|
||||
PrintDisassembler print_disasm = PrintDisassembler(sim_->GetAllocator(), ostream_);
|
||||
#endif
|
||||
print_disasm.Disassemble(sim_->ReadPc());
|
||||
|
||||
// Read the command line.
|
||||
fprintf(ostream_, "sim> ");
|
||||
std::string line;
|
||||
std::getline(*input_stream_, line);
|
||||
|
||||
// Remove all control characters from the command string.
|
||||
line.erase(std::remove_if(line.begin(),
|
||||
line.end(),
|
||||
[](char c) { return std::iscntrl(c); }),
|
||||
line.end());
|
||||
|
||||
// Assume input from std::cin has already been output (e.g: by a terminal)
|
||||
// but input from elsewhere (e.g: from a testing input stream) has not.
|
||||
if (input_stream_ != &std::cin) {
|
||||
fprintf(ostream_, "%s\n", line.c_str());
|
||||
}
|
||||
|
||||
// Parse the command into tokens.
|
||||
std::vector<std::string> tokenized_cmd = Tokenize(line);
|
||||
if (!tokenized_cmd.empty()) {
|
||||
done = ExecDebugCommand(tokenized_cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::optional<uint64_t> Debugger::ParseUint64String(std::string_view uint64_str,
|
||||
int base) {
|
||||
// Clear any previous errors.
|
||||
errno = 0;
|
||||
|
||||
// strtoull uses 0 to indicate that no conversion was possible so first
|
||||
// check that the string isn't zero.
|
||||
if (IsZeroUint64String(uint64_str, base)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cannot use stoi as it might not be possible to use exceptions.
|
||||
char* end;
|
||||
uint64_t value = std::strtoull(uint64_str.data(), &end, base);
|
||||
if (value == 0 || *end != '\0' || errno == ERANGE) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
std::optional<Debugger::RegisterParsedFormat> Debugger::ParseRegString(
|
||||
std::string_view reg_str) {
|
||||
// A register should only have 2 (e.g: X0) or 3 (e.g: X31) characters.
|
||||
if (reg_str.size() < 2 || reg_str.size() > 3) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check for aliases of registers.
|
||||
if (reg_str == "lr") {
|
||||
return {{'X', kLinkRegCode}};
|
||||
} else if (reg_str == "sp") {
|
||||
return {{'X', kSpRegCode}};
|
||||
}
|
||||
|
||||
unsigned max_reg_num;
|
||||
char reg_prefix = std::toupper(reg_str.front());
|
||||
switch (reg_prefix) {
|
||||
case 'W':
|
||||
VIXL_FALLTHROUGH();
|
||||
case 'X':
|
||||
max_reg_num = kNumberOfRegisters - 1;
|
||||
break;
|
||||
case 'V':
|
||||
max_reg_num = kNumberOfVRegisters - 1;
|
||||
break;
|
||||
case 'Z':
|
||||
max_reg_num = kNumberOfZRegisters - 1;
|
||||
break;
|
||||
case 'P':
|
||||
max_reg_num = kNumberOfPRegisters - 1;
|
||||
break;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string_view str_code = reg_str.substr(1, reg_str.size());
|
||||
auto reg_code = ParseUint64String(str_code, 10);
|
||||
if (!reg_code) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (*reg_code > max_reg_num) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return {{reg_prefix, *reg_code}};
|
||||
}
|
||||
|
||||
|
||||
void Debugger::PrintUsage() {
|
||||
for (const auto& cmd : debugger_cmds_) {
|
||||
// Print commands in the following format:
|
||||
// foo / f
|
||||
// foo <arg>
|
||||
// A description of the foo command.
|
||||
//
|
||||
|
||||
std::string_view cmd_word = cmd->GetCommandWord();
|
||||
std::string_view cmd_alias = cmd->GetCommandAlias();
|
||||
if (cmd_alias != "") {
|
||||
fprintf(ostream_, "%s / %s\n", cmd_word.data(), cmd_alias.data());
|
||||
} else {
|
||||
fprintf(ostream_, "%s\n", cmd_word.data());
|
||||
}
|
||||
|
||||
std::string_view args_str = cmd->GetArgsString();
|
||||
if (args_str != "") {
|
||||
fprintf(ostream_, "\t%s %s\n", cmd_word.data(), args_str.data());
|
||||
}
|
||||
|
||||
std::string_view description = cmd->GetDescription();
|
||||
if (description != "") {
|
||||
fprintf(ostream_, "\t%s\n", description.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> Debugger::Tokenize(std::string_view input_line,
|
||||
char separator) {
|
||||
std::vector<std::string> words;
|
||||
|
||||
if (input_line.empty()) {
|
||||
return words;
|
||||
}
|
||||
|
||||
for (auto separator_pos = input_line.find(separator);
|
||||
separator_pos != input_line.npos;
|
||||
separator_pos = input_line.find(separator)) {
|
||||
// Skip consecutive, repeated separators.
|
||||
if (separator_pos != 0) {
|
||||
words.push_back(std::string{input_line.substr(0, separator_pos)});
|
||||
}
|
||||
|
||||
// Remove characters up to and including the separator.
|
||||
input_line.remove_prefix(separator_pos + 1);
|
||||
}
|
||||
|
||||
// Add the rest of the string to the vector.
|
||||
words.push_back(std::string{input_line});
|
||||
|
||||
return words;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn Debugger::ExecDebugCommand(
|
||||
const std::vector<std::string>& tokenized_cmd) {
|
||||
std::string cmd_word = tokenized_cmd.front();
|
||||
for (const auto& cmd : debugger_cmds_) {
|
||||
if (cmd_word == cmd->GetCommandWord() ||
|
||||
cmd_word == cmd->GetCommandAlias()) {
|
||||
const std::vector<std::string> args(tokenized_cmd.begin() + 1,
|
||||
tokenized_cmd.end());
|
||||
|
||||
// Call the handler for the command and pass the arguments.
|
||||
return cmd->Action(args);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(ostream_, "Error: command '%s' not found\n", cmd_word.c_str());
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
bool Debugger::IsZeroUint64String(std::string_view uint64_str, int base) {
|
||||
// Remove any hex prefixes.
|
||||
if (base == 0 || base == 16) {
|
||||
std::string_view prefix = uint64_str.substr(0, 2);
|
||||
if (prefix == "0x" || prefix == "0X") {
|
||||
uint64_str.remove_prefix(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (uint64_str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check all remaining digits in the string for anything other than zero.
|
||||
for (char c : uint64_str) {
|
||||
if (c != '0') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
DebuggerCmd::DebuggerCmd(Simulator* sim,
|
||||
std::string_view cmd_word,
|
||||
std::string_view cmd_alias,
|
||||
std::string_view args_str,
|
||||
std::string_view description)
|
||||
:
|
||||
#ifdef PANDA_BUILD
|
||||
allocator_(sim->GetAllocator()),
|
||||
#endif
|
||||
sim_(sim),
|
||||
ostream_(sim->GetOutputStream()),
|
||||
command_word_(cmd_word, allocator_.Adapter()),
|
||||
command_alias_(cmd_alias, allocator_.Adapter()),
|
||||
args_str_(args_str, allocator_.Adapter()),
|
||||
description_(description, allocator_.Adapter()) {}
|
||||
|
||||
|
||||
DebugReturn HelpCmd::Action(const std::vector<std::string>& args) {
|
||||
USE(args);
|
||||
sim_->GetDebugger()->PrintUsage();
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn BreakCmd::Action(const std::vector<std::string>& args) {
|
||||
if (args.size() != 1) {
|
||||
fprintf(ostream_, "Error: Use `break <address>` to set a breakpoint\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
std::string arg = args.front();
|
||||
auto break_addr = Debugger::ParseUint64String(arg);
|
||||
if (!break_addr) {
|
||||
fprintf(ostream_, "Error: Use `break <address>` to set a breakpoint\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
if (sim_->GetDebugger()->IsBreakpoint(*break_addr)) {
|
||||
sim_->GetDebugger()->RemoveBreakpoint(*break_addr);
|
||||
fprintf(ostream_,
|
||||
"Breakpoint successfully removed at: 0x%" PRIx64 "\n",
|
||||
*break_addr);
|
||||
} else {
|
||||
sim_->GetDebugger()->RegisterBreakpoint(*break_addr);
|
||||
fprintf(ostream_,
|
||||
"Breakpoint successfully added at: 0x%" PRIx64 "\n",
|
||||
*break_addr);
|
||||
}
|
||||
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn StepCmd::Action(const std::vector<std::string>& args) {
|
||||
if (args.size() > 1) {
|
||||
fprintf(ostream_,
|
||||
"Error: use `step [number]` to step an optional number of"
|
||||
" instructions\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
// Step 1 instruction by default.
|
||||
std::optional<uint64_t> number_of_instructions_to_execute{1};
|
||||
|
||||
if (args.size() == 1) {
|
||||
// Parse the argument to step that number of instructions.
|
||||
std::string arg = args.front();
|
||||
number_of_instructions_to_execute = Debugger::ParseUint64String(arg);
|
||||
if (!number_of_instructions_to_execute) {
|
||||
fprintf(ostream_,
|
||||
"Error: use `step [number]` to step an optional number of"
|
||||
" instructions\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
}
|
||||
|
||||
while (!sim_->IsSimulationFinished() &&
|
||||
*number_of_instructions_to_execute > 0) {
|
||||
sim_->ExecuteInstruction();
|
||||
(*number_of_instructions_to_execute)--;
|
||||
|
||||
// The first instruction has already been printed by Debug() so only
|
||||
// enable instruction tracing after the first instruction has been
|
||||
// executed.
|
||||
sim_->SetTraceParameters(sim_->GetTraceParameters() | LOG_DISASM);
|
||||
}
|
||||
|
||||
// Disable instruction tracing after all instructions have been executed.
|
||||
sim_->SetTraceParameters(sim_->GetTraceParameters() & ~LOG_DISASM);
|
||||
|
||||
if (sim_->IsSimulationFinished()) {
|
||||
fprintf(ostream_,
|
||||
"Debugger at the end of simulation, leaving simulator...\n");
|
||||
return DebugExit;
|
||||
}
|
||||
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn ContinueCmd::Action(const std::vector<std::string>& args) {
|
||||
USE(args);
|
||||
|
||||
fprintf(ostream_, "Continuing...\n");
|
||||
|
||||
if (sim_->GetDebugger()->IsAtBreakpoint()) {
|
||||
// This breakpoint has already been hit, so execute it before continuing.
|
||||
sim_->ExecuteInstruction();
|
||||
}
|
||||
|
||||
return DebugExit;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn PrintCmd::Action(const std::vector<std::string>& args) {
|
||||
if (args.size() != 1) {
|
||||
fprintf(ostream_,
|
||||
"Error: use `print <register|all>` to print the contents of a"
|
||||
" specific register or all registers.\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
if (args.front() == "all") {
|
||||
sim_->PrintRegisters();
|
||||
sim_->PrintZRegisters();
|
||||
} else if (args.front() == "system") {
|
||||
sim_->PrintSystemRegisters();
|
||||
} else if (args.front() == "ffr") {
|
||||
sim_->PrintFFR();
|
||||
} else {
|
||||
auto reg = Debugger::ParseRegString(args.front());
|
||||
if (!reg) {
|
||||
fprintf(ostream_,
|
||||
"Error: incorrect register format, use e.g: X0, x0, etc...\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
// Ensure the stack pointer is printed instead of the zero register.
|
||||
if ((*reg).second == kSpRegCode) {
|
||||
(*reg).second = kSPRegInternalCode;
|
||||
}
|
||||
|
||||
// Registers are printed in different ways depending on their type.
|
||||
switch ((*reg).first) {
|
||||
case 'W':
|
||||
sim_->PrintRegister(
|
||||
(*reg).second,
|
||||
static_cast<Simulator::PrintRegisterFormat>(
|
||||
Simulator::PrintRegisterFormat::kPrintWReg |
|
||||
Simulator::PrintRegisterFormat::kPrintRegPartial));
|
||||
break;
|
||||
case 'X':
|
||||
sim_->PrintRegister((*reg).second,
|
||||
Simulator::PrintRegisterFormat::kPrintXReg);
|
||||
break;
|
||||
case 'V':
|
||||
sim_->PrintVRegister((*reg).second);
|
||||
break;
|
||||
case 'Z':
|
||||
sim_->PrintZRegister((*reg).second);
|
||||
break;
|
||||
case 'P':
|
||||
sim_->PrintPRegister((*reg).second);
|
||||
break;
|
||||
default:
|
||||
// ParseRegString should only allow valid register characters.
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn TraceCmd::Action(const std::vector<std::string>& args) {
|
||||
if (args.size() != 0) {
|
||||
fprintf(ostream_, "Error: use `trace` to toggle tracing of registers.\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
int trace_params = sim_->GetTraceParameters();
|
||||
if ((trace_params & LOG_ALL) != LOG_ALL) {
|
||||
fprintf(ostream_,
|
||||
"Enabling disassembly, registers and memory write tracing\n");
|
||||
sim_->SetTraceParameters(trace_params | LOG_ALL);
|
||||
} else {
|
||||
fprintf(ostream_,
|
||||
"Disabling disassembly, registers and memory write tracing\n");
|
||||
sim_->SetTraceParameters(trace_params & ~LOG_ALL);
|
||||
}
|
||||
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
DebugReturn GdbCmd::Action(const std::vector<std::string>& args) {
|
||||
if (args.size() != 0) {
|
||||
fprintf(ostream_,
|
||||
"Error: use `gdb` to enter GDB from the simulator debugger.\n");
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
HostBreakpoint();
|
||||
return DebugContinue;
|
||||
}
|
||||
|
||||
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
@@ -0,0 +1,282 @@
|
||||
// Copyright 2023, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of 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_AARCH64_DEBUGGER_AARCH64_H_
|
||||
#define VIXL_AARCH64_DEBUGGER_AARCH64_H_
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "../globals-vixl.h"
|
||||
#include "../utils-vixl.h"
|
||||
#include "cpu-features.h"
|
||||
|
||||
#include "abi-aarch64.h"
|
||||
#include "cpu-features-auditor-aarch64.h"
|
||||
#include "disasm-aarch64.h"
|
||||
#include "instructions-aarch64.h"
|
||||
#include "simulator-aarch64.h"
|
||||
#include "simulator-constants-aarch64.h"
|
||||
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
class Simulator;
|
||||
|
||||
enum DebugReturn { DebugContinue, DebugExit };
|
||||
|
||||
|
||||
// A debugger command that performs some action when used by the simulator
|
||||
// debugger.
|
||||
class DebuggerCmd {
|
||||
public:
|
||||
DebuggerCmd(Simulator* sim,
|
||||
std::string_view cmd_word,
|
||||
std::string_view cmd_alias,
|
||||
std::string_view usage,
|
||||
std::string_view description);
|
||||
virtual ~DebuggerCmd() {}
|
||||
|
||||
// Perform some action based on the arguments passed in. Returns true if the
|
||||
// debugger should exit after the action, false otherwise.
|
||||
virtual DebugReturn Action(const std::vector<std::string>& args) = 0;
|
||||
|
||||
// Return the command word.
|
||||
std::string_view GetCommandWord() { return command_word_; }
|
||||
// Return the alias for this command. Returns an empty string if this command
|
||||
// has no alias.
|
||||
std::string_view GetCommandAlias() { return command_alias_; }
|
||||
// Return this commands usage.
|
||||
std::string_view GetArgsString() { return args_str_; }
|
||||
// Return this commands description.
|
||||
std::string_view GetDescription() { return description_; }
|
||||
|
||||
protected:
|
||||
AllocatorWrapper allocator_;
|
||||
|
||||
// Simulator which this command will be performed on.
|
||||
Simulator* sim_;
|
||||
// Stream to output the result of the command to.
|
||||
FILE* ostream_;
|
||||
// Command word that, when given to the interactive debugger, calls Action.
|
||||
String command_word_;
|
||||
// Optional alias for the command_word.
|
||||
String command_alias_;
|
||||
// Optional string showing the arguments that can be passed to the command.
|
||||
String args_str_;
|
||||
// Optional description of the command.
|
||||
String description_;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Base debugger command handlers:
|
||||
//
|
||||
|
||||
|
||||
class HelpCmd : public DebuggerCmd {
|
||||
public:
|
||||
HelpCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim, "help", "h", "", "Display this help message.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
class BreakCmd : public DebuggerCmd {
|
||||
public:
|
||||
BreakCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"break",
|
||||
"b",
|
||||
"<address>",
|
||||
"Set or remove a breakpoint.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
class StepCmd : public DebuggerCmd {
|
||||
public:
|
||||
StepCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"step",
|
||||
"s",
|
||||
"[<n>]",
|
||||
"Step n instructions, default step 1 instruction.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
class ContinueCmd : public DebuggerCmd {
|
||||
public:
|
||||
ContinueCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"continue",
|
||||
"c",
|
||||
"",
|
||||
"Exit the debugger and continue executing instructions.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
class PrintCmd : public DebuggerCmd {
|
||||
public:
|
||||
PrintCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"print",
|
||||
"p",
|
||||
"<register|all|system>",
|
||||
"Print the contents of a register, all registers or all"
|
||||
" system registers.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
class TraceCmd : public DebuggerCmd {
|
||||
public:
|
||||
TraceCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"trace",
|
||||
"t",
|
||||
"",
|
||||
"Start/stop memory and register tracing.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
class GdbCmd : public DebuggerCmd {
|
||||
public:
|
||||
GdbCmd(Simulator* sim)
|
||||
: DebuggerCmd(sim,
|
||||
"gdb",
|
||||
"g",
|
||||
"",
|
||||
"Enter an already running instance of gdb.") {}
|
||||
|
||||
DebugReturn Action(const std::vector<std::string>& args) override;
|
||||
};
|
||||
|
||||
|
||||
// A debugger for the Simulator which takes input from the user in order to
|
||||
// control the running of the Simulator.
|
||||
class Debugger {
|
||||
public:
|
||||
// A pair consisting of a register character (e.g: W, X, V) and a register
|
||||
// code (e.g: 0, 1 ...31) which represents a single parsed register.
|
||||
//
|
||||
// Note: the register character is guaranteed to be upper case.
|
||||
using RegisterParsedFormat = std::pair<char, unsigned>;
|
||||
|
||||
Debugger(Simulator* sim);
|
||||
|
||||
// Set the input stream, from which commands are read, to a custom stream.
|
||||
void SetInputStream(std::istream* stream) { input_stream_ = stream; }
|
||||
|
||||
// Register a new command for the debugger.
|
||||
template <class T>
|
||||
void RegisterCmd();
|
||||
|
||||
// Set a breakpoint at the given address.
|
||||
void RegisterBreakpoint(uint64_t addr) { breakpoints_.insert(addr); }
|
||||
// Remove a breakpoint at the given address.
|
||||
void RemoveBreakpoint(uint64_t addr) { breakpoints_.erase(addr); }
|
||||
// Return true if the address is the location of a breakpoint.
|
||||
bool IsBreakpoint(uint64_t addr) const {
|
||||
return (breakpoints_.find(addr) != breakpoints_.end());
|
||||
}
|
||||
// Return true if the simulator pc is a breakpoint.
|
||||
bool IsAtBreakpoint() const;
|
||||
|
||||
// Main loop for the debugger. Keep prompting for user inputted debugger
|
||||
// commands and try to execute them until a command is given that exits the
|
||||
// interactive debugger.
|
||||
void Debug();
|
||||
|
||||
// Get an unsigned integer value from a string and return it in 'value'.
|
||||
// Base is used to determine the numeric base of the number to be read,
|
||||
// i.e: 8 for octal, 10 for decimal, 16 for hexadecimal and 0 for
|
||||
// auto-detect. Return true if an integer value was found, false otherwise.
|
||||
static std::optional<uint64_t> ParseUint64String(std::string_view uint64_str,
|
||||
int base = 0);
|
||||
|
||||
// Get a register from a string and return it in 'reg'. Return true if a
|
||||
// valid register character and code (e.g: W0, X29, V31) was found, false
|
||||
// otherwise.
|
||||
static std::optional<RegisterParsedFormat> ParseRegString(
|
||||
std::string_view reg_str);
|
||||
|
||||
// Print the usage of each debugger command.
|
||||
void PrintUsage();
|
||||
|
||||
private:
|
||||
// Split a string based on the separator given (a single space character by
|
||||
// default) and return as a std::vector of strings.
|
||||
static std::vector<std::string> Tokenize(std::string_view input_line,
|
||||
char separator = ' ');
|
||||
|
||||
// Try to execute a single debugger command.
|
||||
DebugReturn ExecDebugCommand(const std::vector<std::string>& tokenized_cmd);
|
||||
|
||||
// Return true if the string is zero, i.e: all characters in the string
|
||||
// (other than prefixes) are zero.
|
||||
static bool IsZeroUint64String(std::string_view uint64_str, int base);
|
||||
|
||||
// The simulator that this debugger acts on.
|
||||
Simulator* sim_;
|
||||
|
||||
// A vector of all commands recognised by the debugger.
|
||||
#ifndef VIXL_USE_PANDA_ALLOC
|
||||
std::vector<std::unique_ptr<DebuggerCmd>> debugger_cmds_;
|
||||
#else
|
||||
Vector<DebuggerCmd*> debugger_cmds_;
|
||||
#endif
|
||||
|
||||
// A list of all instruction addresses that, when executed by the
|
||||
// simulator, will start the interactive debugger if it hasn't already.
|
||||
UnorderedSet<uint64_t> breakpoints_;
|
||||
|
||||
// Input stream from which commands are read. Default is std::cin.
|
||||
std::istream* input_stream_;
|
||||
|
||||
// Output stream from the simulator.
|
||||
FILE* ostream_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
#endif // VIXL_AARCH64_DEBUGGER_AARCH64_H_
|
||||
@@ -24,12 +24,13 @@
|
||||
// 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 "decoder-aarch64.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../globals-vixl.h"
|
||||
#include "../utils-vixl.h"
|
||||
|
||||
#include "decoder-aarch64.h"
|
||||
#include "decoder-constants-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
@@ -149,9 +150,9 @@ void Decoder::RemoveVisitor(DecoderVisitor* visitor) {
|
||||
}
|
||||
|
||||
void Decoder::VisitNamedInstruction(const Instruction* instr,
|
||||
const std::string& name) {
|
||||
const std::string_view name) {
|
||||
std::list<DecoderVisitor*>::iterator it;
|
||||
Metadata m = {{"form", name}};
|
||||
Metadata m = {{"form", std::string(name)}};
|
||||
for (it = visitors_.begin(); it != visitors_.end(); it++) {
|
||||
(*it)->Visit(&m, instr);
|
||||
}
|
||||
@@ -194,10 +195,10 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
// masked result.
|
||||
uint64_t signature = (static_cast<uint64_t>(y) << 32) | x;
|
||||
switch (signature) {
|
||||
INSTANTIATE_TEMPLATE_M(00000001);
|
||||
INSTANTIATE_TEMPLATE_M(00000002);
|
||||
INSTANTIATE_TEMPLATE_M(00000010);
|
||||
INSTANTIATE_TEMPLATE_M(0000001f);
|
||||
INSTANTIATE_TEMPLATE_M(00000060);
|
||||
INSTANTIATE_TEMPLATE_M(000000df);
|
||||
INSTANTIATE_TEMPLATE_M(00000100);
|
||||
INSTANTIATE_TEMPLATE_M(00000200);
|
||||
INSTANTIATE_TEMPLATE_M(00000400);
|
||||
@@ -218,10 +219,10 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(00003800);
|
||||
INSTANTIATE_TEMPLATE_M(00003c00);
|
||||
INSTANTIATE_TEMPLATE_M(00013000);
|
||||
INSTANTIATE_TEMPLATE_M(00020000);
|
||||
INSTANTIATE_TEMPLATE_M(00020010);
|
||||
INSTANTIATE_TEMPLATE_M(000203e0);
|
||||
INSTANTIATE_TEMPLATE_M(000303e0);
|
||||
INSTANTIATE_TEMPLATE_M(00040000);
|
||||
INSTANTIATE_TEMPLATE_M(00040010);
|
||||
INSTANTIATE_TEMPLATE_M(00060000);
|
||||
INSTANTIATE_TEMPLATE_M(00061000);
|
||||
INSTANTIATE_TEMPLATE_M(00070000);
|
||||
@@ -232,19 +233,22 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(000f0010);
|
||||
INSTANTIATE_TEMPLATE_M(00100000);
|
||||
INSTANTIATE_TEMPLATE_M(00180000);
|
||||
INSTANTIATE_TEMPLATE_M(001d1c00);
|
||||
INSTANTIATE_TEMPLATE_M(001b1c00);
|
||||
INSTANTIATE_TEMPLATE_M(001f0000);
|
||||
INSTANTIATE_TEMPLATE_M(001f0018);
|
||||
INSTANTIATE_TEMPLATE_M(001f2000);
|
||||
INSTANTIATE_TEMPLATE_M(001f3000);
|
||||
INSTANTIATE_TEMPLATE_M(00400000);
|
||||
INSTANTIATE_TEMPLATE_M(00400018);
|
||||
INSTANTIATE_TEMPLATE_M(00400800);
|
||||
INSTANTIATE_TEMPLATE_M(00403000);
|
||||
INSTANTIATE_TEMPLATE_M(00500000);
|
||||
INSTANTIATE_TEMPLATE_M(00500800);
|
||||
INSTANTIATE_TEMPLATE_M(00583000);
|
||||
INSTANTIATE_TEMPLATE_M(005f0000);
|
||||
INSTANTIATE_TEMPLATE_M(00800000);
|
||||
INSTANTIATE_TEMPLATE_M(00800400);
|
||||
INSTANTIATE_TEMPLATE_M(00800c1e);
|
||||
INSTANTIATE_TEMPLATE_M(00800c1d);
|
||||
INSTANTIATE_TEMPLATE_M(0080101f);
|
||||
INSTANTIATE_TEMPLATE_M(00801c00);
|
||||
INSTANTIATE_TEMPLATE_M(00803000);
|
||||
@@ -257,15 +261,15 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(00c00200);
|
||||
INSTANTIATE_TEMPLATE_M(00c00400);
|
||||
INSTANTIATE_TEMPLATE_M(00c00c00);
|
||||
INSTANTIATE_TEMPLATE_M(00c00c1c);
|
||||
INSTANTIATE_TEMPLATE_M(00c00c19);
|
||||
INSTANTIATE_TEMPLATE_M(00c01000);
|
||||
INSTANTIATE_TEMPLATE_M(00c01400);
|
||||
INSTANTIATE_TEMPLATE_M(00c01c00);
|
||||
INSTANTIATE_TEMPLATE_M(00c02000);
|
||||
INSTANTIATE_TEMPLATE_M(00c03000);
|
||||
INSTANTIATE_TEMPLATE_M(00c03c00);
|
||||
INSTANTIATE_TEMPLATE_M(00c70000);
|
||||
INSTANTIATE_TEMPLATE_M(00c83000);
|
||||
INSTANTIATE_TEMPLATE_M(00cf0000);
|
||||
INSTANTIATE_TEMPLATE_M(00d00200);
|
||||
INSTANTIATE_TEMPLATE_M(00d80800);
|
||||
INSTANTIATE_TEMPLATE_M(00d81800);
|
||||
@@ -275,9 +279,9 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(00d92400);
|
||||
INSTANTIATE_TEMPLATE_M(00d93000);
|
||||
INSTANTIATE_TEMPLATE_M(00db0000);
|
||||
INSTANTIATE_TEMPLATE_M(00db2000);
|
||||
INSTANTIATE_TEMPLATE_M(00dc0000);
|
||||
INSTANTIATE_TEMPLATE_M(00dc2000);
|
||||
INSTANTIATE_TEMPLATE_M(00dd2000);
|
||||
INSTANTIATE_TEMPLATE_M(00df0000);
|
||||
INSTANTIATE_TEMPLATE_M(40000000);
|
||||
INSTANTIATE_TEMPLATE_M(40000010);
|
||||
@@ -286,12 +290,11 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(40002010);
|
||||
INSTANTIATE_TEMPLATE_M(40003000);
|
||||
INSTANTIATE_TEMPLATE_M(40003c00);
|
||||
INSTANTIATE_TEMPLATE_M(400f0000);
|
||||
INSTANTIATE_TEMPLATE_M(400f0400);
|
||||
INSTANTIATE_TEMPLATE_M(401f2000);
|
||||
INSTANTIATE_TEMPLATE_M(40400800);
|
||||
INSTANTIATE_TEMPLATE_M(40400c00);
|
||||
INSTANTIATE_TEMPLATE_M(40403c00);
|
||||
INSTANTIATE_TEMPLATE_M(405f0000);
|
||||
INSTANTIATE_TEMPLATE_M(40800000);
|
||||
INSTANTIATE_TEMPLATE_M(40800c00);
|
||||
INSTANTIATE_TEMPLATE_M(40802000);
|
||||
@@ -299,9 +302,10 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(40803400);
|
||||
INSTANTIATE_TEMPLATE_M(40803c00);
|
||||
INSTANTIATE_TEMPLATE_M(40c00000);
|
||||
INSTANTIATE_TEMPLATE_M(40c00400);
|
||||
INSTANTIATE_TEMPLATE_M(40c00800);
|
||||
INSTANTIATE_TEMPLATE_M(40c00c00);
|
||||
INSTANTIATE_TEMPLATE_M(40c00c10);
|
||||
INSTANTIATE_TEMPLATE_M(40c01c00);
|
||||
INSTANTIATE_TEMPLATE_M(40c02000);
|
||||
INSTANTIATE_TEMPLATE_M(40c02010);
|
||||
INSTANTIATE_TEMPLATE_M(40c02c00);
|
||||
@@ -313,16 +317,18 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_M(40d02010);
|
||||
INSTANTIATE_TEMPLATE_M(40d80000);
|
||||
INSTANTIATE_TEMPLATE_M(40d81800);
|
||||
INSTANTIATE_TEMPLATE_M(40dc0000);
|
||||
INSTANTIATE_TEMPLATE_M(bf20c000);
|
||||
INSTANTIATE_TEMPLATE_MV(00000003, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00000003, 00000003);
|
||||
INSTANTIATE_TEMPLATE_MV(00000006, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00000006, 00000006);
|
||||
INSTANTIATE_TEMPLATE_MV(00000007, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(0000001f, 0000001f);
|
||||
INSTANTIATE_TEMPLATE_MV(00000210, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e0, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e0, 000003e0);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e1, 000003e0);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e3, 000003e0);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e3, 000003e3);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e2, 000003e0);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e6, 000003e0);
|
||||
INSTANTIATE_TEMPLATE_MV(000003e6, 000003e6);
|
||||
INSTANTIATE_TEMPLATE_MV(00000c00, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00000fc0, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(000013e0, 00001000);
|
||||
@@ -333,11 +339,13 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_MV(00003000, 00002000);
|
||||
INSTANTIATE_TEMPLATE_MV(00003000, 00003000);
|
||||
INSTANTIATE_TEMPLATE_MV(00003010, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00003c00, 00003c00);
|
||||
INSTANTIATE_TEMPLATE_MV(00040010, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00060000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00061000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00070000, 00030000);
|
||||
INSTANTIATE_TEMPLATE_MV(0007309f, 0000001f);
|
||||
INSTANTIATE_TEMPLATE_MV(00073ee0, 00033060);
|
||||
INSTANTIATE_TEMPLATE_MV(00073f9f, 0000001f);
|
||||
INSTANTIATE_TEMPLATE_MV(000f0000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(000f0010, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00100200, 00000000);
|
||||
@@ -352,13 +360,13 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_MV(001f0000, 00100000);
|
||||
INSTANTIATE_TEMPLATE_MV(001f0000, 001f0000);
|
||||
INSTANTIATE_TEMPLATE_MV(001f3000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(001f3000, 00001000);
|
||||
INSTANTIATE_TEMPLATE_MV(001f3000, 001f0000);
|
||||
INSTANTIATE_TEMPLATE_MV(001f300f, 0000000d);
|
||||
INSTANTIATE_TEMPLATE_MV(001f301f, 0000000d);
|
||||
INSTANTIATE_TEMPLATE_MV(001f33e0, 000103e0);
|
||||
INSTANTIATE_TEMPLATE_MV(001f3800, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(00401000, 00400000);
|
||||
INSTANTIATE_TEMPLATE_MV(00403000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(005f3000, 001f0000);
|
||||
INSTANTIATE_TEMPLATE_MV(005f3000, 001f1000);
|
||||
INSTANTIATE_TEMPLATE_MV(00800010, 00000000);
|
||||
@@ -381,6 +389,7 @@ BitExtractFn DecodeNode::GetBitExtractFunctionHelper(uint32_t x, uint32_t y) {
|
||||
INSTANTIATE_TEMPLATE_MV(40002000, 40000000);
|
||||
INSTANTIATE_TEMPLATE_MV(40003c00, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(40040000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(401f2000, 401f0000);
|
||||
INSTANTIATE_TEMPLATE_MV(40800c00, 40000400);
|
||||
INSTANTIATE_TEMPLATE_MV(40c00000, 00000000);
|
||||
INSTANTIATE_TEMPLATE_MV(40c00000, 00400000);
|
||||
@@ -448,7 +457,7 @@ bool DecodeNode::TryCompileOptimisedDecodeTable(Decoder* decoder) {
|
||||
|
||||
// Set DecodeNode for when the instruction after masking doesn't match the
|
||||
// value.
|
||||
CompileNodeForBits(decoder, "unallocated", 0);
|
||||
CompileNodeForBits(decoder, String("unallocated", GetAllocator().Adapter()), 0);
|
||||
|
||||
// Set DecodeNode for when it does match.
|
||||
CompileNodeForBits(decoder, String(pattern_table_[0].handler, GetAllocator().Adapter()), 1);
|
||||
@@ -481,7 +490,8 @@ CompiledDecodeNode* DecodeNode::Compile(Decoder* decoder) {
|
||||
|
||||
// Create a compiled node that contains a table with an entry for every bit
|
||||
// pattern.
|
||||
CreateCompiledNode(bit_extract_fn, 1U << GetSampledBitsCount());
|
||||
CreateCompiledNode(bit_extract_fn,
|
||||
static_cast<size_t>(1) << GetSampledBitsCount());
|
||||
VIXL_ASSERT(compiled_node_ != NULL);
|
||||
|
||||
// When we find a pattern matches the representation, set the node's decode
|
||||
@@ -537,7 +547,7 @@ DecodeNode::MaskValuePair DecodeNode::GenerateMaskValuePair(
|
||||
}
|
||||
|
||||
uint32_t DecodeNode::GenerateOrderedPattern(uint32_t pattern) const {
|
||||
const std::vector<uint8_t>& sampled_bits = GetSampledBits();
|
||||
const auto& sampled_bits = GetSampledBits();
|
||||
uint64_t temp = 0xffffffffffffffff;
|
||||
|
||||
// Place symbols into the field of set bits. Symbols are two bits wide and
|
||||
|
||||
@@ -412,7 +412,7 @@ class Decoder {
|
||||
List<DecoderVisitor*>::iterator old_end_;
|
||||
};
|
||||
|
||||
void VisitNamedInstruction(const Instruction* instr, const std::string& name);
|
||||
void VisitNamedInstruction(const Instruction* instr, const std::string_view name);
|
||||
|
||||
#ifndef PANDA_BUILD
|
||||
std::list<DecoderVisitor*>* visitors() { return &visitors_; }
|
||||
@@ -508,7 +508,7 @@ class CompiledDecodeNode {
|
||||
|
||||
// Constructor for wrappers around visitor functions. These require no
|
||||
// decoding, so no bit extraction function or decode table is assigned.
|
||||
explicit CompiledDecodeNode(std::string iname, Decoder* decoder)
|
||||
explicit CompiledDecodeNode(const std::string_view iname, Decoder* decoder)
|
||||
: bit_extract_fn_(NULL),
|
||||
instruction_name_(iname),
|
||||
decode_table_(NULL),
|
||||
@@ -559,7 +559,7 @@ class CompiledDecodeNode {
|
||||
|
||||
// Visitor function that handles the instruction identified. Set only for
|
||||
// leaf nodes, where no extra decoding is required, otherwise NULL.
|
||||
std::string instruction_name_;
|
||||
std::string_view instruction_name_;
|
||||
|
||||
// Mapping table from instruction bits to next decode stage.
|
||||
CompiledDecodeNode** decode_table_;
|
||||
@@ -574,15 +574,15 @@ class DecodeNode {
|
||||
public:
|
||||
// Constructor for DecodeNode wrappers around visitor functions. These are
|
||||
// marked as "compiled", as there is no decoding left to do.
|
||||
explicit DecodeNode(const std::string& iname, Decoder* decoder)
|
||||
explicit DecodeNode(const std::string_view iname, Decoder* decoder)
|
||||
:
|
||||
#ifdef PANDA_BUILD
|
||||
allocator_(decoder->GetAllocator()),
|
||||
#endif
|
||||
name_(iname, allocator_.Adapter()),
|
||||
sampled_bits_(allocator_.Adapter()),
|
||||
sampled_bits_(kEmptySampledBits),
|
||||
instruction_name_(iname),
|
||||
pattern_table_(allocator_.Adapter()),
|
||||
pattern_table_(kEmptyPatternTable),
|
||||
decoder_(decoder),
|
||||
compiled_node_(NULL) {}
|
||||
|
||||
@@ -593,9 +593,9 @@ class DecodeNode {
|
||||
allocator_(decoder->GetAllocator()),
|
||||
#endif
|
||||
name_(map.name, allocator_.Adapter()),
|
||||
sampled_bits_(allocator_.Adapter()),
|
||||
sampled_bits_(map.sampled_bits),
|
||||
instruction_name_("node"),
|
||||
pattern_table_(allocator_.Adapter()),
|
||||
pattern_table_(map.mapping),
|
||||
decoder_(decoder),
|
||||
compiled_node_(NULL) {
|
||||
// With the current two bits per symbol encoding scheme, the maximum pattern
|
||||
@@ -620,7 +620,7 @@ class DecodeNode {
|
||||
}
|
||||
|
||||
// Get the bits sampled from the instruction by this node.
|
||||
const Vector<uint8_t>& GetSampledBits() const { return sampled_bits_; }
|
||||
const std::vector<uint8_t>& GetSampledBits() const { return sampled_bits_; }
|
||||
|
||||
// Get the number of bits sampled from the instruction by this node.
|
||||
size_t GetSampledBitsCount() const { return sampled_bits_.size(); }
|
||||
@@ -752,15 +752,15 @@ class DecodeNode {
|
||||
|
||||
// Vector of bits sampled from an instruction to determine which node to look
|
||||
// up next in the decode process.
|
||||
Vector<uint8_t> sampled_bits_;
|
||||
const std::vector<uint8_t>& sampled_bits_;
|
||||
static const std::vector<uint8_t> kEmptySampledBits;
|
||||
|
||||
// For leaf nodes, this is the name of the instruction form that the node
|
||||
// represents. For other nodes, this is always set to "node".
|
||||
std::string instruction_name_;
|
||||
std::string_view instruction_name_;
|
||||
|
||||
// Source mapping from bit pattern to name of next decode stage.
|
||||
Vector<DecodePattern> pattern_table_;
|
||||
const std::vector<DecodePattern>& pattern_table_;
|
||||
static const std::vector<DecodePattern> kEmptyPatternTable;
|
||||
|
||||
// Pointer to the decoder containing this node, used to call its visitor
|
||||
|
||||
+6189
-5068
File diff suppressed because it is too large
Load Diff
@@ -1464,8 +1464,6 @@
|
||||
{"crc32h_32c_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"crc32w_32c_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"crc32x_64c_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"gmi_64g_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"irg_64i_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"lslv_32_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"lslv_64_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
{"lsrv_32_dp_2src"_h, &VISITORCLASS::VisitDataProcessing2Source}, \
|
||||
@@ -2595,7 +2593,6 @@
|
||||
{"dsb_bo_barriers"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"hint_hm_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"mrs_rs_systemmove"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"msr_si_pstate"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"msr_sr_systemmove"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"psb_hc_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"sb_only_barriers"_h, &VISITORCLASS::VisitSystem}, \
|
||||
@@ -2641,19 +2638,17 @@
|
||||
&VISITORCLASS::VisitUnconditionalBranchToRegister}, \
|
||||
{"ret_64r_branch_reg"_h, \
|
||||
&VISITORCLASS::VisitUnconditionalBranchToRegister}, \
|
||||
{"addg_64_addsub_immtags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bcax_vvv16_crypto4"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfcvtn_asimdmisc_4s"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfdot_asimdelem_e"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfdot_asimdsame2_d"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlal_asimdelem_f"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlal_asimdsame2_f_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlal_asimdsame2_f"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmmla_asimdsame2_e"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"dsb_bon_barriers"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"eor3_vvv16_crypto4"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ld64b_64l_memop"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ldgm_64bulk_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ldg_64loffset_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ldtrb_32_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ldtrh_32_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ldtrsb_32_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
@@ -2677,33 +2672,15 @@
|
||||
{"sm3tt2b_vvv_crypto3_imm2"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"sm4ekey_vvv4_cryptosha512_3"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"sm4e_vv4_cryptosha512_2"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"st2g_64soffset_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"st2g_64spost_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"st2g_64spre_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"st64b_64l_memop"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"st64bv_64_memop"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"st64bv0_64_memop"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stgm_64bulk_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stgp_64_ldstpair_off"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stgp_64_ldstpair_post"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stgp_64_ldstpair_pre"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stg_64soffset_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stg_64spost_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stg_64spre_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"sttrb_32_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"sttrh_32_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"sttr_32_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"sttr_64_ldst_unpriv"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stz2g_64soffset_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stz2g_64spost_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stz2g_64spre_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stzgm_64bulk_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stzg_64soffset_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stzg_64spost_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"stzg_64spre_ldsttags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"subg_64_addsub_immtags"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"subps_64s_dp_2src"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"subp_64s_dp_2src"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"tcancel_ex_exception"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"tstart_br_systemresult"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"ttest_br_systemresult"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
@@ -2712,13 +2689,13 @@
|
||||
{"xar_vvv2_crypto3_imm6"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfcvt_z_p_z_s2bf"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfcvtnt_z_p_z_s2bf"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfdot_z_zzz_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfdot_z_zzzi_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalb_z_zzz_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalb_z_zzzi_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalt_z_zzz_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalt_z_zzzi_"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmmla_z_zzz_"_h, &VISITORCLASS::VisitUnimplemented}, { \
|
||||
{"bfdot_z_zzz"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfdot_z_zzzi"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalb_z_zzz"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalb_z_zzzi"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalt_z_zzz"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmlalt_z_zzzi"_h, &VISITORCLASS::VisitUnimplemented}, \
|
||||
{"bfmmla_z_zzz"_h, &VISITORCLASS::VisitUnimplemented}, { \
|
||||
"unallocated"_h, &VISITORCLASS::VisitUnallocated \
|
||||
}
|
||||
|
||||
@@ -2742,7 +2719,6 @@
|
||||
{"pacib1716_hi_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"pacibsp_hi_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"pacibz_hi_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"pssbb_only_barriers"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"sev_hi_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"sevl_hi_hints"_h, &VISITORCLASS::VisitSystem}, \
|
||||
{"ssbb_only_barriers"_h, &VISITORCLASS::VisitSystem}, \
|
||||
|
||||
+943
-702
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ class Disassembler : public DecoderVisitor {
|
||||
Disassembler();
|
||||
#else
|
||||
Disassembler() = delete;
|
||||
Disassembler(PandaAllocator* allocator);
|
||||
Disassembler(AllocatorWrapper allocator);
|
||||
#endif
|
||||
Disassembler(char* text_buffer, int buffer_size);
|
||||
virtual ~Disassembler();
|
||||
@@ -194,6 +194,10 @@ class Disassembler : public DecoderVisitor {
|
||||
void Disassemble_ZdaS_ZnB_ZmB(const Instruction* instr);
|
||||
void Disassemble_Vd4S_Vn16B_Vm16B(const Instruction* instr);
|
||||
|
||||
void DisassembleCpy(const Instruction* instr);
|
||||
void DisassembleSet(const Instruction* instr);
|
||||
void DisassembleMinMaxImm(const Instruction* instr);
|
||||
|
||||
void DisassembleSVEShiftLeftImm(const Instruction* instr);
|
||||
void DisassembleSVEShiftRightImm(const Instruction* instr);
|
||||
void DisassembleSVEAddSubCarry(const Instruction* instr);
|
||||
@@ -235,6 +239,15 @@ class Disassembler : public DecoderVisitor {
|
||||
void DisassembleNEONScalar2RegMiscOnlyD(const Instruction* instr);
|
||||
void DisassembleNEONFPScalar2RegMisc(const Instruction* instr);
|
||||
|
||||
void DisassembleMTELoadTag(const Instruction* instr);
|
||||
void DisassembleMTEStoreTag(const Instruction* instr);
|
||||
void DisassembleMTEStoreTagPair(const Instruction* instr);
|
||||
|
||||
void Disassemble_XdSP_XnSP_Xm(const Instruction* instr);
|
||||
void Disassemble_XdSP_XnSP_uimm6_uimm4(const Instruction* instr);
|
||||
void Disassemble_Xd_XnSP_Xm(const Instruction* instr);
|
||||
void Disassemble_Xd_XnSP_XmSP(const Instruction* instr);
|
||||
|
||||
void Format(const Instruction* instr,
|
||||
const char* mnemonic,
|
||||
const char* format0,
|
||||
@@ -315,7 +328,7 @@ class PrintDisassembler : public Disassembler {
|
||||
: cpu_features_auditor_(NULL),
|
||||
#else
|
||||
explicit PrintDisassembler(FILE* stream) = delete;
|
||||
explicit PrintDisassembler(PandaAllocator* allocator, FILE* stream)
|
||||
explicit PrintDisassembler(AllocatorWrapper allocator, FILE* stream)
|
||||
: Disassembler(allocator), cpu_features_auditor_(NULL), allocator_(allocator),
|
||||
#endif
|
||||
cpu_features_prefix_("// Needs: "),
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "instructions-aarch64.h"
|
||||
|
||||
#include "assembler-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
@@ -198,6 +199,7 @@ bool Instruction::CanTakeSVEMovprfx(uint32_t form_hash,
|
||||
case "decd_z_zs"_h:
|
||||
case "dech_z_zs"_h:
|
||||
case "decw_z_zs"_h:
|
||||
case "ext_z_zi_des"_h:
|
||||
case "faddp_z_p_zz"_h:
|
||||
case "fmaxnmp_z_p_zz"_h:
|
||||
case "fmaxp_z_p_zz"_h:
|
||||
@@ -210,7 +212,6 @@ bool Instruction::CanTakeSVEMovprfx(uint32_t form_hash,
|
||||
case "insr_z_v"_h:
|
||||
case "smaxp_z_p_zz"_h:
|
||||
case "sminp_z_p_zz"_h:
|
||||
case "splice_z_p_zz_con"_h:
|
||||
case "splice_z_p_zz_des"_h:
|
||||
case "sqcadd_z_zz"_h:
|
||||
case "sqdecd_z_zs"_h:
|
||||
|
||||
@@ -119,7 +119,7 @@ const uint64_t kTTBRMask = UINT64_C(1) << 55;
|
||||
|
||||
// We can't define a static kZRegSize because the size depends on the
|
||||
// implementation. However, it is sometimes useful to know the minimum and
|
||||
// maxmimum possible sizes.
|
||||
// maximum possible sizes.
|
||||
const unsigned kZRegMinSize = 128;
|
||||
const unsigned kZRegMinSizeLog2 = 7;
|
||||
const unsigned kZRegMinSizeInBytes = kZRegMinSize / 8;
|
||||
@@ -141,21 +141,25 @@ const unsigned kPRegMaxSizeLog2 = kZRegMaxSizeLog2 - 3;
|
||||
const unsigned kPRegMaxSizeInBytes = kPRegMaxSize / 8;
|
||||
const unsigned kPRegMaxSizeInBytesLog2 = kPRegMaxSizeLog2 - 3;
|
||||
|
||||
const unsigned kMTETagGranuleInBytes = 16;
|
||||
const unsigned kMTETagGranuleInBytesLog2 = 4;
|
||||
const unsigned kMTETagWidth = 4;
|
||||
|
||||
// Make these moved float constants backwards compatible
|
||||
// with explicit vixl::aarch64:: namespace references.
|
||||
using vixl::kDoubleMantissaBits;
|
||||
using vixl::kDoubleExponentBits;
|
||||
using vixl::kFloatMantissaBits;
|
||||
using vixl::kFloatExponentBits;
|
||||
using vixl::kFloat16MantissaBits;
|
||||
using vixl::kDoubleMantissaBits;
|
||||
using vixl::kFloat16ExponentBits;
|
||||
using vixl::kFloat16MantissaBits;
|
||||
using vixl::kFloatExponentBits;
|
||||
using vixl::kFloatMantissaBits;
|
||||
|
||||
using vixl::kFP16PositiveInfinity;
|
||||
using vixl::kFP16NegativeInfinity;
|
||||
using vixl::kFP32PositiveInfinity;
|
||||
using vixl::kFP16PositiveInfinity;
|
||||
using vixl::kFP32NegativeInfinity;
|
||||
using vixl::kFP64PositiveInfinity;
|
||||
using vixl::kFP32PositiveInfinity;
|
||||
using vixl::kFP64NegativeInfinity;
|
||||
using vixl::kFP64PositiveInfinity;
|
||||
|
||||
using vixl::kFP16DefaultNaN;
|
||||
using vixl::kFP32DefaultNaN;
|
||||
@@ -512,6 +516,65 @@ class Instruction {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsMOPSPrologueOf(const Instruction* instr, uint32_t mops_type) const {
|
||||
VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
|
||||
(mops_type == "cpy"_h));
|
||||
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
|
||||
return GetInstructionBits() == instr->Mask(~(0x3U << op_lsb));
|
||||
}
|
||||
|
||||
bool IsMOPSMainOf(const Instruction* instr, uint32_t mops_type) const {
|
||||
VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
|
||||
(mops_type == "cpy"_h));
|
||||
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
|
||||
return GetInstructionBits() ==
|
||||
(instr->Mask(~(0x3U << op_lsb)) | (0x1 << op_lsb));
|
||||
}
|
||||
|
||||
bool IsMOPSEpilogueOf(const Instruction* instr, uint32_t mops_type) const {
|
||||
VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
|
||||
(mops_type == "cpy"_h));
|
||||
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
|
||||
return GetInstructionBits() ==
|
||||
(instr->Mask(~(0x3U << op_lsb)) | (0x2 << op_lsb));
|
||||
}
|
||||
|
||||
template <uint32_t mops_type>
|
||||
bool IsConsistentMOPSTriplet() const {
|
||||
VIXL_STATIC_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
|
||||
(mops_type == "cpy"_h));
|
||||
|
||||
int64_t isize = static_cast<int64_t>(kInstructionSize);
|
||||
const Instruction* prev2 = GetInstructionAtOffset(-2 * isize);
|
||||
const Instruction* prev1 = GetInstructionAtOffset(-1 * isize);
|
||||
const Instruction* next1 = GetInstructionAtOffset(1 * isize);
|
||||
const Instruction* next2 = GetInstructionAtOffset(2 * isize);
|
||||
|
||||
// Use the encoding of the current instruction to determine the expected
|
||||
// adjacent instructions. NB. this doesn't check if the nearby instructions
|
||||
// are MOPS-type, but checks that they form a consistent triplet if they
|
||||
// are. For example, 'mov x0, #0; mov x0, #512; mov x0, #1024' is a
|
||||
// consistent triplet, but they are not MOPS instructions.
|
||||
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
|
||||
const uint32_t kMOPSOpfield = 0x3 << op_lsb;
|
||||
const uint32_t kMOPSPrologue = 0;
|
||||
const uint32_t kMOPSMain = 0x1 << op_lsb;
|
||||
const uint32_t kMOPSEpilogue = 0x2 << op_lsb;
|
||||
switch (Mask(kMOPSOpfield)) {
|
||||
case kMOPSPrologue:
|
||||
return next1->IsMOPSMainOf(this, mops_type) &&
|
||||
next2->IsMOPSEpilogueOf(this, mops_type);
|
||||
case kMOPSMain:
|
||||
return prev1->IsMOPSPrologueOf(this, mops_type) &&
|
||||
next1->IsMOPSEpilogueOf(this, mops_type);
|
||||
case kMOPSEpilogue:
|
||||
return prev2->IsMOPSPrologueOf(this, mops_type) &&
|
||||
prev1->IsMOPSMainOf(this, mops_type);
|
||||
default:
|
||||
VIXL_ABORT_WITH_MSG("Undefined MOPS operation\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int GetImmBranchRangeBitwidth(ImmBranchType branch_type);
|
||||
VIXL_DEPRECATED(
|
||||
"GetImmBranchRangeBitwidth",
|
||||
@@ -764,7 +827,7 @@ class NEONFormatDecoder {
|
||||
enum SubstitutionMode { kPlaceholder, kFormat };
|
||||
|
||||
// Construct a format decoder with increasingly specific format maps for each
|
||||
// subsitution. If no format map is specified, the default is the integer
|
||||
// substitution. If no format map is specified, the default is the integer
|
||||
// format map.
|
||||
explicit NEONFormatDecoder(const Instruction* instr) {
|
||||
instrbits_ = instr->GetInstructionBits();
|
||||
|
||||
@@ -167,6 +167,21 @@ SimFloat16 Simulator::UFixedToFloat16(uint64_t src,
|
||||
}
|
||||
|
||||
|
||||
uint64_t Simulator::GenerateRandomTag(uint16_t exclude) {
|
||||
uint64_t rtag = nrand48(rand_state_) >> 28;
|
||||
VIXL_ASSERT(IsUint4(rtag));
|
||||
|
||||
if (exclude == 0) {
|
||||
exclude = nrand48(rand_state_) >> 27;
|
||||
}
|
||||
|
||||
// TODO: implement this to better match the specification, which calls for a
|
||||
// true random mode, and a pseudo-random mode with state (EL1.TAG) modified by
|
||||
// PRNG.
|
||||
return ChooseNonExcludedTag(rtag, 0, exclude);
|
||||
}
|
||||
|
||||
|
||||
void Simulator::ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr) {
|
||||
dst.ClearForWrite(vform);
|
||||
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
|
||||
@@ -2202,7 +2217,6 @@ LogicVRegister Simulator::extractnarrow(VectorFormat dstform,
|
||||
offset = LaneCountFromFormat(dstform) / 2;
|
||||
} else {
|
||||
offset = 0;
|
||||
dst.ClearForWrite(dstform);
|
||||
}
|
||||
|
||||
for (int i = 0; i < LaneCountFromFormat(srcform); i++) {
|
||||
@@ -2242,6 +2256,10 @@ LogicVRegister Simulator::extractnarrow(VectorFormat dstform,
|
||||
dst.SetUint(dstform, offset + i, result);
|
||||
}
|
||||
}
|
||||
|
||||
if (!upperhalf) {
|
||||
dst.ClearForWrite(dstform);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -2284,7 +2302,7 @@ LogicVRegister Simulator::absdiff(VectorFormat vform,
|
||||
bool src1_gt_src2 = is_signed ? (src1.Int(vform, i) > src2.Int(vform, i))
|
||||
: (src1.Uint(vform, i) > src2.Uint(vform, i));
|
||||
// Always calculate the answer using unsigned arithmetic, to avoid
|
||||
// implemenation-defined signed overflow.
|
||||
// implementation-defined signed overflow.
|
||||
if (src1_gt_src2) {
|
||||
dst.SetUint(vform, i, src1.Uint(vform, i) - src2.Uint(vform, i));
|
||||
} else {
|
||||
@@ -7504,7 +7522,7 @@ void Simulator::SVEGatherLoadScalarPlusVectorHelper(const Instruction* instr,
|
||||
// Note that these instructions don't use the Dtype encoding.
|
||||
int msize_in_bytes_log2 = instr->ExtractBits(24, 23);
|
||||
int scale = instr->ExtractBit(21) * msize_in_bytes_log2;
|
||||
uint64_t base = ReadXRegister(instr->GetRn());
|
||||
uint64_t base = ReadXRegister(instr->GetRn(), Reg31IsStackPointer);
|
||||
LogicSVEAddressVector addr(base,
|
||||
&ReadVRegister(instr->GetRm()),
|
||||
vform,
|
||||
@@ -7813,7 +7831,7 @@ LogicVRegister Simulator::fmatmul(VectorFormat vform,
|
||||
for (int i = 0; i < LaneCountFromFormat(vform); i++) {
|
||||
// Elements outside a multiple of 4T are set to zero. This happens only
|
||||
// for double precision operations, when the VL is a multiple of 128 bits,
|
||||
// but not a mutiple of 256 bits.
|
||||
// but not a multiple of 256 bits.
|
||||
T value = (i < (T_per_segment * segment_count)) ? result[i] : 0;
|
||||
srcdst.SetFloat<T>(vform, i, value);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
// 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 <cctype>
|
||||
|
||||
#include "macro-assembler-aarch64.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
@@ -218,9 +218,8 @@ void VeneerPool::Release() {
|
||||
--monitor_;
|
||||
#ifndef PANDA_BUILD
|
||||
if (monitor_ == 0) {
|
||||
VIXL_ASSERT(IsEmpty() ||
|
||||
masm_->GetCursorOffset() <
|
||||
unresolved_branches_.GetFirstLimit());
|
||||
VIXL_ASSERT(IsEmpty() || masm_->GetCursorOffset() <
|
||||
unresolved_branches_.GetFirstLimit());
|
||||
}
|
||||
#else
|
||||
// Assert disabled, because we use own allocator
|
||||
@@ -1152,7 +1151,7 @@ void MacroAssembler::Movi64bitHelper(const VRegister& vd, uint64_t imm) {
|
||||
Register temp = temps.AcquireX();
|
||||
Mov(temp, imm);
|
||||
if (vd.Is1D()) {
|
||||
mov(vd.D(), 0, temp);
|
||||
fmov(vd.D(), temp);
|
||||
} else {
|
||||
dup(vd.V2D(), temp);
|
||||
}
|
||||
@@ -1188,8 +1187,14 @@ void MacroAssembler::Movi(const VRegister& vd,
|
||||
void MacroAssembler::Movi(const VRegister& vd, uint64_t hi, uint64_t lo) {
|
||||
// TODO: Move 128-bit values in a more efficient way.
|
||||
VIXL_ASSERT(vd.Is128Bits());
|
||||
Movi(vd.V2D(), lo);
|
||||
if (hi != lo) {
|
||||
if (hi == lo) {
|
||||
Movi(vd.V2D(), lo);
|
||||
return;
|
||||
}
|
||||
|
||||
Movi(vd.V1D(), lo);
|
||||
|
||||
if (hi != 0) {
|
||||
UseScratchRegisterScope temps(this);
|
||||
// TODO: Figure out if using a temporary V register to materialise the
|
||||
// immediate is better.
|
||||
@@ -1502,6 +1507,70 @@ void MacroAssembler::Adds(const Register& rd,
|
||||
Add(rd, rn, operand, SetFlags);
|
||||
}
|
||||
|
||||
#define MINMAX(V) \
|
||||
V(Smax, smax, IsInt8) \
|
||||
V(Smin, smin, IsInt8) \
|
||||
V(Umax, umax, IsUint8) \
|
||||
V(Umin, umin, IsUint8)
|
||||
|
||||
#define VIXL_DEFINE_MASM_FUNC(MASM, ASM, RANGE) \
|
||||
void MacroAssembler::MASM(const Register& rd, \
|
||||
const Register& rn, \
|
||||
const Operand& op) { \
|
||||
VIXL_ASSERT(allow_macro_instructions_); \
|
||||
if (op.IsImmediate()) { \
|
||||
int64_t imm = op.GetImmediate(); \
|
||||
if (!RANGE(imm)) { \
|
||||
UseScratchRegisterScope temps(this); \
|
||||
Register temp = temps.AcquireSameSizeAs(rd); \
|
||||
Mov(temp, imm); \
|
||||
MASM(rd, rn, temp); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
SingleEmissionCheckScope guard(this); \
|
||||
ASM(rd, rn, op); \
|
||||
}
|
||||
MINMAX(VIXL_DEFINE_MASM_FUNC)
|
||||
#undef VIXL_DEFINE_MASM_FUNC
|
||||
|
||||
void MacroAssembler::St2g(const Register& rt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
st2g(rt, addr);
|
||||
}
|
||||
|
||||
void MacroAssembler::Stg(const Register& rt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
stg(rt, addr);
|
||||
}
|
||||
|
||||
void MacroAssembler::Stgp(const Register& rt1,
|
||||
const Register& rt2,
|
||||
const MemOperand& addr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
stgp(rt1, rt2, addr);
|
||||
}
|
||||
|
||||
void MacroAssembler::Stz2g(const Register& rt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
stz2g(rt, addr);
|
||||
}
|
||||
|
||||
void MacroAssembler::Stzg(const Register& rt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
stzg(rt, addr);
|
||||
}
|
||||
|
||||
void MacroAssembler::Ldg(const Register& rt, const MemOperand& addr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
ldg(rt, addr);
|
||||
}
|
||||
|
||||
void MacroAssembler::Sub(const Register& rd,
|
||||
const Register& rn,
|
||||
@@ -1565,6 +1634,12 @@ void MacroAssembler::Fmov(VRegister vd, double imm) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
// Floating point immediates are loaded through the literal pool.
|
||||
MacroEmissionCheckScope guard(this);
|
||||
uint64_t rawbits = DoubleToRawbits(imm);
|
||||
|
||||
if (rawbits == 0) {
|
||||
fmov(vd.D(), xzr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vd.Is1H() || vd.Is4H() || vd.Is8H()) {
|
||||
Fmov(vd, Float16(imm));
|
||||
@@ -1577,27 +1652,20 @@ void MacroAssembler::Fmov(VRegister vd, double imm) {
|
||||
}
|
||||
|
||||
VIXL_ASSERT(vd.Is1D() || vd.Is2D());
|
||||
if (IsImmFP64(imm)) {
|
||||
if (IsImmFP64(rawbits)) {
|
||||
fmov(vd, imm);
|
||||
} else {
|
||||
uint64_t rawbits = DoubleToRawbits(imm);
|
||||
if (vd.IsScalar()) {
|
||||
if (rawbits == 0) {
|
||||
fmov(vd, xzr);
|
||||
} else {
|
||||
ldr(vd,
|
||||
} else if (vd.IsScalar()) {
|
||||
ldr(vd,
|
||||
#ifndef PANDA_BUILD
|
||||
new Literal<double>(imm,
|
||||
new Literal<double>(imm,
|
||||
#else
|
||||
allocator_.New<Literal<double>> (imm,
|
||||
allocator_.New<Literal<double>>(imm,
|
||||
#endif
|
||||
&literal_pool_,
|
||||
RawLiteral::kDeletedOnPlacementByPool));
|
||||
}
|
||||
} else {
|
||||
// TODO: consider NEON support for load literal.
|
||||
Movi(vd, rawbits);
|
||||
}
|
||||
&literal_pool_,
|
||||
RawLiteral::kDeletedOnPlacementByPool));
|
||||
} else {
|
||||
// TODO: consider NEON support for load literal.
|
||||
Movi(vd, rawbits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1606,6 +1674,12 @@ void MacroAssembler::Fmov(VRegister vd, float imm) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
// Floating point immediates are loaded through the literal pool.
|
||||
MacroEmissionCheckScope guard(this);
|
||||
uint32_t rawbits = FloatToRawbits(imm);
|
||||
|
||||
if (rawbits == 0) {
|
||||
fmov(vd.S(), wzr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vd.Is1H() || vd.Is4H() || vd.Is8H()) {
|
||||
Fmov(vd, Float16(imm));
|
||||
@@ -1618,27 +1692,20 @@ void MacroAssembler::Fmov(VRegister vd, float imm) {
|
||||
}
|
||||
|
||||
VIXL_ASSERT(vd.Is1S() || vd.Is2S() || vd.Is4S());
|
||||
if (IsImmFP32(imm)) {
|
||||
if (IsImmFP32(rawbits)) {
|
||||
fmov(vd, imm);
|
||||
} else {
|
||||
uint32_t rawbits = FloatToRawbits(imm);
|
||||
if (vd.IsScalar()) {
|
||||
if (rawbits == 0) {
|
||||
fmov(vd, wzr);
|
||||
} else {
|
||||
ldr(vd,
|
||||
} else if (vd.IsScalar()) {
|
||||
ldr(vd,
|
||||
#ifndef PANDA_BUILD
|
||||
new Literal<float>(imm,
|
||||
new Literal<float>(imm,
|
||||
#else
|
||||
allocator_.New<Literal<float>>(imm,
|
||||
allocator_.New<Literal<float>>(imm,
|
||||
#endif
|
||||
&literal_pool_,
|
||||
RawLiteral::kDeletedOnPlacementByPool));
|
||||
}
|
||||
} else {
|
||||
// TODO: consider NEON support for load literal.
|
||||
Movi(vd, rawbits);
|
||||
}
|
||||
&literal_pool_,
|
||||
RawLiteral::kDeletedOnPlacementByPool));
|
||||
} else {
|
||||
// TODO: consider NEON support for load literal.
|
||||
Movi(vd, rawbits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3203,7 +3270,6 @@ CPURegList* UseScratchRegisterScope::GetAvailableListFor(
|
||||
return masm_->GetScratchVRegisterList();
|
||||
case CPURegister::kPRegisterBank:
|
||||
return masm_->GetScratchPRegisterList();
|
||||
return NULL;
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return NULL;
|
||||
|
||||
@@ -1828,7 +1828,7 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
V(casah, Casah) \
|
||||
V(caslh, Caslh) \
|
||||
V(casalh, Casalh)
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
#define DEFINE_MACRO_ASM_FUNC(ASM, MASM) \
|
||||
void MASM(const Register& rs, const Register& rt, const MemOperand& src) { \
|
||||
@@ -1846,7 +1846,7 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
V(caspa, Caspa) \
|
||||
V(caspl, Caspl) \
|
||||
V(caspal, Caspal)
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
#define DEFINE_MACRO_ASM_FUNC(ASM, MASM) \
|
||||
void MASM(const Register& rs, \
|
||||
@@ -1891,7 +1891,7 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
V(MASM##alb, ASM##alb) \
|
||||
V(MASM##ah, ASM##ah) \
|
||||
V(MASM##alh, ASM##alh)
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
#define DEFINE_MACRO_LOAD_ASM_FUNC(MASM, ASM) \
|
||||
void MASM(const Register& rs, const Register& rt, const MemOperand& src) { \
|
||||
@@ -2807,6 +2807,44 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
uxtw(rd, rn);
|
||||
}
|
||||
|
||||
void Addg(const Register& xd,
|
||||
const Register& xn,
|
||||
int offset,
|
||||
int tag_offset) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
addg(xd, xn, offset, tag_offset);
|
||||
}
|
||||
void Gmi(const Register& xd, const Register& xn, const Register& xm) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
gmi(xd, xn, xm);
|
||||
}
|
||||
void Irg(const Register& xd, const Register& xn, const Register& xm = xzr) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
irg(xd, xn, xm);
|
||||
}
|
||||
void Subg(const Register& xd,
|
||||
const Register& xn,
|
||||
int offset,
|
||||
int tag_offset) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
subg(xd, xn, offset, tag_offset);
|
||||
}
|
||||
void Subp(const Register& xd, const Register& xn, const Register& xm) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
subp(xd, xn, xm);
|
||||
}
|
||||
void Subps(const Register& xd, const Register& xn, const Register& xm) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
subps(xd, xn, xm);
|
||||
}
|
||||
void Cmpp(const Register& xn, const Register& xm) { Subps(xzr, xn, xm); }
|
||||
|
||||
// NEON 3 vector register instructions.
|
||||
#define NEON_3VREG_MACRO_LIST(V) \
|
||||
V(add, Add) \
|
||||
@@ -3182,7 +3220,6 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
#define SVE_3VREG_COMMUTATIVE_MACRO_LIST(V) \
|
||||
V(add, Add) \
|
||||
V(and_, And) \
|
||||
V(bic, Bic) \
|
||||
V(eor, Eor) \
|
||||
V(mul, Mul) \
|
||||
V(orr, Orr) \
|
||||
@@ -3698,6 +3735,10 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
MovprfxHelperScope guard(this, zd, pg, zn);
|
||||
asrd(zd, pg, zd, shift);
|
||||
}
|
||||
void Bic(const ZRegister& zd,
|
||||
const PRegisterM& pg,
|
||||
const ZRegister& zn,
|
||||
const ZRegister& zm);
|
||||
void Bic(const PRegisterWithLaneSize& pd,
|
||||
const PRegisterZ& pg,
|
||||
const PRegisterWithLaneSize& pn,
|
||||
@@ -7613,6 +7654,279 @@ MacroAssembler(PandaAllocator* allocator, byte* buffer,
|
||||
const ZRegister& zm,
|
||||
int index);
|
||||
|
||||
// MTE
|
||||
void St2g(const Register& rt, const MemOperand& addr);
|
||||
void Stg(const Register& rt, const MemOperand& addr);
|
||||
void Stgp(const Register& rt1, const Register& rt2, const MemOperand& addr);
|
||||
void Stz2g(const Register& rt, const MemOperand& addr);
|
||||
void Stzg(const Register& rt, const MemOperand& addr);
|
||||
void Ldg(const Register& rt, const MemOperand& addr);
|
||||
|
||||
void Cpye(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpye(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyen(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyen(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyern(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyern(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyewn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyewn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfe(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfe(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfen(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfen(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfern(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfern(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfewn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfewn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfm(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfm(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfmn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfmn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfmrn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfmrn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfmwn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfmwn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfp(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfp(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfpn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfpn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfprn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfprn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyfpwn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyfpwn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpym(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpym(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpymn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpymn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpymrn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpymrn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpymwn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpymwn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyp(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyp(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpypn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpypn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpyprn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpyprn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Cpypwn(const Register& rd, const Register& rs, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cpypwn(rd, rs, rn);
|
||||
}
|
||||
|
||||
void Sete(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
sete(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Seten(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
seten(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setge(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setge(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setgen(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setgen(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setgm(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setgm(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setgmn(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setgmn(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setgp(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setgp(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setgpn(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setgpn(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setm(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setm(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setmn(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setmn(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setp(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setp(rd, rn, rs);
|
||||
}
|
||||
|
||||
void Setpn(const Register& rd, const Register& rn, const Register& rs) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
setpn(rd, rn, rs);
|
||||
}
|
||||
|
||||
// Macro assembler wrappers that package the MOPS instructions into a single
|
||||
// call.
|
||||
#define MOPS_LIST(V) \
|
||||
V(Set, set, ) \
|
||||
V(Setn, set, n) \
|
||||
V(Setg, setg, ) \
|
||||
V(Setgn, setg, n) \
|
||||
V(Cpy, cpy, ) \
|
||||
V(Cpyn, cpy, n) \
|
||||
V(Cpyrn, cpy, rn) \
|
||||
V(Cpywn, cpy, wn) \
|
||||
V(Cpyf, cpyf, ) \
|
||||
V(Cpyfn, cpyf, n) \
|
||||
V(Cpyfrn, cpyf, rn) \
|
||||
V(Cpyfwn, cpyf, wn)
|
||||
|
||||
#define DEFINE_MACRO_ASM_FUNC(MASM, ASMPREFIX, ASMSUFFIX) \
|
||||
void MASM(const Register& ra, const Register& rb, const Register& rc) { \
|
||||
ExactAssemblyScope scope(this, 3 * kInstructionSize); \
|
||||
ASMPREFIX##p##ASMSUFFIX(ra, rb, rc); \
|
||||
ASMPREFIX##m##ASMSUFFIX(ra, rb, rc); \
|
||||
ASMPREFIX##e##ASMSUFFIX(ra, rb, rc); \
|
||||
}
|
||||
MOPS_LIST(DEFINE_MACRO_ASM_FUNC)
|
||||
#undef DEFINE_MACRO_ASM_FUNC
|
||||
|
||||
void Abs(const Register& rd, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
abs(rd, rn);
|
||||
}
|
||||
|
||||
void Cnt(const Register& rd, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
cnt(rd, rn);
|
||||
}
|
||||
|
||||
void Ctz(const Register& rd, const Register& rn) {
|
||||
VIXL_ASSERT(allow_macro_instructions_);
|
||||
SingleEmissionCheckScope guard(this);
|
||||
ctz(rd, rn);
|
||||
}
|
||||
|
||||
void Smax(const Register& rd, const Register& rn, const Operand& op);
|
||||
void Smin(const Register& rd, const Register& rn, const Operand& op);
|
||||
void Umax(const Register& rd, const Register& rn, const Operand& op);
|
||||
void Umin(const Register& rd, const Register& rn, const Operand& op);
|
||||
|
||||
template <typename T>
|
||||
Literal<T>* CreateLiteralDestroyedWithPool(T value) {
|
||||
#ifndef PANDA_BUILD
|
||||
|
||||
@@ -627,6 +627,7 @@ VIXL_SVE_NONCOMM_ARITH_ZZZZII_LIST(VIXL_DEFINE_MASM_FUNC)
|
||||
// non-commutative and no reversed form is provided.
|
||||
#define VIXL_SVE_NONCOMM_ARITH_ZPZZ_LIST(V) \
|
||||
V(Addp, addp) \
|
||||
V(Bic, bic) \
|
||||
V(Faddp, faddp) \
|
||||
V(Fmaxnmp, fmaxnmp) \
|
||||
V(Fminnmp, fminnmp) \
|
||||
@@ -831,11 +832,12 @@ void MacroAssembler::Fdup(const ZRegister& zd, double imm) {
|
||||
Fdup(zd, static_cast<float>(imm));
|
||||
break;
|
||||
case kDRegSize:
|
||||
if (IsImmFP64(imm)) {
|
||||
uint64_t bits = DoubleToRawbits(imm);
|
||||
if (IsImmFP64(bits)) {
|
||||
SingleEmissionCheckScope guard(this);
|
||||
fdup(zd, imm);
|
||||
} else {
|
||||
Dup(zd, DoubleToRawbits(imm));
|
||||
Dup(zd, bits);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ CPURegister CPURegList::PopLowestIndex(RegList mask) {
|
||||
RegList list = list_ & mask;
|
||||
if (list == 0) return NoCPUReg;
|
||||
int index = CountTrailingZeros(list);
|
||||
VIXL_ASSERT(((1 << index) & list) != 0);
|
||||
VIXL_ASSERT(((static_cast<RegList>(1) << index) & list) != 0);
|
||||
Remove(index);
|
||||
return CPURegister(index, size_, type_);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ CPURegister CPURegList::PopHighestIndex(RegList mask) {
|
||||
if (list == 0) return NoCPUReg;
|
||||
int index = CountLeadingZeros(list);
|
||||
index = kRegListSizeInBits - 1 - index;
|
||||
VIXL_ASSERT(((1 << index) & list) != 0);
|
||||
VIXL_ASSERT(((static_cast<RegList>(1) << index) & list) != 0);
|
||||
Remove(index);
|
||||
return CPURegister(index, size_, type_);
|
||||
}
|
||||
@@ -457,5 +457,5 @@ bool GenericOperand::Equals(const GenericOperand& other) const {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace vixl::aarch64
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
@@ -739,7 +739,7 @@ class SVEMemOperand {
|
||||
class IntegerOperand {
|
||||
public:
|
||||
#define VIXL_INT_TYPES(V) \
|
||||
V(char) V(short) V(int) V(long) V(long long) // NOLINT(runtime/int)
|
||||
V(char) V(short) V(int) V(long) V(long long) // NOLINT(google-runtime-int)
|
||||
#define VIXL_DECL_INT_OVERLOADS(T) \
|
||||
/* These are allowed to be implicit constructors because this is a */ \
|
||||
/* wrapper class that doesn't normally perform any type conversion. */ \
|
||||
@@ -877,7 +877,7 @@ class IntegerOperand {
|
||||
return TryEncodeAsShiftedIntNForLane<N, 0>(zd, imm);
|
||||
}
|
||||
|
||||
// As above, but for unsigned fields. This is usuaully a simple operation, but
|
||||
// As above, but for unsigned fields. This is usually a simple operation, but
|
||||
// is provided for symmetry.
|
||||
template <unsigned N, unsigned kShift, typename T>
|
||||
bool TryEncodeAsShiftedUintNForLane(const CPURegister& zd, T* imm) const {
|
||||
@@ -913,7 +913,7 @@ class IntegerOperand {
|
||||
bool IsPositiveOrZero() const { return !is_negative_; }
|
||||
|
||||
uint64_t GetMagnitude() const {
|
||||
return is_negative_ ? -raw_bits_ : raw_bits_;
|
||||
return is_negative_ ? UnsignedNegate(raw_bits_) : raw_bits_;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -997,7 +997,7 @@ class GenericOperand {
|
||||
// We only support sizes up to X/D register sizes.
|
||||
size_t mem_op_size_;
|
||||
};
|
||||
}
|
||||
} // namespace vixl::aarch64
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_AARCH64_OPERANDS_AARCH64_H_
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
|
||||
|
||||
#include "simulator-aarch64.h"
|
||||
|
||||
#include "utils-vixl.h"
|
||||
|
||||
#include "simulator-aarch64.h"
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ class PRegisterZ;
|
||||
// specialised register types can avoid run-time checks, and should therefore be
|
||||
// preferred where run-time polymorphism isn't required.
|
||||
//
|
||||
// Type-specific modifers are typically implemented only on the derived classes.
|
||||
// Type-specific modifiers are typically implemented only on the derived
|
||||
// classes.
|
||||
//
|
||||
// The encoding is such that CPURegister objects are cheap to pass by value.
|
||||
class CPURegister {
|
||||
@@ -859,7 +860,7 @@ AARCH64_REGISTER_CODE_LIST(VIXL_DEFINE_REGISTERS)
|
||||
AARCH64_P_REGISTER_CODE_LIST(VIXL_DEFINE_P_REGISTERS)
|
||||
#undef VIXL_DEFINE_P_REGISTERS
|
||||
|
||||
// Most coersions simply invoke the necessary constructor.
|
||||
// Most coercions simply invoke the necessary constructor.
|
||||
#define VIXL_CPUREG_COERCION_LIST(U) \
|
||||
U(Register, W, R) \
|
||||
U(Register, X, R) \
|
||||
@@ -880,7 +881,7 @@ VIXL_CPUREG_COERCION_LIST(VIXL_DEFINE_CPUREG_COERCION)
|
||||
#undef VIXL_CPUREG_COERCION_LIST
|
||||
#undef VIXL_DEFINE_CPUREG_COERCION
|
||||
|
||||
// NEON lane-format coersions always return VRegisters.
|
||||
// NEON lane-format coercions always return VRegisters.
|
||||
#define VIXL_CPUREG_NEON_COERCION_LIST(V) \
|
||||
V(8, B) \
|
||||
V(16, B) \
|
||||
@@ -900,6 +901,8 @@ VIXL_CPUREG_NEON_COERCION_LIST(VIXL_DEFINE_CPUREG_NEON_COERCION)
|
||||
#undef VIXL_CPUREG_NEON_COERCION_LIST
|
||||
#undef VIXL_DEFINE_CPUREG_NEON_COERCION
|
||||
|
||||
// Semantic type coercion for sdot and udot.
|
||||
// TODO: Use the qualifiers_ field to distinguish this from ::S().
|
||||
VRegister VRegister::S4B() const {
|
||||
VIXL_ASSERT(IsVRegister());
|
||||
return SRegister(GetCode());
|
||||
@@ -1138,8 +1141,7 @@ bool AreSameLaneSize(const CPURegister& reg1,
|
||||
!reg4.IsValid() || (reg4.GetLaneSizeInBits() == reg1.GetLaneSizeInBits());
|
||||
return match;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace vixl::aarch64
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_AARCH64_REGISTERS_AARCH64_H_
|
||||
|
||||
+1226
-599
File diff suppressed because it is too large
Load Diff
+439
-24
@@ -33,10 +33,11 @@
|
||||
|
||||
#include "../globals-vixl.h"
|
||||
#include "../utils-vixl.h"
|
||||
|
||||
#include "cpu-features.h"
|
||||
|
||||
#include "abi-aarch64.h"
|
||||
#include "cpu-features-auditor-aarch64.h"
|
||||
#include "debugger-aarch64.h"
|
||||
#include "disasm-aarch64.h"
|
||||
#include "instructions-aarch64.h"
|
||||
#include "simulator-constants-aarch64.h"
|
||||
@@ -53,9 +54,20 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The hosts that Simulator running on may not have these flags defined.
|
||||
#ifndef PROT_BTI
|
||||
#define PROT_BTI 0x10
|
||||
#endif
|
||||
#ifndef PROT_MTE
|
||||
#define PROT_MTE 0x20
|
||||
#endif
|
||||
|
||||
namespace vixl {
|
||||
namespace aarch64 {
|
||||
|
||||
class Simulator;
|
||||
struct RuntimeCallStructHelper;
|
||||
|
||||
class SimStack {
|
||||
public:
|
||||
SimStack() {}
|
||||
@@ -153,46 +165,235 @@ class SimStack {
|
||||
static const size_t kDefaultUsableSize = 8 * 1024;
|
||||
};
|
||||
|
||||
// Armv8.5 MTE helpers.
|
||||
inline int GetAllocationTagFromAddress(uint64_t address) {
|
||||
return static_cast<int>(ExtractUnsignedBitfield64(59, 56, address));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T AddressUntag(T address) {
|
||||
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||||
// appropriate, but it can't cast one integral type to another.
|
||||
uint64_t bits = (uint64_t)address;
|
||||
return (T)(bits & ~kAddressTagMask);
|
||||
}
|
||||
|
||||
// A callback function, called when a function has been intercepted if a
|
||||
// BranchInterception entry exists in branch_interceptions. The address of
|
||||
// the intercepted function is passed to the callback. For usage see
|
||||
// BranchInterception.
|
||||
using InterceptionCallback = std::function<void(uint64_t)>;
|
||||
|
||||
class MetaDataDepot {
|
||||
public:
|
||||
class MetaDataMTE {
|
||||
public:
|
||||
explicit MetaDataMTE(int tag) : tag_(tag) {}
|
||||
|
||||
int GetTag() const { return tag_; }
|
||||
void SetTag(int tag) {
|
||||
VIXL_ASSERT(IsUint4(tag));
|
||||
tag_ = tag;
|
||||
}
|
||||
|
||||
static bool IsActive() { return is_active; }
|
||||
static void SetActive(bool value) { is_active = value; }
|
||||
|
||||
private:
|
||||
static bool is_active;
|
||||
int16_t tag_;
|
||||
|
||||
friend class MetaDataDepot;
|
||||
};
|
||||
|
||||
// Generate a key for metadata recording from a untagged address.
|
||||
template <typename T>
|
||||
uint64_t GenerateMTEkey(T address) const {
|
||||
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||||
// appropriate, but it can't cast one integral type to another.
|
||||
return (uint64_t)(AddressUntag(address)) >> kMTETagGranuleInBytesLog2;
|
||||
}
|
||||
|
||||
template <typename R, typename T>
|
||||
R GetAttribute(T map, uint64_t key) {
|
||||
auto pair = map->find(key);
|
||||
R value = (pair == map->end()) ? nullptr : &pair->second;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int GetMTETag(T address, Instruction const* pc = nullptr) {
|
||||
uint64_t key = GenerateMTEkey(address);
|
||||
MetaDataMTE* m = GetAttribute<MetaDataMTE*>(&metadata_mte_, key);
|
||||
|
||||
if (!m) {
|
||||
std::stringstream sstream;
|
||||
sstream << std::hex << "MTE ERROR : instruction at 0x"
|
||||
<< reinterpret_cast<uint64_t>(pc)
|
||||
<< " touched a unallocated memory location 0x"
|
||||
<< (uint64_t)(address) << ".\n";
|
||||
VIXL_ABORT_WITH_MSG(sstream.str().c_str());
|
||||
}
|
||||
|
||||
return m->GetTag();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetMTETag(T address, int tag, Instruction const* pc = nullptr) {
|
||||
VIXL_ASSERT(IsAligned((uintptr_t)address, kMTETagGranuleInBytes));
|
||||
uint64_t key = GenerateMTEkey(address);
|
||||
MetaDataMTE* m = GetAttribute<MetaDataMTE*>(&metadata_mte_, key);
|
||||
|
||||
if (!m) {
|
||||
metadata_mte_.insert({key, MetaDataMTE(tag)});
|
||||
} else {
|
||||
// Overwrite
|
||||
if (m->GetTag() == tag) {
|
||||
std::stringstream sstream;
|
||||
sstream << std::hex << "MTE WARNING : instruction at 0x"
|
||||
<< reinterpret_cast<uint64_t>(pc)
|
||||
<< ", the same tag is assigned to the address 0x"
|
||||
<< (uint64_t)(address) << ".\n";
|
||||
VIXL_WARNING(sstream.str().c_str());
|
||||
}
|
||||
m->SetTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t CleanMTETag(T address) {
|
||||
VIXL_ASSERT(
|
||||
IsAligned(reinterpret_cast<uintptr_t>(address), kMTETagGranuleInBytes));
|
||||
uint64_t key = GenerateMTEkey(address);
|
||||
return metadata_mte_.erase(key);
|
||||
}
|
||||
|
||||
size_t GetTotalCountMTE() { return metadata_mte_.size(); }
|
||||
|
||||
// A pure virtual struct that allows the templated BranchInterception struct
|
||||
// to be stored. For more information see BranchInterception.
|
||||
struct BranchInterceptionAbstract {
|
||||
virtual ~BranchInterceptionAbstract() {}
|
||||
// Call the callback_ if one exists, otherwise do a RuntimeCall.
|
||||
virtual void operator()(Simulator* simulator) const = 0;
|
||||
};
|
||||
|
||||
// An entry denoting a function to intercept when branched to during
|
||||
// simulator execution. When a function is intercepted the callback will be
|
||||
// called if one exists otherwise the function will be passed to
|
||||
// RuntimeCall.
|
||||
template <typename R, typename... P>
|
||||
struct BranchInterception : public BranchInterceptionAbstract {
|
||||
BranchInterception(R (*function)(P...),
|
||||
InterceptionCallback callback = nullptr)
|
||||
: function_(function), callback_(callback) {}
|
||||
|
||||
void operator()(Simulator* simulator) const VIXL_OVERRIDE;
|
||||
|
||||
private:
|
||||
// Pointer to the function that will be intercepted.
|
||||
R (*function_)(P...);
|
||||
|
||||
// Function to be called instead of function_
|
||||
InterceptionCallback callback_;
|
||||
};
|
||||
|
||||
// Register a new BranchInterception object. If 'function' is branched to
|
||||
// (e.g: "blr function") in the future; instead, if provided, 'callback' will
|
||||
// be called otherwise a runtime call will be performed on 'function'.
|
||||
//
|
||||
// For example: this can be used to always perform runtime calls on
|
||||
// non-AArch64 functions without using the macroassembler.
|
||||
//
|
||||
// Note: only unconditional branches to registers are currently supported to
|
||||
// be intercepted, e.g: "br"/"blr".
|
||||
//
|
||||
// TODO: support intercepting other branch types.
|
||||
template <typename R, typename... P>
|
||||
void RegisterBranchInterception(R (*function)(P...),
|
||||
InterceptionCallback callback = nullptr) {
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(function);
|
||||
std::unique_ptr<BranchInterceptionAbstract> intercept =
|
||||
std::make_unique<BranchInterception<R, P...>>(function, callback);
|
||||
branch_interceptions_.insert(std::make_pair(addr, std::move(intercept)));
|
||||
}
|
||||
|
||||
// Search for branch interceptions to the branch_target address; If one is
|
||||
// found return it otherwise return nullptr.
|
||||
BranchInterceptionAbstract* FindBranchInterception(uint64_t branch_target) {
|
||||
// Check for interceptions to the target address, if one is found, call it.
|
||||
auto search = branch_interceptions_.find(branch_target);
|
||||
if (search != branch_interceptions_.end()) {
|
||||
return search->second.get();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetState() { branch_interceptions_.clear(); }
|
||||
|
||||
private:
|
||||
// Tag recording of each allocated memory in the tag-granule.
|
||||
std::unordered_map<uint64_t, class MetaDataMTE> metadata_mte_;
|
||||
|
||||
// Store a map of addresses to be intercepted and their corresponding branch
|
||||
// interception object, see 'BranchInterception'.
|
||||
std::unordered_map<uintptr_t, std::unique_ptr<BranchInterceptionAbstract>>
|
||||
branch_interceptions_;
|
||||
};
|
||||
|
||||
|
||||
// Representation of memory, with typed getters and setters for access.
|
||||
class Memory {
|
||||
public:
|
||||
explicit Memory(SimStack::Allocated stack) : stack_(std::move(stack)) {}
|
||||
explicit Memory(SimStack::Allocated stack) : stack_(std::move(stack)) {
|
||||
metadata_depot_ = nullptr;
|
||||
}
|
||||
|
||||
const SimStack::Allocated& GetStack() { return stack_; }
|
||||
|
||||
template <typename T>
|
||||
T AddressUntag(T address) const {
|
||||
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||||
// appropriate, but it can't cast one integral type to another.
|
||||
uint64_t bits = (uint64_t)address;
|
||||
return (T)(bits & ~kAddressTagMask);
|
||||
template <typename A>
|
||||
bool IsMTETagsMatched(A address, Instruction const* pc = nullptr) const {
|
||||
if (MetaDataDepot::MetaDataMTE::IsActive()) {
|
||||
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||||
// appropriate, but it can't cast one integral type to another.
|
||||
uint64_t addr = (uint64_t)address;
|
||||
int pointer_tag = GetAllocationTagFromAddress(addr);
|
||||
int memory_tag = metadata_depot_->GetMTETag(AddressUntag(addr), pc);
|
||||
return pointer_tag == memory_tag;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
T Read(A address) const {
|
||||
T Read(A address, Instruction const* pc = nullptr) const {
|
||||
T value;
|
||||
address = AddressUntag(address);
|
||||
VIXL_STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
|
||||
(sizeof(value) == 4) || (sizeof(value) == 8) ||
|
||||
(sizeof(value) == 16));
|
||||
auto base = reinterpret_cast<const char*>(address);
|
||||
auto base = reinterpret_cast<const char*>(AddressUntag(address));
|
||||
if (stack_.IsAccessInGuardRegion(base, sizeof(value))) {
|
||||
VIXL_ABORT_WITH_MSG("Attempt to read from stack guard region");
|
||||
}
|
||||
if (!IsMTETagsMatched(address, pc)) {
|
||||
VIXL_ABORT_WITH_MSG("Tag mismatch.");
|
||||
}
|
||||
memcpy(&value, base, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
void Write(A address, T value) const {
|
||||
address = AddressUntag(address);
|
||||
void Write(A address, T value, Instruction const* pc = nullptr) const {
|
||||
VIXL_STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
|
||||
(sizeof(value) == 4) || (sizeof(value) == 8) ||
|
||||
(sizeof(value) == 16));
|
||||
auto base = reinterpret_cast<char*>(address);
|
||||
auto base = reinterpret_cast<char*>(AddressUntag(address));
|
||||
if (stack_.IsAccessInGuardRegion(base, sizeof(value))) {
|
||||
VIXL_ABORT_WITH_MSG("Attempt to write to stack guard region");
|
||||
}
|
||||
if (!IsMTETagsMatched(address, pc)) {
|
||||
VIXL_ABORT_WITH_MSG("Tag mismatch.");
|
||||
}
|
||||
memcpy(base, &value, sizeof(value));
|
||||
}
|
||||
|
||||
@@ -243,8 +444,15 @@ class Memory {
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
|
||||
void AppendMetaData(MetaDataDepot* metadata_depot) {
|
||||
VIXL_ASSERT(metadata_depot != nullptr);
|
||||
VIXL_ASSERT(metadata_depot_ == nullptr);
|
||||
metadata_depot_ = metadata_depot;
|
||||
}
|
||||
|
||||
private:
|
||||
SimStack::Allocated stack_;
|
||||
MetaDataDepot* metadata_depot_;
|
||||
};
|
||||
|
||||
// Represent a register (r0-r31, v0-v31, z0-z31, p0-p15).
|
||||
@@ -1027,6 +1235,9 @@ class SimExclusiveGlobalMonitor {
|
||||
};
|
||||
|
||||
|
||||
class Debugger;
|
||||
|
||||
|
||||
class Simulator : public DecoderVisitor {
|
||||
public:
|
||||
#ifndef PANDA_BUILD
|
||||
@@ -1044,6 +1255,12 @@ class Simulator : public DecoderVisitor {
|
||||
#endif
|
||||
~Simulator();
|
||||
|
||||
#ifdef PANDA_BUILD
|
||||
AllocatorWrapper GetAllocator() const {
|
||||
return allocator_;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ResetState();
|
||||
|
||||
// Run the simulator.
|
||||
@@ -1111,6 +1328,8 @@ class Simulator : public DecoderVisitor {
|
||||
static const Instruction* kEndOfSimAddress;
|
||||
|
||||
// Simulation helpers.
|
||||
bool IsSimulationFinished() const { return pc_ == kEndOfSimAddress; }
|
||||
|
||||
const Instruction* ReadPc() const { return pc_; }
|
||||
VIXL_DEPRECATED("ReadPc", const Instruction* pc() const) { return ReadPc(); }
|
||||
|
||||
@@ -1119,7 +1338,7 @@ class Simulator : public DecoderVisitor {
|
||||
void WritePc(const Instruction* new_pc,
|
||||
BranchLogMode log_mode = LogBranches) {
|
||||
if (log_mode == LogBranches) LogTakenBranch(new_pc);
|
||||
pc_ = memory_.AddressUntag(new_pc);
|
||||
pc_ = AddressUntag(new_pc);
|
||||
pc_modified_ = true;
|
||||
}
|
||||
VIXL_DEPRECATED("WritePc", void set_pc(const Instruction* new_pc)) {
|
||||
@@ -1146,6 +1365,8 @@ class Simulator : public DecoderVisitor {
|
||||
bool PcIsInGuardedPage() const { return guard_pages_; }
|
||||
void SetGuardedPages(bool guard_pages) { guard_pages_ = guard_pages; }
|
||||
|
||||
const Instruction* GetLastExecutedInstruction() const { return last_instr_; }
|
||||
|
||||
void ExecuteInstruction() {
|
||||
// The program counter should always be aligned.
|
||||
VIXL_ASSERT(IsWordAligned(pc_));
|
||||
@@ -1258,6 +1479,26 @@ class Simulator : public DecoderVisitor {
|
||||
void SimulateNEONFPMulByElementLong(const Instruction* instr);
|
||||
void SimulateNEONComplexMulByElement(const Instruction* instr);
|
||||
void SimulateNEONDotProdByElement(const Instruction* instr);
|
||||
void SimulateMTEAddSubTag(const Instruction* instr);
|
||||
void SimulateMTETagMaskInsert(const Instruction* instr);
|
||||
void SimulateMTESubPointer(const Instruction* instr);
|
||||
void SimulateMTELoadTag(const Instruction* instr);
|
||||
void SimulateMTEStoreTag(const Instruction* instr);
|
||||
void SimulateMTEStoreTagPair(const Instruction* instr);
|
||||
void Simulate_XdSP_XnSP_Xm(const Instruction* instr);
|
||||
void SimulateCpy(const Instruction* instr);
|
||||
void SimulateCpyFP(const Instruction* instr);
|
||||
void SimulateCpyP(const Instruction* instr);
|
||||
void SimulateCpyM(const Instruction* instr);
|
||||
void SimulateCpyE(const Instruction* instr);
|
||||
void SimulateSetP(const Instruction* instr);
|
||||
void SimulateSetM(const Instruction* instr);
|
||||
void SimulateSetE(const Instruction* instr);
|
||||
void SimulateSetGP(const Instruction* instr);
|
||||
void SimulateSetGM(const Instruction* instr);
|
||||
void SimulateSignedMinMax(const Instruction* instr);
|
||||
void SimulateUnsignedMinMax(const Instruction* instr);
|
||||
|
||||
|
||||
// Integer register accessors.
|
||||
|
||||
@@ -1789,12 +2030,14 @@ class Simulator : public DecoderVisitor {
|
||||
|
||||
template <typename T, typename A>
|
||||
T MemRead(A address) const {
|
||||
return memory_.Read<T>(address);
|
||||
Instruction const* pc = ReadPc();
|
||||
return memory_.Read<T>(address, pc);
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
void MemWrite(A address, T value) const {
|
||||
return memory_.Write(address, value);
|
||||
Instruction const* pc = ReadPc();
|
||||
return memory_.Write(address, value, pc);
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
@@ -2319,7 +2562,9 @@ class Simulator : public DecoderVisitor {
|
||||
void LogPWrite(int rt_code, uintptr_t address) {
|
||||
if (ShouldTraceWrites()) PrintPWrite(rt_code, address);
|
||||
}
|
||||
|
||||
void LogMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value) {
|
||||
if (ShouldTraceWrites()) PrintMemTransfer(dst, src, value);
|
||||
}
|
||||
// Helpers for the above, where the access operation is parameterised.
|
||||
// - For loads, set op = "<-".
|
||||
// - For stores, set op = "->".
|
||||
@@ -2331,6 +2576,7 @@ class Simulator : public DecoderVisitor {
|
||||
PrintRegisterFormat format,
|
||||
const char* op,
|
||||
uintptr_t address);
|
||||
void PrintMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value);
|
||||
// Simple, unpredicated SVE accesses always access the whole vector, and never
|
||||
// know the lane type, so these don't accept a `format`.
|
||||
void PrintZAccess(int rt_code, const char* op, uintptr_t address);
|
||||
@@ -2598,6 +2844,48 @@ class Simulator : public DecoderVisitor {
|
||||
PointerType type);
|
||||
uint64_t AddPAC(uint64_t ptr, uint64_t context, PACKey key, PointerType type);
|
||||
uint64_t StripPAC(uint64_t ptr, PointerType type);
|
||||
void PACHelper(int dst,
|
||||
int src,
|
||||
PACKey key,
|
||||
decltype(&Simulator::AddPAC) pac_fn);
|
||||
|
||||
// Armv8.5 MTE helpers.
|
||||
uint64_t ChooseNonExcludedTag(uint64_t tag,
|
||||
uint64_t offset,
|
||||
uint64_t exclude = 0) {
|
||||
VIXL_ASSERT(IsUint4(tag) && IsUint4(offset) && IsUint16(exclude));
|
||||
|
||||
if (exclude == 0xffff) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset == 0) {
|
||||
while ((exclude & (1 << tag)) != 0) {
|
||||
tag = (tag + 1) % 16;
|
||||
}
|
||||
}
|
||||
|
||||
while (offset > 0) {
|
||||
offset--;
|
||||
tag = (tag + 1) % 16;
|
||||
while ((exclude & (1 << tag)) != 0) {
|
||||
tag = (tag + 1) % 16;
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
uint64_t GetAddressWithAllocationTag(uint64_t addr, uint64_t tag) {
|
||||
VIXL_ASSERT(IsUint4(tag));
|
||||
return (addr & ~(UINT64_C(0xf) << 56)) | (tag << 56);
|
||||
}
|
||||
|
||||
// Create or remove a mapping with memory protection. Memory attributes such
|
||||
// as MTE and BTI are represented by metadata in Simulator.
|
||||
void* Mmap(
|
||||
void* address, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
|
||||
int Munmap(void* address, size_t length, int prot);
|
||||
|
||||
// The common CPUFeatures interface with the set of available features.
|
||||
|
||||
@@ -2666,6 +2954,7 @@ class Simulator : public DecoderVisitor {
|
||||
R DoRuntimeCall(R (*function)(P...),
|
||||
std::tuple<P...> arguments,
|
||||
local_index_sequence<I...>) {
|
||||
USE(arguments);
|
||||
return function(std::get<I>(arguments)...);
|
||||
}
|
||||
|
||||
@@ -2796,6 +3085,75 @@ class Simulator : public DecoderVisitor {
|
||||
|
||||
SimPRegister& GetPTrue() { return pregister_all_true_; }
|
||||
|
||||
template <typename T>
|
||||
size_t CleanGranuleTag(T address, size_t length = kMTETagGranuleInBytes) {
|
||||
size_t count = 0;
|
||||
for (size_t offset = 0; offset < length; offset += kMTETagGranuleInBytes) {
|
||||
count +=
|
||||
meta_data_.CleanMTETag(reinterpret_cast<uintptr_t>(address) + offset);
|
||||
}
|
||||
size_t expected =
|
||||
length / kMTETagGranuleInBytes + (length % kMTETagGranuleInBytes != 0);
|
||||
|
||||
// Give a warning when the memory region that is being unmapped isn't all
|
||||
// either MTE protected or not.
|
||||
if (count != expected) {
|
||||
std::stringstream sstream;
|
||||
sstream << std::hex
|
||||
<< "MTE WARNING : the memory region being unmapped "
|
||||
"starting at address 0x"
|
||||
<< reinterpret_cast<uint64_t>(address)
|
||||
<< "is not fully MTE protected.\n";
|
||||
VIXL_WARNING(sstream.str().c_str());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetGranuleTag(T address,
|
||||
int tag,
|
||||
size_t length = kMTETagGranuleInBytes) {
|
||||
for (size_t offset = 0; offset < length; offset += kMTETagGranuleInBytes) {
|
||||
meta_data_.SetMTETag((uintptr_t)(address) + offset, tag);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int GetGranuleTag(T address) {
|
||||
return meta_data_.GetMTETag(address);
|
||||
}
|
||||
|
||||
// Generate a random address tag, and any tags specified in the input are
|
||||
// excluded from the selection.
|
||||
uint64_t GenerateRandomTag(uint16_t exclude = 0);
|
||||
|
||||
// Register a new BranchInterception object. If 'function' is branched to
|
||||
// (e.g: "bl function") in the future; instead, if provided, 'callback' will
|
||||
// be called otherwise a runtime call will be performed on 'function'.
|
||||
//
|
||||
// For example: this can be used to always perform runtime calls on
|
||||
// non-AArch64 functions without using the macroassembler.
|
||||
template <typename R, typename... P>
|
||||
void RegisterBranchInterception(R (*function)(P...),
|
||||
InterceptionCallback callback = nullptr) {
|
||||
meta_data_.RegisterBranchInterception(*function, callback);
|
||||
}
|
||||
|
||||
// Return the current output stream in use by the simulator.
|
||||
FILE* GetOutputStream() const { return stream_; }
|
||||
|
||||
bool IsDebuggerEnabled() const { return debugger_enabled_; }
|
||||
|
||||
void SetDebuggerEnabled(bool enabled) { debugger_enabled_ = enabled; }
|
||||
|
||||
Debugger* GetDebugger() const {
|
||||
#ifndef PANDA_BUILD
|
||||
return debugger_.get();
|
||||
#else
|
||||
return debugger_;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* clr_normal;
|
||||
const char* clr_flag_name;
|
||||
@@ -2907,13 +3265,45 @@ class Simulator : public DecoderVisitor {
|
||||
AddrMode addr_mode);
|
||||
void NEONLoadStoreSingleStructHelper(const Instruction* instr,
|
||||
AddrMode addr_mode);
|
||||
template <uint32_t mops_type>
|
||||
void MOPSPHelper(const Instruction* instr) {
|
||||
VIXL_ASSERT(instr->IsConsistentMOPSTriplet<mops_type>());
|
||||
|
||||
uint64_t AddressUntag(uint64_t address) { return address & ~kAddressTagMask; }
|
||||
int d = instr->GetRd();
|
||||
int n = instr->GetRn();
|
||||
int s = instr->GetRs();
|
||||
|
||||
template <typename T>
|
||||
T* AddressUntag(T* address) {
|
||||
uintptr_t address_raw = reinterpret_cast<uintptr_t>(address);
|
||||
return reinterpret_cast<T*>(AddressUntag(address_raw));
|
||||
// Aliased registers and xzr are disallowed for Xd and Xn.
|
||||
if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31)) {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
|
||||
// Additionally, Xs may not be xzr for cpy.
|
||||
if ((mops_type == "cpy"_h) && (s == 31)) {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
|
||||
// Bits 31 and 30 must be zero.
|
||||
if (instr->ExtractBits(31, 30) != 0) {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
|
||||
// Saturate copy count.
|
||||
uint64_t xn = ReadXRegister(n);
|
||||
int saturation_bits = (mops_type == "cpy"_h) ? 55 : 63;
|
||||
if ((xn >> saturation_bits) != 0) {
|
||||
xn = (UINT64_C(1) << saturation_bits) - 1;
|
||||
if (mops_type == "setg"_h) {
|
||||
// Align saturated value to granule.
|
||||
xn &= ~UINT64_C(kMTETagGranuleInBytes - 1);
|
||||
}
|
||||
WriteXRegister(n, xn);
|
||||
}
|
||||
|
||||
ReadNzcv().SetN(0);
|
||||
ReadNzcv().SetZ(0);
|
||||
ReadNzcv().SetC(1); // Indicates "option B" implementation.
|
||||
ReadNzcv().SetV(0);
|
||||
}
|
||||
|
||||
int64_t ShiftOperand(unsigned reg_size,
|
||||
@@ -4747,7 +5137,7 @@ class Simulator : public DecoderVisitor {
|
||||
const Instruction* pc_;
|
||||
|
||||
// Pointer to the last simulated instruction, used for checking the validity
|
||||
// of the current instruction with movprfx.
|
||||
// of the current instruction with the previous instruction, such as movprfx.
|
||||
Instruction const* last_instr_;
|
||||
|
||||
// Branch type register, used for branch target identification.
|
||||
@@ -4883,6 +5273,20 @@ class Simulator : public DecoderVisitor {
|
||||
|
||||
// A configurable size of SVE vector registers.
|
||||
unsigned vector_length_;
|
||||
|
||||
// Representation of memory attributes such as MTE tagging and BTI page
|
||||
// protection in addition to branch interceptions.
|
||||
MetaDataDepot meta_data_;
|
||||
|
||||
// True if the debugger is enabled and might get entered.
|
||||
bool debugger_enabled_;
|
||||
|
||||
// Debugger for the simulator.
|
||||
#ifndef PANDA_BUILD
|
||||
std::unique_ptr<Debugger> debugger_;
|
||||
#else
|
||||
Debugger* debugger_{nullptr};
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) && __cplusplus < 201402L
|
||||
@@ -4893,6 +5297,17 @@ struct Simulator::emulated_make_index_sequence_helper<0, I...>
|
||||
: Simulator::emulated_index_sequence<I...> {};
|
||||
#endif
|
||||
|
||||
template <typename R, typename... P>
|
||||
void MetaDataDepot::BranchInterception<R, P...>::operator()(
|
||||
Simulator* simulator) const {
|
||||
if (callback_ == nullptr) {
|
||||
Simulator::RuntimeCallStructHelper<R, P...>::
|
||||
Wrapper(simulator, reinterpret_cast<uint64_t>(function_));
|
||||
} else {
|
||||
callback_(reinterpret_cast<uint64_t>(function_));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aarch64
|
||||
} // namespace vixl
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ enum DebugHltOpcode {
|
||||
kDisableCPUFeaturesOpcode,
|
||||
kSaveCPUFeaturesOpcode,
|
||||
kRestoreCPUFeaturesOpcode,
|
||||
kMTEActive,
|
||||
kMTEInactive,
|
||||
// Aliases.
|
||||
kDebugHltFirstOpcode = kUnreachableOpcode,
|
||||
kDebugHltLastOpcode = kLogOpcode
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
|
||||
#include "code-buffer-vixl.h"
|
||||
|
||||
// Microsoft Visual C++ defines a `mvn` macro that conflicts with our own
|
||||
// definition.
|
||||
#if defined(_MSC_VER) && defined(mvn)
|
||||
#undef mvn
|
||||
#endif
|
||||
|
||||
namespace vixl {
|
||||
|
||||
class CodeBufferCheckScope;
|
||||
|
||||
@@ -125,11 +125,12 @@ char* stpcpy (char *dst, const char *src) {
|
||||
#endif
|
||||
|
||||
void CodeBuffer::EmitString(const char* string) {
|
||||
VIXL_ASSERT(HasSpaceFor(strlen(string) + 1));
|
||||
const auto len = strlen(string) + 1;
|
||||
VIXL_ASSERT(HasSpaceFor(len));
|
||||
char* dst = reinterpret_cast<char*>(cursor_);
|
||||
dirty_ = true;
|
||||
char* null_char = stpcpy(dst, string);
|
||||
cursor_ = reinterpret_cast<byte*>(null_char) + 1;
|
||||
memcpy(dst, string, len);
|
||||
cursor_ = reinterpret_cast<byte*>(dst + len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -145,8 +145,9 @@ class CodeBuffer {
|
||||
void Emit(T value) {
|
||||
VIXL_ASSERT(HasSpaceFor(sizeof(value)));
|
||||
dirty_ = true;
|
||||
memcpy(cursor_, &value, sizeof(value));
|
||||
cursor_ += sizeof(value);
|
||||
byte* c = cursor_;
|
||||
memcpy(c, &value, sizeof(value));
|
||||
cursor_ = c + sizeof(value);
|
||||
}
|
||||
|
||||
void UpdateData(size_t offset, const void* data, size_t size);
|
||||
|
||||
@@ -68,14 +68,19 @@ class CodeBufferCheckScope {
|
||||
size_t size,
|
||||
BufferSpacePolicy check_policy = kReserveBufferSpace,
|
||||
SizePolicy size_policy = kMaximumSize)
|
||||
: assembler_(NULL), initialised_(false) {
|
||||
: CodeBufferCheckScope() {
|
||||
Open(assembler, size, check_policy, size_policy);
|
||||
}
|
||||
|
||||
// This constructor does not implicitly initialise the scope. Instead, the
|
||||
// user is required to explicitly call the `Open` function before using the
|
||||
// scope.
|
||||
CodeBufferCheckScope() : assembler_(NULL), initialised_(false) {
|
||||
CodeBufferCheckScope()
|
||||
: assembler_(NULL),
|
||||
assert_policy_(kMaximumSize),
|
||||
limit_(0),
|
||||
previous_allow_assembler_(false),
|
||||
initialised_(false) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
@@ -152,14 +157,15 @@ class EmissionCheckScope : public CodeBufferCheckScope {
|
||||
// constructed.
|
||||
EmissionCheckScope(MacroAssemblerInterface* masm,
|
||||
size_t size,
|
||||
SizePolicy size_policy = kMaximumSize) {
|
||||
SizePolicy size_policy = kMaximumSize)
|
||||
: EmissionCheckScope() {
|
||||
Open(masm, size, size_policy);
|
||||
}
|
||||
|
||||
// This constructor does not implicitly initialise the scope. Instead, the
|
||||
// user is required to explicitly call the `Open` function before using the
|
||||
// scope.
|
||||
EmissionCheckScope() {}
|
||||
EmissionCheckScope() : masm_(nullptr), pool_policy_(kBlockPools) {}
|
||||
|
||||
virtual ~EmissionCheckScope() { Close(); }
|
||||
|
||||
@@ -250,14 +256,15 @@ class ExactAssemblyScope : public EmissionCheckScope {
|
||||
// constructed.
|
||||
ExactAssemblyScope(MacroAssemblerInterface* masm,
|
||||
size_t size,
|
||||
SizePolicy size_policy = kExactSize) {
|
||||
SizePolicy size_policy = kExactSize)
|
||||
: ExactAssemblyScope() {
|
||||
Open(masm, size, size_policy);
|
||||
}
|
||||
|
||||
// This constructor does not implicitly initialise the scope. Instead, the
|
||||
// user is required to explicitly call the `Open` function before using the
|
||||
// scope.
|
||||
ExactAssemblyScope() {}
|
||||
ExactAssemblyScope() : previous_allow_macro_assembler_(false) {}
|
||||
|
||||
virtual ~ExactAssemblyScope() { Close(); }
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "compiler-intrinsics-vixl.h"
|
||||
|
||||
#include "utils-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#define VIXL_COMPILER_INTRINSICS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "globals-vixl.h"
|
||||
|
||||
namespace vixl {
|
||||
@@ -112,7 +113,8 @@ inline int CountLeadingSignBits(V value, int width = (sizeof(V) * 8)) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
#if COMPILER_HAS_BUILTIN_CLRSB
|
||||
VIXL_ASSERT((LLONG_MIN <= value) && (value <= LLONG_MAX));
|
||||
int ll_width = sizeof(long long) * kBitsPerByte; // NOLINT(runtime/int)
|
||||
int ll_width =
|
||||
sizeof(long long) * kBitsPerByte; // NOLINT(google-runtime-int)
|
||||
int result = __builtin_clrsbll(value) - (ll_width - width);
|
||||
// Check that the value fits in the specified width.
|
||||
VIXL_ASSERT(result >= 0);
|
||||
|
||||
+2
-1
@@ -24,9 +24,10 @@
|
||||
// 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 "cpu-features.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "cpu-features.h"
|
||||
#include "globals-vixl.h"
|
||||
#include "utils-vixl.h"
|
||||
|
||||
|
||||
+19
-1
@@ -166,6 +166,7 @@ namespace vixl {
|
||||
/* Memory Tagging Extension. */ \
|
||||
V(kMTEInstructions, "MTE (EL0 instructions)", NULL) \
|
||||
V(kMTE, "MTE", NULL) \
|
||||
V(kMTE3, "MTE (asymmetric)", "mte3") \
|
||||
/* PAuth extensions. */ \
|
||||
V(kPAuthEnhancedPAC, "PAuth EnhancedPAC", NULL) \
|
||||
V(kPAuthEnhancedPAC2, "PAuth EnhancedPAC2", NULL) \
|
||||
@@ -183,7 +184,24 @@ namespace vixl {
|
||||
/* Enhanced Counter Virtualization */ \
|
||||
V(kECV, "ECV", "ecv") \
|
||||
/* Increased precision of Reciprocal Estimate and Square Root Estimate */ \
|
||||
V(kRPRES, "RPRES", "rpres")
|
||||
V(kRPRES, "RPRES", "rpres") \
|
||||
/* Memory operation instructions, for memcpy, memset */ \
|
||||
V(kMOPS, "Memory ops", NULL) \
|
||||
/* Scalable Matrix Extension (SME) */ \
|
||||
V(kSME, "SME", "sme") \
|
||||
V(kSMEi16i64, "SME (i16i64)", "smei16i64") \
|
||||
V(kSMEf64f64, "SME (f64f64)", "smef64f64") \
|
||||
V(kSMEi8i32, "SME (i8i32)", "smei8i32") \
|
||||
V(kSMEf16f32, "SME (f16f32)", "smef16f32") \
|
||||
V(kSMEb16f32, "SME (b16f32)", "smeb16f32") \
|
||||
V(kSMEf32f32, "SME (f32f32)", "smef32f32") \
|
||||
V(kSMEfa64, "SME (fa64)", "smefa64") \
|
||||
/* WFET and WFIT instruction support */ \
|
||||
V(kWFXT, "WFXT", "wfxt") \
|
||||
/* Extended BFloat16 instructions */ \
|
||||
V(kEBF16, "EBF16", "ebf16") \
|
||||
V(kSVE_EBF16, "EBF16 (SVE)", "sveebf16") \
|
||||
V(kCSSC, "CSSC", "cssc")
|
||||
// clang-format on
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -27,8 +27,8 @@
|
||||
#ifndef VIXL_GLOBALS_H
|
||||
#define VIXL_GLOBALS_H
|
||||
|
||||
#if __cplusplus < 201402L
|
||||
#error VIXL requires C++14
|
||||
#if __cplusplus < 201703L
|
||||
#error VIXL requires C++17
|
||||
#endif
|
||||
|
||||
// Get standard C99 macros for integer types.
|
||||
@@ -158,7 +158,7 @@ struct Unsigned<64> {
|
||||
#endif
|
||||
// This is not as powerful as template based assertions, but it is simple.
|
||||
// It assumes that the descriptions are unique. If this starts being a problem,
|
||||
// we can switch to a different implemention.
|
||||
// we can switch to a different implementation.
|
||||
#define VIXL_CONCAT(a, b) a##b
|
||||
#if __cplusplus >= 201103L
|
||||
#define VIXL_STATIC_ASSERT_LINE(line_unused, condition, message) \
|
||||
@@ -207,7 +207,7 @@ inline void USE(const T1&, const T2&, const T3&, const T4&) {}
|
||||
#if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L
|
||||
#define VIXL_FALLTHROUGH() [[clang::fallthrough]]
|
||||
// Fallthrough annotation for GCC >= 7.
|
||||
#elif __GNUC__ >= 7
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 7
|
||||
#define VIXL_FALLTHROUGH() __attribute__((fallthrough))
|
||||
#else
|
||||
#define VIXL_FALLTHROUGH() \
|
||||
|
||||
+9
-5
@@ -27,9 +27,8 @@
|
||||
#ifndef VIXL_INVALSET_H_
|
||||
#define VIXL_INVALSET_H_
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "globals-vixl.h"
|
||||
@@ -119,7 +118,7 @@ class InvalSet {
|
||||
size_t size() const;
|
||||
|
||||
// Returns true if no elements are stored in the set.
|
||||
// Note that this does not mean the the backing storage is empty: it can still
|
||||
// Note that this does not mean the backing storage is empty: it can still
|
||||
// contain invalid elements.
|
||||
bool empty() const;
|
||||
|
||||
@@ -256,8 +255,13 @@ class InvalSet {
|
||||
|
||||
|
||||
template <class S>
|
||||
class InvalSetIterator : public std::iterator<std::forward_iterator_tag,
|
||||
typename S::_ElementType> {
|
||||
class InvalSetIterator {
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = typename S::_ElementType;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = S*;
|
||||
using reference = S&;
|
||||
|
||||
private:
|
||||
// Redefine types to mirror the associated set types.
|
||||
typedef typename S::_ElementType ElementType;
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
#ifndef VIXL_POOL_MANAGER_IMPL_H_
|
||||
#define VIXL_POOL_MANAGER_IMPL_H_
|
||||
|
||||
#include "pool-manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "assembler-base-vixl.h"
|
||||
#include "pool-manager.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
@@ -523,6 +523,6 @@ int PoolManager<T>::GetPoolSizeForTest() const {
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_POOL_MANAGER_IMPL_H_
|
||||
|
||||
+3
-4
@@ -27,11 +27,10 @@
|
||||
#ifndef VIXL_POOL_MANAGER_H_
|
||||
#define VIXL_POOL_MANAGER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "globals-vixl.h"
|
||||
@@ -372,8 +371,8 @@ class ForwardReference {
|
||||
|
||||
// Specify the possible locations where the object could be stored. AArch32's
|
||||
// PC offset, and T32's PC alignment calculations should be applied by the
|
||||
// Assembler, not here. The PoolManager deals only with simple locationes.
|
||||
// Including min_object_adddress_ is necessary to handle AArch32 some
|
||||
// Assembler, not here. The PoolManager deals only with simple locations.
|
||||
// Including min_object_address_ is necessary to handle AArch32 some
|
||||
// instructions which have a minimum offset of 0, but also have the implicit
|
||||
// PC offset.
|
||||
// Note that this structure cannot handle sparse ranges, such as A32's ADR,
|
||||
|
||||
+3
-3
@@ -24,10 +24,10 @@
|
||||
// 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 <cstdio>
|
||||
|
||||
#include "utils-vixl.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// The default NaN values (for FPCR.DN=1).
|
||||
@@ -391,7 +391,7 @@ float FPToFloat(double value,
|
||||
}
|
||||
|
||||
VIXL_UNREACHABLE();
|
||||
return value;
|
||||
return static_cast<float>(value);
|
||||
}
|
||||
|
||||
// TODO: We should consider implementing a full FPToDouble(Float16)
|
||||
|
||||
+38
-9
@@ -30,9 +30,9 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "compiler-intrinsics-vixl.h"
|
||||
#include "globals-vixl.h"
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
@@ -72,6 +73,9 @@ using Map = ark::ArenaMap<K, V>;
|
||||
template <typename K, typename V>
|
||||
using UnorderedMap = ark::ArenaUnorderedMap<K, V>;
|
||||
|
||||
template <typename K>
|
||||
using UnorderedSet = ark::ArenaUnorderedSet<K>;
|
||||
|
||||
using String = ark::ArenaString;
|
||||
|
||||
template <typename T>
|
||||
@@ -86,6 +90,9 @@ using Map = std::map<K, V>;
|
||||
template <typename K, typename V>
|
||||
using UnorderedMap = std::unordered_map<K, V>;
|
||||
|
||||
template <typename K>
|
||||
using UnorderedSet = std::unordered_set<K>;
|
||||
|
||||
using String = std::string;
|
||||
|
||||
template <typename T>
|
||||
@@ -422,17 +429,37 @@ VIXL_DEPRECATED("RawbitsToDouble",
|
||||
return RawbitsToDouble(bits);
|
||||
}
|
||||
|
||||
// Some compilers dislike negating unsigned integers,
|
||||
// so we provide an equivalent.
|
||||
template <typename T>
|
||||
T UnsignedNegate(T value) {
|
||||
VIXL_STATIC_ASSERT(std::is_unsigned<T>::value);
|
||||
return ~value + 1;
|
||||
}
|
||||
|
||||
// An absolute operation for signed integers that is defined for results outside
|
||||
// the representable range. Specifically, Abs(MIN_INT) is MIN_INT.
|
||||
template <typename T>
|
||||
T Abs(T val) {
|
||||
// TODO: this static assertion is for signed integer inputs, as that's the
|
||||
// only type tested. However, the code should work for all numeric inputs.
|
||||
// Remove the assertion and this comment when more tests are available.
|
||||
VIXL_STATIC_ASSERT(std::is_signed<T>::value && std::is_integral<T>::value);
|
||||
return ((val >= -std::numeric_limits<T>::max()) && (val < 0)) ? -val : val;
|
||||
}
|
||||
|
||||
// Convert unsigned to signed numbers in a well-defined way (using two's
|
||||
// complement representations).
|
||||
inline int64_t RawbitsToInt64(uint64_t bits) {
|
||||
return (bits >= UINT64_C(0x8000000000000000))
|
||||
? (-static_cast<int64_t>(-bits - 1) - 1)
|
||||
? (-static_cast<int64_t>(UnsignedNegate(bits) - 1) - 1)
|
||||
: static_cast<int64_t>(bits);
|
||||
}
|
||||
|
||||
inline int32_t RawbitsToInt32(uint32_t bits) {
|
||||
return (bits >= UINT64_C(0x80000000)) ? (-static_cast<int32_t>(-bits - 1) - 1)
|
||||
: static_cast<int32_t>(bits);
|
||||
return (bits >= UINT64_C(0x80000000))
|
||||
? (-static_cast<int32_t>(UnsignedNegate(bits) - 1) - 1)
|
||||
: static_cast<int32_t>(bits);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
@@ -458,7 +485,7 @@ class SimFloat16 : public Float16 {
|
||||
bool operator>(SimFloat16 rhs) const;
|
||||
bool operator==(SimFloat16 rhs) const;
|
||||
bool operator!=(SimFloat16 rhs) const;
|
||||
// This is necessary for conversions peformed in (macro asm) Fmov.
|
||||
// This is necessary for conversions performed in (macro asm) Fmov.
|
||||
bool operator==(double rhs) const;
|
||||
operator double() const;
|
||||
};
|
||||
@@ -615,7 +642,9 @@ inline float FusedMultiplyAdd(float op1, float op2, float a) {
|
||||
}
|
||||
|
||||
|
||||
inline uint64_t LowestSetBit(uint64_t value) { return value & -value; }
|
||||
inline uint64_t LowestSetBit(uint64_t value) {
|
||||
return value & UnsignedNegate(value);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
@@ -978,7 +1007,7 @@ class Uint32 {
|
||||
}
|
||||
int32_t GetSigned() const { return data_; }
|
||||
Uint32 operator~() const { return Uint32(~data_); }
|
||||
Uint32 operator-() const { return Uint32(-data_); }
|
||||
Uint32 operator-() const { return Uint32(UnsignedNegate(data_)); }
|
||||
bool operator==(Uint32 value) const { return data_ == value.data_; }
|
||||
bool operator!=(Uint32 value) const { return data_ != value.data_; }
|
||||
bool operator>(Uint32 value) const { return data_ > value.data_; }
|
||||
@@ -1046,7 +1075,7 @@ class Uint64 {
|
||||
Uint32 GetHigh32() const { return Uint32(data_ >> 32); }
|
||||
Uint32 GetLow32() const { return Uint32(data_ & 0xffffffff); }
|
||||
Uint64 operator~() const { return Uint64(~data_); }
|
||||
Uint64 operator-() const { return Uint64(-data_); }
|
||||
Uint64 operator-() const { return Uint64(UnsignedNegate(data_)); }
|
||||
bool operator==(Uint64 value) const { return data_ == value.data_; }
|
||||
bool operator!=(Uint64 value) const { return data_ != value.data_; }
|
||||
Uint64 operator+(Uint64 value) const { return Uint64(data_ + value.data_); }
|
||||
@@ -1352,7 +1381,7 @@ T FPRound(int64_t sign,
|
||||
// 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;
|
||||
shift += static_cast<int>(-exponent + 1);
|
||||
|
||||
// Handle inputs that would produce a zero output.
|
||||
//
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
"type": "assembler",
|
||||
"test-cases": [
|
||||
{
|
||||
"name": "Unconditionnal",
|
||||
"name": "Unconditional",
|
||||
"operands": [
|
||||
"cond", "rd", "rn", "shift", "rs"
|
||||
],
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
"type": "assembler",
|
||||
"test-cases": [
|
||||
{
|
||||
"name": "Unconditionnal",
|
||||
"name": "Unconditional",
|
||||
"operands": [
|
||||
"cond", "rd", "rn", "rm"
|
||||
],
|
||||
|
||||
@@ -1100,7 +1100,7 @@
|
||||
"identifier": "OffsetLowerThan4096",
|
||||
"type": "int32_t",
|
||||
// These variants are a random sample of 500 integers out of all integers
|
||||
// from 1 to 4094 (included). We've added 0 and 4095 explicitely.
|
||||
// from 1 to 4094 (included). We've added 0 and 4095 explicitly.
|
||||
"variants": [
|
||||
"0",
|
||||
"4095",
|
||||
@@ -1700,7 +1700,7 @@
|
||||
],
|
||||
"default": "NoFlag"
|
||||
},
|
||||
// TODO: Consider having a seperate list for inputs for which we are only
|
||||
// TODO: Consider having a separate list for inputs for which we are only
|
||||
// interested in recording the value after the instruction has executed.
|
||||
// This applies to `Q` and `GE`.
|
||||
{
|
||||
@@ -1781,7 +1781,7 @@
|
||||
"identifier": "RegisterOffsetLowerThan4096",
|
||||
"type": "Register",
|
||||
// These values are a random sample of 500 integers out of all integers
|
||||
// from 1 to 4094 (included). We've added 0 and 4095 explicitely.
|
||||
// from 1 to 4094 (included). We've added 0 and 4095 explicitly.
|
||||
"values": [
|
||||
"0",
|
||||
"4095",
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace aarch32 {
|
||||
${instruction_list_declaration}
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace aarch32 {
|
||||
${instruction_list_declaration}
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace aarch32 {
|
||||
${instruction_list_declaration}
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace aarch32 {
|
||||
${instruction_list_declaration}
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` and
|
||||
// `Inputs` have various layouts across generated tests so they absolutely
|
||||
|
||||
@@ -1490,7 +1490,7 @@ void EmitLdrdLiteralTest(MacroAssembler* masm, TestMacroAssembler* test) {
|
||||
VIXL_CHECK(masm->GetCursorOffset() == end);
|
||||
}
|
||||
|
||||
// Check that the pool has not been emited along the way.
|
||||
// Check that the pool has not been emitted along the way.
|
||||
CHECK_POOL_SIZE(8);
|
||||
// This extra instruction should trigger an emit of the pool.
|
||||
__ Nop();
|
||||
@@ -3653,7 +3653,7 @@ static void NearBranchAndLiteralFuzzHelper(InstructionSet isa,
|
||||
// below). The cases are split in 4 groups:
|
||||
//
|
||||
// - 0..3: Generate various amount of nops.
|
||||
// - 4..7: Generate various load intstructions with literals.
|
||||
// - 4..7: Generate various load instructions with literals.
|
||||
// - 8..14: Generate various branch instructions.
|
||||
// - 15..19: Generate various amount of nops.
|
||||
//
|
||||
@@ -4849,7 +4849,7 @@ TEST_T32(branch_fuzz_example) {
|
||||
|
||||
// Generate a "B" and a "Cbz" which have the same checkpoint. Without proper
|
||||
// management (i.e. if the veneers were only generated at the shared
|
||||
// checkpoint), one one of the branches would be out of range.
|
||||
// checkpoint), one of the branches would be out of range.
|
||||
TEST_T32(veneer_simultaneous) {
|
||||
SETUP();
|
||||
|
||||
@@ -5109,7 +5109,7 @@ TEST_T32(veneer_and_literal4) {
|
||||
__ Ldr(r11, literal);
|
||||
|
||||
// The range for ldr is 4095, the range for cbz is 127. Generate nops
|
||||
// to have the ldr becomming out of range just before the cbz.
|
||||
// to have the ldr becoming out of range just before the cbz.
|
||||
const int NUM_NOPS = 2044;
|
||||
const int NUM_RANGE = 58;
|
||||
|
||||
@@ -5184,7 +5184,7 @@ TEST_T32(veneer_and_literal5) {
|
||||
__ add(r1, r1, 3);
|
||||
}
|
||||
__ Bind(&labels[test_num]);
|
||||
// Emit the literal pool if it has not beeen emitted (it's the case for
|
||||
// Emit the literal pool if it has not been emitted (it's the case for
|
||||
// the lower values of test_num).
|
||||
__ EmitLiteralPool(PoolManager<int32_t>::kBranchRequired);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ namespace aarch32 {
|
||||
|
||||
|
||||
// The following definitions are defined again in each generated test, therefore
|
||||
// we need to place them in an anomymous namespace. It expresses that they are
|
||||
// we need to place them in an anonymous namespace. It expresses that they are
|
||||
// local to this file only, and the compiler is not allowed to share these types
|
||||
// across test files during template instantiation. Specifically, `Operands` has
|
||||
// various layouts across generated tests so it absolutely cannot be shared.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user