mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
New arch support (#461)
* This branch contains support for new architectures. * Initial start to support for AArch 32 * Progress * Forgot the new files * Added all data Integer processing instructions without S + ADDS and started ANDS * Updated * Finished Integer Data Processing with three registers, added integer data processing with 2 regs + immediate, started MUL instructions * UMULL, UMULLS, UMLAL, UMLALS * Corrected condition for addend or 0 immediate for UMULL/UMLAL + SMULL/SMLAL instructions * Correct ops in Binary.cpp * UMAAL * SMULL, SMULLS, SMLAL, SMLALS + corrected acc was missing shift left in concatination * Updated decoding instructions based on top level encodings * Update returns around kDataProcessingRI and kDataProcessingI with comments to explain the correlation to the instruction rep * Added appropriate inst.category flags to Multiply and accumulate * Load/Store Word, Unsigned Byte (immediate, literal) && start of Logical Arithmetic (three register, immediate shift) * Was missing UMAAL DEF_ISEL in Binary.cpp * AddAddrRegOp * Logical Arithmetic (three register, immediate shift) without accounting for the possible PC jump * Made DecodeA32ExpandImm much much smaller * Replaced some imm ops with AddImmOp calls * Created AddShiftOp * Added interpreter for evaluating new PC value at decoding time to handle direct jumps and conditional jumps * Created EvalPCDest added PC evaluation to Logical Arithmetic Instructions * AddShiftOp -> AddShiftOp, AddShiftThenExtractOp, AddExtractThenShiftOp * Cleaned up some formatting, Renamed DecodeA32ExpandImm to ExpandTo32AddImmAddCarry and added a clarifying comment * Added comment to EvalPCDest for clarity * Cleaned up some things, updated the decoding semantics and semantics for the logical instructions * Shortened kLogArithEvaluators and fixed a bug * Updates from testing instructions * Fixed DEF_ISEL for pre/post index instructions in MEM.cpp * Integer Test and Compare (two register, immediate shift) * Logical Arithmetic (two register and immediate) * Integer Test and Compare (one register and immediate) * Added to the top level encoding infrastructure to handle the Data-processing register (register shift) set of instructions and 3 corresponding subsets * Add structs for the 3 subsets of Data-processing register (register shift) * Code status before refactoring operand types * This branch contains support for new architectures. * Initial start to support for AArch 32 * Progress * Forgot the new files * Added all data Integer processing instructions without S + ADDS and started ANDS * Updated * Finished Integer Data Processing with three registers, added integer data processing with 2 regs + immediate, started MUL instructions * UMULL, UMULLS, UMLAL, UMLALS * Corrected condition for addend or 0 immediate for UMULL/UMLAL + SMULL/SMLAL instructions * Correct ops in Binary.cpp * UMAAL * SMULL, SMULLS, SMLAL, SMLALS + corrected acc was missing shift left in concatination * Updated decoding instructions based on top level encodings * Update returns around kDataProcessingRI and kDataProcessingI with comments to explain the correlation to the instruction rep * Added appropriate inst.category flags to Multiply and accumulate * Load/Store Word, Unsigned Byte (immediate, literal) && start of Logical Arithmetic (three register, immediate shift) * Was missing UMAAL DEF_ISEL in Binary.cpp * AddAddrRegOp * Logical Arithmetic (three register, immediate shift) without accounting for the possible PC jump * Made DecodeA32ExpandImm much much smaller * Replaced some imm ops with AddImmOp calls * Created AddShiftOp * Added interpreter for evaluating new PC value at decoding time to handle direct jumps and conditional jumps * Created EvalPCDest added PC evaluation to Logical Arithmetic Instructions * AddShiftOp -> AddShiftOp, AddShiftThenExtractOp, AddExtractThenShiftOp * Cleaned up some formatting, Renamed DecodeA32ExpandImm to ExpandTo32AddImmAddCarry and added a clarifying comment * Added comment to EvalPCDest for clarity * Cleaned up some things, updated the decoding semantics and semantics for the logical instructions * Shortened kLogArithEvaluators and fixed a bug * Updates from testing instructions * Fixed DEF_ISEL for pre/post index instructions in MEM.cpp * Integer Test and Compare (two register, immediate shift) * Logical Arithmetic (two register and immediate) * Integer Test and Compare (one register and immediate) * Added to the top level encoding infrastructure to handle the Data-processing register (register shift) set of instructions and 3 corresponding subsets * Add structs for the 3 subsets of Data-processing register (register shift) * Code status before refactoring operand types * Finished updates off master * Start of operand refactor * Finished Expression Operand Support * Fix the .gitignore to add AArch32 to lib/Arch && removed all extra rrx ops from semantics * Updated .gitignore again, Added AddShiftRegRegOperand, Updated AddShiftRegImmOperand, Finished Register shift instructions for Integer Test and Compare, Logical Arithmetic, Integer Data Processing * Updated ROR in AddShiftRegRegOperand * Created ExtractAndZExtExpr * Fixed comment formatting in if else statements * Created RORExpr * Small fixes * Small fix in Logical Arithmetic (two register and immediate) * Corrected AddShiftRegRegOperand and cleaned it up. Split the carry op into a separate function. * conditional support + Start of Branch instructions * Created AddExprOp, cleaned up some expressions in reg shifted reg, and updated some occurances of ShiftThenExtractOp with ExtractAndZExtExpr * Updates from testing register shifted by register value inst * Fix to ROR in AddShiftRegCarryOperand * Corrected negation in DecodeCondition * DecodeCondition edit * DecodeCondition and AddShiftRegCarryOperand edits * Updated arch_for_decode to arch * Halfword Multiply and Accumulate * Edits from testing Halfword Multiply and Accumulate * Changed order of operands in Halfword Multiply and Accumulate to better reflect inst format + updated inst errors * Branch (Imm) & BX/BXL * Update aarch32 cmake * cmake update * CLZ * Forgot BITBYTE.cpp * MOVT * Integer Saturating Arithmetic * updated semantics in SMLAWh & SMLAh to use Select for setting PSTATE.Q * Started Load/Store Word, Unsigned Byte (register) & fixed MOV halfword * Load/Store Word, Unsigned Byte (register) * Finished testing load/Store Word, Unsigned Byte (register) * Load/Store Dual, Half, Signed Byte (register) * Rest of Extra load store: Load/Store Dual, Half, Signed Byte (immediate, literal) * Finished testing all the Load/store additions * Signed multiply, Divide * Cleaned up SExt some * Saturate Insts and Start of Load Store Multiple - STMDB and LDM (aliases which support PUSH and POP of multiple regs) * Condensed args in STMDB and LDM semantics * Rest of Multiple Load/Store that do not execute in a different mode * Bitfield Extract * Extend and Add * fix * NOP * Small fix * Simplified the bit reps in TryMoveSpecialRegisterAndHintsI * Moved Bitfield extract semantics out of BINARY and into BITBYTE * Finished correcting S/ZExt and Trunc use * Ran scripts/format-files to format * Smoke Test * Add false delay slot to kCategoryConditionalDirectFunctionCall * CI: Use single packaging job, add changelog support (#491) * CI: Add tag handler (#492) * Delay slot fixes to TraceLifter Co-authored-by: Peter Goodman <peter.goodman@gmail.com> Co-authored-by: Alessandro Gario <5714290+alessandrogario@users.noreply.github.com>
This commit is contained in:
@@ -63,6 +63,7 @@ jobs:
|
||||
run: |
|
||||
remill-lift-${{ matrix.llvm }} --arch amd64 --ir_out /dev/stdout --bytes c704ba01000000
|
||||
remill-lift-${{ matrix.llvm }} --arch aarch64 --ir_out /dev/stdout --address 0x400544 --bytes FD7BBFA90000009000601891FD030091B7FFFF97E0031F2AFD7BC1A8C0035FD6
|
||||
remill-lift-${{ matrix.llvm }} --arch aarch32 -ir_out /dev/stderr --bytes 0cd04de208008de504108de500208de508309de504009de500109de5903122e0c20fa0e110109fe5001091e5002081e5040081e50cd08de21eff2fe14000000000000000
|
||||
|
||||
- name: Locate the packages
|
||||
id: package_names
|
||||
@@ -123,6 +124,7 @@ jobs:
|
||||
run: |
|
||||
remill-lift-${{ matrix.llvm }} --arch amd64 --ir_out /dev/stdout --bytes c704ba01000000
|
||||
remill-lift-${{ matrix.llvm }} --arch aarch64 --ir_out /dev/stdout --address 0x400544 --bytes FD7BBFA90000009000601891FD030091B7FFFF97E0031F2AFD7BC1A8C0035FD6
|
||||
remill-lift-${{ matrix.llvm }} --arch aarch32 -ir_out /dev/stderr --bytes 0cd04de208008de504108de500208de508309de504009de500109de5903122e0c20fa0e110109fe5001091e5002081e5040081e50cd08de21eff2fe14000000000000000
|
||||
|
||||
- name: Locate the packages
|
||||
id: package_names
|
||||
|
||||
@@ -183,6 +183,7 @@ else()
|
||||
endif()
|
||||
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_X86 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/X86/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_AARCH32 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/AArch32/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_AARCH64 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/AArch64/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_SPARC32 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/SPARC32/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_SPARC64 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/SPARC64/Runtime")
|
||||
@@ -265,6 +266,7 @@ endif()
|
||||
target_compile_definitions(remill_settings INTERFACE
|
||||
"REMILL_INSTALL_SEMANTICS_DIR=\"${REMILL_INSTALL_SEMANTICS_DIR}/\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_X86=\"${REMILL_BUILD_SEMANTICS_DIR_X86}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_AARCH32=\"${REMILL_BUILD_SEMANTICS_DIR_AARCH32}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_AARCH64=\"${REMILL_BUILD_SEMANTICS_DIR_AARCH64}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_SPARC32=\"${REMILL_BUILD_SEMANTICS_DIR_SPARC32}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_SPARC64=\"${REMILL_BUILD_SEMANTICS_DIR_SPARC64}\""
|
||||
@@ -347,6 +349,7 @@ endif()
|
||||
set(REMILL_BC_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_bc.${static_lib_extension}")
|
||||
set(REMILL_ARCH_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_arch.${static_lib_extension}")
|
||||
set(REMILL_ARCH_X86_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_arch_x86.${static_lib_extension}")
|
||||
set(REMILL_ARCH_AARCH32_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_arch_aarch32.${static_lib_extension}")
|
||||
set(REMILL_ARCH_AARCH64_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_arch_aarch64.${static_lib_extension}")
|
||||
set(REMILL_ARCH_SPARC32_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_arch_sparc32.${static_lib_extension}")
|
||||
set(REMILL_ARCH_SPARC64_LIBRARY_LOCATION "${REMILL_INSTALL_LIB_DIR}/${static_lib_prefix}remill_arch_sparc64.${static_lib_extension}")
|
||||
|
||||
@@ -145,6 +145,7 @@ math(EXPR REMILL_LLVM_VERSION_NUMBER "${LLVM_MAJOR_VERSION} * 100 + ${LLVM_MINOR
|
||||
set(REMILL_INSTALL_SEMANTICS_DIR "${CMAKE_INSTALL_PREFIX}/${REMILL_INSTALL_SHARE_DIR}/remill/${REMILL_LLVM_VERSION}/semantics" CACHE PATH "Directory into which semantics are installed")
|
||||
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_X86 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/X86/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_AARCH32 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/AArch32/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_AARCH64 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/AArch64/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_SPARC32 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/SPARC32/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_SPARC64 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/SPARC64/Runtime")
|
||||
@@ -231,6 +232,7 @@ endif()
|
||||
target_compile_definitions(remill_settings INTERFACE
|
||||
"REMILL_INSTALL_SEMANTICS_DIR=\"${REMILL_INSTALL_SEMANTICS_DIR}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_X86=\"${REMILL_BUILD_SEMANTICS_DIR_X86}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_AARCH32=\"${REMILL_BUILD_SEMANTICS_DIR_AARCH32}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_AARCH64=\"${REMILL_BUILD_SEMANTICS_DIR_AARCH64}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_SPARC32=\"${REMILL_BUILD_SEMANTICS_DIR_SPARC32}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_SPARC64=\"${REMILL_BUILD_SEMANTICS_DIR_SPARC64}\""
|
||||
|
||||
@@ -65,6 +65,9 @@ if(NOT TARGET remill)
|
||||
add_library(remill_arch_x86 STATIC IMPORTED)
|
||||
set_property(TARGET remill_arch_x86 PROPERTY IMPORTED_LOCATION "@REMILL_ARCH_X86_LIBRARY_LOCATION@")
|
||||
|
||||
add_library(remill_arch_aarch32 STATIC IMPORTED)
|
||||
set_property(TARGET remill_arch_aarch32 PROPERTY IMPORTED_LOCATION "@REMILL_ARCH_AARCH32_LIBRARY_LOCATION@")
|
||||
|
||||
add_library(remill_arch_aarch64 STATIC IMPORTED)
|
||||
set_property(TARGET remill_arch_aarch64 PROPERTY IMPORTED_LOCATION "@REMILL_ARCH_AARCH64_LIBRARY_LOCATION@")
|
||||
|
||||
@@ -84,6 +87,7 @@ if(NOT TARGET remill)
|
||||
remill_os
|
||||
remill_arch
|
||||
remill_arch_x86
|
||||
remill_arch_aarch32
|
||||
remill_arch_aarch64
|
||||
remill_arch_sparc32
|
||||
remill_arch_sparc64
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace {
|
||||
|
||||
// Read a register directly. Sometimes this is needed for suppressed operands.
|
||||
ALWAYS_INLINE static addr_t _Read(Memory *, Reg reg) {
|
||||
return reg.aword;
|
||||
}
|
||||
|
||||
// Write directly to a register. This is sometimes needed for suppressed
|
||||
// register operands.
|
||||
ALWAYS_INLINE static void _Write(Memory *, Reg ®, addr_t val) {
|
||||
reg.aword = val;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic fatal "-Wpadded"
|
||||
|
||||
#include "remill/Arch/Runtime/State.h"
|
||||
#include "remill/Arch/Runtime/Types.h"
|
||||
|
||||
struct Reg final {
|
||||
alignas(4) uint32_t dword;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(uint32_t) == sizeof(Reg), "Invalid packing of `Reg`.");
|
||||
static_assert(0 == __builtin_offsetof(Reg, dword),
|
||||
"Invalid packing of `Reg::dword`.");
|
||||
|
||||
struct alignas(8) GPR final {
|
||||
|
||||
// Prevents LLVM from casting a `GPR` into an `i64` to access `X0`.
|
||||
volatile uint32_t _0;
|
||||
Reg r0;
|
||||
volatile uint32_t _1;
|
||||
Reg r1;
|
||||
volatile uint32_t _2;
|
||||
Reg r2;
|
||||
volatile uint32_t _3;
|
||||
Reg r3;
|
||||
volatile uint32_t _4;
|
||||
Reg r4;
|
||||
volatile uint32_t _5;
|
||||
Reg r5;
|
||||
volatile uint32_t _6;
|
||||
Reg r6;
|
||||
volatile uint32_t _7;
|
||||
Reg r7;
|
||||
volatile uint32_t _8;
|
||||
Reg r8;
|
||||
volatile uint32_t _9;
|
||||
Reg r9;
|
||||
volatile uint32_t _10;
|
||||
Reg r10;
|
||||
volatile uint32_t _11;
|
||||
Reg r11;
|
||||
volatile uint32_t _12;
|
||||
Reg r12;
|
||||
|
||||
// R13 is SP (stack pointer)
|
||||
volatile uint32_t _13;
|
||||
Reg r13;
|
||||
|
||||
// R14 is LR (link register)
|
||||
volatile uint32_t _14;
|
||||
Reg r14;
|
||||
|
||||
// R15 is PC (program counter)
|
||||
volatile uint32_t _15;
|
||||
Reg r15;
|
||||
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// System registers affecting control and status of the machine.
|
||||
struct alignas(8) SR final {
|
||||
|
||||
uint8_t _2;
|
||||
uint8_t n; // Negative condition flag.
|
||||
uint8_t _3;
|
||||
uint8_t z; // Zero condition flag
|
||||
uint8_t _4;
|
||||
uint8_t c; // Carry condition flag
|
||||
uint8_t _5;
|
||||
uint8_t v; // Overflow condition flag
|
||||
|
||||
uint8_t _6;
|
||||
uint8_t ixc; // Inexact (cumulative).
|
||||
uint8_t _7;
|
||||
uint8_t ofc; // Overflow (cumulative).
|
||||
uint8_t _8;
|
||||
uint8_t ufc; // Underflow (cumulative).
|
||||
uint8_t _9;
|
||||
uint8_t idc; // Input denormal (cumulative).
|
||||
uint8_t _10;
|
||||
uint8_t ioc; // Invalid operation (cumulative).
|
||||
uint8_t _11;
|
||||
uint8_t q; // Sticky overflow bit.
|
||||
|
||||
uint8_t _padding[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct alignas(16) State final : public ArchState {
|
||||
|
||||
|
||||
GPR gpr; // 528 bytes.
|
||||
SR sr;
|
||||
uint64_t _0;
|
||||
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
using AArch32State = State;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// We need this for boolean conditions, used in branch instructions.
|
||||
typedef RnW<uint8_t> R8W;
|
||||
|
||||
typedef RnW<uint8_t> R8W;
|
||||
typedef RnW<uint16_t> R16W;
|
||||
|
||||
// Note: AArch64 zero-extends like x86, but the smallest register size that
|
||||
// can be accessed is 32 bits.
|
||||
typedef RnW<uint32_t> R32W;
|
||||
|
||||
typedef Rn<uint8_t> R8;
|
||||
|
||||
//typedef Rn<uint16_t> R16;
|
||||
typedef Rn<uint32_t> R32;
|
||||
|
||||
typedef Vn<vec8_t> V8;
|
||||
typedef Vn<vec16_t> V16;
|
||||
typedef Vn<vec32_t> V32;
|
||||
typedef Vn<vec64_t> V64;
|
||||
typedef Vn<vec128_t> V128;
|
||||
typedef VnW<vec128_t> V128W;
|
||||
|
||||
typedef MnW<uint8_t> M8W;
|
||||
typedef MnW<uint16_t> M16W;
|
||||
typedef MnW<uint32_t> M32W;
|
||||
typedef MnW<uint64_t> M64W;
|
||||
|
||||
typedef MVnW<vec8_t> MV8W;
|
||||
typedef MVnW<vec16_t> MV16W;
|
||||
typedef MVnW<vec32_t> MV32W;
|
||||
typedef MVnW<vec64_t> MV64W;
|
||||
typedef MVnW<vec128_t> MV128W;
|
||||
|
||||
typedef Mn<uint8_t> M8;
|
||||
typedef Mn<uint16_t> M16;
|
||||
|
||||
typedef Mn<uint32_t> M32;
|
||||
typedef Mn<uint64_t> M64;
|
||||
|
||||
typedef MVn<vec8_t> MV8;
|
||||
typedef MVn<vec16_t> MV16;
|
||||
typedef MVn<vec32_t> MV32;
|
||||
typedef MVn<vec64_t> MV64;
|
||||
typedef MVn<vec128_t> MV128;
|
||||
typedef MVn<vec256_t> MV256;
|
||||
|
||||
typedef In<uint8_t> I8;
|
||||
typedef In<uint16_t> I16;
|
||||
typedef In<uint32_t> I32;
|
||||
|
||||
typedef In<float32_t> F32;
|
||||
typedef In<float64_t> F64;
|
||||
|
||||
typedef In<addr_t> PC;
|
||||
typedef In<addr_t> ADDR;
|
||||
+15
-10
@@ -31,6 +31,7 @@
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// clang-format on
|
||||
|
||||
#include <functional>
|
||||
@@ -134,12 +135,12 @@ class Arch {
|
||||
// Factory method for loading the correct architecture class for a given
|
||||
// operating system and architecture class.
|
||||
static auto Get(llvm::LLVMContext &context, std::string_view os,
|
||||
std::string_view arch_name) -> ArchPtr;
|
||||
std::string_view arch_name) -> ArchPtr;
|
||||
|
||||
// Factory method for loading the correct architecture class for a given
|
||||
// operating system and architecture class.
|
||||
static auto Get(llvm::LLVMContext &context, OSName os,
|
||||
ArchName arch_name) -> ArchPtr;
|
||||
static auto Get(llvm::LLVMContext &context, OSName os, ArchName arch_name)
|
||||
-> ArchPtr;
|
||||
|
||||
// Return the type of the state structure.
|
||||
llvm::StructType *StateStructType(void) const;
|
||||
@@ -246,6 +247,7 @@ class Arch {
|
||||
|
||||
bool IsX86(void) const;
|
||||
bool IsAMD64(void) const;
|
||||
bool IsAArch32(void) const;
|
||||
bool IsAArch64(void) const;
|
||||
bool IsSPARC32(void) const;
|
||||
bool IsSPARC64(void) const;
|
||||
@@ -278,26 +280,29 @@ class Arch {
|
||||
llvm::Triple BasicTriple(void) const;
|
||||
|
||||
// Add a register into this
|
||||
const Register *AddRegister(const char *reg_name,
|
||||
llvm::Type *val_type, size_t offset,
|
||||
const char *parent_reg_name) const;
|
||||
const Register *AddRegister(const char *reg_name, llvm::Type *val_type,
|
||||
size_t offset, const char *parent_reg_name) const;
|
||||
|
||||
private:
|
||||
// Defined in `lib/Arch/X86/Arch.cpp`.
|
||||
static ArchPtr GetX86(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/AArch32/Arch.cpp`.
|
||||
static ArchPtr GetAArch32(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/AArch64/Arch.cpp`.
|
||||
static ArchPtr GetAArch64(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/SPARC32/Arch.cpp`.
|
||||
static ArchPtr GetSPARC(
|
||||
llvm::LLVMContext *context, OSName os, ArchName arch_name);
|
||||
static ArchPtr GetSPARC(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/SPARC64/Arch.cpp`.
|
||||
static ArchPtr GetSPARC64(
|
||||
llvm::LLVMContext *context, OSName os, ArchName arch_name);
|
||||
static ArchPtr GetSPARC64(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
mutable std::unique_ptr<ArchImpl> impl;
|
||||
|
||||
|
||||
@@ -17,14 +17,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
class Constant;
|
||||
class Type;
|
||||
} // namespace llvm
|
||||
|
||||
namespace remill {
|
||||
|
||||
class Arch;
|
||||
struct Register;
|
||||
class OperandExpression;
|
||||
|
||||
enum ArchName : unsigned;
|
||||
|
||||
struct LLVMOpExpr {
|
||||
unsigned llvm_opcode;
|
||||
OperandExpression *op1;
|
||||
OperandExpression *op2;
|
||||
};
|
||||
|
||||
|
||||
class OperandExpression : public std::variant<LLVMOpExpr, const Register *,
|
||||
llvm::Constant *, std::string> {
|
||||
public:
|
||||
std::string Serialize(void) const;
|
||||
llvm::Type *type{nullptr};
|
||||
};
|
||||
|
||||
// Generic instruction operand.
|
||||
class Operand {
|
||||
public:
|
||||
@@ -36,7 +58,8 @@ class Operand {
|
||||
kTypeRegister,
|
||||
kTypeShiftRegister,
|
||||
kTypeImmediate,
|
||||
kTypeAddress
|
||||
kTypeAddress,
|
||||
kTypeExpression,
|
||||
} type;
|
||||
|
||||
enum Action { kActionInvalid, kActionRead, kActionWrite } action;
|
||||
@@ -61,8 +84,9 @@ class Operand {
|
||||
Register reg;
|
||||
uint64_t shift_size;
|
||||
uint64_t extract_size;
|
||||
bool shift_first;
|
||||
|
||||
enum Shift : unsigned {
|
||||
enum Shift : uint8_t {
|
||||
kShiftInvalid,
|
||||
kShiftLeftWithZeroes, // Shift left, filling low order bits with zero.
|
||||
kShiftLeftWithOnes, // Shift left, filling low order bits with one.
|
||||
@@ -72,7 +96,7 @@ class Operand {
|
||||
kShiftRightAround // Rotate right.
|
||||
} shift_op;
|
||||
|
||||
enum Extend : unsigned {
|
||||
enum Extend : uint8_t {
|
||||
kExtendInvalid,
|
||||
kExtendUnsigned,
|
||||
kExtendSigned,
|
||||
@@ -124,6 +148,23 @@ class Operand {
|
||||
}
|
||||
} addr;
|
||||
|
||||
OperandExpression *expr;
|
||||
|
||||
std::string Serialize(void) const;
|
||||
};
|
||||
|
||||
class Condition {
|
||||
public:
|
||||
enum Kind {
|
||||
kTypeTrue,
|
||||
kTypeIsOne,
|
||||
kTypeIsZero,
|
||||
kTypeIsEqual,
|
||||
} kind;
|
||||
|
||||
Operand::Register lhs_reg;
|
||||
Operand::Register rhs_reg;
|
||||
|
||||
std::string Serialize(void) const;
|
||||
};
|
||||
|
||||
@@ -178,9 +219,13 @@ class Instruction {
|
||||
kCategoryError,
|
||||
kCategoryDirectJump,
|
||||
kCategoryIndirectJump,
|
||||
kCategoryConditionalIndirectJump,
|
||||
kCategoryDirectFunctionCall,
|
||||
kCategoryConditionalDirectFunctionCall,
|
||||
kCategoryIndirectFunctionCall,
|
||||
kCategoryConditionalIndirectFunctionCall,
|
||||
kCategoryFunctionReturn,
|
||||
kCategoryConditionalFunctionReturn,
|
||||
kCategoryConditionalBranch,
|
||||
kCategoryAsyncHyperCall,
|
||||
kCategoryConditionalAsyncHyperCall,
|
||||
@@ -211,29 +256,41 @@ class Instruction {
|
||||
inline bool IsIndirectControlFlow(void) const {
|
||||
switch (category) {
|
||||
case kCategoryIndirectFunctionCall:
|
||||
case kCategoryConditionalIndirectFunctionCall:
|
||||
case kCategoryIndirectJump:
|
||||
case kCategoryConditionalBranch:
|
||||
case kCategoryConditionalIndirectJump:
|
||||
case kCategoryAsyncHyperCall:
|
||||
case kCategoryConditionalAsyncHyperCall:
|
||||
case kCategoryFunctionReturn: return true;
|
||||
case kCategoryFunctionReturn:
|
||||
case kCategoryConditionalFunctionReturn: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsConditionalBranch(void) const {
|
||||
return kCategoryConditionalBranch == category;
|
||||
switch (category) {
|
||||
case kCategoryConditionalDirectFunctionCall:
|
||||
case kCategoryConditionalBranch:
|
||||
case kCategoryConditionalIndirectJump:
|
||||
case kCategoryConditionalAsyncHyperCall:
|
||||
case kCategoryConditionalFunctionReturn: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsFunctionCall(void) const {
|
||||
switch (category) {
|
||||
case kCategoryDirectFunctionCall:
|
||||
case kCategoryConditionalDirectFunctionCall:
|
||||
case kCategoryConditionalIndirectFunctionCall:
|
||||
case kCategoryIndirectFunctionCall: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsFunctionReturn(void) const {
|
||||
return kCategoryFunctionReturn == category;
|
||||
return kCategoryFunctionReturn == category ||
|
||||
kCategoryConditionalFunctionReturn == category;
|
||||
}
|
||||
|
||||
inline bool IsValid(void) const {
|
||||
@@ -254,6 +311,27 @@ class Instruction {
|
||||
inline bool IsNoOp(void) const {
|
||||
return kCategoryNoOp == category;
|
||||
}
|
||||
|
||||
// This allocates an OperandExpression
|
||||
OperandExpression *AllocateExpression(void);
|
||||
OperandExpression *EmplaceRegister(const Register *);
|
||||
OperandExpression *EmplaceRegister(std::string_view reg_name);
|
||||
OperandExpression *EmplaceConstant(llvm::Constant *);
|
||||
OperandExpression *EmplaceVariable(std::string_view, llvm::Type *);
|
||||
OperandExpression *EmplaceBinaryOp(unsigned opcode, OperandExpression *op1,
|
||||
OperandExpression *op2);
|
||||
OperandExpression *EmplaceUnaryOp(unsigned opcode, OperandExpression *op1,
|
||||
llvm::Type *);
|
||||
|
||||
Operand &EmplaceOperand(const Operand::Register &op);
|
||||
Operand &EmplaceOperand(const Operand::Immediate &op);
|
||||
Operand &EmplaceOperand(const Operand::ShiftRegister &op);
|
||||
Operand &EmplaceOperand(const Operand::Address &op);
|
||||
|
||||
private:
|
||||
static constexpr auto kMaxNumExpr = 64u;
|
||||
OperandExpression exprs[kMaxNumExpr];
|
||||
unsigned next_expr_index{0};
|
||||
};
|
||||
|
||||
} // namespace remill
|
||||
|
||||
+45
-43
@@ -17,49 +17,50 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef REMILL_ARCH
|
||||
# if defined(__x86_64__)
|
||||
# define REMILL_ARCH "amd64_avx"
|
||||
# define REMILL_ON_AMD64 1
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# elif defined(__i386__) || defined(_M_X86)
|
||||
# define REMILL_ARCH "x86"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 1
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# elif defined(__aarch64__)
|
||||
# define REMILL_ARCH "aarch64"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 1
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# elif defined(__sparc__) || defined(__sparc) || defined(__sparc_v8__) || defined(__sparc_v9__) || defined(__sparcv8) || defined(__sparcv9)
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# if (defined(__LP64__) && __LP64__) || (defined(_LP64) && _LP64)
|
||||
# define REMILL_ARCH "sparc64"
|
||||
# define REMILL_ON_SPARC64 1
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# else
|
||||
# define REMILL_ARCH "sparc32"
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 1
|
||||
# endif
|
||||
# else
|
||||
# error "Cannot infer current architecture."
|
||||
# define REMILL_ARCH "invalid"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# endif
|
||||
# if defined(__x86_64__)
|
||||
# define REMILL_ARCH "amd64_avx"
|
||||
# define REMILL_ON_AMD64 1
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# elif defined(__i386__) || defined(_M_X86)
|
||||
# define REMILL_ARCH "x86"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 1
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# elif defined(__aarch64__)
|
||||
# define REMILL_ARCH "aarch64"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 1
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# elif defined(__sparc__) || defined(__sparc) || defined(__sparc_v8__) || \
|
||||
defined(__sparc_v9__) || defined(__sparcv8) || defined(__sparcv9)
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# if (defined(__LP64__) && __LP64__) || (defined(_LP64) && _LP64)
|
||||
# define REMILL_ARCH "sparc64"
|
||||
# define REMILL_ON_SPARC64 1
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# else
|
||||
# define REMILL_ARCH "sparc32"
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 1
|
||||
# endif
|
||||
# else
|
||||
# error "Cannot infer current architecture."
|
||||
# define REMILL_ARCH "invalid"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <string_view>
|
||||
@@ -80,6 +81,7 @@ enum ArchName : uint32_t {
|
||||
kArchAMD64_AVX,
|
||||
kArchAMD64_AVX512,
|
||||
|
||||
kArchAArch32LittleEndian,
|
||||
kArchAArch64LittleEndian,
|
||||
|
||||
kArchSparc32,
|
||||
|
||||
@@ -58,6 +58,36 @@
|
||||
ALWAYS_INLINE __attribute__((flatten)) static Memory *name( \
|
||||
Memory *memory, State &state, ##__VA_ARGS__)
|
||||
|
||||
template <typename R, typename... Args>
|
||||
inline static constexpr auto Specialize(R (*)(Args...), R (*b)(Args...))
|
||||
-> R (*)(Args...) {
|
||||
return b;
|
||||
}
|
||||
|
||||
// Define a semantics implementing function.
|
||||
#define DEF_COND_SEM(name, ...) \
|
||||
ALWAYS_INLINE __attribute__((flatten)) static Memory *name##_impl( \
|
||||
Memory *memory, State &state, ##__VA_ARGS__); \
|
||||
static Memory *name##_spec(Memory *memory, State &state, R8 __cond, \
|
||||
R8W __branch_taken, ##__VA_ARGS__) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
template <typename... Args> \
|
||||
ALWAYS_INLINE __attribute__((flatten)) static Memory *name##_wrapped( \
|
||||
Memory *memory, State &state, R8 __cond, R8W __branch_taken, \
|
||||
Args... args) { \
|
||||
if (Read(__cond)) { \
|
||||
Write(__branch_taken, true); \
|
||||
return name##_impl(memory, state, args...); \
|
||||
} else { \
|
||||
Write(__branch_taken, false); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
static constexpr auto name = Specialize(name##_spec, name##_wrapped); \
|
||||
ALWAYS_INLINE __attribute__((flatten)) static Memory *name##_impl( \
|
||||
Memory *memory, State &state, ##__VA_ARGS__)
|
||||
|
||||
// Define a semantics implementing function.
|
||||
#define DEF_HELPER(name, ...) \
|
||||
ALWAYS_INLINE __attribute__((flatten)) static auto name( \
|
||||
|
||||
@@ -60,6 +60,9 @@ class SyncHyperCall {
|
||||
// TODO(pag): How to distinguish little- and big-endian?
|
||||
kAArch64EmulateInstruction = 0x200U,
|
||||
kAArch64Breakpoint,
|
||||
kAArch32EmulateInstruction = 0x300U,
|
||||
|
||||
kAArch32CheckNotEL2,
|
||||
|
||||
kSPARC32EmulateInstruction = 0x400U,
|
||||
kSPARC64EmulateInstruction,
|
||||
|
||||
@@ -1465,7 +1465,8 @@ ALWAYS_INLINE static Memory *__remill_write_memory_128(Memory *mem, addr_t addr,
|
||||
ALWAYS_INLINE static uint##size##_t name(uint##size##_t val) { \
|
||||
const auto in_val = static_cast<uint##input_size##_t>(val); \
|
||||
return in_val ? (static_cast<uint##size##_t>(builtin(in_val)) - \
|
||||
static_cast<uint##input_size##_t>(disp)) : size; \
|
||||
static_cast<uint##input_size##_t>(disp)) \
|
||||
: size; \
|
||||
}
|
||||
|
||||
MAKE_BUILTIN(CountLeadingZeros, 8, 32, __builtin_clz, 24)
|
||||
|
||||
@@ -41,7 +41,8 @@ typedef uint64_t addr64_t;
|
||||
typedef IF_64BIT_ELSE(addr64_t, addr32_t) addr_t;
|
||||
typedef IF_64BIT_ELSE(int64_t, int32_t) addr_diff_t;
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || defined (__arm__)
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || \
|
||||
defined(__arm__)
|
||||
typedef unsigned uint128_t __attribute__((mode(TI)));
|
||||
typedef int int128_t __attribute__((mode(TI)));
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
@@ -34,6 +34,7 @@ union PtrReg final {
|
||||
static_assert(sizeof(PtrReg) == 4);
|
||||
|
||||
struct GPR {
|
||||
|
||||
// Prevents LLVM from casting a `GPR` into an `i64` to access `I0`.
|
||||
volatile addr_t _0;
|
||||
Reg i0;
|
||||
@@ -126,7 +127,8 @@ struct alignas(8) FPURegs final {
|
||||
vec128_t v[8];
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(128 == sizeof(struct FPURegs), "Invalid packing of `struct FPURegs`.");
|
||||
static_assert(128 == sizeof(struct FPURegs),
|
||||
"Invalid packing of `struct FPURegs`.");
|
||||
|
||||
struct FSRReg final {
|
||||
volatile uint8_t _0;
|
||||
@@ -153,9 +155,10 @@ struct FSRReg final {
|
||||
uint8_t fcc2;
|
||||
volatile uint8_t _11;
|
||||
uint8_t fcc3;
|
||||
}__attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(24 == sizeof(struct FSRReg), "Invalid packing of `struct FSRReg`.");
|
||||
static_assert(24 == sizeof(struct FSRReg),
|
||||
"Invalid packing of `struct FSRReg`.");
|
||||
|
||||
// Integer condition code register flags
|
||||
struct ICCRFlags final {
|
||||
@@ -174,52 +177,52 @@ struct ICCRFlags final {
|
||||
union GSRFlags final {
|
||||
uint64_t flat;
|
||||
struct {
|
||||
uint64_t align:3;
|
||||
uint64_t scale:5;
|
||||
uint64_t reserved_0:17;
|
||||
uint64_t irnd:2;
|
||||
uint64_t im:1;
|
||||
uint64_t reserved_1:4;
|
||||
uint64_t mask:32;
|
||||
uint64_t align : 3;
|
||||
uint64_t scale : 5;
|
||||
uint64_t reserved_0 : 17;
|
||||
uint64_t irnd : 2;
|
||||
uint64_t im : 1;
|
||||
uint64_t reserved_1 : 4;
|
||||
uint64_t mask : 32;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ASR final {
|
||||
Reg yreg; // ASR 0
|
||||
Reg yreg; // ASR 0
|
||||
volatile uint32_t _0;
|
||||
ICCRFlags ccr; // ASR 2
|
||||
ICCRFlags ccr; // ASR 2
|
||||
volatile addr_t _1;
|
||||
union {
|
||||
uint32_t asi_flat;
|
||||
struct {
|
||||
uint32_t asi:8; // ASR 3
|
||||
uint32_t padding_1:24;
|
||||
uint32_t asi : 8; // ASR 3
|
||||
uint32_t padding_1 : 24;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
volatile uint64_t _2;
|
||||
uint64_t tick; // ASR 4
|
||||
uint64_t tick; // ASR 4
|
||||
volatile uint64_t _3;
|
||||
union {
|
||||
uint32_t fprs_flat;
|
||||
struct {
|
||||
uint32_t fprs:3; // ASR 6
|
||||
uint32_t padding_2:29;
|
||||
uint32_t fprs : 3; // ASR 6
|
||||
uint32_t padding_2 : 29;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
volatile uint32_t _4;
|
||||
GSRFlags gsr;
|
||||
volatile uint64_t _5;
|
||||
addr64_t softint; // ASR 20
|
||||
addr64_t softint; // ASR 20
|
||||
volatile uint64_t _6;
|
||||
addr64_t stick; // ASR 24
|
||||
addr64_t stick; // ASR 24
|
||||
volatile uint64_t _7;
|
||||
addr64_t stick_cmpr; // ASR 25
|
||||
addr64_t stick_cmpr; // ASR 25
|
||||
volatile uint64_t _8;
|
||||
addr64_t cfr; // ASR 26
|
||||
addr64_t cfr; // ASR 26
|
||||
volatile uint64_t _9;
|
||||
addr64_t pause; // ASR 27
|
||||
addr64_t pause; // ASR 27
|
||||
volatile uint64_t _10;
|
||||
addr64_t mwait; // ASR 28
|
||||
addr64_t mwait; // ASR 28
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CSR {
|
||||
@@ -232,47 +235,47 @@ struct CSR {
|
||||
static_assert(8 == sizeof(struct CSR), "Invalid packing of `struct CSR`.");
|
||||
|
||||
struct PSR {
|
||||
uint64_t tpc;
|
||||
uint64_t tnpc;
|
||||
uint64_t tstate;
|
||||
uint64_t tick;
|
||||
uint64_t tba;
|
||||
volatile uint8_t _0; //padding
|
||||
uint8_t tt;
|
||||
uint8_t tl;
|
||||
uint64_t tpc;
|
||||
uint64_t tnpc;
|
||||
uint64_t tstate;
|
||||
uint64_t tick;
|
||||
uint64_t tba;
|
||||
volatile uint8_t _0; //padding
|
||||
uint8_t tt;
|
||||
uint8_t tl;
|
||||
union {
|
||||
uint16_t pstate;
|
||||
uint16_t pstate;
|
||||
struct {
|
||||
uint16_t res1:1;
|
||||
uint16_t ie:1;
|
||||
uint16_t priv:1;
|
||||
uint16_t am:1;
|
||||
uint16_t pef:1;
|
||||
uint16_t res2:1;
|
||||
uint16_t mm:1;
|
||||
uint16_t tle:1;
|
||||
uint16_t cle:1;
|
||||
uint16_t res3:1;
|
||||
uint16_t res4:1;
|
||||
uint16_t tct:1;
|
||||
uint16_t padding:4;
|
||||
uint16_t res1 : 1;
|
||||
uint16_t ie : 1;
|
||||
uint16_t priv : 1;
|
||||
uint16_t am : 1;
|
||||
uint16_t pef : 1;
|
||||
uint16_t res2 : 1;
|
||||
uint16_t mm : 1;
|
||||
uint16_t tle : 1;
|
||||
uint16_t cle : 1;
|
||||
uint16_t res3 : 1;
|
||||
uint16_t res4 : 1;
|
||||
uint16_t tct : 1;
|
||||
uint16_t padding : 4;
|
||||
} __attribute__((packed)) ps;
|
||||
} __attribute__((packed));
|
||||
volatile uint8_t _1;
|
||||
uint8_t pil;
|
||||
uint8_t cwp;
|
||||
uint8_t cansave;
|
||||
uint8_t pil;
|
||||
uint8_t cwp;
|
||||
uint8_t cansave;
|
||||
volatile uint8_t _2;
|
||||
uint8_t canrestore;
|
||||
uint8_t cleanwin;
|
||||
uint8_t otherwin;
|
||||
uint8_t canrestore;
|
||||
uint8_t cleanwin;
|
||||
uint8_t otherwin;
|
||||
volatile uint8_t _3;
|
||||
union {
|
||||
uint8_t wstate;
|
||||
uint8_t wstate;
|
||||
struct {
|
||||
uint8_t normal:2;
|
||||
uint8_t other:3;
|
||||
uint8_t padding:3;
|
||||
uint8_t normal : 2;
|
||||
uint8_t other : 3;
|
||||
uint8_t padding : 3;
|
||||
} __attribute__((packed)) ws;
|
||||
} __attribute__((packed));
|
||||
uint8_t gl;
|
||||
@@ -319,19 +322,19 @@ struct RegisterWindow {
|
||||
struct alignas(16) State : public ArchState {
|
||||
FPURegs fpreg; // 512 bytes
|
||||
volatile uint64_t _0;
|
||||
GPR gpr; // 256 bytes
|
||||
GPR gpr; // 256 bytes
|
||||
volatile uint64_t _1;
|
||||
ASR asr; // 176 bytes
|
||||
ASR asr; // 176 bytes
|
||||
volatile uint64_t _2;
|
||||
PSR psr; // 56 bytes
|
||||
PSR psr; // 56 bytes
|
||||
volatile uint64_t _3;
|
||||
FSRReg fsr; // 24 bytes
|
||||
FSRReg fsr; // 24 bytes
|
||||
volatile uint64_t _4;
|
||||
CSR csr; // 8 bytes
|
||||
CSR csr; // 8 bytes
|
||||
volatile uint32_t _5;
|
||||
Reg pc; // 4 bytes
|
||||
Reg pc; // 4 bytes
|
||||
volatile uint32_t _6;
|
||||
Reg next_pc; // 4 bytes
|
||||
Reg next_pc; // 4 bytes
|
||||
volatile uint32_t _7;
|
||||
|
||||
// NOTE(pag): This *must* go at the end, as if we change the target arch/data
|
||||
@@ -341,7 +344,8 @@ struct alignas(16) State : public ArchState {
|
||||
uint32_t window;
|
||||
#else
|
||||
RegisterWindow *window; // smuggled.
|
||||
static_assert(sizeof(RegisterWindow *) == 4, "Invalid size of `RegisterWindow`");
|
||||
static_assert(sizeof(RegisterWindow *) == 4,
|
||||
"Invalid size of `RegisterWindow`");
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -26,17 +26,16 @@ struct Reg final {
|
||||
addr_t qword;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(Reg) == 8,
|
||||
"Invalid size of `Reg`.");
|
||||
static_assert(sizeof(Reg) == 8, "Invalid size of `Reg`.");
|
||||
|
||||
union PtrReg final {
|
||||
addr_t qword;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(PtrReg) == 8,
|
||||
"Invalid size of `PtrReg`.");
|
||||
static_assert(sizeof(PtrReg) == 8, "Invalid size of `PtrReg`.");
|
||||
|
||||
struct GPR {
|
||||
|
||||
// Prevents LLVM from casting a `GPR` into an `i64` to access `I0`.
|
||||
volatile addr_t _0;
|
||||
Reg i0;
|
||||
@@ -107,8 +106,7 @@ struct GPR {
|
||||
Reg g7;
|
||||
};
|
||||
|
||||
static_assert(512 == sizeof(GPR),
|
||||
"Invalid packing of `struct GPR`.");
|
||||
static_assert(512 == sizeof(GPR), "Invalid packing of `struct GPR`.");
|
||||
|
||||
enum AlternativeSpaceIdentifier : uint32_t {
|
||||
ASI_PST8_PRIMARY = 0xc0,
|
||||
@@ -132,7 +130,8 @@ struct FPURegs final {
|
||||
vec128_t v[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(((128 * 16) / 8) == sizeof(struct FPURegs), "Invalid packing of `struct FPURegs`.");
|
||||
static_assert(((128 * 16) / 8) == sizeof(struct FPURegs),
|
||||
"Invalid packing of `struct FPURegs`.");
|
||||
|
||||
struct FSRReg final {
|
||||
volatile uint8_t _0;
|
||||
@@ -159,9 +158,10 @@ struct FSRReg final {
|
||||
uint8_t fcc2;
|
||||
volatile uint8_t _11;
|
||||
uint8_t fcc3;
|
||||
}__attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(24 == sizeof(struct FSRReg), "Invalid packing of `struct FSRReg`.");
|
||||
static_assert(24 == sizeof(struct FSRReg),
|
||||
"Invalid packing of `struct FSRReg`.");
|
||||
|
||||
// Integer condition code register flags
|
||||
struct ICCRFlags final {
|
||||
@@ -177,57 +177,58 @@ struct ICCRFlags final {
|
||||
} __attribute__((packed)) icc, xcc;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(16 == sizeof(struct ICCRFlags), "Invalid packing of `struct ICCRFlags`.");
|
||||
static_assert(16 == sizeof(struct ICCRFlags),
|
||||
"Invalid packing of `struct ICCRFlags`.");
|
||||
|
||||
union GSRFlags final {
|
||||
uint64_t flat;
|
||||
struct {
|
||||
uint64_t align:3;
|
||||
uint64_t scale:5;
|
||||
uint64_t reserved_0:17;
|
||||
uint64_t irnd:2;
|
||||
uint64_t im:1;
|
||||
uint64_t reserved_1:4;
|
||||
uint64_t mask:32;
|
||||
uint64_t align : 3;
|
||||
uint64_t scale : 5;
|
||||
uint64_t reserved_0 : 17;
|
||||
uint64_t irnd : 2;
|
||||
uint64_t im : 1;
|
||||
uint64_t reserved_1 : 4;
|
||||
uint64_t mask : 32;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ASR final {
|
||||
Reg yreg; // ASR 0
|
||||
Reg yreg; // ASR 0
|
||||
volatile uint64_t _0;
|
||||
ICCRFlags ccr; // ASR 2
|
||||
ICCRFlags ccr; // ASR 2
|
||||
volatile uint64_t _1;
|
||||
union {
|
||||
uint64_t asi_flat;
|
||||
struct {
|
||||
uint64_t asi:8; // ASR 3
|
||||
uint64_t padding_1:56;
|
||||
uint64_t asi : 8; // ASR 3
|
||||
uint64_t padding_1 : 56;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
volatile uint64_t _2;
|
||||
uint64_t tick; // ASR 4
|
||||
uint64_t tick; // ASR 4
|
||||
volatile uint64_t _3;
|
||||
union {
|
||||
uint64_t fprs_flat;
|
||||
struct {
|
||||
uint64_t fprs:3; // ASR 6
|
||||
uint64_t padding_2:61;
|
||||
uint64_t fprs : 3; // ASR 6
|
||||
uint64_t padding_2 : 61;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
volatile uint64_t _4;
|
||||
GSRFlags gsr;
|
||||
volatile uint64_t _5;
|
||||
uint64_t softint; // ASR 20
|
||||
uint64_t softint; // ASR 20
|
||||
volatile uint64_t _6;
|
||||
uint64_t stick; // ASR 24
|
||||
uint64_t stick; // ASR 24
|
||||
volatile uint64_t _7;
|
||||
uint64_t stick_cmpr; // ASR 25
|
||||
uint64_t stick_cmpr; // ASR 25
|
||||
volatile uint64_t _8;
|
||||
uint64_t cfr; // ASR 26
|
||||
uint64_t cfr; // ASR 26
|
||||
volatile uint64_t _9;
|
||||
uint64_t pause; // ASR 27
|
||||
uint64_t pause; // ASR 27
|
||||
volatile uint64_t _10;
|
||||
uint64_t mwait; // ASR 28
|
||||
uint64_t mwait; // ASR 28
|
||||
};
|
||||
|
||||
static_assert(192 == sizeof(struct ASR), "Invalid packing of `struct ASR`.");
|
||||
@@ -242,50 +243,50 @@ struct CSR {
|
||||
static_assert(8 == sizeof(struct CSR), "Invalid packing of `struct CSR`.");
|
||||
|
||||
struct PSR {
|
||||
uint64_t tpc;
|
||||
uint64_t tnpc;
|
||||
uint64_t tstate;
|
||||
uint64_t tick;
|
||||
uint64_t tba;
|
||||
volatile uint8_t _0; //padding
|
||||
uint8_t tt;
|
||||
uint8_t tl;
|
||||
uint64_t tpc;
|
||||
uint64_t tnpc;
|
||||
uint64_t tstate;
|
||||
uint64_t tick;
|
||||
uint64_t tba;
|
||||
volatile uint8_t _0; //padding
|
||||
uint8_t tt;
|
||||
uint8_t tl;
|
||||
union {
|
||||
uint16_t pstate;
|
||||
uint16_t pstate;
|
||||
struct {
|
||||
uint16_t res1:1;
|
||||
uint16_t ie:1;
|
||||
uint16_t priv:1;
|
||||
uint16_t am:1;
|
||||
uint16_t pef:1;
|
||||
uint16_t res2:1;
|
||||
uint16_t mm:1;
|
||||
uint16_t tle:1;
|
||||
uint16_t cle:1;
|
||||
uint16_t res3:1;
|
||||
uint16_t res4:1;
|
||||
uint16_t tct:1;
|
||||
uint16_t padding:4;
|
||||
uint16_t res1 : 1;
|
||||
uint16_t ie : 1;
|
||||
uint16_t priv : 1;
|
||||
uint16_t am : 1;
|
||||
uint16_t pef : 1;
|
||||
uint16_t res2 : 1;
|
||||
uint16_t mm : 1;
|
||||
uint16_t tle : 1;
|
||||
uint16_t cle : 1;
|
||||
uint16_t res3 : 1;
|
||||
uint16_t res4 : 1;
|
||||
uint16_t tct : 1;
|
||||
uint16_t padding : 4;
|
||||
} __attribute__((packed)) ps;
|
||||
} __attribute__((packed));
|
||||
volatile uint8_t _1;
|
||||
uint8_t pil;
|
||||
uint8_t cwp;
|
||||
uint8_t cansave;
|
||||
uint8_t pil;
|
||||
uint8_t cwp;
|
||||
uint8_t cansave;
|
||||
volatile uint8_t _2;
|
||||
uint8_t canrestore;
|
||||
uint8_t cleanwin;
|
||||
uint8_t otherwin;
|
||||
uint8_t canrestore;
|
||||
uint8_t cleanwin;
|
||||
uint8_t otherwin;
|
||||
volatile uint8_t _3;
|
||||
union {
|
||||
uint8_t wstate;
|
||||
uint8_t wstate;
|
||||
struct {
|
||||
uint8_t normal:2;
|
||||
uint8_t other:3;
|
||||
uint8_t padding:3;
|
||||
uint8_t normal : 2;
|
||||
uint8_t other : 3;
|
||||
uint8_t padding : 3;
|
||||
} __attribute__((packed)) ws;
|
||||
} __attribute__((packed));
|
||||
uint8_t gl;
|
||||
uint8_t gl;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RegisterWindow {
|
||||
@@ -330,19 +331,19 @@ struct RegisterWindow {
|
||||
struct alignas(16) State : public ArchState {
|
||||
FPURegs fpreg; // 512 bytes
|
||||
volatile uint64_t _0;
|
||||
GPR gpr; // 512 bytes
|
||||
GPR gpr; // 512 bytes
|
||||
volatile uint64_t _1;
|
||||
ASR asr; // 176 bytes
|
||||
ASR asr; // 176 bytes
|
||||
volatile uint64_t _2;
|
||||
PSR psr; // 56 bytes
|
||||
PSR psr; // 56 bytes
|
||||
volatile uint64_t _3;
|
||||
FSRReg fsr; // 24 bytes
|
||||
FSRReg fsr; // 24 bytes
|
||||
volatile uint64_t _4;
|
||||
CSR csr; // 8 bytes
|
||||
CSR csr; // 8 bytes
|
||||
volatile uint64_t _5;
|
||||
Reg pc; // 8 bytes
|
||||
Reg pc; // 8 bytes
|
||||
volatile uint64_t _6;
|
||||
Reg next_pc; // 8 bytes
|
||||
Reg next_pc; // 8 bytes
|
||||
volatile uint64_t _7;
|
||||
|
||||
// NOTE(pag): This *must* go at the end, as if we change the target arch/data
|
||||
@@ -352,7 +353,8 @@ struct alignas(16) State : public ArchState {
|
||||
uint64_t window;
|
||||
#else
|
||||
RegisterWindow *window; // smuggled.
|
||||
static_assert(sizeof(RegisterWindow *) == 8, "Invalid size of `RegisterWindow`");
|
||||
static_assert(sizeof(RegisterWindow *) == 8,
|
||||
"Invalid size of `RegisterWindow`");
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<llvm-c/Types.h>)
|
||||
#include <llvm-c/Types.h>
|
||||
# include <llvm-c/Types.h>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
// TODO(pag): This is a rather ugly hack; had some issues with anvill not
|
||||
// compiling on macOS due to these C types.
|
||||
struct LLVMOpaqueNamedMDNode;
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "remill/BC/Version.h"
|
||||
|
||||
#include <llvm/IR/Instruction.h>
|
||||
|
||||
#include "remill/BC/Version.h"
|
||||
|
||||
|
||||
/* In llvm-11 llvm::CallSite got partially replace by llvm::AbstractCallSite
|
||||
* for read-only operations and llvm::CallBase was made public (was considered
|
||||
@@ -29,83 +29,81 @@
|
||||
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(11, 0)
|
||||
|
||||
#include <llvm/IR/CallSite.h>
|
||||
# include <llvm/IR/CallSite.h>
|
||||
namespace remill::compat::llvm {
|
||||
|
||||
struct CallSite : private ::llvm::CallSite {
|
||||
using parent = ::llvm::CallSite;
|
||||
struct CallSite : private ::llvm::CallSite {
|
||||
using parent = ::llvm::CallSite;
|
||||
|
||||
/* List of "allowed" methods (thanks to private inheritance)
|
||||
/* List of "allowed" methods (thanks to private inheritance)
|
||||
* that prevent user from accidentally using functionality that
|
||||
* would break other llvm version.
|
||||
* If you want to add method here, make sure other versions have it
|
||||
* as well.
|
||||
*/
|
||||
using parent::parent;
|
||||
using parent::isInvoke;
|
||||
using parent::isCall;
|
||||
using parent::operator bool;
|
||||
using parent::getCalledValue;
|
||||
using parent::getCalledFunction;
|
||||
using parent::setCalledFunction;
|
||||
using parent::getInstruction;
|
||||
};
|
||||
using parent::isCall;
|
||||
using parent::isInvoke;
|
||||
using parent::parent;
|
||||
using parent::operator bool;
|
||||
using parent::getCalledFunction;
|
||||
using parent::getCalledValue;
|
||||
using parent::getInstruction;
|
||||
using parent::setCalledFunction;
|
||||
};
|
||||
|
||||
} // namespace remill::compat::llvm
|
||||
} // namespace remill::compat::llvm
|
||||
|
||||
#else
|
||||
|
||||
#include <llvm/IR/AbstractCallSite.h>
|
||||
#include <llvm/Analysis/InlineCost.h>
|
||||
#include <llvm/Transforms/Utils/Cloning.h>
|
||||
# include <llvm/Analysis/InlineCost.h>
|
||||
# include <llvm/IR/AbstractCallSite.h>
|
||||
# include <llvm/Transforms/Utils/Cloning.h>
|
||||
namespace remill::compat::llvm {
|
||||
|
||||
struct CallSite {
|
||||
::llvm::CallBase *cb;
|
||||
struct CallSite {
|
||||
::llvm::CallBase *cb;
|
||||
|
||||
CallSite(::llvm::Instruction *inst)
|
||||
: cb(::llvm::dyn_cast<::llvm::CallBase>(inst))
|
||||
{}
|
||||
CallSite(::llvm::Instruction *inst)
|
||||
: cb(::llvm::dyn_cast<::llvm::CallBase>(inst)) {}
|
||||
|
||||
CallSite(::llvm::User *user)
|
||||
: CallSite(::llvm::dyn_cast<::llvm::Instruction>(user))
|
||||
{}
|
||||
CallSite(::llvm::User *user)
|
||||
: CallSite(::llvm::dyn_cast<::llvm::Instruction>(user)) {}
|
||||
|
||||
bool isInvoke() const {
|
||||
return ::llvm::isa<::llvm::InvokeInst>(cb);
|
||||
bool isInvoke() const {
|
||||
return ::llvm::isa<::llvm::InvokeInst>(cb);
|
||||
}
|
||||
|
||||
bool isCall() const {
|
||||
return ::llvm::isa<::llvm::CallInst>(cb);
|
||||
}
|
||||
|
||||
::llvm::Value *getCalledValue() {
|
||||
if (!static_cast<bool>(*this)) {
|
||||
return nullptr;
|
||||
}
|
||||
return cb->getCalledOperand();
|
||||
}
|
||||
|
||||
bool isCall() const {
|
||||
return ::llvm::isa<::llvm::CallInst>(cb);
|
||||
::llvm::Function *getCalledFunction() const {
|
||||
if (!*this) {
|
||||
return nullptr;
|
||||
}
|
||||
return cb->getCalledFunction();
|
||||
}
|
||||
|
||||
::llvm::Value *getCalledValue() {
|
||||
if (!static_cast<bool>(*this)) {
|
||||
return nullptr;
|
||||
}
|
||||
return cb->getCalledOperand();
|
||||
}
|
||||
void setCalledFunction(::llvm::Function *fn) {
|
||||
return cb->setCalledFunction(fn);
|
||||
}
|
||||
|
||||
::llvm::Function *getCalledFunction() const {
|
||||
if ( !*this) {
|
||||
return nullptr;
|
||||
}
|
||||
return cb->getCalledFunction();
|
||||
}
|
||||
operator bool() const {
|
||||
return cb;
|
||||
}
|
||||
|
||||
void setCalledFunction(::llvm::Function *fn) {
|
||||
return cb->setCalledFunction(fn);
|
||||
}
|
||||
::llvm::CallBase *getInstruction() {
|
||||
return cb;
|
||||
}
|
||||
};
|
||||
|
||||
operator bool() const {
|
||||
return cb;
|
||||
}
|
||||
|
||||
::llvm::CallBase *getInstruction() {
|
||||
return cb;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remill::compat::llvm
|
||||
} // namespace remill::compat::llvm
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,7 @@ class Arch;
|
||||
class Instruction;
|
||||
class IntrinsicTable;
|
||||
class Operand;
|
||||
class OperandExpression;
|
||||
class TraceLifter;
|
||||
|
||||
enum LiftStatus {
|
||||
@@ -112,6 +113,18 @@ class InstructionLifter {
|
||||
llvm::BasicBlock *block,
|
||||
llvm::Argument *arg, Operand &op);
|
||||
|
||||
// Lift an expression operand.
|
||||
virtual llvm::Value *LiftExpressionOperand(Instruction &inst,
|
||||
llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr,
|
||||
llvm::Argument *arg, Operand &op);
|
||||
|
||||
// Lift an expression operand.
|
||||
virtual llvm::Value *
|
||||
LiftExpressionOperandRec(Instruction &inst, llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr, llvm::Argument *arg,
|
||||
const OperandExpression *op);
|
||||
|
||||
// Lift an indirect memory operand to a value.
|
||||
virtual llvm::Value *
|
||||
LiftAddressOperand(Instruction &inst, llvm::BasicBlock *block,
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <remill/BC/Lifter.h>
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <remill/BC/Lifter.h>
|
||||
|
||||
namespace remill {
|
||||
|
||||
using TraceMap = std::unordered_map<uint64_t, llvm::Function *>;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// clang-format on
|
||||
|
||||
#include <array>
|
||||
@@ -134,8 +135,7 @@ llvm::Value *LoadBranchTakenRef(llvm::BasicBlock *block);
|
||||
llvm::Function *FindFunction(llvm::Module *M, std::string_view name);
|
||||
|
||||
// Find a global variable with name `name` in the module `M`.
|
||||
llvm::GlobalVariable *FindGlobaVariable(llvm::Module *M,
|
||||
std::string_view name);
|
||||
llvm::GlobalVariable *FindGlobaVariable(llvm::Module *M, std::string_view name);
|
||||
|
||||
// Try to verify a module.
|
||||
bool VerifyModule(llvm::Module *module);
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "Arch.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <llvm/ADT/Triple.h>
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "remill/Arch/Instruction.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
#include "remill/BC/ABI.h"
|
||||
#include "remill/BC/Util.h"
|
||||
#include "remill/BC/Version.h"
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
// clang-format off
|
||||
#define ADDRESS_SIZE 32
|
||||
#include "remill/Arch/AArch32/Runtime/State.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace remill {
|
||||
|
||||
AArch32Arch::AArch32Arch(llvm::LLVMContext *context_, OSName os_name_,
|
||||
ArchName arch_name_)
|
||||
: Arch(context_, os_name_, arch_name_) {}
|
||||
|
||||
AArch32Arch::~AArch32Arch(void) {}
|
||||
|
||||
// Maximum number of bytes in an instruction for this particular architecture.
|
||||
uint64_t AArch32Arch::MaxInstructionSize(void) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Default calling convention for this architecture.
|
||||
llvm::CallingConv::ID AArch32Arch::DefaultCallingConv(void) const {
|
||||
return llvm::CallingConv::C; // cdecl.
|
||||
}
|
||||
|
||||
// Get the LLVM triple for this architecture.
|
||||
llvm::Triple AArch32Arch::Triple(void) const {
|
||||
auto triple = BasicTriple();
|
||||
switch (arch_name) {
|
||||
case kArchAArch32LittleEndian: triple.setArch(llvm::Triple::arm); break;
|
||||
default:
|
||||
LOG(FATAL) << "Cannot get triple for non-aarch32 architecture "
|
||||
<< GetArchName(arch_name);
|
||||
}
|
||||
|
||||
return triple;
|
||||
}
|
||||
|
||||
// Get the LLVM DataLayout for a module.
|
||||
llvm::DataLayout AArch32Arch::DataLayout(void) const {
|
||||
std::string dl;
|
||||
switch (os_name) {
|
||||
case kOSInvalid:
|
||||
LOG(FATAL) << "Cannot convert module for an unrecognized OS.";
|
||||
break;
|
||||
|
||||
case kOSLinux:
|
||||
case kOSSolaris:
|
||||
case kOSmacOS:
|
||||
case kOSWindows:
|
||||
dl = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64";
|
||||
break;
|
||||
}
|
||||
|
||||
return llvm::DataLayout(dl);
|
||||
}
|
||||
|
||||
// Returns the name of the stack pointer register.
|
||||
std::string_view AArch32Arch::StackPointerRegisterName(void) const {
|
||||
return "SP";
|
||||
}
|
||||
|
||||
// Returns the name of the program counter register.
|
||||
std::string_view AArch32Arch::ProgramCounterRegisterName(void) const {
|
||||
return "PC";
|
||||
}
|
||||
|
||||
// Populate the `__remill_basic_block` function with variables.
|
||||
void AArch32Arch::PopulateBasicBlockFunction(llvm::Module *module,
|
||||
llvm::Function *bb_func) const {
|
||||
const auto &dl = module->getDataLayout();
|
||||
CHECK_EQ(sizeof(State), dl.getTypeAllocSize(StateStructType()))
|
||||
<< "Mismatch between size of State type for x86/amd64 and what is in "
|
||||
<< "the bitcode module";
|
||||
|
||||
auto &context = module->getContext();
|
||||
auto u8 = llvm::Type::getInt8Ty(context);
|
||||
|
||||
// auto u16 = llvm::Type::getInt16Ty(context);
|
||||
auto u32 = llvm::Type::getInt32Ty(context);
|
||||
|
||||
// auto u64 = llvm::Type::getInt64Ty(context);
|
||||
// auto f64 = llvm::Type::getDoubleTy(context);
|
||||
// auto v128 = llvm::ArrayType::get(llvm::Type::getInt8Ty(context), 128u / 8u);
|
||||
// auto v256 = llvm::ArrayType::get(llvm::Type::getInt8Ty(context), 256u / 8u);
|
||||
// auto v512 = llvm::ArrayType::get(llvm::Type::getInt8Ty(context), 512u / 8u);
|
||||
auto addr = llvm::Type::getIntNTy(context, address_size);
|
||||
|
||||
//auto zero_addr_val = llvm::Constant::getNullValue(addr);
|
||||
|
||||
const auto entry_block = &bb_func->getEntryBlock();
|
||||
llvm::IRBuilder<> ir(entry_block);
|
||||
|
||||
#define OFFSET_OF(type, access) \
|
||||
(reinterpret_cast<uintptr_t>(&reinterpret_cast<const volatile char &>( \
|
||||
static_cast<type *>(nullptr)->access)))
|
||||
|
||||
#define REG(name, access, type) \
|
||||
AddRegister(#name, type, OFFSET_OF(State, access), nullptr)
|
||||
|
||||
#define SUB_REG(name, access, type, parent_reg_name) \
|
||||
AddRegister(#name, type, OFFSET_OF(State, access), #parent_reg_name)
|
||||
|
||||
REG(R0, gpr.r0.dword, u32);
|
||||
REG(R1, gpr.r1.dword, u32);
|
||||
REG(R2, gpr.r2.dword, u32);
|
||||
REG(R3, gpr.r3.dword, u32);
|
||||
REG(R4, gpr.r4.dword, u32);
|
||||
REG(R5, gpr.r5.dword, u32);
|
||||
REG(R6, gpr.r6.dword, u32);
|
||||
REG(R7, gpr.r7.dword, u32);
|
||||
REG(R8, gpr.r8.dword, u32);
|
||||
REG(R9, gpr.r9.dword, u32);
|
||||
REG(R10, gpr.r10.dword, u32);
|
||||
REG(R11, gpr.r11.dword, u32);
|
||||
REG(R12, gpr.r12.dword, u32);
|
||||
REG(R13, gpr.r13.dword, u32);
|
||||
REG(R14, gpr.r14.dword, u32);
|
||||
REG(R15, gpr.r15.dword, u32);
|
||||
|
||||
SUB_REG(SP, gpr.r13.dword, u32, R13);
|
||||
SUB_REG(LR, gpr.r14.dword, u32, R14);
|
||||
SUB_REG(PC, gpr.r15.dword, u32, R15);
|
||||
|
||||
REG(N, sr.n, u8);
|
||||
REG(C, sr.c, u8);
|
||||
REG(Z, sr.z, u8);
|
||||
REG(V, sr.v, u8);
|
||||
|
||||
const auto pc_arg = NthArgument(bb_func, kPCArgNum);
|
||||
const auto state_ptr_arg = NthArgument(bb_func, kStatePointerArgNum);
|
||||
ir.CreateStore(pc_arg, ir.CreateAlloca(addr, nullptr, "NEXT_PC"));
|
||||
|
||||
auto zero_c = ir.CreateAlloca(u8, nullptr, "ZERO_C");
|
||||
ir.CreateStore(llvm::Constant::getNullValue(u8), zero_c);
|
||||
ir.CreateAlloca(u32, nullptr, "SUPPRESS_WRITEBACK");
|
||||
(void) this->RegisterByName("PC")->AddressOf(state_ptr_arg, ir);
|
||||
}
|
||||
|
||||
// TODO(pag): We pretend that these are singletons, but they aren't really!
|
||||
Arch::ArchPtr Arch::GetAArch32(llvm::LLVMContext *context_, OSName os_name_,
|
||||
ArchName arch_name_) {
|
||||
return std::make_unique<AArch32Arch>(context_, os_name_, arch_name_);
|
||||
}
|
||||
|
||||
} // namespace remill
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
|
||||
namespace remill {
|
||||
class AArch32Arch final : public Arch {
|
||||
public:
|
||||
AArch32Arch(llvm::LLVMContext *context_, OSName os_name_,
|
||||
ArchName arch_name_);
|
||||
|
||||
virtual ~AArch32Arch(void);
|
||||
|
||||
// Returns the name of the stack pointer register.
|
||||
std::string_view StackPointerRegisterName(void) const override;
|
||||
|
||||
// Returns the name of the program counter register.
|
||||
std::string_view ProgramCounterRegisterName(void) const override;
|
||||
|
||||
// Decode an instuction.
|
||||
bool DecodeInstruction(uint64_t address, std::string_view inst_bytes,
|
||||
Instruction &inst) const override;
|
||||
|
||||
// Maximum number of bytes in an instruction.
|
||||
uint64_t MaxInstructionSize(void) const override;
|
||||
|
||||
llvm::Triple Triple(void) const override;
|
||||
llvm::DataLayout DataLayout(void) const override;
|
||||
|
||||
// Default calling convention for this architecture.
|
||||
llvm::CallingConv::ID DefaultCallingConv(void) const override;
|
||||
|
||||
// Populate the `__remill_basic_block` function with variables.
|
||||
void PopulateBasicBlockFunction(llvm::Module *module,
|
||||
llvm::Function *bb_func) const override;
|
||||
|
||||
private:
|
||||
AArch32Arch(void) = delete;
|
||||
};
|
||||
|
||||
} // namespace remill
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2020 Trail of Bits, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
add_library(remill_arch_aarch32 STATIC
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Definitions.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/HyperCall.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Intrinsics.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Operators.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Runtime.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/State.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/Runtime/Types.h"
|
||||
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/AArch32/Runtime/Operators.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/AArch32/Runtime/State.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/AArch32/Runtime/Types.h"
|
||||
|
||||
Arch.cpp
|
||||
Decode.cpp
|
||||
# Decode.h
|
||||
# Extract.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(Runtime)
|
||||
|
||||
set_property(TARGET remill_arch_aarch32 PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
target_link_libraries(remill_arch_aarch32 LINK_PUBLIC
|
||||
remill_settings
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS remill_arch_aarch32
|
||||
EXPORT remillTargets
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
|
||||
#include "remill/Arch/AArch32/Runtime/State.h"
|
||||
#include "remill/Arch/Runtime/Float.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
|
||||
// Instructions will be lifted into clones of this function.
|
||||
[[gnu::used]] Memory *__remill_basic_block(State &, addr_t, Memory *);
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // extern C
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (c) 2017 Trail of Bits, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(arm_runtime)
|
||||
|
||||
set(ARMRUNTIME_SOURCEFILES
|
||||
Instructions.cpp
|
||||
BasicBlock.cpp
|
||||
|
||||
"${REMILL_LIB_DIR}/Arch/Runtime/Intrinsics.cpp"
|
||||
)
|
||||
|
||||
set_source_files_properties(Instructions.cpp PROPERTIES COMPILE_FLAGS "-O3 -g0")
|
||||
set_source_files_properties(BasicBlock.cpp PROPERTIES COMPILE_FLAGS "-O0 -g3")
|
||||
|
||||
function(add_runtime_helper target_name little_endian)
|
||||
message(" > Generating runtime target: ${target_name}")
|
||||
|
||||
# Visual C++ requires C++14
|
||||
if(WIN32)
|
||||
set(required_cpp_standard "c++14")
|
||||
else()
|
||||
set(required_cpp_standard "c++17")
|
||||
endif()
|
||||
|
||||
# necessary to build code as 32-bit
|
||||
# on aarch64
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|arm64.*|ARM64.*)")
|
||||
set(arch_flags "--target=arm-linux-gnueabihf")
|
||||
else()
|
||||
set(arch_flags "-m32")
|
||||
endif()
|
||||
|
||||
add_runtime(${target_name}
|
||||
SOURCES ${ARMRUNTIME_SOURCEFILES}
|
||||
ADDRESS_SIZE 32
|
||||
DEFINITIONS "LITTLE_ENDIAN=${little_endian}"
|
||||
BCFLAGS "${arch_flags}" "-std=${required_cpp_standard}"
|
||||
INCLUDEDIRECTORIES "${REMILL_INCLUDE_DIR}" "${REMILL_SOURCE_DIR}"
|
||||
INSTALLDESTINATION "${REMILL_INSTALL_SEMANTICS_DIR}"
|
||||
|
||||
DEPENDENCIES
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/BINARY.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/FLAGS.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/COND.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/LOGICAL.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/MEM.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/BRANCH.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/BITBYTE.cpp"
|
||||
"${REMILL_LIB_DIR}/Arch/AArch32/Semantics/MISC.cpp"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
add_runtime_helper(aarch32 1)
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
|
||||
// clang-format off
|
||||
#include "remill/Arch/Runtime/Float.h"
|
||||
#include "remill/Arch/Runtime/Intrinsics.h"
|
||||
#include "remill/Arch/Runtime/Operators.h"
|
||||
#include "remill/Arch/AArch32/Runtime/State.h"
|
||||
#include "remill/Arch/AArch32/Runtime/Types.h"
|
||||
#include "remill/Arch/AArch32/Runtime/Operators.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
#define REG_PC state.gpr.r15.dword
|
||||
#define REG_LR state.gpr.r14.dword
|
||||
#define REG_SP state.gpr.r13.dword
|
||||
|
||||
#define HYPER_CALL state.hyper_call
|
||||
#define INTERRUPT_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL_VECTOR state.hyper_call_vector
|
||||
|
||||
namespace {
|
||||
|
||||
// Takes the place of an unsupported instruction.
|
||||
DEF_SEM(HandleUnsupported) {
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32EmulateInstruction);
|
||||
}
|
||||
|
||||
// Takes the place of an invalid instruction.
|
||||
DEF_SEM(HandleInvalidInstruction) {
|
||||
HYPER_CALL = AsyncHyperCall::kInvalidInstruction;
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Takes the place of an unsupported instruction.
|
||||
DEF_ISEL(UNSUPPORTED_INSTRUCTION) = HandleUnsupported;
|
||||
DEF_ISEL(INVALID_INSTRUCTION) = HandleInvalidInstruction;
|
||||
|
||||
// clang-format off
|
||||
#include "lib/Arch/AArch32/Semantics/FLAGS.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/BINARY.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/MEM.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/LOGICAL.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/BITBYTE.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/BRANCH.cpp"
|
||||
|
||||
//#include "lib/Arch/AArch32/Semantics/CALL_RET.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/COND.cpp"
|
||||
|
||||
//#include "lib/Arch/AArch32/Semantics/CONVERT.cpp"
|
||||
//#include "lib/Arch/AArch32/Semantics/DATAXFER.cpp"
|
||||
#include "lib/Arch/AArch32/Semantics/MISC.cpp"
|
||||
|
||||
//#include "lib/Arch/AArch32/Semantics/SHIFT.cpp"
|
||||
//#include "lib/Arch/AArch32/Semantics/SIMD.cpp"
|
||||
//#include "lib/Arch/AArch32/Semantics/SYSTEM.cpp"
|
||||
|
||||
// clang-format on
|
||||
@@ -0,0 +1,728 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
T AddWithCarryNZCV(State &state, T lhs, T rhs, T carry) {
|
||||
auto unsigned_result = UAdd(UAdd(ZExt(lhs), ZExt(rhs)), ZExt(carry));
|
||||
auto signed_result = SAdd(SAdd(SExt(lhs), SExt(rhs)), Signed(ZExt(carry)));
|
||||
auto result = TruncTo<T>(unsigned_result);
|
||||
state.sr.n = SignFlag(result);
|
||||
state.sr.z = ZeroFlag(result);
|
||||
state.sr.c = UCmpNeq(ZExt(result), unsigned_result);
|
||||
state.sr.v = SCmpNeq(SExt(result), signed_result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(AND, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, UAnd(Read(src1), value));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(ANDS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto value = Read(src2);
|
||||
auto res = UAnd(Read(src1), value);
|
||||
WriteZExt(dst, res);
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
state.sr.c = Read(carry_out);
|
||||
|
||||
// PSTATE.V unchanged
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(EOR, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, UXor(Read(src1), value));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(EORS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto value = Read(src2);
|
||||
auto res = UXor(Read(src1), value);
|
||||
Write(dst, res);
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
state.sr.c = Read(carry_out);
|
||||
|
||||
// PSTATE.V unchanged
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(RSB, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, USub(value, Read(src1)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(RSBS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
auto res = AddWithCarryNZCV(state, UNot(lhs), rhs, uint32_t(1));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SUB, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, USub(Read(src1), value));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SUBS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
auto res = AddWithCarryNZCV(state, lhs, UNot(rhs), uint32_t(1));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(ADD, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, UAdd(Read(src1), value));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(ADDS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
auto res = AddWithCarryNZCV(state, lhs, rhs, uint32_t(0));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(ADC, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, UAdd(UAdd(Read(src1), value), uint32_t(state.sr.c)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(ADCS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
auto res = AddWithCarryNZCV(state, lhs, rhs, uint32_t(state.sr.c));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SBC, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, UAdd(UAdd(Read(src1), UNot(value)), uint32_t(state.sr.c)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SBCS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
auto res = AddWithCarryNZCV(state, lhs, UNot(rhs), uint32_t(state.sr.c));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(RSC, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
Write(dst, UAdd(UAdd(value, UNot(Read(src1))), uint32_t(state.sr.c)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(RSCS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
auto res = AddWithCarryNZCV(state, UNot(lhs), rhs, uint32_t(state.sr.c));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(ANDrr) = AND;
|
||||
DEF_ISEL(ANDSrr) = ANDS;
|
||||
DEF_ISEL(EORrr) = EOR;
|
||||
DEF_ISEL(EORSrr) = EORS;
|
||||
DEF_ISEL(ADDrr) = ADD;
|
||||
DEF_ISEL(ADDSrr) = ADDS;
|
||||
DEF_ISEL(ADCrr) = ADC;
|
||||
DEF_ISEL(ADCSrr) = ADCS;
|
||||
DEF_ISEL(RSBrr) = RSB;
|
||||
DEF_ISEL(RSBSrr) = RSBS;
|
||||
DEF_ISEL(SUBrr) = SUB;
|
||||
DEF_ISEL(SUBSrr) = SUBS;
|
||||
DEF_ISEL(SBCrr) = SBC;
|
||||
DEF_ISEL(SBCSrr) = SBCS;
|
||||
DEF_ISEL(RSCrr) = RSC;
|
||||
DEF_ISEL(RSCSrr) = RSCS;
|
||||
|
||||
// Multiply and Accumulate
|
||||
namespace {
|
||||
DEF_COND_SEM(MUL, R32W dst, R32 src1, R32 src2, R32 src3) {
|
||||
auto rhs = Signed(Read(src2));
|
||||
auto lhs = Signed(Read(src1));
|
||||
auto acc = Signed(Read(src3));
|
||||
auto res = Unsigned(SAdd(SMul(lhs, rhs), acc));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(MULS, R32W dst, R32 src1, R32 src2, R32 src3) {
|
||||
auto rhs = Signed(Read(src2));
|
||||
auto lhs = Signed(Read(src1));
|
||||
auto acc = Signed(Read(src3));
|
||||
auto res = Unsigned(SAdd(SMul(lhs, rhs), acc));
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
|
||||
// PSTATE.C, PSTATE.V unchanged
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UMAAL, R32W dst_hi, R32W dst_lo, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) {
|
||||
auto rhs = ZExt(Read(src3));
|
||||
auto lhs = ZExt(Read(src2));
|
||||
auto acc_hi = ZExt(Read(src1));
|
||||
auto acc_lo = ZExt(Read(src4));
|
||||
auto res = UAdd(UAdd(UMul(lhs, rhs), acc_hi), acc_lo);
|
||||
Write(dst_lo, TruncTo<uint32_t>(res));
|
||||
Write(dst_hi, TruncTo<uint32_t>(UShr(res, 32ul)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(MLS, R32W dst, R32 src1, R32 src2, R32 src3) {
|
||||
auto rhs = Signed(Read(src2));
|
||||
auto lhs = Signed(Read(src1));
|
||||
auto acc = Signed(Read(src3));
|
||||
auto res = Unsigned(SSub(acc, SMul(lhs, rhs)));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UMULL, R32W dst_hi, R32W dst_lo, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) {
|
||||
auto rhs = ZExt(Read(src3));
|
||||
auto lhs = ZExt(Read(src2));
|
||||
auto acc = UOr(UShl(ZExt(Read(src1)), 32ul),
|
||||
ZExt(Read(src4))); // UInt(R[dHi]:R[dLo])
|
||||
auto res = UAdd(UMul(lhs, rhs), acc);
|
||||
Write(dst_hi, TruncTo<uint32_t>(UShr(res, 32ul)));
|
||||
Write(dst_lo, TruncTo<uint32_t>(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UMULLS, R32W dst_hi, R32W dst_lo, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) {
|
||||
auto rhs = ZExt(Read(src3));
|
||||
auto lhs = ZExt(Read(src2));
|
||||
auto acc = UOr(UShl(ZExt(Read(src1)), 32ul),
|
||||
ZExt(Read(src4))); // UInt(R[dHi]:R[dLo])
|
||||
auto res = UAdd(UMul(lhs, rhs), acc);
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
|
||||
// PSTATE.C, PSTATE.V unchanged
|
||||
Write(dst_hi, TruncTo<uint32_t>(UShr(res, 32ul)));
|
||||
Write(dst_lo, TruncTo<uint32_t>(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMULL, R32W dst_hi, R32W dst_lo, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) {
|
||||
auto rhs = SExt(Signed(Read(src3)));
|
||||
auto lhs = SExt(Signed(Read(src2)));
|
||||
auto acc = SOr(SShl(SExt(Read(src1)), 32ul),
|
||||
Signed(ZExt(Read(src4)))); // UInt(R[dHi]:R[dLo])
|
||||
auto res = SAdd(SMul(lhs, rhs), acc);
|
||||
Write(dst_hi, TruncTo<uint32_t>(SShr(res, 32ul)));
|
||||
Write(dst_lo, TruncTo<uint32_t>(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMULLS, R32W dst_hi, R32W dst_lo, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) {
|
||||
auto rhs = SExt(Signed(Read(src3)));
|
||||
auto lhs = SExt(Signed(Read(src2)));
|
||||
auto acc = SOr(SShl(SExt(Read(src1)), 32ul),
|
||||
Signed(ZExt(Read(src4)))); // UInt(R[dHi]:R[dLo])
|
||||
auto res = SAdd(SMul(lhs, rhs), acc);
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
|
||||
// PSTATE.C, PSTATE.V unchanged
|
||||
Write(dst_hi, TruncTo<uint32_t>(SShr(res, 32ul)));
|
||||
Write(dst_lo, TruncTo<uint32_t>(res));
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(MUL) = MUL;
|
||||
DEF_ISEL(MULS) = MULS;
|
||||
DEF_ISEL(MLA) = MUL;
|
||||
DEF_ISEL(MLAS) = MULS;
|
||||
DEF_ISEL(MLS) = MLS;
|
||||
DEF_ISEL(UMAAL) = UMAAL;
|
||||
DEF_ISEL(UMULL) = UMULL;
|
||||
DEF_ISEL(UMULLS) = UMULLS;
|
||||
DEF_ISEL(UMLAL) = UMULL;
|
||||
DEF_ISEL(UMLALS) = UMULLS;
|
||||
DEF_ISEL(SMULL) = SMULL;
|
||||
DEF_ISEL(SMULLS) = SMULLS;
|
||||
DEF_ISEL(SMLAL) = SMULL;
|
||||
DEF_ISEL(SMLALS) = SMULLS;
|
||||
|
||||
// Halfword Multiply and Accumulate
|
||||
namespace {
|
||||
DEF_COND_SEM(SMLAh, R32W dst, R32 src1, R32 src2, R32 src3) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto acc = SExt(Signed(Read(src3)));
|
||||
auto res = SAdd(SMul(lhs, rhs), acc);
|
||||
auto trun_res = TruncTo<uint32_t>(res);
|
||||
Write(dst, trun_res);
|
||||
|
||||
// if result != SInt(result<31:0>) then // Signed overflow
|
||||
// PSTATE.Q = '1';
|
||||
state.sr.q = Select(SCmpNeq(res, SExt(trun_res)), uint8_t(1), state.sr.q);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMULWh, R32W dst, R32 src1, R32 src2) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto res = SShr(SMul(lhs, rhs), 16ul); // R[d] = result<47:16>
|
||||
auto trun_res = TruncTo<uint32_t>(res);
|
||||
Write(dst, trun_res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMLAWh, R32W dst, R32 src1, R32 src2, R32 src3) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto acc = SShl(SExt(Signed(Read(src3))), 16ul); // SInt(R[a]) << 16
|
||||
auto res = SShr(SAdd(SMul(lhs, rhs), acc), 16ul); // R[d] = result<47:16>
|
||||
auto trun_res = TruncTo<uint32_t>(res);
|
||||
Write(dst, trun_res);
|
||||
|
||||
// if (result >> 16) != SInt(R[d]) then // Signed overflow
|
||||
// PSTATE.Q = '1';
|
||||
state.sr.q = Select(SCmpNeq(res, SExt(trun_res)), uint8_t(1), state.sr.q);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMULh, R32W dst, R32 src1, R32 src2) {
|
||||
auto rhs = Signed(Read(src2));
|
||||
auto lhs = Signed(Read(src1));
|
||||
auto res = SMul(lhs, rhs);
|
||||
Write(dst, TruncTo<uint32_t>(res));
|
||||
return memory;
|
||||
|
||||
// Signed overflow cannot occur
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMLALh, R32W dst_hi, R32W dst_lo, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) {
|
||||
auto rhs = SExt(Signed(Read(src3)));
|
||||
auto lhs = SExt(Signed(Read(src2)));
|
||||
auto acc = SOr(SShl(SExt(Signed(Read(src1))), 32ul),
|
||||
Signed(ZExt(Read(src4)))); // UInt(R[dHi]:R[dLo])
|
||||
auto res = SAdd(SMul(lhs, rhs), acc);
|
||||
Write(dst_hi, TruncTo<uint32_t>(SShr(res, 32ul)));
|
||||
Write(dst_lo, TruncTo<uint32_t>(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(SMLABB) = SMLAh;
|
||||
DEF_ISEL(SMLABT) = SMLAh;
|
||||
DEF_ISEL(SMLATB) = SMLAh;
|
||||
DEF_ISEL(SMLATT) = SMLAh;
|
||||
DEF_ISEL(SMLAWB) = SMLAWh;
|
||||
DEF_ISEL(SMULWB) = SMULWh;
|
||||
DEF_ISEL(SMLAWT) = SMLAWh;
|
||||
DEF_ISEL(SMULWT) = SMULWh;
|
||||
DEF_ISEL(SMULBB) = SMULh;
|
||||
DEF_ISEL(SMULBT) = SMULh;
|
||||
DEF_ISEL(SMULTB) = SMULh;
|
||||
DEF_ISEL(SMULTT) = SMULh;
|
||||
DEF_ISEL(SMLALBB) = SMLALh;
|
||||
DEF_ISEL(SMLALBT) = SMLALh;
|
||||
DEF_ISEL(SMLALTB) = SMLALh;
|
||||
DEF_ISEL(SMLALTT) = SMLALh;
|
||||
|
||||
// Saturate 16-bit && Saturate 32-bit
|
||||
namespace {
|
||||
template <typename T>
|
||||
T UnsignedSatQ(State &state, T res, uint32_t nbits) {
|
||||
auto upper_bound = T((1 << nbits) - 1);
|
||||
auto lower_bound = T(0);
|
||||
state.sr.q = Select(BOr(UCmpGt(res, upper_bound), UCmpLt(res, lower_bound)),
|
||||
uint8_t(1u), state.sr.q);
|
||||
res = Select(UCmpGt(res, upper_bound), upper_bound, res);
|
||||
res = Select(UCmpLt(res, lower_bound), lower_bound, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T SignedSatQ(State &state, T res, int32_t nbits) {
|
||||
nbits--;
|
||||
auto upper_bound = T((1 << nbits) - 1);
|
||||
auto lower_bound = T(-(1 << nbits));
|
||||
state.sr.q = Select(BOr(SCmpGt(res, upper_bound), SCmpLt(res, lower_bound)),
|
||||
uint8_t(1u), state.sr.q);
|
||||
res = Select(SCmpGt(res, upper_bound), upper_bound, res);
|
||||
res = Select(SCmpLt(res, lower_bound), lower_bound, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(USAT, R32W dst, I32 imm, R32 src) {
|
||||
auto res = UnsignedSatQ(state, Read(src), Read(imm));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SSAT, R32W dst, I32 imm, R32 src) {
|
||||
auto res = SignedSatQ(state, Signed(Read(src)), Signed(Read(imm)));
|
||||
Write(dst, Unsigned(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(USAT16, R32W dst, I32 imm1, R32 src1) {
|
||||
auto src = Read(src1);
|
||||
auto imm = Read(imm1);
|
||||
auto high = UnsignedSatQ(state, Trunc(UShr(src, 16u)), imm);
|
||||
auto low = UnsignedSatQ(state, Trunc(src), imm);
|
||||
auto res = UOr(UShl(ZExt(high), 16u), ZExt(low));
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SSAT16, R32W dst, I32 imm1, R32 src1) {
|
||||
auto src = Signed(Read(src1));
|
||||
auto imm = Signed(Read(imm1));
|
||||
auto high = SignedSatQ(state, Trunc(SShr(src, 16u)), imm);
|
||||
auto low = SignedSatQ(state, Trunc(src), imm);
|
||||
auto res = SOr(SShl(SExt(high), 16u), Signed(ZExt(low)));
|
||||
Write(dst, Unsigned(res));
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(USAT) = USAT;
|
||||
DEF_ISEL(SSAT) = SSAT;
|
||||
DEF_ISEL(USAT16) = USAT16;
|
||||
DEF_ISEL(SSAT16) = SSAT16;
|
||||
|
||||
// Integer Saturating Arithmetic
|
||||
namespace {
|
||||
DEF_COND_SEM(QADD, R32W dst, R32 src1, R32 src2) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto res = SAdd(lhs, rhs);
|
||||
res = SignedSatQ(state, res, 32);
|
||||
Write(dst, Trunc(Unsigned(res)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(QDADD, R32W dst, R32 src1, R32 src2) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
rhs = SignedSatQ(state, SShl(rhs, 1u), 32);
|
||||
auto res = SAdd(lhs, rhs);
|
||||
res = SignedSatQ(state, res, 32);
|
||||
Write(dst, Trunc(Unsigned(res)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(QSUB, R32W dst, R32 src1, R32 src2) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto res = SSub(lhs, rhs);
|
||||
res = SignedSatQ(state, res, 32);
|
||||
Write(dst, Trunc(Unsigned(res)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(QDSUB, R32W dst, R32 src1, R32 src2) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
rhs = SignedSatQ(state, SShl(rhs, 1u), 32);
|
||||
auto res = SSub(lhs, rhs);
|
||||
res = SignedSatQ(state, res, 32);
|
||||
Write(dst, Trunc(Unsigned(res)));
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(QADD) = QADD;
|
||||
DEF_ISEL(QDADD) = QDADD;
|
||||
DEF_ISEL(QSUB) = QSUB;
|
||||
DEF_ISEL(QDSUB) = QDSUB;
|
||||
|
||||
// TODO Signed multiply, Divide
|
||||
namespace {
|
||||
DEF_COND_SEM(SMLAD, R32W dst, R32 src1, R32 src2, R32 src3) { // rn rm ra
|
||||
auto rn = Signed(Read(src1));
|
||||
auto rm = Signed(Read(src2));
|
||||
auto ra = Signed(Read(src3));
|
||||
auto prod1 = SMul(SExtTo<int64_t>(Trunc(rn)), SExtTo<int64_t>(Trunc(rm)));
|
||||
auto prod2 =
|
||||
SMul(SExtTo<int64_t>(SShr(rn, 16u)), SExtTo<int64_t>(SShr(rm, 16u)));
|
||||
auto res = SAdd(SAdd(prod1, prod2), SExt(ra));
|
||||
WriteTrunc(dst, Unsigned(res));
|
||||
|
||||
// if result != SInt(result<31:0>) then // Signed overflow
|
||||
// PSTATE.Q = '1';
|
||||
state.sr.q =
|
||||
Select(SCmpNeq(res, SExtTo<int64_t>(Trunc(res))), uint8_t(1), state.sr.q);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMLSD, R32W dst, R32 src1, R32 src2, R32 src3) { // rn rm ra
|
||||
auto rn = Signed(Read(src1));
|
||||
auto rm = Signed(Read(src2));
|
||||
auto ra = Signed(Read(src3));
|
||||
auto prod1 = SMul(SExtTo<int64_t>(Signed(Trunc(rn))),
|
||||
SExtTo<int64_t>(Signed(Trunc(rm))));
|
||||
auto prod2 =
|
||||
SMul(SExtTo<int64_t>(SShr(rn, 16u)), SExtTo<int64_t>(SShr(rm, 16u)));
|
||||
auto res = SAdd(SSub(prod1, prod2), SExt(ra));
|
||||
WriteTrunc(dst, Unsigned(res));
|
||||
|
||||
// if result != SInt(result<31:0>) then // Signed overflow
|
||||
// PSTATE.Q = '1';
|
||||
state.sr.q =
|
||||
Select(SCmpNeq(res, SExtTo<int64_t>(Trunc(res))), uint8_t(1), state.sr.q);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SDIV, R32W dst, R32 src1, R32 src2, R32 src3) { // rn rm
|
||||
auto rn = Signed(Read(src1));
|
||||
auto rm = Signed(Read(src2));
|
||||
if (!rm) {
|
||||
WriteZExt(dst, uint32_t(0));
|
||||
} else {
|
||||
WriteZExt(dst, Unsigned(SDiv(rn, rm)));
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UDIV, R32W dst, R32 src1, R32 src2, R32 src3) { // rn rm
|
||||
auto rn = Read(src1);
|
||||
auto rm = Read(src2);
|
||||
if (!rm) {
|
||||
WriteZExt(dst, uint32_t(0));
|
||||
} else {
|
||||
WriteZExt(dst, UDiv(rn, rm));
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMLALD, R32W dst_lo, R32W dst_hi, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) { // ra - lo rd - hi rn rm ra - lo rd - hi
|
||||
auto rn = Signed(Read(src1));
|
||||
auto rm = Signed(Read(src2));
|
||||
auto lo = SExt(Signed(Read(src3)));
|
||||
auto hi = SExt(Signed(Read(src4)));
|
||||
auto prod1 = SMul(SExtTo<int64_t>(Trunc(rn)), SExtTo<int64_t>(Trunc(rm)));
|
||||
auto prod2 =
|
||||
SMul(SExtTo<int64_t>(SShr(rn, 16u)), SExtTo<int64_t>(SShr(rm, 16u)));
|
||||
auto res = SAdd(SAdd(prod1, prod2), SOr(lo, SShl(hi, 32u)));
|
||||
WriteTrunc(dst_lo, Unsigned(res));
|
||||
WriteTrunc(dst_hi, Unsigned(SShr(res, 32u)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMLSLD, R32W dst_lo, R32W dst_hi, R32 src1, R32 src2, R32 src3,
|
||||
R32 src4) { // ra - lo rd - hi rn rm ra - lo rd - hi
|
||||
auto rn = Signed(Read(src1));
|
||||
auto rm = Signed(Read(src2));
|
||||
auto lo = SExt(Signed(Read(src3)));
|
||||
auto hi = SExt(Signed(Read(src4)));
|
||||
auto prod1 = SMul(SExtTo<int64_t>(Trunc(rn)), SExtTo<int64_t>(Trunc(rm)));
|
||||
auto prod2 =
|
||||
SMul(SExtTo<int64_t>(SShr(rn, 16u)), SExtTo<int64_t>(SShr(rm, 16u)));
|
||||
auto res = SAdd(SSub(prod1, prod2), SOr(lo, SShl(hi, 32u)));
|
||||
WriteTrunc(dst_lo, Unsigned(res));
|
||||
WriteTrunc(dst_hi, Unsigned(SShr(res, 32u)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMMLA, R32W dst, R32 src1, R32 src2, R32 src3, I32 src4) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto acc = SShl(SExt(Signed(Read(src3))), 32u);
|
||||
auto round = Signed(ZExt(Read(src4)));
|
||||
auto res = SShr(SAdd(SAdd(acc, SMul(lhs, rhs)), round), 32u);
|
||||
WriteTrunc(dst, Unsigned(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SMMLS, R32W dst, R32 src1, R32 src2, R32 src3, I32 src4) {
|
||||
auto rhs = SExt(Signed(Read(src2)));
|
||||
auto lhs = SExt(Signed(Read(src1)));
|
||||
auto acc = SShl(SExt(Signed(Read(src3))), 32u);
|
||||
auto round = Signed(ZExt(Read(src4)));
|
||||
auto res = SShr(SAdd(SSub(acc, SMul(lhs, rhs)), round), 32u);
|
||||
WriteTrunc(dst, Unsigned(res));
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(SMLAD) = SMLAD;
|
||||
DEF_ISEL(SMLADX) = SMLAD;
|
||||
DEF_ISEL(SMLSD) = SMLSD;
|
||||
DEF_ISEL(SMLSDX) = SMLSD;
|
||||
DEF_ISEL(SMUAD) = SMLAD;
|
||||
DEF_ISEL(SMUADX) = SMLAD;
|
||||
DEF_ISEL(SMUSD) = SMLSD;
|
||||
DEF_ISEL(SMUSDX) = SMLSD;
|
||||
DEF_ISEL(SDIV) = SDIV;
|
||||
DEF_ISEL(UDIV) = UDIV;
|
||||
DEF_ISEL(SMLALD) = SMLALD;
|
||||
DEF_ISEL(SMLALDX) = SMLALD;
|
||||
DEF_ISEL(SMLSLD) = SMLSLD;
|
||||
DEF_ISEL(SMLSLDX) = SMLSLD;
|
||||
DEF_ISEL(SMMLA) = SMMLA;
|
||||
DEF_ISEL(SMMLAR) = SMMLA;
|
||||
DEF_ISEL(SMMLS) = SMMLS;
|
||||
DEF_ISEL(SMMLSR) = SMMLS;
|
||||
DEF_ISEL(SMMUL) = SMMLA;
|
||||
DEF_ISEL(SMMULR) = SMMLA;
|
||||
|
||||
|
||||
// Extend and Add
|
||||
namespace {
|
||||
template <typename T>
|
||||
T ROR_C(T val, T shift, T nbits) {
|
||||
if (shift == 0) {
|
||||
return val;
|
||||
}
|
||||
|
||||
auto m = URem(shift, nbits);
|
||||
auto shr = UShr(val, m);
|
||||
auto shl = UShl(val, nbits - m);
|
||||
auto res = UOr(shr, shl);
|
||||
return res;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SXTAB16, R32W dst, R32 src1, R32 src2, I32 src3) {
|
||||
auto src = Read(src2);
|
||||
auto src_add = Read(src1);
|
||||
auto rot = Read(src3);
|
||||
|
||||
src = ROR_C(src, rot, 32u);
|
||||
|
||||
// low/high 16 bits of rn + the low byte sign extended of the low/high 16 bits of rm
|
||||
auto low =
|
||||
ZExt(UAdd(Trunc(src_add),
|
||||
Unsigned(SExtTo<uint16_t>(Signed(TruncTo<uint8_t>(src))))));
|
||||
auto high = SExt(UAdd(
|
||||
Trunc(UShr(src_add, 16u)),
|
||||
Unsigned(SExtTo<uint16_t>(Signed(TruncTo<uint8_t>(UShr(src, 16u)))))));
|
||||
auto res = UOr(low, UShl(Unsigned(high), 16u));
|
||||
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SXTAB, R32W dst, R32 src1, R32 src2, I32 src3) {
|
||||
auto src = Read(src2);
|
||||
auto src_add = Read(src1);
|
||||
auto rot = Read(src3);
|
||||
|
||||
src = ROR_C(src, rot, 32u);
|
||||
|
||||
// Extract low byte
|
||||
auto res =
|
||||
UAdd(Unsigned(SExtTo<uint32_t>(Signed(TruncTo<uint8_t>(src)))), src_add);
|
||||
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(SXTAH, R32W dst, R32 src1, R32 src2, I32 src3) {
|
||||
auto src = Read(src2);
|
||||
auto src_add = Read(src1);
|
||||
auto rot = Read(src3);
|
||||
|
||||
src = ROR_C(src, rot, 32u);
|
||||
|
||||
// Extract low 2 bytes and sign extend
|
||||
auto res = UAdd(Unsigned(SExt(Signed(Trunc(src)))), src_add);
|
||||
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UXTAB16, R32W dst, R32 src1, R32 src2, I32 src3) {
|
||||
auto src = Read(src2);
|
||||
auto src_add = Read(src1);
|
||||
auto rot = Read(src3);
|
||||
|
||||
src = ROR_C(src, rot, 32u);
|
||||
|
||||
// low/high 16 bits of rn + the low byte of the low/high 16 bits of rm
|
||||
auto low = ZExt(UAdd(Trunc(src_add), UAnd(Trunc(src), uint16_t(255u))));
|
||||
auto high = ZExt(UAdd(Trunc(UShr(src_add, 16u)),
|
||||
UAnd(Trunc(UShr(src, 16u)), uint16_t(255u))));
|
||||
auto res = UOr(low, UShl(high, 16u));
|
||||
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UXTAB, R32W dst, R32 src1, R32 src2, I32 src3) {
|
||||
auto src = Read(src2);
|
||||
auto src_add = Read(src1);
|
||||
auto rot = Read(src3);
|
||||
|
||||
src = ROR_C(src, rot, 32u);
|
||||
|
||||
// Extract low byte i.e. 0b11111111 = 255
|
||||
auto res = UAdd(UAnd(src, uint32_t(255u)), src_add);
|
||||
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UXTAH, R32W dst, R32 src1, R32 src2, I32 src3) {
|
||||
auto src = Read(src2);
|
||||
auto src_add = Read(src1);
|
||||
auto rot = Read(src3);
|
||||
|
||||
src = ROR_C(src, rot, 32u);
|
||||
|
||||
// Extract low 2 bytes i.e. 0b1111111111111111 = 65535
|
||||
auto res = UAdd(UAnd(src, uint32_t(65535u)), src_add);
|
||||
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(SXTAB16) = SXTAB16;
|
||||
DEF_ISEL(SXTB16) = SXTAB16;
|
||||
DEF_ISEL(SXTAB) = SXTAB;
|
||||
DEF_ISEL(SXTB) = SXTAB;
|
||||
DEF_ISEL(SXTAH) = SXTAH;
|
||||
DEF_ISEL(SXTH) = SXTAH;
|
||||
DEF_ISEL(UXTAB16) = UXTAB16;
|
||||
DEF_ISEL(UXTB16) = UXTAB16;
|
||||
DEF_ISEL(UXTAB) = UXTAB;
|
||||
DEF_ISEL(UXTB) = UXTAB;
|
||||
DEF_ISEL(UXTAH) = UXTAH;
|
||||
DEF_ISEL(UXTH) = UXTAH;
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
DEF_COND_SEM(CLZ, R32W dst, R32 src) {
|
||||
auto count = CountLeadingZeros(Read(src));
|
||||
WriteZExt(dst, count);
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(CLZ) = CLZ;
|
||||
|
||||
// Bitfield Extract
|
||||
namespace {
|
||||
DEF_COND_SEM(SBFX, R32W dst, R32 src1, I32 src2, I32 src3) {
|
||||
auto src = Signed(Read(src1));
|
||||
auto lsbit = Read(src2);
|
||||
auto widthminus1 = Read(src3);
|
||||
auto msbit = Signed(lsbit + widthminus1);
|
||||
|
||||
// Extract <msbit:lsbit> and retain high bit sign of msbit
|
||||
// Shift lift to remove the high bits, then shift right to remove the low bits
|
||||
auto res = SShr(SShl(src, int32_t(31) - Signed(msbit)),
|
||||
int32_t(31) - Signed(widthminus1));
|
||||
Write(dst, Unsigned(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(UBFX, R32W dst, R32 src1, I32 src2, I32 src3) {
|
||||
auto src = Read(src1);
|
||||
auto lsbit = Read(src2);
|
||||
auto widthminus1 = Read(src3);
|
||||
auto msbit = lsbit + widthminus1;
|
||||
|
||||
// Extract <msbit:lsbit> unsigned
|
||||
// Shift lift to remove the high bits, then shift right to remove the low bits
|
||||
auto res = UShr(UShl(src, uint32_t(31) - msbit), uint32_t(31) - widthminus1);
|
||||
Write(dst, res);
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(SBFX) = SBFX;
|
||||
DEF_ISEL(UBFX) = UBFX;
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
DEF_SEM(B, R8, R8W, I32 taken_pc, R32W next_pc_dst) {
|
||||
auto new_pc = Read(taken_pc);
|
||||
Write(REG_PC, new_pc);
|
||||
Write(next_pc_dst, new_pc);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(BCOND, R8 cond, R8W branch_taken, I32 taken_pc, I32 not_taken_pc,
|
||||
R32W next_pc_dst) {
|
||||
auto c = Read(cond);
|
||||
auto new_pc = Select(c, Read(taken_pc), Read(not_taken_pc));
|
||||
Write(REG_PC, new_pc);
|
||||
Write(next_pc_dst, new_pc);
|
||||
Write(branch_taken, c);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(BL, R8, R8W, PC target_addr, PC ret_addr, R32W next_pc_dst,
|
||||
R32W return_pc_dst) {
|
||||
const auto return_pc = Read(ret_addr);
|
||||
const auto new_pc = Read(target_addr);
|
||||
Write(REG_LR, return_pc);
|
||||
Write(REG_PC, new_pc);
|
||||
Write(next_pc_dst, new_pc);
|
||||
Write(return_pc_dst, return_pc);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(BLCOND, R8 cond, R8W branch_taken, PC target_addr, PC ret_addr,
|
||||
R32W next_pc_dst, R32W return_pc_dst) {
|
||||
auto c = Read(cond);
|
||||
const auto return_pc = Read(ret_addr);
|
||||
if (c) {
|
||||
const auto target_pc = Read(target_addr);
|
||||
Write(REG_LR, return_pc);
|
||||
Write(REG_PC, target_pc);
|
||||
Write(next_pc_dst, target_pc);
|
||||
} else {
|
||||
Write(REG_PC, return_pc);
|
||||
Write(next_pc_dst, return_pc);
|
||||
}
|
||||
Write(return_pc_dst, return_pc);
|
||||
Write(branch_taken, c);
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(B) = B;
|
||||
DEF_ISEL(BCOND) = BCOND;
|
||||
DEF_ISEL(BL) = BL;
|
||||
DEF_ISEL(BLCOND) = BLCOND;
|
||||
DEF_ISEL(BLX) = BL;
|
||||
DEF_ISEL(BLXCOND) = BLCOND;
|
||||
DEF_ISEL(BX) = B;
|
||||
DEF_ISEL(BXCOND) = BCOND;
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
DEF_COND_SEM(TST, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto res = UAnd(Read(src1), Read(src2));
|
||||
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
state.sr.c = Read(carry_out);
|
||||
|
||||
// PSTATE.V unchanged
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(TEQ, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto res = UXor(Read(src1), Read(src2));
|
||||
|
||||
state.sr.n = SignFlag(res);
|
||||
state.sr.z = ZeroFlag(res);
|
||||
state.sr.c = Read(carry_out);
|
||||
|
||||
// PSTATE.V unchanged
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(CMP, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
AddWithCarryNZCV(state, lhs, UNot(rhs), uint32_t(1));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(CMN, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto rhs = Read(src2);
|
||||
auto lhs = Read(src1);
|
||||
AddWithCarryNZCV(state, lhs, rhs, uint32_t(0));
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(TSTr) = TST;
|
||||
DEF_ISEL(TEQr) = TEQ;
|
||||
DEF_ISEL(CMPr) = CMP;
|
||||
DEF_ISEL(CMNr) = CMN;
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
// Used to select specializations of flags computations based on what operator
|
||||
// is executed.
|
||||
enum : uint32_t { kLHS = 2415899639U, kRHS = 70623199U };
|
||||
|
||||
// Zero flags, tells us whether or not a value is zero.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool ZeroFlag(T res) {
|
||||
return T(0) == res;
|
||||
}
|
||||
|
||||
// Zero flags, tells us whether or not a value is zero.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool NotZeroFlag(T res) {
|
||||
return T(0) != res;
|
||||
}
|
||||
|
||||
// Sign flag, tells us if a result is signed or unsigned.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool SignFlag(T res) {
|
||||
return 0 > Signed(res);
|
||||
}
|
||||
|
||||
// Tests whether there is an even number of bits in the low order byte.
|
||||
[[gnu::const]] ALWAYS_INLINE static bool ParityFlag(uint8_t r0) {
|
||||
return !__builtin_parity(static_cast<unsigned>(r0));
|
||||
|
||||
// auto r1 = r0 >> 1_u8;
|
||||
// auto r2 = r1 >> 1_u8;
|
||||
// auto r3 = r2 >> 1_u8;
|
||||
// auto r4 = r3 >> 1_u8;
|
||||
// auto r5 = r4 >> 1_u8;
|
||||
// auto r6 = r5 >> 1_u8;
|
||||
// auto r7 = r6 >> 1_u8;
|
||||
//
|
||||
// return !(1 & (r0 ^ r1 ^ r2 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7));
|
||||
}
|
||||
|
||||
struct tag_add {};
|
||||
struct tag_sub {};
|
||||
struct tag_div {};
|
||||
struct tag_mul {};
|
||||
|
||||
// Generic overflow flag.
|
||||
template <typename T>
|
||||
struct Overflow;
|
||||
|
||||
// Computes an overflow flag when two numbers are added together.
|
||||
template <>
|
||||
struct Overflow<tag_add> {
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Overflow::Flag` for addition.");
|
||||
enum { kSignShift = sizeof(T) * 8 - 1 };
|
||||
|
||||
const T sign_lhs = lhs >> kSignShift;
|
||||
const T sign_rhs = rhs >> kSignShift;
|
||||
const T sign_res = res >> kSignShift;
|
||||
return 2 == (sign_lhs ^ sign_res) + (sign_rhs ^ sign_res);
|
||||
}
|
||||
};
|
||||
|
||||
// Computes an overflow flag when one number is subtracted from another.
|
||||
template <>
|
||||
struct Overflow<tag_sub> {
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Overflow::Flag` for "
|
||||
"subtraction.");
|
||||
enum { kSignShift = sizeof(T) * 8 - 1 };
|
||||
|
||||
const T sign_lhs = lhs >> kSignShift;
|
||||
const T sign_rhs = rhs >> kSignShift;
|
||||
const T sign_res = res >> kSignShift;
|
||||
return 2 == (sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res);
|
||||
}
|
||||
};
|
||||
|
||||
// Computes an overflow flag when one number is multiplied with another.
|
||||
template <>
|
||||
struct Overflow<tag_mul> {
|
||||
|
||||
// Integer multiplication overflow check, where result is twice the width of
|
||||
// the operands.
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
return static_cast<R>(static_cast<T>(res)) != res;
|
||||
}
|
||||
|
||||
// Signed integer multiplication overflow check, where the result is
|
||||
// truncated to the size of the operands.
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T lhs, T rhs, T,
|
||||
typename std::enable_if<std::is_signed<T>::value, int>::type = 0) {
|
||||
auto lhs_wide = SExt(lhs);
|
||||
auto rhs_wide = SExt(rhs);
|
||||
return Flag<T, decltype(lhs_wide)>(lhs, rhs, lhs_wide * rhs_wide);
|
||||
}
|
||||
};
|
||||
|
||||
// Generic carry flag.
|
||||
template <typename Tag>
|
||||
struct Carry;
|
||||
|
||||
// Computes an carry flag when two numbers are added together.
|
||||
template <>
|
||||
struct Carry<tag_add> {
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Carry::Flag` for addition.");
|
||||
return res < lhs || res < rhs;
|
||||
}
|
||||
};
|
||||
|
||||
// Computes an carry flag when one number is subtracted from another.
|
||||
template <>
|
||||
struct Carry<tag_sub> {
|
||||
template <typename T>
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Carry::Flag` for addition.");
|
||||
return lhs < rhs;
|
||||
}
|
||||
};
|
||||
|
||||
ALWAYS_INLINE static void SetFPSRStatusFlags(State &state, int mask) {
|
||||
|
||||
// TODO(Sonya): Update these flags to work on AArch32
|
||||
// state.sr.ixc |= static_cast<uint8_t>(0 != (mask & FE_INEXACT));
|
||||
// state.sr.ofc |= static_cast<uint8_t>(0 != (mask & FE_OVERFLOW));
|
||||
// state.sr.ufc |= static_cast<uint8_t>(0 != (mask & FE_UNDERFLOW));
|
||||
// state.sr.ioc |= static_cast<uint64_t>(0 != (mask & FE_INVALID));
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
ALWAYS_INLINE static auto CheckedFloatUnaryOp(State &state, F func, T arg1)
|
||||
-> decltype(func(arg1)) {
|
||||
|
||||
//state.sr.idc |= IsDenormal(arg1);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto res = func(arg1);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except /* zero */);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
ALWAYS_INLINE static auto CheckedFloatBinOp(State &state, F func, T arg1,
|
||||
T arg2)
|
||||
-> decltype(func(arg1, arg2)) {
|
||||
|
||||
//state.sr.idc |= IsDenormal(arg1) | IsDenormal(arg2);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto res = func(arg1, arg2);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except /* zero */);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
DEF_COND_SEM(ORR, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = Read(src2);
|
||||
auto result = UOr(Read(src1), value);
|
||||
Write(dst, result);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(ORRS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto value = Read(src2);
|
||||
auto result = UOr(Read(src1), value);
|
||||
Write(dst, result);
|
||||
|
||||
state.sr.n = SignFlag(result);
|
||||
state.sr.z = ZeroFlag(result);
|
||||
state.sr.c = Read(carry_out);
|
||||
|
||||
// PSTATE.V unchanged
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(BIC, R32W dst, R32 src1, I32 src2) {
|
||||
auto value = UNot(Read(src2));
|
||||
auto result = UAnd(Read(src1), value);
|
||||
Write(dst, result);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(BICS, R32W dst, R32 src1, I32 src2, I8 carry_out) {
|
||||
auto value = UNot(Read(src2));
|
||||
auto result = UAnd(Read(src1), value);
|
||||
Write(dst, result);
|
||||
|
||||
state.sr.n = SignFlag(result);
|
||||
state.sr.z = ZeroFlag(result);
|
||||
state.sr.c = Read(carry_out);
|
||||
|
||||
// PSTATE.V unchanged
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(ORRrr) = ORR;
|
||||
DEF_ISEL(ORRSrr) = ORRS;
|
||||
DEF_ISEL(MOVrr) = ORR;
|
||||
DEF_ISEL(MOVSrr) = ORRS;
|
||||
DEF_ISEL(BICrr) = BIC;
|
||||
DEF_ISEL(BICSrr) = BICS;
|
||||
DEF_ISEL(MVNrr) = BIC;
|
||||
DEF_ISEL(MVNSrr) = BICS;
|
||||
|
||||
DEF_ISEL(MOVW) = ORR;
|
||||
|
||||
namespace {
|
||||
DEF_COND_SEM(MOVT, R32W dst, R32 src1, R32 src2) {
|
||||
auto value = ZExt(Trunc(Read(src1)));
|
||||
auto result = UOr(UShl(Read(src2), 16), value);
|
||||
Write(dst, result);
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(MOVT) = MOVT;
|
||||
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(STR, M32W dst, R32 src1) {
|
||||
auto src = Read(src1);
|
||||
Write(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(STRB, M8W dst, R32 src1) {
|
||||
auto src = Read(src1);
|
||||
Write(dst, TruncTo<uint8_t>(src));
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(STRp, M32W dst, R32 src1, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
Write(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(STRBp, M8W dst, R32 src1, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
Write(dst, TruncTo<uint8_t>(src));
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(LDR, M32 src1, R32W dst) {
|
||||
auto src = Read(src1);
|
||||
WriteZExt(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(LDRB, M8 src1, R32W dst) {
|
||||
auto src = Read(src1);
|
||||
WriteZExt(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(LDRp, M32 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteZExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(LDRBp, M8 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteZExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(STRT, M32W dst, R32 src1, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
Write(dst, TruncTo<uint32_t>(src));
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(STRTB, M8W dst, R32 src1, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
Write(dst, TruncTo<uint8_t>(src));
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(LDRT, M32 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteZExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(LDRTB, M8 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteZExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(STR) = STR;
|
||||
DEF_ISEL(STRB) = STRB;
|
||||
DEF_ISEL(STRp) = STRp;
|
||||
DEF_ISEL(STRBp) = STRBp;
|
||||
DEF_ISEL(LDR) = LDR;
|
||||
DEF_ISEL(LDRB) = LDRB;
|
||||
DEF_ISEL(LDRp) = LDRp;
|
||||
DEF_ISEL(LDRBp) = LDRBp;
|
||||
DEF_ISEL(STRT) = STRT;
|
||||
DEF_ISEL(STRBT) = STRTB;
|
||||
DEF_ISEL(LDRT) = LDRT;
|
||||
DEF_ISEL(LDRBT) = LDRTB;
|
||||
|
||||
namespace {
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(STRH, M16W dst, R32 src1) {
|
||||
auto src = Read(src1);
|
||||
Write(dst, TruncTo<uint16_t>(src));
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(STRHp, M16W dst, R32 src1, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
Write(dst, TruncTo<uint16_t>(src));
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(LDRH, M16 src1, R32W dst) {
|
||||
auto src = Read(src1);
|
||||
WriteZExt(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(LDRHp, M16 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteZExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(STRD, M64W dst, R32 src1, R32 src2) {
|
||||
auto lhs = UShl(ZExt<uint64_t>(Read(src2)), 32ul);
|
||||
auto rhs = ZExt<uint64_t>(Read(src1));
|
||||
auto src = UOr(lhs, rhs);
|
||||
WriteTrunc(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(STRDp, M64W dst, R32 src1, R32 src2, R32W dst_reg, R32 src_new) {
|
||||
auto lhs = UShl(ZExt<uint64_t>(Read(src2)), 32ul);
|
||||
auto rhs = ZExt<uint64_t>(Read(src1));
|
||||
auto src = UOr(lhs, rhs);
|
||||
auto new_val = Read(src_new);
|
||||
WriteTrunc(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(LDRD, M64 src1, R32W dst1, R32W dst2) {
|
||||
auto src = Read(src1);
|
||||
Write(dst1, TruncTo<uint32_t>(src));
|
||||
Write(dst2, TruncTo<uint32_t>(UShr(src, 32ul)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(LDRDp, M64 src1, R32W dst1, R32W dst2, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
Write(dst1, TruncTo<uint32_t>(src));
|
||||
Write(dst2, TruncTo<uint32_t>(UShr(src, 32ul)));
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(LDRSB, M8 src1, R32W dst) {
|
||||
auto src = Read(src1);
|
||||
WriteSExt(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(LDRSBp, M8 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteSExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Offset
|
||||
DEF_COND_SEM(LDRSH, M16 src1, R32W dst) {
|
||||
auto src = Read(src1);
|
||||
WriteSExt(dst, src);
|
||||
return memory;
|
||||
}
|
||||
|
||||
// Pre + Post
|
||||
DEF_COND_SEM(LDRSHp, M16 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteSExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(STRHT, M16W dst, R32 src1, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteTrunc(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(LDRHT, M16 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteZExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(LDRSBT, M8 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteSExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(LDRSHT, M16 src1, R32W dst, R32W dst_reg, R32 src2) {
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kAArch32CheckNotEL2);
|
||||
auto src = Read(src1);
|
||||
auto new_val = Read(src2);
|
||||
WriteSExt(dst, src);
|
||||
Write(dst_reg, new_val);
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(STRH) = STRH;
|
||||
DEF_ISEL(STRHp) = STRHp;
|
||||
DEF_ISEL(LDRH) = LDRH;
|
||||
DEF_ISEL(LDRHp) = LDRHp;
|
||||
DEF_ISEL(STRD) = STRD;
|
||||
DEF_ISEL(STRDp) = STRDp;
|
||||
DEF_ISEL(LDRD) = LDRD;
|
||||
DEF_ISEL(LDRDp) = LDRDp;
|
||||
DEF_ISEL(LDRSB) = LDRSB;
|
||||
DEF_ISEL(LDRSBp) = LDRSBp;
|
||||
DEF_ISEL(LDRSH) = LDRSH;
|
||||
DEF_ISEL(LDRSHp) = LDRSHp;
|
||||
DEF_ISEL(STRHT) = STRHT;
|
||||
DEF_ISEL(LDRHT) = LDRHT;
|
||||
DEF_ISEL(LDRSBT) = LDRSBT;
|
||||
DEF_ISEL(LDRSHT) = LDRSHT;
|
||||
|
||||
// Load/Store Multiple
|
||||
namespace {
|
||||
DEF_COND_SEM(LDM, I16 reg_list, R32W dst, R32 dst_new, M32 src_mem, R32W dst0,
|
||||
R32W dst1, R32W dst2, R32W dst3, R32W dst4, R32W dst5, R32W dst6,
|
||||
R32W dst7, R32W dst8, R32W dst9, R32W dst10, R32W dst11,
|
||||
R32W dst12, R32W dst13, R32W dst14, R32W dst15) {
|
||||
auto regs = Read(reg_list);
|
||||
uint32_t index = 0;
|
||||
if (UAnd(regs, uint16_t(0b1u))) {
|
||||
Write(dst0, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 1))) {
|
||||
Write(dst1, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 2))) {
|
||||
Write(dst2, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 3))) {
|
||||
Write(dst3, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 4))) {
|
||||
Write(dst4, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 5))) {
|
||||
Write(dst5, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 6))) {
|
||||
Write(dst6, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 7))) {
|
||||
Write(dst7, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 8))) {
|
||||
Write(dst8, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 9))) {
|
||||
Write(dst9, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 10))) {
|
||||
Write(dst10, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 11))) {
|
||||
Write(dst11, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 12))) {
|
||||
Write(dst12, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 13))) {
|
||||
Write(dst13, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 14))) {
|
||||
Write(dst14, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 15))) {
|
||||
Write(dst15, Read(GetElementPtr(src_mem, index++)));
|
||||
}
|
||||
Write(dst, Read(dst_new));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_COND_SEM(STMDB, I16 reg_list, R32W dst, R32 dst_new, M32W dst_mem, R32 src0,
|
||||
R32 src1, R32 src2, R32 src3, R32 src4, R32 src5, R32 src6,
|
||||
R32 src7, R32 src8, R32 src9, R32 src10, R32 src11, R32 src12,
|
||||
R32 src13, R32 src14, R32 src15) {
|
||||
auto regs = Read(reg_list);
|
||||
uint32_t index = 0;
|
||||
if (UAnd(regs, uint16_t(0b1u))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src0));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 1))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src1));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 2))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src2));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 3))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src3));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 4))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src4));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 5))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src5));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 6))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src6));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 7))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src7));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 8))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src8));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 9))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src9));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 10))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src10));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 11))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src11));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 12))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src12));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 13))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src13));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 14))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src14));
|
||||
}
|
||||
if (UAnd(regs, uint16_t(0b1u << 15))) {
|
||||
Write(GetElementPtr(dst_mem, index++), Read(src15));
|
||||
}
|
||||
Write(dst, Read(dst_new));
|
||||
return memory;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(STMDA) = STMDB;
|
||||
DEF_ISEL(LDMDA) = LDM;
|
||||
DEF_ISEL(STM) = STMDB;
|
||||
DEF_ISEL(LDM) = LDM;
|
||||
|
||||
//DEF_ISEL(STMu) = STMu;
|
||||
DEF_ISEL(STMDB) = STMDB;
|
||||
DEF_ISEL(LDMDB) = LDM;
|
||||
|
||||
//DEF_ISEL(LDMu) = LDMu;
|
||||
DEF_ISEL(STMIB) = STMDB;
|
||||
DEF_ISEL(LDMIB) = LDM;
|
||||
|
||||
//DEF_ISEL(LDMe) = LDMe;
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Trail of Bits, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
DEF_COND_SEM(DoNOP) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(NOP) = DoNOP;
|
||||
DEF_ISEL(HINT_1) = DoNOP;
|
||||
DEF_ISEL(HINT_2) = DoNOP;
|
||||
DEF_ISEL(HINT_3) = DoNOP;
|
||||
DEF_ISEL(HINT_4) = DoNOP;
|
||||
DEF_ISEL(HINT_5) = DoNOP;
|
||||
DEF_ISEL(HINT_6) = DoNOP;
|
||||
DEF_ISEL(HINT_7) = DoNOP;
|
||||
DEF_ISEL(HINT_8) = DoNOP;
|
||||
DEF_ISEL(HINT_9) = DoNOP;
|
||||
DEF_ISEL(HINT_10) = DoNOP;
|
||||
DEF_ISEL(HINT_11) = DoNOP;
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#define REMILL_AARCH_STRICT_REGNUM
|
||||
|
||||
#include "Decode.h"
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
@@ -41,13 +42,11 @@
|
||||
#include "remill/BC/Version.h"
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
|
||||
#include "Decode.h"
|
||||
|
||||
// clang-format off
|
||||
#define ADDRESS_SIZE_BITS 64
|
||||
#define INCLUDED_FROM_REMILL
|
||||
#include "remill/Arch/AArch64/Runtime/State.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace remill {
|
||||
@@ -461,7 +460,8 @@ void AArch64Arch::PopulateBasicBlockFunction(llvm::Module *module,
|
||||
|
||||
const auto pc_arg = NthArgument(bb_func, kPCArgNum);
|
||||
const auto state_ptr_arg = NthArgument(bb_func, kStatePointerArgNum);
|
||||
llvm::StringRef next_pc_name(kNextPCVariableName.data(), kNextPCVariableName.size());
|
||||
llvm::StringRef next_pc_name(kNextPCVariableName.data(),
|
||||
kNextPCVariableName.size());
|
||||
ir.CreateStore(pc_arg, ir.CreateAlloca(addr, nullptr, next_pc_name));
|
||||
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "WZR"));
|
||||
@@ -1859,7 +1859,7 @@ bool TryDecodeBL_ONLY_BRANCH_IMM(const InstData &data, Instruction &inst) {
|
||||
(data.imm26.simm26 << 2ULL));
|
||||
inst.branch_not_taken_pc = inst.next_pc;
|
||||
AddPCDisp(inst, data.imm26.simm26 << 2LL);
|
||||
DecodeFallThroughPC(inst); // Decodes the return address.
|
||||
DecodeFallThroughPC(inst); // Decodes the return address.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,8 @@ DEF_SEM(TBZ, I8 bit_pos, R8W cond, PC taken, PC not_taken, S src, R64W pc_dst) {
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
DEF_SEM(TBNZ, I8 bit_pos, R8W cond, PC taken, PC not_taken, S src, R64W pc_dst) {
|
||||
DEF_SEM(TBNZ, I8 bit_pos, R8W cond, PC taken, PC not_taken, S src,
|
||||
R64W pc_dst) {
|
||||
addr_t taken_pc = Read(taken);
|
||||
addr_t not_taken_pc = Read(not_taken);
|
||||
auto bit_n = ZExtTo<S>(Read(bit_pos));
|
||||
|
||||
+24
-15
@@ -46,7 +46,7 @@
|
||||
DEFINE_string(arch, REMILL_ARCH,
|
||||
"Architecture of the code being translated. "
|
||||
"Valid architectures: x86, amd64 (with or without "
|
||||
"`_avx` or `_avx512` appended), aarch64");
|
||||
"`_avx` or `_avx512` appended), aarch64, aarch32");
|
||||
|
||||
DECLARE_string(os);
|
||||
|
||||
@@ -81,14 +81,13 @@ static unsigned AddressSize(ArchName arch_name) {
|
||||
case kArchX86:
|
||||
case kArchX86_AVX:
|
||||
case kArchX86_AVX512:
|
||||
case kArchSparc32:
|
||||
return 32;
|
||||
case kArchAArch32LittleEndian:
|
||||
case kArchSparc32: return 32;
|
||||
case kArchAMD64:
|
||||
case kArchAMD64_AVX:
|
||||
case kArchAMD64_AVX512:
|
||||
case kArchAArch64LittleEndian:
|
||||
case kArchSparc64:
|
||||
return 64;
|
||||
case kArchSparc64: return 64;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -168,6 +167,11 @@ auto Arch::Build(llvm::LLVMContext *context_, OSName os_name_,
|
||||
return GetAArch64(context_, os_name_, arch_name_);
|
||||
}
|
||||
|
||||
case kArchAArch32LittleEndian: {
|
||||
DLOG(INFO) << "Using architecture: AArch32, feature set: Little Endian";
|
||||
return GetAArch32(context_, os_name_, arch_name_);
|
||||
}
|
||||
|
||||
case kArchX86: {
|
||||
DLOG(INFO) << "Using architecture: X86";
|
||||
return GetX86(context_, os_name_, arch_name_);
|
||||
@@ -215,8 +219,8 @@ auto Arch::Get(llvm::LLVMContext &context, std::string_view os,
|
||||
return Arch::Build(&context, GetOSName(os), GetArchName(arch_name));
|
||||
}
|
||||
|
||||
auto Arch::Get(llvm::LLVMContext &context, OSName os,
|
||||
ArchName arch_name) -> ArchPtr {
|
||||
auto Arch::Get(llvm::LLVMContext &context, OSName os, ArchName arch_name)
|
||||
-> ArchPtr {
|
||||
return Arch::Build(&context, os, arch_name);
|
||||
}
|
||||
|
||||
@@ -282,8 +286,8 @@ void Arch::ForEachRegister(std::function<void(const Register *)> cb) const {
|
||||
// Return information about a register, given its name.
|
||||
const Register *Arch::RegisterByName(std::string_view name_) const {
|
||||
std::string name(name_.data(), name_.size());
|
||||
auto [curr_val_it, added] = impl->reg_by_name.emplace(std::move(name),
|
||||
nullptr);
|
||||
auto [curr_val_it, added] =
|
||||
impl->reg_by_name.emplace(std::move(name), nullptr);
|
||||
if (added) {
|
||||
return nullptr;
|
||||
} else {
|
||||
@@ -352,6 +356,10 @@ bool Arch::IsAMD64(void) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool Arch::IsAArch32(void) const {
|
||||
return remill::kArchAArch32LittleEndian == arch_name;
|
||||
}
|
||||
|
||||
bool Arch::IsAArch64(void) const {
|
||||
return remill::kArchAArch64LittleEndian == arch_name;
|
||||
}
|
||||
@@ -638,9 +646,9 @@ void Arch::PrepareModule(llvm::Module *mod) const {
|
||||
PrepareModuleDataLayout(mod);
|
||||
}
|
||||
|
||||
const Register *Arch::AddRegister(
|
||||
const char *reg_name_, llvm::Type *val_type, size_t offset,
|
||||
const char *parent_reg_name) const {
|
||||
const Register *Arch::AddRegister(const char *reg_name_, llvm::Type *val_type,
|
||||
size_t offset,
|
||||
const char *parent_reg_name) const {
|
||||
|
||||
const std::string reg_name(reg_name_);
|
||||
auto ® = impl->reg_by_name[reg_name];
|
||||
@@ -660,8 +668,8 @@ const Register *Arch::AddRegister(
|
||||
gep_index_list.push_back(
|
||||
llvm::Constant::getNullValue(llvm::Type::getInt32Ty(*context)));
|
||||
|
||||
auto [gep_offset, gep_type_at_offset] = BuildIndexes(
|
||||
dl, impl->state_type, 0, offset, gep_index_list);
|
||||
auto [gep_offset, gep_type_at_offset] =
|
||||
BuildIndexes(dl, impl->state_type, 0, offset, gep_index_list);
|
||||
|
||||
if (!val_type) {
|
||||
CHECK_EQ(gep_offset, offset);
|
||||
@@ -706,7 +714,8 @@ void Arch::InitFromSemanticsModule(llvm::Module *module) const {
|
||||
|
||||
const auto &dl = module->getDataLayout();
|
||||
const auto basic_block = BasicBlockFunction(module);
|
||||
const auto state_ptr_type = NthArgument(basic_block, kStatePointerArgNum)->getType();
|
||||
const auto state_ptr_type =
|
||||
NthArgument(basic_block, kStatePointerArgNum)->getType();
|
||||
const auto state_type =
|
||||
llvm::dyn_cast<llvm::StructType>(state_ptr_type->getPointerElementType());
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ add_library(remill_arch STATIC
|
||||
Name.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(AArch32)
|
||||
add_subdirectory(AArch64)
|
||||
add_subdirectory(SPARC32)
|
||||
add_subdirectory(SPARC64)
|
||||
@@ -30,6 +31,7 @@ add_subdirectory(X86)
|
||||
set_property(TARGET remill_arch PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
target_link_libraries(remill_arch LINK_PUBLIC
|
||||
remill_arch_aarch32
|
||||
remill_arch_aarch64
|
||||
remill_arch_sparc32
|
||||
remill_arch_sparc64
|
||||
|
||||
+423
-32
@@ -17,20 +17,45 @@
|
||||
#include "remill/Arch/Instruction.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <llvm/IR/Instruction.h>
|
||||
#include <llvm/IR/Instructions.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
#include "remill/BC/Util.h"
|
||||
|
||||
namespace remill {
|
||||
|
||||
std::string OperandExpression::Serialize(void) const {
|
||||
std::stringstream ss;
|
||||
if (auto llvm_op = std::get_if<LLVMOpExpr>(this)) {
|
||||
ss << "(" << llvm::Instruction::getOpcodeName(llvm_op->llvm_opcode) << " "
|
||||
<< llvm_op->op1->Serialize();
|
||||
if (llvm_op->op2) {
|
||||
ss << " " << llvm_op->op2->Serialize();
|
||||
} else {
|
||||
ss << " to " << remill::LLVMThingToString(type);
|
||||
}
|
||||
ss << ")";
|
||||
} else if (auto reg_op = std::get_if<const Register *>(this)) {
|
||||
ss << (*reg_op)->name;
|
||||
} else if (auto ci_op = std::get_if<llvm::Constant *>(this)) {
|
||||
ss << remill::LLVMThingToString(*ci_op);
|
||||
} else if (auto str_op = std::get_if<std::string>(this)) {
|
||||
ss << *str_op;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Operand::Register::Register(void) : size(0) {}
|
||||
|
||||
Operand::ShiftRegister::ShiftRegister(void)
|
||||
: shift_size(0),
|
||||
extract_size(0),
|
||||
shift_first(false),
|
||||
shift_op(Operand::ShiftRegister::kShiftInvalid),
|
||||
extend_op(Operand::ShiftRegister::kExtendInvalid) {}
|
||||
|
||||
@@ -45,7 +70,8 @@ Operand::Address::Address(void)
|
||||
Operand::Operand(void)
|
||||
: type(Operand::kTypeInvalid),
|
||||
action(Operand::kActionInvalid),
|
||||
size(0) {}
|
||||
size(0),
|
||||
expr(nullptr) {}
|
||||
|
||||
namespace {
|
||||
static int64_t SignedImmediate(uint64_t val, uint64_t size) {
|
||||
@@ -72,49 +98,83 @@ std::string Operand::Serialize(void) const {
|
||||
ss << "(REG_" << reg.size << " " << reg.name << ")";
|
||||
break;
|
||||
|
||||
case Operand::kTypeShiftRegister:
|
||||
case Operand::kTypeShiftRegister: {
|
||||
auto shift_begin = [&](void) {
|
||||
switch (shift_reg.shift_op) {
|
||||
case Operand::ShiftRegister::kShiftInvalid: break;
|
||||
|
||||
switch (shift_reg.shift_op) {
|
||||
case Operand::ShiftRegister::kShiftInvalid: break;
|
||||
case Operand::ShiftRegister::kShiftLeftWithZeroes:
|
||||
ss << "(LSL ";
|
||||
break;
|
||||
|
||||
case Operand::ShiftRegister::kShiftLeftWithZeroes: ss << "(LSL "; break;
|
||||
case Operand::ShiftRegister::kShiftLeftWithOnes: ss << "(MSL "; break;
|
||||
|
||||
case Operand::ShiftRegister::kShiftLeftWithOnes: ss << "(MSL "; break;
|
||||
case Operand::ShiftRegister::kShiftUnsignedRight:
|
||||
ss << "(LSR ";
|
||||
break;
|
||||
|
||||
case Operand::ShiftRegister::kShiftUnsignedRight: ss << "(LSR "; break;
|
||||
case Operand::ShiftRegister::kShiftSignedRight: ss << "(ASR "; break;
|
||||
|
||||
case Operand::ShiftRegister::kShiftSignedRight: ss << "(ASR "; break;
|
||||
case Operand::ShiftRegister::kShiftLeftAround: ss << "(ROL "; break;
|
||||
|
||||
case Operand::ShiftRegister::kShiftLeftAround: ss << "(ROL "; break;
|
||||
case Operand::ShiftRegister::kShiftRightAround: ss << "(ROR "; break;
|
||||
}
|
||||
};
|
||||
|
||||
case Operand::ShiftRegister::kShiftRightAround: ss << "(ROR "; break;
|
||||
auto shift_end = [&](void) {
|
||||
if (Operand::ShiftRegister::kShiftInvalid != shift_reg.shift_op) {
|
||||
ss << " " << shift_reg.shift_size << ")";
|
||||
}
|
||||
};
|
||||
|
||||
auto extract_begin = [&](void) {
|
||||
switch (shift_reg.extend_op) {
|
||||
case Operand::ShiftRegister::kExtendInvalid: break;
|
||||
|
||||
case Operand::ShiftRegister::kExtendSigned:
|
||||
ss << "(SEXT (TRUNC ";
|
||||
break;
|
||||
|
||||
case Operand::ShiftRegister::kExtendUnsigned:
|
||||
ss << "(ZEXT (TRUNC ";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto extract_end = [&](void) {
|
||||
switch (shift_reg.extend_op) {
|
||||
case Operand::ShiftRegister::kExtendInvalid: break;
|
||||
|
||||
case Operand::ShiftRegister::kExtendSigned:
|
||||
ss << " " << shift_reg.extract_size << ") " << size << ")";
|
||||
break;
|
||||
|
||||
case Operand::ShiftRegister::kExtendUnsigned:
|
||||
ss << " " << shift_reg.extract_size << ") " << size << ")";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (shift_reg.shift_first) {
|
||||
extract_begin();
|
||||
shift_begin();
|
||||
} else {
|
||||
shift_begin();
|
||||
extract_begin();
|
||||
}
|
||||
|
||||
switch (shift_reg.extend_op) {
|
||||
case Operand::ShiftRegister::kExtendInvalid:
|
||||
ss << "(REG_" << shift_reg.reg.size << " " << shift_reg.reg.name
|
||||
<< ")";
|
||||
break;
|
||||
ss << "(REG_" << shift_reg.reg.size << " " << shift_reg.reg.name << ")";
|
||||
|
||||
case Operand::ShiftRegister::kExtendSigned:
|
||||
ss << "(SEXT (TRUNC (REG_" << shift_reg.reg.size << " "
|
||||
<< shift_reg.reg.name << ") " << shift_reg.extract_size << ") "
|
||||
<< size << ")";
|
||||
break;
|
||||
|
||||
case Operand::ShiftRegister::kExtendUnsigned:
|
||||
ss << "(ZEXT (TRUNC (REG_" << shift_reg.reg.size << " "
|
||||
<< shift_reg.reg.name << ") " << shift_reg.extract_size << ") "
|
||||
<< size << ")";
|
||||
break;
|
||||
}
|
||||
|
||||
if (Operand::ShiftRegister::kShiftInvalid != shift_reg.shift_op) {
|
||||
ss << " " << shift_reg.shift_size << ")";
|
||||
if (shift_reg.shift_first) {
|
||||
shift_end();
|
||||
extract_end();
|
||||
} else {
|
||||
extract_end();
|
||||
shift_end();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
case Operand::kTypeImmediate:
|
||||
ss << "(";
|
||||
if (imm.is_signed) {
|
||||
@@ -130,7 +190,7 @@ std::string Operand::Serialize(void) const {
|
||||
}
|
||||
break;
|
||||
|
||||
case Operand::kTypeAddress:
|
||||
case Operand::kTypeAddress: {
|
||||
ss << "(";
|
||||
|
||||
// Nice version of the memory size.
|
||||
@@ -211,11 +271,34 @@ std::string Operand::Serialize(void) const {
|
||||
}
|
||||
ss << ")"; // End of `(ADDR_`.
|
||||
break;
|
||||
}
|
||||
case Operand::kTypeExpression: ss << expr->Serialize(); break;
|
||||
}
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Condition::Serialize(void) const {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "(";
|
||||
switch (kind) {
|
||||
case Condition::kTypeIsEqual:
|
||||
ss << "(REG_" << lhs_reg.size << " " << lhs_reg.name << ") = (REG_"
|
||||
<< rhs_reg.size << " " << rhs_reg.name << ")";
|
||||
break;
|
||||
case Condition::kTypeIsOne:
|
||||
ss << "(REG_" << lhs_reg.size << " " << lhs_reg.name << ") = 1";
|
||||
break;
|
||||
case Condition::kTypeIsZero:
|
||||
ss << "(REG_" << lhs_reg.size << " " << lhs_reg.name << ") = 0";
|
||||
break;
|
||||
case Condition::kTypeTrue: ss << "TRUE"; break;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
Instruction::Instruction(void)
|
||||
: pc(0),
|
||||
next_pc(0),
|
||||
@@ -246,6 +329,310 @@ void Instruction::Reset(void) {
|
||||
operands.clear();
|
||||
function.clear();
|
||||
bytes.clear();
|
||||
next_expr_index = 0;
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::AllocateExpression(void) {
|
||||
CHECK_LT(next_expr_index, kMaxNumExpr);
|
||||
return &(exprs[next_expr_index++]);
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::EmplaceRegister(const Register *reg) {
|
||||
auto expr = AllocateExpression();
|
||||
expr->emplace<const Register *>(reg);
|
||||
expr->type = reg->type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::EmplaceRegister(std::string_view reg_name) {
|
||||
return EmplaceRegister(arch->RegisterByName(reg_name));
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::EmplaceConstant(llvm::Constant *val) {
|
||||
auto expr = AllocateExpression();
|
||||
expr->emplace<llvm::Constant *>(val);
|
||||
expr->type = val->getType();
|
||||
return expr;
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::EmplaceVariable(std::string_view var_name,
|
||||
llvm::Type *type) {
|
||||
auto expr = AllocateExpression();
|
||||
expr->emplace<std::string>(var_name.data(), var_name.size());
|
||||
expr->type = type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::EmplaceBinaryOp(unsigned opcode,
|
||||
OperandExpression *op1,
|
||||
OperandExpression *op2) {
|
||||
auto expr = AllocateExpression();
|
||||
expr->emplace<LLVMOpExpr>(LLVMOpExpr{opcode, op1, op2});
|
||||
expr->type = op1->type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
OperandExpression *Instruction::EmplaceUnaryOp(unsigned opcode,
|
||||
OperandExpression *op1,
|
||||
llvm::Type *type) {
|
||||
auto expr = AllocateExpression();
|
||||
expr->emplace<LLVMOpExpr>(LLVMOpExpr{opcode, op1, nullptr});
|
||||
expr->type = type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Operand &Instruction::EmplaceOperand(const Operand::Register ®_op) {
|
||||
operands.emplace_back();
|
||||
auto &op = operands.back();
|
||||
op.type = Operand::kTypeExpression;
|
||||
op.size = reg_op.size;
|
||||
if (auto reg = arch->RegisterByName(reg_op.name)) {
|
||||
op.expr = EmplaceRegister(reg);
|
||||
} else {
|
||||
auto &context = *arch->context;
|
||||
auto ty = llvm::Type::getIntNTy(context, reg_op.size);
|
||||
op.expr = EmplaceVariable(reg_op.name, ty);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
Operand &Instruction::EmplaceOperand(const Operand::Immediate &imm_op) {
|
||||
operands.emplace_back();
|
||||
auto &op = operands.back();
|
||||
auto &context = *arch->context;
|
||||
|
||||
auto ty = llvm::Type::getIntNTy(context, arch->address_size);
|
||||
op.expr =
|
||||
EmplaceConstant(llvm::ConstantInt::get(ty, imm_op.val, imm_op.is_signed));
|
||||
op.size = arch->address_size;
|
||||
op.type = Operand::kTypeExpression;
|
||||
return op;
|
||||
}
|
||||
|
||||
Operand &Instruction::EmplaceOperand(const Operand::ShiftRegister &shift_op) {
|
||||
operands.emplace_back();
|
||||
auto &op = operands.back();
|
||||
op.type = Operand::kTypeExpression;
|
||||
op.size = arch->address_size;
|
||||
auto &arch_reg = shift_op.reg;
|
||||
|
||||
auto &context = *arch->context;
|
||||
auto reg = arch->RegisterByName(arch_reg.name);
|
||||
auto reg_type = reg->type;
|
||||
auto reg_size = reg->size * 8u;
|
||||
auto op_type = llvm::Type::getIntNTy(context, op.size);
|
||||
|
||||
const uint64_t zero = 0;
|
||||
const uint64_t one = 1;
|
||||
const uint64_t shift_size = shift_op.shift_size;
|
||||
|
||||
const auto shift_val = llvm::ConstantInt::get(op_type, shift_size);
|
||||
|
||||
auto expr = EmplaceRegister(reg);
|
||||
|
||||
auto curr_size = reg_size;
|
||||
|
||||
auto do_extract = [&](void) {
|
||||
if (Operand::ShiftRegister::kExtendInvalid != shift_op.extend_op) {
|
||||
|
||||
auto extract_type = llvm::Type::getIntNTy(context, shift_op.extract_size);
|
||||
|
||||
if (reg_size > shift_op.extract_size) {
|
||||
curr_size = shift_op.extract_size;
|
||||
expr = EmplaceUnaryOp(llvm::Instruction::Trunc, expr, extract_type);
|
||||
|
||||
} else {
|
||||
CHECK(reg_size == shift_op.extract_size)
|
||||
<< "Invalid extraction size. Can't extract "
|
||||
<< shift_op.extract_size << " bits from a " << reg_size
|
||||
<< "-bit value in operand " << op.Serialize()
|
||||
<< " of instruction at " << std::hex << pc;
|
||||
}
|
||||
|
||||
if (op.size > shift_op.extract_size) {
|
||||
switch (shift_op.extend_op) {
|
||||
case Operand::ShiftRegister::kExtendSigned:
|
||||
expr = EmplaceUnaryOp(llvm::Instruction::SExt, expr, op_type);
|
||||
curr_size = op.size;
|
||||
break;
|
||||
case Operand::ShiftRegister::kExtendUnsigned:
|
||||
expr = EmplaceUnaryOp(llvm::Instruction::ZExt, expr, op_type);
|
||||
curr_size = op.size;
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "Invalid extend operation type for instruction at "
|
||||
<< std::hex << pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK(curr_size <= op.size);
|
||||
|
||||
if (curr_size < op.size) {
|
||||
expr = EmplaceUnaryOp(llvm::Instruction::ZExt, expr, op_type);
|
||||
curr_size = op.size;
|
||||
}
|
||||
};
|
||||
|
||||
auto do_shift = [&](void) {
|
||||
if (Operand::ShiftRegister::kShiftInvalid != shift_op.shift_op) {
|
||||
|
||||
CHECK(shift_size < op.size)
|
||||
<< "Shift of size " << shift_size
|
||||
<< " is wider than the base register size in shift register in "
|
||||
<< Serialize();
|
||||
|
||||
switch (shift_op.shift_op) {
|
||||
|
||||
// Left shift.
|
||||
case Operand::ShiftRegister::kShiftLeftWithZeroes:
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
||||
EmplaceConstant(shift_val));
|
||||
break;
|
||||
|
||||
// Masking shift left.
|
||||
case Operand::ShiftRegister::kShiftLeftWithOnes: {
|
||||
const auto mask_val =
|
||||
llvm::ConstantInt::get(reg_type, ~((~zero) << shift_size));
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
||||
EmplaceConstant(shift_val));
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::Or, expr,
|
||||
EmplaceConstant(mask_val));
|
||||
break;
|
||||
}
|
||||
|
||||
// Logical right shift.
|
||||
case Operand::ShiftRegister::kShiftUnsignedRight:
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::LShr, expr,
|
||||
EmplaceConstant(shift_val));
|
||||
break;
|
||||
|
||||
// Arithmetic right shift.
|
||||
case Operand::ShiftRegister::kShiftSignedRight:
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::AShr, expr,
|
||||
EmplaceConstant(shift_val));
|
||||
break;
|
||||
|
||||
// Rotate left.
|
||||
case Operand::ShiftRegister::kShiftLeftAround: {
|
||||
const uint64_t shr_amount = (~shift_size + one) & (op.size - one);
|
||||
const auto shr_val = llvm::ConstantInt::get(op_type, shr_amount);
|
||||
auto expr1 = EmplaceBinaryOp(llvm::Instruction::LShr, expr,
|
||||
EmplaceConstant(shr_val));
|
||||
auto expr2 = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
||||
EmplaceConstant(shift_val));
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::Or, expr1, expr2);
|
||||
break;
|
||||
}
|
||||
|
||||
// Rotate right.
|
||||
case Operand::ShiftRegister::kShiftRightAround: {
|
||||
const uint64_t shl_amount = (~shift_size + one) & (op.size - one);
|
||||
const auto shl_val = llvm::ConstantInt::get(op_type, shl_amount);
|
||||
auto expr1 = EmplaceBinaryOp(llvm::Instruction::LShr, expr,
|
||||
EmplaceConstant(shift_val));
|
||||
auto expr2 = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
||||
EmplaceConstant(shl_val));
|
||||
expr = EmplaceBinaryOp(llvm::Instruction::Or, expr1, expr2);
|
||||
break;
|
||||
}
|
||||
|
||||
case Operand::ShiftRegister::kShiftInvalid: break;
|
||||
}
|
||||
}
|
||||
if (curr_size < op.size) {
|
||||
expr = EmplaceUnaryOp(llvm::Instruction::ZExt, expr, op_type);
|
||||
curr_size = op.size;
|
||||
}
|
||||
};
|
||||
|
||||
if (shift_op.shift_first) {
|
||||
do_shift();
|
||||
do_extract();
|
||||
} else {
|
||||
do_extract();
|
||||
do_shift();
|
||||
}
|
||||
op.expr = expr;
|
||||
return op;
|
||||
}
|
||||
|
||||
Operand &Instruction::EmplaceOperand(const Operand::Address &addr_op) {
|
||||
operands.emplace_back();
|
||||
auto &op = operands.back();
|
||||
|
||||
const auto word_type = arch->AddressType();
|
||||
const auto zero = llvm::ConstantInt::get(word_type, 0, false);
|
||||
const auto word_size = arch->address_size;
|
||||
|
||||
CHECK(word_size >= addr_op.base_reg.size)
|
||||
<< "Memory base register " << addr_op.base_reg.name
|
||||
<< "for instruction at " << std::hex << pc
|
||||
<< " is wider than the machine word size.";
|
||||
|
||||
CHECK(word_size >= addr_op.index_reg.size)
|
||||
<< "Memory index register " << addr_op.base_reg.name
|
||||
<< "for instruction at " << std::hex << pc
|
||||
<< " is wider than the machine word size.";
|
||||
|
||||
auto reg_or_zero = [=](const Operand::Register ®) {
|
||||
if (!reg.name.empty()) {
|
||||
if (auto reg_pointer = arch->RegisterByName(reg.name)) {
|
||||
return EmplaceRegister(reg_pointer);
|
||||
} else {
|
||||
return EmplaceVariable(reg.name,
|
||||
llvm::Type::getIntNTy(*arch->context, reg.size));
|
||||
}
|
||||
} else {
|
||||
return EmplaceConstant(zero);
|
||||
}
|
||||
};
|
||||
|
||||
auto addr = reg_or_zero(addr_op.base_reg);
|
||||
|
||||
if (!addr_op.index_reg.name.empty() && addr_op.scale) {
|
||||
auto index = reg_or_zero(addr_op.index_reg);
|
||||
if (addr_op.scale != 1) {
|
||||
auto scale = llvm::ConstantInt::get(
|
||||
word_type, static_cast<uint64_t>(addr_op.scale), true);
|
||||
index = EmplaceBinaryOp(llvm::Instruction::Mul, index,
|
||||
EmplaceConstant(scale));
|
||||
}
|
||||
addr = EmplaceBinaryOp(llvm::Instruction::Add, addr, index);
|
||||
}
|
||||
|
||||
if (addr_op.displacement) {
|
||||
if (0 < addr_op.displacement) {
|
||||
auto disp = llvm::ConstantInt::get(
|
||||
word_type, static_cast<uint64_t>(addr_op.displacement));
|
||||
addr =
|
||||
EmplaceBinaryOp(llvm::Instruction::Add, addr, EmplaceConstant(disp));
|
||||
} else {
|
||||
auto disp = llvm::ConstantInt::get(
|
||||
word_type, static_cast<uint64_t>(-addr_op.displacement));
|
||||
addr =
|
||||
EmplaceBinaryOp(llvm::Instruction::Sub, addr, EmplaceConstant(disp));
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the segmented address.
|
||||
if (!addr_op.segment_base_reg.name.empty()) {
|
||||
auto segment = reg_or_zero(addr_op.segment_base_reg);
|
||||
addr = EmplaceBinaryOp(llvm::Instruction::Add, addr, segment);
|
||||
}
|
||||
|
||||
// Memory address is smaller than the machine word size (e.g. 32-bit address
|
||||
// used in 64-bit).
|
||||
if (addr_op.address_size < word_size) {
|
||||
auto addr_type = llvm::Type::getIntNTy(
|
||||
*arch->context, static_cast<unsigned>(addr_op.address_size));
|
||||
|
||||
addr = EmplaceUnaryOp(llvm::Instruction::Trunc, addr, addr_type);
|
||||
addr = EmplaceUnaryOp(llvm::Instruction::ZExt, addr, word_type);
|
||||
}
|
||||
op.expr = addr;
|
||||
op.type = Operand::kTypeExpression;
|
||||
return op;
|
||||
}
|
||||
|
||||
std::string Instruction::Serialize(void) const {
|
||||
@@ -259,6 +646,7 @@ std::string Instruction::Serialize(void) const {
|
||||
case kArchX86:
|
||||
case kArchX86_AVX:
|
||||
case kArchX86_AVX512: ss << "X86"; break;
|
||||
case kArchAArch32LittleEndian: ss << "AArch32"; break;
|
||||
case kArchAArch64LittleEndian: ss << "AArch64"; break;
|
||||
case kArchSparc32: ss << "SPARC32"; break;
|
||||
case kArchSparc64: ss << "SPARC64"; break;
|
||||
@@ -339,6 +727,9 @@ std::string Instruction::Serialize(void) const {
|
||||
ss << " (COND_BRANCH (TAKEN " << std::hex << branch_taken_pc << ")"
|
||||
<< " (NOT_TAKEN " << branch_not_taken_pc << std::dec << "))";
|
||||
break;
|
||||
case kCategoryConditionalIndirectJump:
|
||||
ss << " (COND_BRANCH (TAKEN <unknown>)"
|
||||
<< " (NOT_TAKEN " << branch_not_taken_pc << std::dec << "))";
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ ArchName GetArchName(const llvm::Triple &triple) {
|
||||
case llvm::Triple::ArchType::x86: return kArchX86;
|
||||
case llvm::Triple::ArchType::x86_64: return kArchAMD64;
|
||||
case llvm::Triple::ArchType::aarch64: return kArchAArch64LittleEndian;
|
||||
case llvm::Triple::ArchType::arm: return kArchAArch32LittleEndian;
|
||||
case llvm::Triple::ArchType::thumb: return kArchAArch32LittleEndian;
|
||||
default: return kArchInvalid;
|
||||
}
|
||||
}
|
||||
@@ -48,6 +50,9 @@ ArchName GetArchName(std::string_view arch_name) {
|
||||
} else if (arch_name == "amd64_avx512") {
|
||||
return kArchAMD64_AVX512;
|
||||
|
||||
} else if (arch_name == "aarch32") {
|
||||
return kArchAArch32LittleEndian;
|
||||
|
||||
} else if (arch_name == "aarch64") {
|
||||
return kArchAArch64LittleEndian;
|
||||
|
||||
@@ -72,6 +77,7 @@ static const std::string_view kArchNames[] = {
|
||||
[kArchAMD64] = "amd64",
|
||||
[kArchAMD64_AVX] = "amd64_avx",
|
||||
[kArchAMD64_AVX512] = "amd64_avx512",
|
||||
[kArchAArch32LittleEndian] = "aarch32",
|
||||
[kArchAArch64LittleEndian] = "aarch64",
|
||||
[kArchSparc32] = "sparc32",
|
||||
[kArchSparc64] = "sparc64",
|
||||
|
||||
+80
-89
@@ -14,21 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "Decode.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
#include "remill/BC/ABI.h"
|
||||
#include "remill/BC/Util.h"
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
#include "Decode.h"
|
||||
|
||||
// clang-format off
|
||||
#define ADDRESS_SIZE_BITS 32
|
||||
#define INCLUDED_FROM_REMILL
|
||||
#include "remill/Arch/SPARC32/Runtime/State.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace remill {
|
||||
@@ -39,84 +40,72 @@ static const std::string_view kPCRegName = "pc";
|
||||
} // namespace
|
||||
|
||||
|
||||
const std::string_view kCCRName[4] = {
|
||||
"icc", {}, "xcc", {}
|
||||
};
|
||||
const std::string_view kCCRName[4] = {"icc", {}, "xcc", {}};
|
||||
|
||||
const std::string_view kFCCRName[8] = {
|
||||
"fcc0", "fcc1", "fcc2", "fcc3",
|
||||
"icc", {}, "xcc", {}
|
||||
};
|
||||
const std::string_view kFCCRName[8] = {"fcc0", "fcc1", "fcc2", "fcc3",
|
||||
"icc", {}, "xcc", {}};
|
||||
|
||||
const std::string_view kReadIntRegName[32] = {
|
||||
"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
|
||||
"o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
|
||||
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
|
||||
"i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7"
|
||||
};
|
||||
"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", "o0", "o1", "o2",
|
||||
"o3", "o4", "o5", "sp", "o7", "l0", "l1", "l2", "l3", "l4", "l5",
|
||||
"l6", "l7", "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7"};
|
||||
|
||||
const std::string_view kWriteIntRegName[32] = {
|
||||
"ignore_write_to_g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
|
||||
"o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7",
|
||||
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
|
||||
"i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7"
|
||||
};
|
||||
const std::string_view kWriteIntRegName[32] = {"ignore_write_to_g0",
|
||||
"g1",
|
||||
"g2",
|
||||
"g3",
|
||||
"g4",
|
||||
"g5",
|
||||
"g6",
|
||||
"g7",
|
||||
"o0",
|
||||
"o1",
|
||||
"o2",
|
||||
"o3",
|
||||
"o4",
|
||||
"o5",
|
||||
"o6",
|
||||
"o7",
|
||||
"l0",
|
||||
"l1",
|
||||
"l2",
|
||||
"l3",
|
||||
"l4",
|
||||
"l5",
|
||||
"l6",
|
||||
"l7",
|
||||
"i0",
|
||||
"i1",
|
||||
"i2",
|
||||
"i3",
|
||||
"i4",
|
||||
"i5",
|
||||
"i6",
|
||||
"i7"};
|
||||
|
||||
const std::string_view kCondName[16] = {
|
||||
[0b0000] = "N",
|
||||
[0b0001] = "E",
|
||||
[0b0010] = "LE",
|
||||
[0b0011] = "L",
|
||||
[0b0100] = "LEU",
|
||||
[0b0101] = "CS",
|
||||
[0b0110] = "NEG",
|
||||
[0b0111] = "VS",
|
||||
[0b1000] = "A",
|
||||
[0b1001] = "NE",
|
||||
[0b1010] = "G",
|
||||
[0b1011] = "GE",
|
||||
[0b1100] = "GU",
|
||||
[0b1101] = "CC",
|
||||
[0b1110] = "POS",
|
||||
[0b1111] = "VC",
|
||||
[0b0000] = "N", [0b0001] = "E", [0b0010] = "LE", [0b0011] = "L",
|
||||
[0b0100] = "LEU", [0b0101] = "CS", [0b0110] = "NEG", [0b0111] = "VS",
|
||||
[0b1000] = "A", [0b1001] = "NE", [0b1010] = "G", [0b1011] = "GE",
|
||||
[0b1100] = "GU", [0b1101] = "CC", [0b1110] = "POS", [0b1111] = "VC",
|
||||
};
|
||||
|
||||
const std::string_view kFCondName[16] = {
|
||||
[0b0000] = "N",
|
||||
[0b0001] = "NE",
|
||||
[0b0010] = "LG",
|
||||
[0b0011] = "UL",
|
||||
[0b0100] = "L",
|
||||
[0b0101] = "UG",
|
||||
[0b0110] = "G",
|
||||
[0b0111] = "U",
|
||||
[0b1000] = "A",
|
||||
[0b1001] = "E",
|
||||
[0b1010] = "UE",
|
||||
[0b1011] = "GE",
|
||||
[0b1100] = "UGE",
|
||||
[0b1101] = "LE",
|
||||
[0b1110] = "ULE",
|
||||
[0b1111] = "O"
|
||||
};
|
||||
[0b0000] = "N", [0b0001] = "NE", [0b0010] = "LG", [0b0011] = "UL",
|
||||
[0b0100] = "L", [0b0101] = "UG", [0b0110] = "G", [0b0111] = "U",
|
||||
[0b1000] = "A", [0b1001] = "E", [0b1010] = "UE", [0b1011] = "GE",
|
||||
[0b1100] = "UGE", [0b1101] = "LE", [0b1110] = "ULE", [0b1111] = "O"};
|
||||
|
||||
const std::string_view kRCondName[8] = {
|
||||
[0b000] = {},
|
||||
[0b001] = "Z",
|
||||
[0b010] = "LEZ",
|
||||
[0b011] = "LZ",
|
||||
[0b100] = {},
|
||||
[0b101] = "NZ",
|
||||
[0b110] = "GZ",
|
||||
[0b111] = "GEZ"
|
||||
};
|
||||
[0b000] = {}, [0b001] = "Z", [0b010] = "LEZ", [0b011] = "LZ",
|
||||
[0b100] = {}, [0b101] = "NZ", [0b110] = "GZ", [0b111] = "GEZ"};
|
||||
|
||||
void AddSrcRegop(Instruction &inst, const char *reg_name, unsigned size) {
|
||||
inst.operands.emplace_back();
|
||||
auto &op = inst.operands.back();
|
||||
op.type = Operand::kTypeRegister;
|
||||
op.size = size;
|
||||
op.action = Operand::kActionRead;
|
||||
op.action = Operand::kActionRead;
|
||||
op.reg.name = reg_name;
|
||||
op.reg.size = size;
|
||||
}
|
||||
@@ -126,18 +115,17 @@ void AddDestRegop(Instruction &inst, const char *reg_name, unsigned size) {
|
||||
auto &op = inst.operands.back();
|
||||
op.type = Operand::kTypeRegister;
|
||||
op.size = size;
|
||||
op.action = Operand::kActionWrite;
|
||||
op.action = Operand::kActionWrite;
|
||||
op.reg.name = reg_name;
|
||||
op.reg.size = size;
|
||||
}
|
||||
|
||||
void AddImmop(Instruction &inst, uint64_t imm,
|
||||
unsigned size, bool is_signed) {
|
||||
void AddImmop(Instruction &inst, uint64_t imm, unsigned size, bool is_signed) {
|
||||
inst.operands.emplace_back();
|
||||
auto &op = inst.operands.back();
|
||||
op.type = Operand::kTypeImmediate;
|
||||
op.size = size;
|
||||
op.action = Operand::kActionRead;
|
||||
op.action = Operand::kActionRead;
|
||||
op.imm.val = imm;
|
||||
op.imm.is_signed = is_signed;
|
||||
}
|
||||
@@ -178,9 +166,8 @@ class SPARC32Arch final : public Arch {
|
||||
llvm::DataLayout DataLayout(void) const final;
|
||||
|
||||
// Decode an instruction.
|
||||
bool DecodeInstruction(
|
||||
uint64_t address, std::string_view instr_bytes,
|
||||
Instruction &inst) const final;
|
||||
bool DecodeInstruction(uint64_t address, std::string_view instr_bytes,
|
||||
Instruction &inst) const final;
|
||||
|
||||
// Returns `true` if memory access are little endian byte ordered.
|
||||
bool MemoryAccessIsLittleEndian(void) const final {
|
||||
@@ -264,7 +251,8 @@ void SPARC32Arch::PopulateBasicBlockFunction(llvm::Module *module,
|
||||
REG(o7, gpr.o7.dword, u32);
|
||||
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "g0"), false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "ignore_write_to_g0"), false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "ignore_write_to_g0"),
|
||||
false);
|
||||
|
||||
REG(g1, gpr.g1.dword, u32);
|
||||
REG(g2, gpr.g2.dword, u32);
|
||||
@@ -392,23 +380,27 @@ void SPARC32Arch::PopulateBasicBlockFunction(llvm::Module *module,
|
||||
|
||||
// `WINDOW_LINK = &(WINDOW->prev_window);`
|
||||
llvm::Value *gep_indexes[2] = {zero_u32, llvm::ConstantInt::get(u32, 33)};
|
||||
auto window_link = ir.CreateInBoundsGEP(window_type, window, gep_indexes, "WINDOW_LINK");
|
||||
auto window_link =
|
||||
ir.CreateInBoundsGEP(window_type, window, gep_indexes, "WINDOW_LINK");
|
||||
auto nullptr_window = llvm::Constant::getNullValue(prev_window_link->type);
|
||||
ir.CreateStore(nullptr_window, window_link, false);
|
||||
|
||||
ir.CreateStore(zero_u8, ir.CreateAlloca(u8, nullptr, "IGNORE_BRANCH_TAKEN"), false);
|
||||
ir.CreateStore(zero_u8, ir.CreateAlloca(u8, nullptr, "IGNORE_BRANCH_TAKEN"),
|
||||
false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "IGNORE_PC"), false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "IGNORE_NEXT_PC"), false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "IGNORE_RETURN_PC"), false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "IGNORE_NEXT_PC"),
|
||||
false);
|
||||
ir.CreateStore(zero_u32, ir.CreateAlloca(u32, nullptr, "IGNORE_RETURN_PC"),
|
||||
false);
|
||||
|
||||
const auto pc_arg = NthArgument(bb_func, kPCArgNum);
|
||||
const auto state_ptr_arg = NthArgument(bb_func, kStatePointerArgNum);
|
||||
|
||||
(void) RegisterByName(kNextPCVariableName)->AddressOf(state_ptr_arg, ir);
|
||||
|
||||
ir.CreateStore(
|
||||
pc_arg, RegisterByName(kPCVariableName)->AddressOf(state_ptr_arg, ir),
|
||||
false);
|
||||
ir.CreateStore(pc_arg,
|
||||
RegisterByName(kPCVariableName)->AddressOf(state_ptr_arg, ir),
|
||||
false);
|
||||
}
|
||||
|
||||
llvm::Triple SPARC32Arch::Triple(void) const {
|
||||
@@ -445,8 +437,9 @@ bool SPARC32Arch::NextInstructionIsDelayed(const Instruction &inst,
|
||||
}
|
||||
|
||||
// Decode an instruction.
|
||||
bool SPARC32Arch::DecodeInstruction(
|
||||
uint64_t address, std::string_view inst_bytes, Instruction &inst) const {
|
||||
bool SPARC32Arch::DecodeInstruction(uint64_t address,
|
||||
std::string_view inst_bytes,
|
||||
Instruction &inst) const {
|
||||
inst.pc = address;
|
||||
inst.arch_name = arch_name;
|
||||
inst.arch = this;
|
||||
@@ -475,12 +468,11 @@ bool SPARC32Arch::DecodeInstruction(
|
||||
if (!sparc32::TryDecode(inst)) {
|
||||
inst.category = Instruction::kCategoryInvalid;
|
||||
inst.operands.clear();
|
||||
LOG(ERROR)
|
||||
<< "Unable to decode: " << inst.Serialize();
|
||||
LOG(ERROR) << "Unable to decode: " << inst.Serialize();
|
||||
return false;
|
||||
}
|
||||
|
||||
// LOG(ERROR) << inst.Serialize();
|
||||
// LOG(ERROR) << inst.Serialize();
|
||||
|
||||
return inst.IsValid();
|
||||
}
|
||||
@@ -488,15 +480,14 @@ bool SPARC32Arch::DecodeInstruction(
|
||||
} // namespace sparc
|
||||
|
||||
// TODO(pag): We pretend that these are singletons, but they aren't really!
|
||||
Arch::ArchPtr Arch::GetSPARC(
|
||||
llvm::LLVMContext *context_, OSName os_name_, ArchName arch_name_) {
|
||||
Arch::ArchPtr Arch::GetSPARC(llvm::LLVMContext *context_, OSName os_name_,
|
||||
ArchName arch_name_) {
|
||||
if (arch_name_ == kArchSparc32) {
|
||||
return std::make_unique<sparc::SPARC32Arch>(context_, os_name_, arch_name_);
|
||||
|
||||
} else {
|
||||
LOG(FATAL)
|
||||
<< "Invalid arch name passed to Arch::GetSPARC: "
|
||||
<< GetArchName(arch_name_);
|
||||
LOG(FATAL) << "Invalid arch name passed to Arch::GetSPARC: "
|
||||
<< GetArchName(arch_name_);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
+145
-144
@@ -16,9 +16,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
|
||||
@@ -28,10 +29,10 @@ namespace sparc {
|
||||
union Format0a {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t imm22:22;
|
||||
uint32_t op2:3;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t imm22 : 22;
|
||||
uint32_t op2 : 3;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format0a) == 4, " ");
|
||||
@@ -39,11 +40,11 @@ static_assert(sizeof(Format0a) == 4, " ");
|
||||
union Format0b {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
int32_t disp22:22;
|
||||
uint32_t op2:3;
|
||||
uint32_t cond:4;
|
||||
uint32_t a:1;
|
||||
uint32_t op:2;
|
||||
int32_t disp22 : 22;
|
||||
uint32_t op2 : 3;
|
||||
uint32_t cond : 4;
|
||||
uint32_t a : 1;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format0b) == 4, " ");
|
||||
@@ -51,14 +52,14 @@ static_assert(sizeof(Format0b) == 4, " ");
|
||||
union Format0c {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
int32_t disp19:19;
|
||||
uint32_t p:1;
|
||||
uint32_t cc0:1;
|
||||
uint32_t cc1:1;
|
||||
uint32_t op2:3;
|
||||
uint32_t cond:4;
|
||||
uint32_t a:1;
|
||||
uint32_t op:2;
|
||||
int32_t disp19 : 19;
|
||||
uint32_t p : 1;
|
||||
uint32_t cc0 : 1;
|
||||
uint32_t cc1 : 1;
|
||||
uint32_t op2 : 3;
|
||||
uint32_t cond : 4;
|
||||
uint32_t a : 1;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format0c) == 4, " ");
|
||||
@@ -66,15 +67,15 @@ static_assert(sizeof(Format0c) == 4, " ");
|
||||
union Format0d {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t d16lo:14;
|
||||
uint32_t rs1:5;
|
||||
uint32_t p:1;
|
||||
uint32_t d16hi:2;
|
||||
uint32_t op2:3;
|
||||
uint32_t rcond:3;
|
||||
uint32_t must_be_zero:1; // Bit 28.
|
||||
uint32_t a:1;
|
||||
uint32_t op:2;
|
||||
uint32_t d16lo : 14;
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t p : 1;
|
||||
uint32_t d16hi : 2;
|
||||
uint32_t op2 : 3;
|
||||
uint32_t rcond : 3;
|
||||
uint32_t must_be_zero : 1; // Bit 28.
|
||||
uint32_t a : 1;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format0d) == 4, " ");
|
||||
@@ -82,14 +83,15 @@ static_assert(sizeof(Format0d) == 4, " ");
|
||||
union Format3 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t ai0_ai1_b:14;
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t ai0_ai1_b : 14;
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3) == 4, " ");
|
||||
|
||||
// SPARC Format 3a
|
||||
//_________________________________________________________________
|
||||
//| op| rd | op3 | rs1 |i| asi | rs2 |
|
||||
@@ -97,13 +99,13 @@ static_assert(sizeof(Format3) == 4, " ");
|
||||
union Format3ai0 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t asi:8;
|
||||
uint32_t i:1; // Must be 0.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t asi : 8;
|
||||
uint32_t i : 1; // Must be 0.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3ai0) == 4, " ");
|
||||
@@ -115,12 +117,12 @@ static_assert(sizeof(Format3ai0) == 4, " ");
|
||||
union Format3ai1 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
int32_t simm13:13;
|
||||
uint32_t i:1; // Must be 1.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
int32_t simm13 : 13;
|
||||
uint32_t i : 1; // Must be 1.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -131,12 +133,12 @@ union Format3ai1 {
|
||||
union Format3b {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t opf:9;
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2; // 3
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t opf : 9;
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2; // 3
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3b) == 4, " ");
|
||||
@@ -144,14 +146,14 @@ static_assert(sizeof(Format3b) == 4, " ");
|
||||
union Format3c {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t opf:9;
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t cc0:1;
|
||||
uint32_t cc1:1;
|
||||
uint32_t _1:3;
|
||||
uint32_t op:2; // 3
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t opf : 9;
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t cc0 : 1;
|
||||
uint32_t cc1 : 1;
|
||||
uint32_t _1 : 3;
|
||||
uint32_t op : 2; // 3
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3b) == 4, " ");
|
||||
@@ -159,14 +161,14 @@ static_assert(sizeof(Format3b) == 4, " ");
|
||||
union Format3di0 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t _1:5;
|
||||
uint32_t rcond:3;
|
||||
uint32_t i:1;
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2; // 3
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t _1 : 5;
|
||||
uint32_t rcond : 3;
|
||||
uint32_t i : 1;
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2; // 3
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3di0) == 4, " ");
|
||||
@@ -174,13 +176,13 @@ static_assert(sizeof(Format3di0) == 4, " ");
|
||||
union Format3di1 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t simm10:10;
|
||||
uint32_t rcond:3;
|
||||
uint32_t i:1;
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2; // 3
|
||||
uint32_t simm10 : 10;
|
||||
uint32_t rcond : 3;
|
||||
uint32_t i : 1;
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2; // 3
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3di1) == 4, " ");
|
||||
@@ -188,14 +190,14 @@ static_assert(sizeof(Format3di1) == 4, " ");
|
||||
union Format3ei0 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t _1:7;
|
||||
uint32_t x:1;
|
||||
uint32_t i:1; // Must be 0.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t _1 : 7;
|
||||
uint32_t x : 1;
|
||||
uint32_t i : 1; // Must be 0.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3ei0) == 4, " ");
|
||||
@@ -203,14 +205,14 @@ static_assert(sizeof(Format3ei0) == 4, " ");
|
||||
union Format3ei1 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t shcnt32:5;
|
||||
uint32_t _1:7;
|
||||
uint32_t x:1;
|
||||
uint32_t i:1; // Must be 0.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t shcnt32 : 5;
|
||||
uint32_t _1 : 7;
|
||||
uint32_t x : 1;
|
||||
uint32_t i : 1; // Must be 0.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3ei1) == 4, " ");
|
||||
@@ -218,14 +220,14 @@ static_assert(sizeof(Format3ei1) == 4, " ");
|
||||
union Format3ei2 {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t shcnt64:6;
|
||||
uint32_t _1:6;
|
||||
uint32_t x:1;
|
||||
uint32_t i:1; // Must be 0.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t shcnt64 : 6;
|
||||
uint32_t _1 : 6;
|
||||
uint32_t x : 1;
|
||||
uint32_t i : 1; // Must be 0.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3ei2) == 4, " ");
|
||||
@@ -233,14 +235,14 @@ static_assert(sizeof(Format3ei2) == 4, " ");
|
||||
union Format3f {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t mmask:4;
|
||||
uint32_t cmask:3;
|
||||
uint32_t _1:6;
|
||||
uint32_t i:1; // Must be 1.
|
||||
uint32_t bits:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t _2:5;
|
||||
uint32_t op:2;
|
||||
uint32_t mmask : 4;
|
||||
uint32_t cmask : 3;
|
||||
uint32_t _1 : 6;
|
||||
uint32_t i : 1; // Must be 1.
|
||||
uint32_t bits : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t _2 : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format3f) == 4, " ");
|
||||
@@ -248,15 +250,15 @@ static_assert(sizeof(Format3f) == 4, " ");
|
||||
union Format4a {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t unused:6;
|
||||
uint32_t cc0:1;
|
||||
uint32_t cc1:1;
|
||||
uint32_t i:1; // 0.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t unused : 6;
|
||||
uint32_t cc0 : 1;
|
||||
uint32_t cc1 : 1;
|
||||
uint32_t i : 1; // 0.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format4a) == 4, " ");
|
||||
@@ -264,14 +266,14 @@ static_assert(sizeof(Format4a) == 4, " ");
|
||||
union Format4b {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
int32_t simm11:11;
|
||||
uint32_t cc0:1;
|
||||
uint32_t cc1:1;
|
||||
uint32_t i:1; // 0.
|
||||
uint32_t rs1:5;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
int32_t simm11 : 11;
|
||||
uint32_t cc0 : 1;
|
||||
uint32_t cc1 : 1;
|
||||
uint32_t i : 1; // 0.
|
||||
uint32_t rs1 : 5;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format4b) == 4, " ");
|
||||
@@ -279,16 +281,16 @@ static_assert(sizeof(Format4b) == 4, " ");
|
||||
union Format4c {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
uint32_t rs2:5;
|
||||
uint32_t unused:6;
|
||||
uint32_t cc0:1;
|
||||
uint32_t cc1:1;
|
||||
uint32_t i:1; // 0.
|
||||
uint32_t cond:4;
|
||||
uint32_t cc2:1;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
uint32_t rs2 : 5;
|
||||
uint32_t unused : 6;
|
||||
uint32_t cc0 : 1;
|
||||
uint32_t cc1 : 1;
|
||||
uint32_t i : 1; // 0.
|
||||
uint32_t cond : 4;
|
||||
uint32_t cc2 : 1;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format4c) == 4, " ");
|
||||
@@ -296,15 +298,15 @@ static_assert(sizeof(Format4c) == 4, " ");
|
||||
union Format4d {
|
||||
uint32_t flat;
|
||||
struct {
|
||||
int32_t simm11:11;
|
||||
uint32_t cc0:1;
|
||||
uint32_t cc1:1;
|
||||
uint32_t i:1; // 0.
|
||||
uint32_t cond:4;
|
||||
uint32_t cc2:1;
|
||||
uint32_t op3:6;
|
||||
uint32_t rd:5;
|
||||
uint32_t op:2;
|
||||
int32_t simm11 : 11;
|
||||
uint32_t cc0 : 1;
|
||||
uint32_t cc1 : 1;
|
||||
uint32_t i : 1; // 0.
|
||||
uint32_t cond : 4;
|
||||
uint32_t cc2 : 1;
|
||||
uint32_t op3 : 6;
|
||||
uint32_t rd : 5;
|
||||
uint32_t op : 2;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(Format4d) == 4, " ");
|
||||
@@ -319,8 +321,7 @@ extern const std::string_view kRCondName[8];
|
||||
|
||||
void AddSrcRegop(Instruction &inst, const char *reg_name, unsigned size);
|
||||
void AddDestRegop(Instruction &inst, const char *reg_name, unsigned size);
|
||||
void AddImmop(Instruction &inst, uint64_t imm,
|
||||
unsigned size, bool is_signed);
|
||||
void AddImmop(Instruction &inst, uint64_t imm, unsigned size, bool is_signed);
|
||||
|
||||
} // namespace sparc
|
||||
|
||||
|
||||
+497
-739
File diff suppressed because it is too large
Load Diff
@@ -24,111 +24,111 @@
|
||||
#include "remill/Arch/SPARC32/Runtime/State.h"
|
||||
#include "remill/Arch/SPARC32/Runtime/Types.h"
|
||||
|
||||
#define REG_PC state.pc.aword
|
||||
#define REG_NPC state.next_pc.aword
|
||||
#define REG_SP state.gpr.o6.aword
|
||||
#define REG_FP state.gpr.i6.aword
|
||||
#define REG_PC state.pc.aword
|
||||
#define REG_NPC state.next_pc.aword
|
||||
#define REG_SP state.gpr.o6.aword
|
||||
#define REG_FP state.gpr.i6.aword
|
||||
|
||||
#define REG_G0 state.gpr.g0.aword
|
||||
#define REG_G1 state.gpr.g1.aword
|
||||
#define REG_G7 state.gpr.g7.aword // Thread local pointer
|
||||
#define REG_G0 state.gpr.g0.aword
|
||||
#define REG_G1 state.gpr.g1.aword
|
||||
#define REG_G7 state.gpr.g7.aword // Thread local pointer
|
||||
|
||||
#define REG_L0 state.gpr.l0.aword
|
||||
#define REG_L1 state.gpr.l1.aword
|
||||
#define REG_L2 state.gpr.l2.aword
|
||||
#define REG_L3 state.gpr.l3.aword
|
||||
#define REG_L4 state.gpr.l4.aword
|
||||
#define REG_L5 state.gpr.l5.aword
|
||||
#define REG_L6 state.gpr.l6.aword
|
||||
#define REG_L7 state.gpr.l7.aword
|
||||
#define REG_L0 state.gpr.l0.aword
|
||||
#define REG_L1 state.gpr.l1.aword
|
||||
#define REG_L2 state.gpr.l2.aword
|
||||
#define REG_L3 state.gpr.l3.aword
|
||||
#define REG_L4 state.gpr.l4.aword
|
||||
#define REG_L5 state.gpr.l5.aword
|
||||
#define REG_L6 state.gpr.l6.aword
|
||||
#define REG_L7 state.gpr.l7.aword
|
||||
|
||||
#define REG_I0 state.gpr.i0.aword
|
||||
#define REG_I1 state.gpr.i1.aword
|
||||
#define REG_I2 state.gpr.i2.aword
|
||||
#define REG_I3 state.gpr.i3.aword
|
||||
#define REG_I4 state.gpr.i4.aword
|
||||
#define REG_I5 state.gpr.i5.aword
|
||||
#define REG_I6 state.gpr.i6.aword
|
||||
#define REG_I7 state.gpr.i7.aword
|
||||
#define REG_I0 state.gpr.i0.aword
|
||||
#define REG_I1 state.gpr.i1.aword
|
||||
#define REG_I2 state.gpr.i2.aword
|
||||
#define REG_I3 state.gpr.i3.aword
|
||||
#define REG_I4 state.gpr.i4.aword
|
||||
#define REG_I5 state.gpr.i5.aword
|
||||
#define REG_I6 state.gpr.i6.aword
|
||||
#define REG_I7 state.gpr.i7.aword
|
||||
|
||||
#define REG_O0 state.gpr.o0.aword
|
||||
#define REG_O1 state.gpr.o1.aword
|
||||
#define REG_O2 state.gpr.o2.aword
|
||||
#define REG_O3 state.gpr.o3.aword
|
||||
#define REG_O4 state.gpr.o4.aword
|
||||
#define REG_O5 state.gpr.o5.aword
|
||||
#define REG_O6 state.gpr.o6.aword
|
||||
#define REG_O7 state.gpr.o7.aword
|
||||
#define REG_O0 state.gpr.o0.aword
|
||||
#define REG_O1 state.gpr.o1.aword
|
||||
#define REG_O2 state.gpr.o2.aword
|
||||
#define REG_O3 state.gpr.o3.aword
|
||||
#define REG_O4 state.gpr.o4.aword
|
||||
#define REG_O5 state.gpr.o5.aword
|
||||
#define REG_O6 state.gpr.o6.aword
|
||||
#define REG_O7 state.gpr.o7.aword
|
||||
|
||||
#define REG_F0 state.fpreg.v[0].floats.elems[0]
|
||||
#define REG_F1 state.fpreg.v[0].floats.elems[1]
|
||||
#define REG_F2 state.fpreg.v[0].floats.elems[2]
|
||||
#define REG_F3 state.fpreg.v[0].floats.elems[3]
|
||||
#define REG_F0 state.fpreg.v[0].floats.elems[0]
|
||||
#define REG_F1 state.fpreg.v[0].floats.elems[1]
|
||||
#define REG_F2 state.fpreg.v[0].floats.elems[2]
|
||||
#define REG_F3 state.fpreg.v[0].floats.elems[3]
|
||||
|
||||
#define REG_D0 state.fpreg.v[0].qwords.elems[0]
|
||||
#define REG_D0 state.fpreg.v[0].qwords.elems[0]
|
||||
|
||||
// GSR Register
|
||||
#define GSR_ALIGN state.asr.gsr.align
|
||||
#define GSR_MASK state.asr.gsr.mask
|
||||
#define GSR_ALIGN state.asr.gsr.align
|
||||
#define GSR_MASK state.asr.gsr.mask
|
||||
|
||||
#define REG_Y state.asr.yreg.aword
|
||||
#define REG_Y state.asr.yreg.aword
|
||||
|
||||
#define FLAG_ICC_CF state.asr.ccr.icc.c
|
||||
#define FLAG_ICC_VF state.asr.ccr.icc.v
|
||||
#define FLAG_ICC_ZF state.asr.ccr.icc.z
|
||||
#define FLAG_ICC_NF state.asr.ccr.icc.n
|
||||
#define FLAG_ICC_CF state.asr.ccr.icc.c
|
||||
#define FLAG_ICC_VF state.asr.ccr.icc.v
|
||||
#define FLAG_ICC_ZF state.asr.ccr.icc.z
|
||||
#define FLAG_ICC_NF state.asr.ccr.icc.n
|
||||
|
||||
#define FLAG_XCC_CF state.asr.ccr.xcc.c
|
||||
#define FLAG_XCC_VF state.asr.ccr.xcc.v
|
||||
#define FLAG_XCC_ZF state.asr.ccr.xcc.z
|
||||
#define FLAG_XCC_NF state.asr.ccr.xcc.n
|
||||
#define FLAG_XCC_CF state.asr.ccr.xcc.c
|
||||
#define FLAG_XCC_VF state.asr.ccr.xcc.v
|
||||
#define FLAG_XCC_ZF state.asr.ccr.xcc.z
|
||||
#define FLAG_XCC_NF state.asr.ccr.xcc.n
|
||||
|
||||
#define REG_ICC state.asr.ccr.icc.flat
|
||||
#define REG_XCC state.asr.ccr.xcc.flat
|
||||
#define REG_CCC state.csr.ccc
|
||||
#define REG_ICC state.asr.ccr.icc.flat
|
||||
#define REG_XCC state.asr.ccr.xcc.flat
|
||||
#define REG_CCC state.csr.ccc
|
||||
|
||||
#define FSR_FCC0 state.fsr.fcc0
|
||||
#define FSR_FCC1 state.fsr.fcc1
|
||||
#define FSR_FCC2 state.fsr.fcc2
|
||||
#define FSR_FCC3 state.fsr.fcc3
|
||||
#define FSR_FCC0 state.fsr.fcc0
|
||||
#define FSR_FCC1 state.fsr.fcc1
|
||||
#define FSR_FCC2 state.fsr.fcc2
|
||||
#define FSR_FCC3 state.fsr.fcc3
|
||||
|
||||
#define FSR_CEXC state.fsr.cexc
|
||||
#define FSR_FTT state.fsr.ftt
|
||||
#define FSR_RD state.fsr.rd
|
||||
#define FSR_CEXC state.fsr.cexc
|
||||
#define FSR_FTT state.fsr.ftt
|
||||
#define FSR_RD state.fsr.rd
|
||||
|
||||
#define PSR_TPC state.psr.tpc
|
||||
#define PSR_TNPC state.psr.tnpc
|
||||
#define PSR_TSTATE state.psr.tstate
|
||||
#define PSR_TT state.psr.tt
|
||||
#define PSR_TBA state.psr.tba
|
||||
#define PSR_PSTATE state.psr.pstate
|
||||
#define PSR_TL state.psr.tl
|
||||
#define PSR_PIL state.psr.pil
|
||||
#define PSR_WSTATE state.psr.wstate
|
||||
#define PSR_CWP state.psr.cwp
|
||||
#define PSR_CANSAVE state.psr.cansave
|
||||
#define PSR_CANRESTORE state.psr.canrestore
|
||||
#define PSR_CLEANWIN state.psr.cleanwin
|
||||
#define PSR_OTHERWIN state.psr.otherwin
|
||||
#define PSR_GL state.psr.gl
|
||||
#define PSR_TPC state.psr.tpc
|
||||
#define PSR_TNPC state.psr.tnpc
|
||||
#define PSR_TSTATE state.psr.tstate
|
||||
#define PSR_TT state.psr.tt
|
||||
#define PSR_TBA state.psr.tba
|
||||
#define PSR_PSTATE state.psr.pstate
|
||||
#define PSR_TL state.psr.tl
|
||||
#define PSR_PIL state.psr.pil
|
||||
#define PSR_WSTATE state.psr.wstate
|
||||
#define PSR_CWP state.psr.cwp
|
||||
#define PSR_CANSAVE state.psr.cansave
|
||||
#define PSR_CANRESTORE state.psr.canrestore
|
||||
#define PSR_CLEANWIN state.psr.cleanwin
|
||||
#define PSR_OTHERWIN state.psr.otherwin
|
||||
#define PSR_GL state.psr.gl
|
||||
|
||||
#define ASR_Y state.asr.yreg.dword
|
||||
#define ASR_ASI state.asr.asi_flat
|
||||
#define ASR_PC state.pc.aword
|
||||
#define ASR_FPRS state.asr.fprs_flat
|
||||
#define ASR_GSR state.asr.gsr.flat
|
||||
#define ASR_SOFTINT state.asr.softint
|
||||
#define ASR_STICK_CMPR state.asr.stick_cmpr
|
||||
#define ASR_PAUSE state.asr.pause
|
||||
#define ASR_Y state.asr.yreg.dword
|
||||
#define ASR_ASI state.asr.asi_flat
|
||||
#define ASR_PC state.pc.aword
|
||||
#define ASR_FPRS state.asr.fprs_flat
|
||||
#define ASR_GSR state.asr.gsr.flat
|
||||
#define ASR_SOFTINT state.asr.softint
|
||||
#define ASR_STICK_CMPR state.asr.stick_cmpr
|
||||
#define ASR_PAUSE state.asr.pause
|
||||
|
||||
#define HYPER_CALL state.hyper_call
|
||||
#define INTERRUPT_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL state.hyper_call
|
||||
#define INTERRUPT_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL_VECTOR state.hyper_call_vector
|
||||
|
||||
#if ADDRESS_SIZE_BITS == 64
|
||||
# define SPARC_STACKBIAS 0
|
||||
# define SPARC_STACKBIAS 0
|
||||
#else
|
||||
# define SPARC_STACKBIAS 0
|
||||
# define SPARC_STACKBIAS 0
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -136,7 +136,9 @@ namespace {
|
||||
// Takes the place of an unsupported instruction.
|
||||
DEF_SEM(HandleUnsupported) {
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction, kSPARC64EmulateInstruction));
|
||||
state, memory,
|
||||
SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction,
|
||||
kSPARC64EmulateInstruction));
|
||||
}
|
||||
|
||||
// Takes the place of an invalid instruction.
|
||||
@@ -145,12 +147,13 @@ DEF_SEM(HandleInvalidInstruction) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_HELPER(SAVE_WINDOW, RegisterWindow *window, RegisterWindow *&prev_window) -> void {
|
||||
DEF_HELPER(SAVE_WINDOW, RegisterWindow *window, RegisterWindow *&prev_window)
|
||||
->void {
|
||||
|
||||
// TODO(pag): These two lines should be uncommented for correctness, but then
|
||||
// they don't result in as nice bitcode in McSema :-(
|
||||
// window->prev_window = state.window;
|
||||
// state.window = window;
|
||||
// TODO(pag): These two lines should be uncommented for correctness, but then
|
||||
// they don't result in as nice bitcode in McSema :-(
|
||||
// window->prev_window = state.window;
|
||||
// state.window = window;
|
||||
|
||||
prev_window = window;
|
||||
|
||||
@@ -183,18 +186,18 @@ DEF_HELPER(SAVE_WINDOW, RegisterWindow *window, RegisterWindow *&prev_window) ->
|
||||
Write(REG_I7, REG_O7);
|
||||
}
|
||||
|
||||
DEF_HELPER(RESTORE_WINDOW, RegisterWindow *&prev_window) -> void {
|
||||
DEF_HELPER(RESTORE_WINDOW, RegisterWindow *&prev_window)->void {
|
||||
|
||||
const auto window = prev_window ? prev_window : state.window;
|
||||
if (!window) {
|
||||
memory = __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCWindowUnderflow);
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCWindowUnderflow);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(pag): This next line should be uncommented for correctness, but then
|
||||
// it means not as nice bitcode for mcsema.
|
||||
// state.window = window->prev_window;
|
||||
// TODO(pag): This next line should be uncommented for correctness, but then
|
||||
// it means not as nice bitcode for mcsema.
|
||||
// state.window = window->prev_window;
|
||||
|
||||
// Move input register to output
|
||||
Write(REG_O0, REG_I0);
|
||||
@@ -233,6 +236,7 @@ DEF_ISEL(INVALID_INSTRUCTION) = HandleInvalidInstruction;
|
||||
|
||||
#include "lib/Arch/SPARC32/Semantics/FLAGS.cpp"
|
||||
#include "lib/Arch/SPARC32/Semantics/COND.cpp"
|
||||
|
||||
#include "lib/Arch/SPARC32/Semantics/BINARY.cpp"
|
||||
#include "lib/Arch/SPARC32/Semantics/BRANCH.cpp"
|
||||
#include "lib/Arch/SPARC32/Semantics/DATAXFER.cpp"
|
||||
@@ -241,4 +245,3 @@ DEF_ISEL(INVALID_INSTRUCTION) = HandleInvalidInstruction;
|
||||
#include "lib/Arch/SPARC32/Semantics/MISC.cpp"
|
||||
#include "lib/Arch/SPARC32/Semantics/TRAP.cpp"
|
||||
#include "lib/Arch/SPARC32/Semantics/WINDOW.cpp"
|
||||
|
||||
|
||||
@@ -18,35 +18,39 @@ ALWAYS_INLINE static void WriteFlagsAddSub(State &state, T lhs, T rhs, T res) {
|
||||
}
|
||||
|
||||
template <typename Tag, typename T>
|
||||
ALWAYS_INLINE static void WriteXCCFlagsIncDec(State &state, T lhs, T rhs, T res) {
|
||||
ALWAYS_INLINE static void WriteXCCFlagsIncDec(State &state, T lhs, T rhs,
|
||||
T res) {
|
||||
FLAG_XCC_ZF = ZeroFlag(res);
|
||||
FLAG_XCC_NF = SignFlag(res);
|
||||
FLAG_XCC_VF = Overflow<Tag>::Flag(lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename Tag, typename T>
|
||||
ALWAYS_INLINE static void WriteICCFlagsIncDec(State &state, T lhs, T rhs, T res) {
|
||||
ALWAYS_INLINE static void WriteICCFlagsIncDec(State &state, T lhs, T rhs,
|
||||
T res) {
|
||||
FLAG_ICC_ZF = ZeroFlag(res);
|
||||
FLAG_ICC_NF = SignFlag(res);
|
||||
FLAG_ICC_VF = Overflow<Tag>::Flag(lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
ALWAYS_INLINE static void WriteICCFlagsAddSub(State &state, uint32_t lhs, uint32_t rhs, uint32_t res) {
|
||||
ALWAYS_INLINE static void WriteICCFlagsAddSub(State &state, uint32_t lhs,
|
||||
uint32_t rhs, uint32_t res) {
|
||||
FLAG_ICC_CF = Carry<Tag>::Flag(lhs, rhs, res);
|
||||
WriteICCFlagsIncDec<Tag>(state, lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
ALWAYS_INLINE static void WriteXCCFlagsAddSub(State &state, uint64_t lhs, uint64_t rhs, uint64_t res) {
|
||||
ALWAYS_INLINE static void WriteXCCFlagsAddSub(State &state, uint64_t lhs,
|
||||
uint64_t rhs, uint64_t res) {
|
||||
FLAG_XCC_CF = Carry<Tag>::Flag(lhs, rhs, res);
|
||||
WriteXCCFlagsIncDec<Tag>(state, lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(SUB, S1 src1, S2 src2, D dst) {
|
||||
Write(dst, USub(Read(src1), Read(src2)));
|
||||
return memory;
|
||||
Write(dst, USub(Read(src1), Read(src2)));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename S1, typename S2, typename D>
|
||||
@@ -200,8 +204,8 @@ DEF_SEM(SDIV, S1 src1, S2 src2, D dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto lhs_wide = ZExt(lhs);
|
||||
auto y = Read(REG_Y);
|
||||
auto y_lhs_wide = Signed(UOr(decltype(lhs_wide)(
|
||||
UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto y_lhs_wide = Signed(
|
||||
UOr(decltype(lhs_wide)(UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto rhs = Signed(Read(src2));
|
||||
auto rhs_wide = SExt(rhs);
|
||||
auto quot = SDiv(y_lhs_wide, rhs_wide);
|
||||
@@ -214,8 +218,8 @@ DEF_SEM(SDIVcc, S1 src1, S2 src2, D dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto lhs_wide = ZExt(lhs);
|
||||
auto y = Read(REG_Y);
|
||||
auto y_lhs_wide = Signed(UOr(decltype(lhs_wide)(
|
||||
UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto y_lhs_wide = Signed(
|
||||
UOr(decltype(lhs_wide)(UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto rhs = Read(src2);
|
||||
auto rhs_wide = SExt(rhs);
|
||||
auto quot = SDiv(y_lhs_wide, rhs_wide);
|
||||
@@ -287,7 +291,8 @@ DEF_SEM(MULSCC_R32, R32 src1, R32 src2, R32W dest) {
|
||||
auto lsb_y = UAnd(y, Literal<decltype(y)>(0x1));
|
||||
auto masked_rs1 = UAnd(rs1, Literal<decltype(rs1)>(0xffffffff));
|
||||
auto masked_rs2 = UAnd(rs2, Literal<decltype(rs2)>(0xffffffff));
|
||||
auto new_rs2 = Select(UCmpEq(lsb_y, 0), Literal<decltype(rs2)>(0), masked_rs2);
|
||||
auto new_rs2 =
|
||||
Select(UCmpEq(lsb_y, 0), Literal<decltype(rs2)>(0), masked_rs2);
|
||||
|
||||
auto flag_nf = Literal<uint32_t>(Read(FLAG_ICC_NF));
|
||||
auto flag_vf = Literal<uint32_t>(Read(FLAG_ICC_VF));
|
||||
@@ -296,21 +301,23 @@ DEF_SEM(MULSCC_R32, R32 src1, R32 src2, R32W dest) {
|
||||
auto new_rs1 = UOr(UShr(masked_rs1, Literal<decltype(masked_rs1)>(1)),
|
||||
Literal<decltype(masked_rs1)>(shifted_flag));
|
||||
auto res = UAdd(new_rs1, new_rs2);
|
||||
|
||||
// Y register is shifted right by one bit, with the LSB of the unshifted
|
||||
// r[rs1] replacing the MSB of Y
|
||||
auto shifted_y = UShr(y, Literal<decltype(y)>(1));
|
||||
auto lsb_rs1 = UAnd(rs1, Literal<decltype(rs1)>(0x1));
|
||||
auto new_y = UOr(shifted_y, decltype(y)(UShl(lsb_rs1, Literal<decltype(lsb_rs1)>(31))));
|
||||
auto lsb_rs1 = UAnd(rs1, Literal<decltype(rs1)>(0x1));
|
||||
auto new_y = UOr(shifted_y,
|
||||
decltype(y)(UShl(lsb_rs1, Literal<decltype(lsb_rs1)>(31))));
|
||||
Write(REG_Y, new_y);
|
||||
Write(dest, res);
|
||||
|
||||
|
||||
WriteICCFlagsAddSub<tag_add>(
|
||||
state, static_cast<uint32_t>(new_rs1),
|
||||
static_cast<uint32_t>(new_rs2), static_cast<uint32_t>(res));
|
||||
WriteICCFlagsAddSub<tag_add>(state, static_cast<uint32_t>(new_rs1),
|
||||
static_cast<uint32_t>(new_rs2),
|
||||
static_cast<uint32_t>(res));
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(MULScc) = MULSCC_R32;
|
||||
|
||||
@@ -7,8 +7,7 @@ namespace {
|
||||
// NOTE(pag): `new_pc == pc_of_jmp + 4`, and `new_npc`
|
||||
// is the target EA.
|
||||
template <typename T>
|
||||
DEF_SEM(JMPL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
T dst_pc, T dst_npc) {
|
||||
DEF_SEM(JMPL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst, T dst_pc, T dst_npc) {
|
||||
auto new_dst = Read(pc_of_jmp);
|
||||
auto new_dst_pc = Read(new_pc);
|
||||
auto new_dst_npc = Read(new_npc);
|
||||
@@ -20,8 +19,8 @@ DEF_SEM(JMPL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
|
||||
// This is a variation on JMPL that also stores the return address.
|
||||
template <typename T>
|
||||
DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
T dst_pc, T dst_npc, T return_pc_dst) {
|
||||
DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst, T dst_pc, T dst_npc,
|
||||
T return_pc_dst) {
|
||||
Write(dst, Read(pc_of_jmp));
|
||||
Write(dst_pc, Read(new_pc));
|
||||
Write(dst_npc, Read(new_npc));
|
||||
@@ -37,10 +36,10 @@ DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
// is placed inside of a delay slot.
|
||||
#define MAKE_BRANCH(name, cond, cc) \
|
||||
namespace { \
|
||||
DEF_SEM(name ## cond ## _ ## cc, R8W branch_taken, PC new_taken_pc, PC new_taken_npc, \
|
||||
PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
DEF_SEM(name##cond##_##cc, R8W branch_taken, PC new_taken_pc, \
|
||||
PC new_taken_npc, PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
R32W pc_dst, R32W npc_dst) { \
|
||||
if (Cond ## cond ## _ ## cc(state)) { \
|
||||
if (Cond##cond##_##cc(state)) { \
|
||||
Write(branch_taken, true); \
|
||||
Write(pc_dst, Read(new_taken_pc)); \
|
||||
Write(npc_dst, Read(new_taken_npc)); \
|
||||
@@ -52,35 +51,31 @@ DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## cond ## _ ## cc) = name ## cond ## _ ## cc;
|
||||
DEF_ISEL(name##cond##_##cc) = name##cond##_##cc;
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(BA, PC new_taken_pc, PC new_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(BA, PC new_taken_pc, PC new_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_taken_pc));
|
||||
Write(npc_dst, Read(new_taken_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(BN, PC new_not_taken_pc, PC new_not_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(BN, PC new_not_taken_pc, PC new_not_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_not_taken_pc));
|
||||
Write(npc_dst, Read(new_not_taken_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(FBA, PC new_taken_pc, PC new_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(FBA, PC new_taken_pc, PC new_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_taken_pc));
|
||||
Write(npc_dst, Read(new_taken_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(FBN, PC new_not_taken_pc, PC new_not_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(FBN, PC new_not_taken_pc, PC new_not_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_not_taken_pc));
|
||||
Write(npc_dst, Read(new_not_taken_npc));
|
||||
return memory;
|
||||
@@ -112,8 +107,8 @@ DEF_SEM(FBN, PC new_not_taken_pc, PC new_not_taken_npc,
|
||||
// 5 B*A(a=1) any cti 12,40,44,... (16 annulled)
|
||||
// 6 B*cc dcti 12,unpredictable
|
||||
DEF_SEM(UNSUPPORTED_DCTI) {
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCUnhandledDCTI);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCUnhandledDCTI);
|
||||
}
|
||||
|
||||
// TODO(pag): Double check that `new_pc` reads `rs1/rs2` from the pre-
|
||||
@@ -154,8 +149,8 @@ DEF_ISEL(FBN_fcc2) = FBN<R32W>;
|
||||
DEF_ISEL(FBN_fcc3) = FBN<R32W>;
|
||||
|
||||
#define MAKE_BRANCH_CC(name, cond) \
|
||||
MAKE_BRANCH(name, cond, icc) \
|
||||
MAKE_BRANCH(name, cond, xcc) \
|
||||
MAKE_BRANCH(name, cond, icc) \
|
||||
MAKE_BRANCH(name, cond, xcc)
|
||||
|
||||
MAKE_BRANCH_CC(B, NE)
|
||||
MAKE_BRANCH_CC(B, E)
|
||||
@@ -173,10 +168,10 @@ MAKE_BRANCH_CC(B, VC)
|
||||
MAKE_BRANCH_CC(B, VS)
|
||||
|
||||
#define MAKE_BRANCH_F(name, cond) \
|
||||
MAKE_BRANCH(name, cond, fcc0) \
|
||||
MAKE_BRANCH(name, cond, fcc1) \
|
||||
MAKE_BRANCH(name, cond, fcc2) \
|
||||
MAKE_BRANCH(name, cond, fcc3)
|
||||
MAKE_BRANCH(name, cond, fcc0) \
|
||||
MAKE_BRANCH(name, cond, fcc1) \
|
||||
MAKE_BRANCH(name, cond, fcc2) \
|
||||
MAKE_BRANCH(name, cond, fcc3)
|
||||
|
||||
MAKE_BRANCH_F(FB, U)
|
||||
MAKE_BRANCH_F(FB, G)
|
||||
@@ -202,22 +197,22 @@ MAKE_BRANCH_F(FB, O)
|
||||
|
||||
#define MAKE_BRANCH(name, cond) \
|
||||
namespace { \
|
||||
DEF_SEM(name ## cond, R8W branch_taken, PC new_taken_pc, PC new_taken_npc, \
|
||||
PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
R32W pc_dst, R32W npc_dst) { \
|
||||
if (Cond ## cond ## _ccc(state)) { \
|
||||
Write(branch_taken, true); \
|
||||
Write(pc_dst, Read(new_taken_pc)); \
|
||||
Write(npc_dst, Read(new_taken_npc)); \
|
||||
} else { \
|
||||
Write(branch_taken, false); \
|
||||
Write(pc_dst, Read(new_not_taken_pc)); \
|
||||
Write(npc_dst, Read(new_not_taken_npc)); \
|
||||
} \
|
||||
return memory; \
|
||||
DEF_SEM(name##cond, R8W branch_taken, PC new_taken_pc, PC new_taken_npc, \
|
||||
PC new_not_taken_pc, PC new_not_taken_npc, R32W pc_dst, \
|
||||
R32W npc_dst) { \
|
||||
if (Cond##cond##_ccc(state)) { \
|
||||
Write(branch_taken, true); \
|
||||
Write(pc_dst, Read(new_taken_pc)); \
|
||||
Write(npc_dst, Read(new_taken_npc)); \
|
||||
} else { \
|
||||
Write(branch_taken, false); \
|
||||
Write(pc_dst, Read(new_not_taken_pc)); \
|
||||
Write(npc_dst, Read(new_not_taken_npc)); \
|
||||
} \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## cond) = name ## cond;
|
||||
DEF_ISEL(name##cond) = name##cond;
|
||||
|
||||
MAKE_BRANCH(CB, A)
|
||||
MAKE_BRANCH(CB, N)
|
||||
|
||||
+166
-232
@@ -3,106 +3,90 @@
|
||||
*/
|
||||
|
||||
#define MAKE_CONDITIONS(cc) \
|
||||
static inline bool \
|
||||
CondA_ ## cc(const State &state) { \
|
||||
return true; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondN_ ## cc(const State &state) { \
|
||||
return false; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondE_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return flag_zf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondNE_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return !flag_zf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondG_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return (flag_nf == flag_vf) && !flag_zf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondLE_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return (flag_nf != flag_vf) || flag_zf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondGE_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return flag_nf == flag_vf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondL_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return flag_nf != flag_vf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondGU_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return !(flag_cf || flag_zf); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondLEU_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return flag_cf || flag_zf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondCS_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
return flag_cf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondCC_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
return !flag_cf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondPOS_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
return !flag_nf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondNEG_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
return flag_nf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondVS_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return flag_vf; \
|
||||
} \
|
||||
static inline bool \
|
||||
CondVC_ ## cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return !flag_vf; \
|
||||
}
|
||||
static inline bool CondA_##cc(const State &state) { \
|
||||
return true; \
|
||||
} \
|
||||
static inline bool CondN_##cc(const State &state) { \
|
||||
return false; \
|
||||
} \
|
||||
static inline bool CondE_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return flag_zf; \
|
||||
} \
|
||||
static inline bool CondNE_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return !flag_zf; \
|
||||
} \
|
||||
static inline bool CondG_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return (flag_nf == flag_vf) && !flag_zf; \
|
||||
} \
|
||||
static inline bool CondLE_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return (flag_nf != flag_vf) || flag_zf; \
|
||||
} \
|
||||
static inline bool CondGE_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return flag_nf == flag_vf; \
|
||||
} \
|
||||
static inline bool CondL_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return flag_nf != flag_vf; \
|
||||
} \
|
||||
static inline bool CondGU_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return !(flag_cf || flag_zf); \
|
||||
} \
|
||||
static inline bool CondLEU_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
const bool flag_zf = ccr.z; \
|
||||
return flag_cf || flag_zf; \
|
||||
} \
|
||||
static inline bool CondCS_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
return flag_cf; \
|
||||
} \
|
||||
static inline bool CondCC_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_cf = ccr.c; \
|
||||
return !flag_cf; \
|
||||
} \
|
||||
static inline bool CondPOS_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
return !flag_nf; \
|
||||
} \
|
||||
static inline bool CondNEG_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_nf = ccr.n; \
|
||||
return flag_nf; \
|
||||
} \
|
||||
static inline bool CondVS_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return flag_vf; \
|
||||
} \
|
||||
static inline bool CondVC_##cc(const State &state) { \
|
||||
const auto ccr = state.asr.ccr.cc; \
|
||||
const bool flag_vf = ccr.v; \
|
||||
return !flag_vf; \
|
||||
}
|
||||
|
||||
MAKE_CONDITIONS(xcc)
|
||||
MAKE_CONDITIONS(icc)
|
||||
@@ -111,206 +95,156 @@ MAKE_CONDITIONS(icc)
|
||||
|
||||
|
||||
#define MAKE_CONDITIONS(fcc) \
|
||||
static inline bool \
|
||||
CondU_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondG_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondUG_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x2); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondL_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondUL_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondLG_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x1);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondLGU_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x1);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondNE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x1);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondUE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x0);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondGE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x0);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondUGE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x0);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondLE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x1) \
|
||||
|| (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondULE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) \
|
||||
|| (state.fsr.fcc == 0x1) \
|
||||
|| (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool \
|
||||
CondGLE_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x1) \
|
||||
|| (state.fsr.fcc == 0x0);\
|
||||
} \
|
||||
static inline bool \
|
||||
CondO_ ## fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) \
|
||||
|| (state.fsr.fcc == 0x1) \
|
||||
|| (state.fsr.fcc == 0x0);\
|
||||
}
|
||||
static inline bool CondU_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3); \
|
||||
} \
|
||||
static inline bool CondG_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2); \
|
||||
} \
|
||||
static inline bool CondUG_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x2); \
|
||||
} \
|
||||
static inline bool CondL_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool CondUL_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool CondLG_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) || (state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool CondLGU_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x2) || \
|
||||
(state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool CondNE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x2) || \
|
||||
(state.fsr.fcc == 0x1); \
|
||||
} \
|
||||
static inline bool CondE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondUE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondGE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) || (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondUGE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x2) || \
|
||||
(state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondLE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x1) || (state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondULE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x3) || (state.fsr.fcc == 0x1) || \
|
||||
(state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondGLE_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) || (state.fsr.fcc == 0x1) || \
|
||||
(state.fsr.fcc == 0x0); \
|
||||
} \
|
||||
static inline bool CondO_##fcc(const State &state) { \
|
||||
return (state.fsr.fcc == 0x2) || (state.fsr.fcc == 0x1) || \
|
||||
(state.fsr.fcc == 0x0); \
|
||||
}
|
||||
|
||||
MAKE_CONDITIONS(fcc0)
|
||||
MAKE_CONDITIONS(fcc1)
|
||||
MAKE_CONDITIONS(fcc2)
|
||||
MAKE_CONDITIONS(fcc3)
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
CondRZ(const State &state, T cc) {
|
||||
template <typename T>
|
||||
static inline bool CondRZ(const State &state, T cc) {
|
||||
return cc == 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
CondRLEZ(const State &state, T cc) {
|
||||
template <typename T>
|
||||
static inline bool CondRLEZ(const State &state, T cc) {
|
||||
return Signed(cc) <= 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
CondRLZ(const State &state, T cc) {
|
||||
template <typename T>
|
||||
static inline bool CondRLZ(const State &state, T cc) {
|
||||
return Signed(cc) < 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
CondRNZ(const State &state, T cc) {
|
||||
template <typename T>
|
||||
static inline bool CondRNZ(const State &state, T cc) {
|
||||
return cc != 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
CondRGZ(const State &state, T cc) {
|
||||
template <typename T>
|
||||
static inline bool CondRGZ(const State &state, T cc) {
|
||||
return Signed(cc) > 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool
|
||||
CondRGEZ(const State &state, T cc) {
|
||||
template <typename T>
|
||||
static inline bool CondRGEZ(const State &state, T cc) {
|
||||
return Signed(cc) >= 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
CondA_ccc(const State &state) {
|
||||
static inline bool CondA_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1000;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
CondN_ccc(const State &state) {
|
||||
static inline bool CondN_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0000;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond3_ccc(const State &state) {
|
||||
static inline bool Cond3_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0111;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond2_ccc(const State &state) {
|
||||
static inline bool Cond2_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0110;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond23_ccc(const State &state) {
|
||||
static inline bool Cond23_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0101;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond1_ccc(const State &state) {
|
||||
static inline bool Cond1_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0100;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond13_ccc(const State &state) {
|
||||
static inline bool Cond13_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0011;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond12_ccc(const State &state) {
|
||||
static inline bool Cond12_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0010;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond123_ccc(const State &state) {
|
||||
static inline bool Cond123_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b0001;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond0_ccc(const State &state) {
|
||||
static inline bool Cond0_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1001;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond03_ccc(const State &state) {
|
||||
static inline bool Cond03_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1010;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond02_ccc(const State &state) {
|
||||
static inline bool Cond02_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1011;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond023_ccc(const State &state) {
|
||||
static inline bool Cond023_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1100;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond01_ccc(const State &state) {
|
||||
static inline bool Cond01_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1101;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond013_ccc(const State &state) {
|
||||
static inline bool Cond013_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1110;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Cond012_ccc(const State &state) {
|
||||
static inline bool Cond012_ccc(const State &state) {
|
||||
return state.csr.ccc == 0b1111;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ DEF_SEM(LDSTUB, M8W src_mem, R dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(CASA) = CASA;
|
||||
DEF_ISEL(CASAX) = CASAX;
|
||||
@@ -169,7 +169,7 @@ DEF_SEM(MOVN_xcc, S src, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(MOVA_icc) = MOVA_icc<R32, R32W>;
|
||||
DEF_ISEL(MOVA_xcc) = MOVA_xcc<R32, R32W>;
|
||||
@@ -179,22 +179,22 @@ DEF_ISEL(MOVN_xcc) = MOVN_xcc<R32, R32W>;
|
||||
|
||||
#define MAKE_SEMANTICS(name, cond, cc) \
|
||||
namespace { \
|
||||
template <typename S, typename D> \
|
||||
DEF_SEM(name ## cond ## _ ## cc, S src, D dst) { \
|
||||
auto new_value = Read(src); \
|
||||
auto old_value = Read(dst); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_value, \
|
||||
decltype(new_value)(old_value)); \
|
||||
WriteZExt(dst, value); \
|
||||
return memory; \
|
||||
} \
|
||||
template <typename S, typename D> \
|
||||
DEF_SEM(name##cond##_##cc, S src, D dst) { \
|
||||
auto new_value = Read(src); \
|
||||
auto old_value = Read(dst); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = \
|
||||
Select(branch_taken, new_value, decltype(new_value)(old_value)); \
|
||||
WriteZExt(dst, value); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_ISEL(MOV ## cond ## _ ## cc) = name ## cond ## _ ## cc<R32, R32W>;
|
||||
} \
|
||||
DEF_ISEL(MOV##cond##_##cc) = name##cond##_##cc<R32, R32W>;
|
||||
|
||||
#define MAKE_SEMANTICS_CC(name, cond) \
|
||||
MAKE_SEMANTICS(name, cond, icc) \
|
||||
MAKE_SEMANTICS(name, cond, xcc)
|
||||
MAKE_SEMANTICS(name, cond, icc) \
|
||||
MAKE_SEMANTICS(name, cond, xcc)
|
||||
|
||||
#define MAKE_SEMANTICS_FCC(name, cond) \
|
||||
MAKE_SEMANTICS(name, cond, fcc0) \
|
||||
@@ -235,7 +235,7 @@ MAKE_SEMANTICS_FCC(MOVF, LE)
|
||||
MAKE_SEMANTICS_FCC(MOVF, ULE)
|
||||
MAKE_SEMANTICS_FCC(MOVF, O)
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#undef MAKE_SEMANTICS
|
||||
#undef MAKE_SEMANTICS_CC
|
||||
@@ -244,17 +244,18 @@ MAKE_SEMANTICS_FCC(MOVF, O)
|
||||
#define MAKE_SEMANTICS(name, cond) \
|
||||
namespace { \
|
||||
template <typename C, typename S, typename D> \
|
||||
DEF_SEM(name ## cond, C reg_cc, S src, D dst) { \
|
||||
auto new_value = Read(src); \
|
||||
auto old_value = Read(dst); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_value, decltype(new_value)(old_value)); \
|
||||
WriteZExt(dst, value); \
|
||||
return memory; \
|
||||
DEF_SEM(name##cond, C reg_cc, S src, D dst) { \
|
||||
auto new_value = Read(src); \
|
||||
auto old_value = Read(dst); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = \
|
||||
Select(cond_taken, new_value, decltype(new_value)(old_value)); \
|
||||
WriteZExt(dst, value); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(MOVR ## cond) = name ## cond<R32, R32, R32W>;
|
||||
DEF_ISEL(MOVR##cond) = name##cond<R32, R32, R32W>;
|
||||
|
||||
MAKE_SEMANTICS(MOVR, Z)
|
||||
MAKE_SEMANTICS(MOVR, LEZ)
|
||||
@@ -303,7 +304,7 @@ DEF_SEM(FMoveNeverQuad, V64 src, V64W dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FMOVSA_icc) = FMoveAlwaysSingle;
|
||||
DEF_ISEL(FMOVSA_xcc) = FMoveAlwaysSingle;
|
||||
@@ -350,43 +351,40 @@ DEF_ISEL(FMOVQN_fcc3) = FMoveNeverQuad;
|
||||
|
||||
#define MAKE_SEMANTICS(name, cond, cc) \
|
||||
namespace { \
|
||||
DEF_SEM(FMOVS ## cond ## _ ## cc, V32 src, V32W dst) { \
|
||||
DEF_SEM(FMOVS##cond##_##cc, V32 src, V32W dst) { \
|
||||
auto new_val = FExtractV32(FReadV32(src), 0); \
|
||||
auto old_val = FExtractV32(FReadV32(dst), 0); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = Select(branch_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV32(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(FMOVD ## cond ## _ ## cc, V64 src, V64W dst) { \
|
||||
DEF_SEM(FMOVD##cond##_##cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = Select(branch_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(FMOVQ ## cond ## _ ## cc, V64 src, V64W dst) { \
|
||||
DEF_SEM(FMOVQ##cond##_##cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = Select(branch_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(FMOVS ## cond ## _ ## cc) = FMOVS ## cond ## _ ## cc; \
|
||||
DEF_ISEL(FMOVD ## cond ## _ ## cc) = FMOVD ## cond ## _ ## cc; \
|
||||
DEF_ISEL(FMOVQ ## cond ## _ ## cc) = FMOVQ ## cond ## _ ## cc;
|
||||
DEF_ISEL(FMOVS##cond##_##cc) = FMOVS##cond##_##cc; \
|
||||
DEF_ISEL(FMOVD##cond##_##cc) = FMOVD##cond##_##cc; \
|
||||
DEF_ISEL(FMOVQ##cond##_##cc) = FMOVQ##cond##_##cc;
|
||||
|
||||
#define MAKE_SEMANTICS_CC(name, cond) \
|
||||
MAKE_SEMANTICS(name, cond, icc) \
|
||||
@@ -434,49 +432,43 @@ MAKE_SEMANTICS_FCC(FMOV, O)
|
||||
|
||||
#define MAKE_SEMANTICS(name, cond) \
|
||||
namespace { \
|
||||
DEF_SEM(name ## S ## cond, R32 reg_cc, V32 src, V32W dst) { \
|
||||
DEF_SEM(name##S##cond, R32 reg_cc, V32 src, V32W dst) { \
|
||||
auto new_val = FExtractV32(FReadV32(src), 0); \
|
||||
auto old_val = FExtractV32(FReadV32(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, \
|
||||
new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV32(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(name ## D ## cond, R32 reg_cc, V64 src, V64W dst) { \
|
||||
DEF_SEM(name##D##cond, R32 reg_cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, \
|
||||
new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(name ## Q ## cond, R32 reg_cc, V64 src, V64W dst) { \
|
||||
DEF_SEM(name##Q##cond, R32 reg_cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, \
|
||||
new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## S ## cond) = name ## S ## cond; \
|
||||
DEF_ISEL(name ## D ## cond) = name ## D ## cond; \
|
||||
DEF_ISEL(name ## Q ## cond) = name ## Q ## cond;
|
||||
DEF_ISEL(name##S##cond) = name##S##cond; \
|
||||
DEF_ISEL(name##D##cond) = name##D##cond; \
|
||||
DEF_ISEL(name##Q##cond) = name##Q##cond;
|
||||
|
||||
MAKE_SEMANTICS(FMOVR, Z)
|
||||
MAKE_SEMANTICS(FMOVR, LEZ)
|
||||
|
||||
@@ -20,38 +20,35 @@ namespace {
|
||||
|
||||
// Zero flags, tells us whether or not a value is zero.
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool ZeroFlag(T res) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool ZeroFlag(T res) {
|
||||
return T(0) == res;
|
||||
}
|
||||
|
||||
// Zero flags, tells us whether or not a value is zero.
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool NotZeroFlag(T res) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool NotZeroFlag(T res) {
|
||||
return T(0) != res;
|
||||
}
|
||||
|
||||
// Sign flag, tells us if a result is signed or unsigned.
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool SignFlag(T res) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool SignFlag(T res) {
|
||||
return 0 > Signed(res);
|
||||
}
|
||||
|
||||
// Tests whether there is an even number of bits in the low order byte.
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool ParityFlag(uint8_t r0) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool ParityFlag(uint8_t r0) {
|
||||
return !__builtin_parity(static_cast<unsigned>(r0));
|
||||
// auto r1 = r0 >> 1_u8;
|
||||
// auto r2 = r1 >> 1_u8;
|
||||
// auto r3 = r2 >> 1_u8;
|
||||
// auto r4 = r3 >> 1_u8;
|
||||
// auto r5 = r4 >> 1_u8;
|
||||
// auto r6 = r5 >> 1_u8;
|
||||
// auto r7 = r6 >> 1_u8;
|
||||
//
|
||||
// return !(1 & (r0 ^ r1 ^ r2 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7));
|
||||
|
||||
// auto r1 = r0 >> 1_u8;
|
||||
// auto r2 = r1 >> 1_u8;
|
||||
// auto r3 = r2 >> 1_u8;
|
||||
// auto r4 = r3 >> 1_u8;
|
||||
// auto r5 = r4 >> 1_u8;
|
||||
// auto r6 = r5 >> 1_u8;
|
||||
// auto r7 = r6 >> 1_u8;
|
||||
//
|
||||
// return !(1 & (r0 ^ r1 ^ r2 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7));
|
||||
}
|
||||
|
||||
struct tag_add {};
|
||||
@@ -68,13 +65,10 @@ struct Overflow;
|
||||
template <>
|
||||
struct Overflow<tag_add> {
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Overflow::Flag` for addition.");
|
||||
enum {
|
||||
kSignShift = sizeof(T) * 8 - 1
|
||||
};
|
||||
enum { kSignShift = sizeof(T) * 8 - 1 };
|
||||
|
||||
// Overflow occurs on addition if both operands have the same sign and
|
||||
// the sign of the sum is different.
|
||||
@@ -90,14 +84,11 @@ struct Overflow<tag_add> {
|
||||
template <>
|
||||
struct Overflow<tag_sub> {
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Overflow::Flag` for "
|
||||
"subtraction.");
|
||||
enum {
|
||||
kSignShift = sizeof(T) * 8 - 1
|
||||
};
|
||||
enum { kSignShift = sizeof(T) * 8 - 1 };
|
||||
|
||||
// Overflow occurs on subtraction if the operands have different signs and
|
||||
// the sign of the difference differs from the sign of r[rs1].
|
||||
@@ -116,10 +107,9 @@ struct Overflow<tag_mul> {
|
||||
// Integer multiplication overflow check, where result is twice the width of
|
||||
// the operands.
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(
|
||||
T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R),int>::type=0) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
return static_cast<R>(static_cast<T>(res)) != res;
|
||||
}
|
||||
@@ -127,10 +117,9 @@ struct Overflow<tag_mul> {
|
||||
// Signed integer multiplication overflow check, where the result is
|
||||
// truncated to the size of the operands.
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(
|
||||
T lhs, T rhs, T,
|
||||
typename std::enable_if<std::is_signed<T>::value,int>::type=0) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T lhs, T rhs, T,
|
||||
typename std::enable_if<std::is_signed<T>::value, int>::type = 0) {
|
||||
auto lhs_wide = SExt(lhs);
|
||||
auto rhs_wide = SExt(rhs);
|
||||
return Flag<T, decltype(lhs_wide)>(lhs, rhs, lhs_wide * rhs_wide);
|
||||
@@ -141,31 +130,23 @@ struct Overflow<tag_mul> {
|
||||
template <>
|
||||
struct Overflow<tag_sdiv> {
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(
|
||||
T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R),int>::type=0) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
enum {
|
||||
kSignShift = sizeof(T) * 8 - 1
|
||||
};
|
||||
enum { kSignShift = sizeof(T) * 8 - 1 };
|
||||
|
||||
return (SExt(res << kSignShift) > 0) || (SExt(res << kSignShift) < -1);
|
||||
}
|
||||
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static R Value(
|
||||
T lhs, T rhs, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R),int>::type=0) {
|
||||
[[gnu::const]] ALWAYS_INLINE static R
|
||||
Value(T lhs, T rhs, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
enum {
|
||||
kSignShift = sizeof(T) * 8 - 1
|
||||
};
|
||||
enum { kSignShift = sizeof(T) * 8 - 1 };
|
||||
|
||||
enum:R {
|
||||
kValueMax = static_cast<R>(1) << sizeof(T) * 8
|
||||
};
|
||||
enum : R { kValueMax = static_cast<R>(1) << sizeof(T) * 8 };
|
||||
|
||||
if (SExt(res << kSignShift) > 0) {
|
||||
return kValueMax - 1;
|
||||
@@ -175,37 +156,28 @@ struct Overflow<tag_sdiv> {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Overflow<tag_udiv> {
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(
|
||||
T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R),int>::type=0) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool
|
||||
Flag(T, T, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
enum {
|
||||
kShift = sizeof(T) * 8
|
||||
};
|
||||
enum { kShift = sizeof(T) * 8 };
|
||||
|
||||
return (SExt(res << kShift) > 0);
|
||||
}
|
||||
|
||||
template <typename T, typename R>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static R Value(
|
||||
T lhs, T rhs, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R),int>::type=0) {
|
||||
|
||||
enum {
|
||||
kShift = sizeof(T) * 8
|
||||
};
|
||||
[[gnu::const]] ALWAYS_INLINE static R
|
||||
Value(T lhs, T rhs, R res,
|
||||
typename std::enable_if<sizeof(T) < sizeof(R), int>::type = 0) {
|
||||
|
||||
enum:R {
|
||||
kValueMax = static_cast<R>(1) << sizeof(T) * 8
|
||||
};
|
||||
enum { kShift = sizeof(T) * 8 };
|
||||
|
||||
enum : R { kValueMax = static_cast<R>(1) << sizeof(T) * 8 };
|
||||
|
||||
if (SExt(res << kShift) > 0) {
|
||||
return kValueMax - 1;
|
||||
@@ -223,8 +195,7 @@ struct Carry;
|
||||
template <>
|
||||
struct Carry<tag_add> {
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T res) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Carry::Flag` for addition.");
|
||||
return res < lhs || res < rhs;
|
||||
@@ -235,8 +206,7 @@ struct Carry<tag_add> {
|
||||
template <>
|
||||
struct Carry<tag_sub> {
|
||||
template <typename T>
|
||||
[[gnu::const]]
|
||||
ALWAYS_INLINE static bool Flag(T lhs, T rhs, T) {
|
||||
[[gnu::const]] ALWAYS_INLINE static bool Flag(T lhs, T rhs, T) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Invalid specialization of `Carry::Flag` for addition.");
|
||||
return lhs < rhs;
|
||||
@@ -248,6 +218,4 @@ ALWAYS_INLINE void SetFPSRStatusFlags(State &state, int mask) {
|
||||
state.fsr.cexc = static_cast<uint8_t>(mask & FE_ALL_EXCEPT);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -8,15 +8,15 @@ namespace {
|
||||
DEF_SEM(FADDS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sum = FAdd(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sum);
|
||||
return memory;
|
||||
@@ -25,15 +25,15 @@ DEF_SEM(FADDS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FADDD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sum = FAdd64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sum);
|
||||
return memory;
|
||||
@@ -42,14 +42,15 @@ DEF_SEM(FADDD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
DEF_SEM(FSUBS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sub = FSub32(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sub);
|
||||
return memory;
|
||||
@@ -58,14 +59,15 @@ DEF_SEM(FSUBS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FSUBD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sub = FSub64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sub);
|
||||
return memory;
|
||||
@@ -74,15 +76,15 @@ DEF_SEM(FSUBD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
DEF_SEM(FMULS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul32(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
@@ -91,15 +93,15 @@ DEF_SEM(FMULS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FMULD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
@@ -108,14 +110,14 @@ DEF_SEM(FMULD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
DEF_SEM(FDIVS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the fp exception and prevent recording
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto div = FDiv32(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, div);
|
||||
return memory;
|
||||
@@ -124,52 +126,52 @@ DEF_SEM(FDIVS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FDIVD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the fp exception and prevent recording
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto div = FDiv64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, div);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(FsMULD, RF32 src1, RF32 src2, RF64W dst) {
|
||||
DEF_SEM(FsMULD, RF32 src1, RF32 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the fp exception and prevent recording
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(FdMULQ, RF64 src1, RF64 src2, RF64W dst) {
|
||||
DEF_SEM(FdMULQ, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the fp exception and prevent recording
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FADDS) = FADDS;
|
||||
DEF_ISEL(FADDD) = FADDD;
|
||||
@@ -333,7 +335,7 @@ DEF_SEM(FSTOX, RF32 src, R64W dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FMOVS) = FMOVS;
|
||||
DEF_ISEL(FMOVD) = FMOVD;
|
||||
@@ -374,24 +376,24 @@ DEF_ISEL(FXTOQ) = FXTOQ;
|
||||
|
||||
|
||||
#define MAKE_COMPARE(fcc) \
|
||||
template <typename S> \
|
||||
void FCompare_ ## fcc(State &state, Memory *memory, \
|
||||
S val1, S val2, bool signal) { \
|
||||
if (std::isnan(val1) || std::isnan(val2)) { \
|
||||
Write(state.fsr.fcc, Literal<R8>(3)); \
|
||||
} else { \
|
||||
if (FCmpEq(val1, val2)) { \
|
||||
/* result = '00'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(0)); \
|
||||
} else if (FCmpLt(val1, val2)) { \
|
||||
/* result = '01'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(1)); \
|
||||
} else { /* FCmpGt(val1, val2) */ \
|
||||
/* result = '10'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(2)); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
template <typename S> \
|
||||
void FCompare_##fcc(State &state, Memory *memory, S val1, S val2, \
|
||||
bool signal) { \
|
||||
if (std::isnan(val1) || std::isnan(val2)) { \
|
||||
Write(state.fsr.fcc, Literal<R8>(3)); \
|
||||
} else { \
|
||||
if (FCmpEq(val1, val2)) { \
|
||||
/* result = '00'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(0)); \
|
||||
} else if (FCmpLt(val1, val2)) { \
|
||||
/* result = '01'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(1)); \
|
||||
} else { /* FCmpGt(val1, val2) */ \
|
||||
/* result = '10'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(2)); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
@@ -401,53 +403,53 @@ MAKE_COMPARE(fcc1)
|
||||
MAKE_COMPARE(fcc2)
|
||||
MAKE_COMPARE(fcc3)
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#undef MAKE_COMPARE
|
||||
|
||||
#define MAKE_SEMANTICS_FCMP(fcc) \
|
||||
DEF_SEM(FCMPS_ ## fcc, RF32 src1, RF32 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPD_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
DEF_SEM(FCMPS_##fcc, RF32 src1, RF32 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPQ_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPD_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
}
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPQ_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
}
|
||||
|
||||
#define MAKE_SEMANTICS_FCMPE(fcc) \
|
||||
DEF_SEM(FCMPES_ ## fcc, RF32 src1, RF32 src2) { \
|
||||
DEF_SEM(FCMPES_##fcc, RF32 src1, RF32 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPED_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPED_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPEQ_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPEQ_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
}
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -461,7 +463,7 @@ MAKE_SEMANTICS_FCMPE(fcc1)
|
||||
MAKE_SEMANTICS_FCMPE(fcc2)
|
||||
MAKE_SEMANTICS_FCMPE(fcc3)
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#undef MAKE_SEMANTICS_FCMP
|
||||
#undef MAKE_SEMANTICS_FCMPE
|
||||
|
||||
@@ -14,15 +14,15 @@ DEF_ISEL(NOP) = NOP;
|
||||
|
||||
#define MAKE_SEMANTICS_WR(op) \
|
||||
namespace { \
|
||||
DEF_SEM(WR ## op, R32 src1, I32 src2) { \
|
||||
auto lhs = Read(src1); \
|
||||
auto rhs = Read(src2); \
|
||||
auto res = UXor(lhs, rhs); \
|
||||
WriteZExt(ASR_ ## op, res); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(WR##op, R32 src1, I32 src2) { \
|
||||
auto lhs = Read(src1); \
|
||||
auto rhs = Read(src2); \
|
||||
auto res = UXor(lhs, rhs); \
|
||||
WriteZExt(ASR_##op, res); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_ISEL(WR ## op) = WR ## op;
|
||||
} \
|
||||
DEF_ISEL(WR##op) = WR##op;
|
||||
|
||||
MAKE_SEMANTICS_WR(Y)
|
||||
MAKE_SEMANTICS_WR(PAUSE)
|
||||
@@ -34,13 +34,13 @@ MAKE_SEMANTICS_WR(ASI)
|
||||
|
||||
#define MAKE_SEMANTICS_RD(op) \
|
||||
namespace { \
|
||||
DEF_SEM(RD ## op, R32W dst) { \
|
||||
auto asr = Read(ASR_ ## op); \
|
||||
Write(dst, asr); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(RD##op, R32W dst) { \
|
||||
auto asr = Read(ASR_##op); \
|
||||
Write(dst, asr); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_ISEL(RD ## op) = RD ## op;
|
||||
} \
|
||||
DEF_ISEL(RD##op) = RD##op;
|
||||
|
||||
MAKE_SEMANTICS_RD(Y)
|
||||
MAKE_SEMANTICS_RD(ASI)
|
||||
@@ -52,16 +52,19 @@ namespace {
|
||||
DEF_SEM(IMPDEP1, I32 opf) {
|
||||
HYPER_CALL_VECTOR = Literal<decltype(state.hyper_call_vector)>(Read(opf));
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction, kSPARC64EmulateInstruction));
|
||||
state, memory,
|
||||
SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction,
|
||||
kSPARC64EmulateInstruction));
|
||||
}
|
||||
|
||||
DEF_SEM(IMPDEP2, I32 opf) {
|
||||
HYPER_CALL_VECTOR = Literal<decltype(state.hyper_call_vector)>(Read(opf));
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction, kSPARC64EmulateInstruction));
|
||||
state, memory,
|
||||
SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction,
|
||||
kSPARC64EmulateInstruction));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
DEF_ISEL(IMPDEP1) = IMPDEP1;
|
||||
DEF_ISEL(IMPDEP2) = IMPDEP2;
|
||||
|
||||
|
||||
@@ -8,47 +8,52 @@
|
||||
// is placed inside of a delay slot.
|
||||
#define MAKE_TRAP(cond, cc) \
|
||||
namespace { \
|
||||
DEF_SEM(T ## cond, R8W branch_taken, PC new_pc, PC new_npc, \
|
||||
I32 vec_a, I32 vec_b, R32W pc_dst, R32W npc_dst) { \
|
||||
Write(branch_taken, Cond ## cond ## _ ## cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond ## cond; \
|
||||
DEF_SEM(T##cond, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, \
|
||||
I32 vec_b, R32W pc_dst, R32W npc_dst) { \
|
||||
Write(branch_taken, Cond##cond##_##cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond##cond; \
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(T ## cond ## _sync, R8W branch_taken, PC new_pc, PC new_npc, \
|
||||
I32 vec_a, I32 vec_b, R32W pc_dst, R32W npc_dst) { \
|
||||
Write(branch_taken, Cond ## cond ## _ ## cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond ## cond; \
|
||||
DEF_SEM(T##cond##_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, \
|
||||
I32 vec_b, R32W pc_dst, R32W npc_dst) { \
|
||||
Write(branch_taken, Cond##cond##_##cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond##cond; \
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu); \
|
||||
return __remill_sync_hyper_call( \
|
||||
state, memory, SyncHyperCall::kSPARCTrapCond ## cond); \
|
||||
return __remill_sync_hyper_call(state, memory, \
|
||||
SyncHyperCall::kSPARCTrapCond##cond); \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(T ## cond) = T ## cond; \
|
||||
DEF_ISEL(T ## cond ## _sync) = T ## cond ## _sync
|
||||
DEF_ISEL(T##cond) = T##cond; \
|
||||
DEF_ISEL(T##cond##_sync) = T##cond##_sync
|
||||
|
||||
namespace {
|
||||
|
||||
DEF_SEM(TA, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R32W pc_dst, R32W npc_dst) {
|
||||
DEF_SEM(TA, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R32W pc_dst, R32W npc_dst) {
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCondA;
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu);
|
||||
Write(branch_taken, true);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(TA_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R32W pc_dst, R32W npc_dst) {
|
||||
DEF_SEM(TA_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R32W pc_dst, R32W npc_dst) {
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCondA;
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu);
|
||||
return __remill_sync_hyper_call(state, memory, SyncHyperCall::kSPARCTrapCondA);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCTrapCondA);
|
||||
}
|
||||
|
||||
DEF_SEM(TN, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R32W pc_dst, R32W npc_dst) {
|
||||
DEF_SEM(TN, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R32W pc_dst, R32W npc_dst) {
|
||||
Write(pc_dst, Read(new_pc));
|
||||
Write(npc_dst, Read(new_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(TN_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) {
|
||||
DEF_SEM(TN_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R64W pc_dst, R64W npc_dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -79,6 +84,7 @@ MAKE_TRAP(VS, icc);
|
||||
namespace {
|
||||
|
||||
DEF_SEM(UNIMP_SYNC, I32 struct_size) {
|
||||
|
||||
// TODO(pag): See if callees inspect the struct size when this is after the
|
||||
// delay slot of a CALL. See "Programming Note" in v8 manual, B.31,
|
||||
// p137.
|
||||
@@ -88,6 +94,7 @@ DEF_SEM(UNIMP_SYNC, I32 struct_size) {
|
||||
}
|
||||
|
||||
DEF_SEM(UNIMP_ASYNC, I32 struct_size) {
|
||||
|
||||
// TODO(pag): See if callees inspect the struct size when this is after the
|
||||
// delay slot of a CALL. See "Programming Note" in v8 manual, B.31,
|
||||
// p137.
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
namespace {
|
||||
|
||||
template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(SAVE, S1 src1, S2 src2, D dst, RegisterWindow *window, RegisterWindow *&prev_window) {
|
||||
DEF_SEM(SAVE, S1 src1, S2 src2, D dst, RegisterWindow *window,
|
||||
RegisterWindow *&prev_window) {
|
||||
addr_t sp_base = Read(src1);
|
||||
addr_t sp_offset = Read(src2);
|
||||
addr_t new_sp = UAdd(sp_base, sp_offset);
|
||||
|
||||
+29
-25
@@ -14,21 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "Decode.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
#include "remill/BC/ABI.h"
|
||||
#include "remill/BC/Util.h"
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
#include "Decode.h"
|
||||
|
||||
// clang-format off
|
||||
#define ADDRESS_SIZE_BITS 64
|
||||
#define INCLUDED_FROM_REMILL
|
||||
#include "remill/Arch/SPARC64/Runtime/State.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace remill {
|
||||
@@ -73,9 +74,8 @@ class SPARC64Arch final : public Arch {
|
||||
llvm::DataLayout DataLayout(void) const final;
|
||||
|
||||
// Decode an instruction.
|
||||
bool DecodeInstruction(
|
||||
uint64_t address, std::string_view instr_bytes,
|
||||
Instruction &inst) const final;
|
||||
bool DecodeInstruction(uint64_t address, std::string_view instr_bytes,
|
||||
Instruction &inst) const final;
|
||||
|
||||
// Returns `true` if memory access are little endian byte ordered.
|
||||
bool MemoryAccessIsLittleEndian(void) const final {
|
||||
@@ -160,7 +160,8 @@ void SPARC64Arch::PopulateBasicBlockFunction(llvm::Module *module,
|
||||
REG(o7, gpr.o7.qword, u64);
|
||||
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "g0"), false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "ignore_write_to_g0"), false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "ignore_write_to_g0"),
|
||||
false);
|
||||
|
||||
REG(g1, gpr.g1.qword, u64);
|
||||
REG(g2, gpr.g2.qword, u64);
|
||||
@@ -352,23 +353,27 @@ void SPARC64Arch::PopulateBasicBlockFunction(llvm::Module *module,
|
||||
|
||||
// `WINDOW_LINK = &(WINDOW->prev_window);`
|
||||
llvm::Value *gep_indexes[2] = {zero_u32, llvm::ConstantInt::get(u32, 33)};
|
||||
auto window_link = ir.CreateInBoundsGEP(window_type, window, gep_indexes, "WINDOW_LINK");
|
||||
auto window_link =
|
||||
ir.CreateInBoundsGEP(window_type, window, gep_indexes, "WINDOW_LINK");
|
||||
auto nullptr_window = llvm::Constant::getNullValue(prev_window_link->type);
|
||||
ir.CreateStore(nullptr_window, window_link, false);
|
||||
|
||||
ir.CreateStore(zero_u8, ir.CreateAlloca(u8, nullptr, "IGNORE_BRANCH_TAKEN"), false);
|
||||
ir.CreateStore(zero_u8, ir.CreateAlloca(u8, nullptr, "IGNORE_BRANCH_TAKEN"),
|
||||
false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "IGNORE_PC"), false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "IGNORE_NEXT_PC"), false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "IGNORE_RETURN_PC"), false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "IGNORE_NEXT_PC"),
|
||||
false);
|
||||
ir.CreateStore(zero_u64, ir.CreateAlloca(u64, nullptr, "IGNORE_RETURN_PC"),
|
||||
false);
|
||||
|
||||
const auto pc_arg = NthArgument(bb_func, kPCArgNum);
|
||||
const auto state_ptr_arg = NthArgument(bb_func, kStatePointerArgNum);
|
||||
|
||||
(void) RegisterByName(kNextPCVariableName)->AddressOf(state_ptr_arg, ir);
|
||||
|
||||
ir.CreateStore(
|
||||
pc_arg, RegisterByName(kPCVariableName)->AddressOf(state_ptr_arg, ir),
|
||||
false);
|
||||
ir.CreateStore(pc_arg,
|
||||
RegisterByName(kPCVariableName)->AddressOf(state_ptr_arg, ir),
|
||||
false);
|
||||
}
|
||||
|
||||
llvm::Triple SPARC64Arch::Triple(void) const {
|
||||
@@ -405,8 +410,9 @@ bool SPARC64Arch::NextInstructionIsDelayed(const Instruction &inst,
|
||||
}
|
||||
|
||||
// Decode an instruction.
|
||||
bool SPARC64Arch::DecodeInstruction(
|
||||
uint64_t address, std::string_view inst_bytes, Instruction &inst) const {
|
||||
bool SPARC64Arch::DecodeInstruction(uint64_t address,
|
||||
std::string_view inst_bytes,
|
||||
Instruction &inst) const {
|
||||
|
||||
inst.pc = address;
|
||||
inst.arch_name = arch_name;
|
||||
@@ -436,27 +442,25 @@ bool SPARC64Arch::DecodeInstruction(
|
||||
if (!sparc64::TryDecode(inst)) {
|
||||
inst.category = Instruction::kCategoryInvalid;
|
||||
inst.operands.clear();
|
||||
LOG(ERROR)
|
||||
<< "Unable to decode: " << inst.Serialize();
|
||||
LOG(ERROR) << "Unable to decode: " << inst.Serialize();
|
||||
return false;
|
||||
}
|
||||
|
||||
return inst.IsValid();
|
||||
}
|
||||
|
||||
} // namespace sparc
|
||||
} // namespace sparc
|
||||
|
||||
Arch::ArchPtr Arch::GetSPARC64(
|
||||
llvm::LLVMContext *context_, OSName os_name_, ArchName arch_name_) {
|
||||
Arch::ArchPtr Arch::GetSPARC64(llvm::LLVMContext *context_, OSName os_name_,
|
||||
ArchName arch_name_) {
|
||||
if (arch_name_ == kArchSparc64) {
|
||||
return std::make_unique<sparc::SPARC64Arch>(context_, os_name_, arch_name_);
|
||||
|
||||
} else {
|
||||
LOG(FATAL)
|
||||
<< "Invalid arch name passed to Arch::GetSPARC::"
|
||||
<< GetArchName(arch_name_);
|
||||
LOG(FATAL) << "Invalid arch name passed to Arch::GetSPARC::"
|
||||
<< GetArchName(arch_name_);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace remill
|
||||
} // namespace remill
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
|
||||
namespace remill {
|
||||
namespace sparc64 {
|
||||
|
||||
bool TryDecode(Instruction &inst) ;
|
||||
bool TryDecode(Instruction &inst);
|
||||
|
||||
} // namespace sparc64
|
||||
} // namespace remill
|
||||
|
||||
+772
-990
File diff suppressed because it is too large
Load Diff
@@ -24,111 +24,111 @@
|
||||
#include "remill/Arch/SPARC64/Runtime/State.h"
|
||||
#include "remill/Arch/SPARC64/Runtime/Types.h"
|
||||
|
||||
#define REG_PC state.pc.aword
|
||||
#define REG_NPC state.next_pc.aword
|
||||
#define REG_SP state.gpr.o6.aword
|
||||
#define REG_FP state.gpr.i6.aword
|
||||
#define REG_PC state.pc.aword
|
||||
#define REG_NPC state.next_pc.aword
|
||||
#define REG_SP state.gpr.o6.aword
|
||||
#define REG_FP state.gpr.i6.aword
|
||||
|
||||
#define REG_G0 state.gpr.g0.aword
|
||||
#define REG_G1 state.gpr.g1.aword
|
||||
#define REG_G7 state.gpr.g7.aword // Thread local pointer
|
||||
#define REG_G0 state.gpr.g0.aword
|
||||
#define REG_G1 state.gpr.g1.aword
|
||||
#define REG_G7 state.gpr.g7.aword // Thread local pointer
|
||||
|
||||
#define REG_L0 state.gpr.l0.aword
|
||||
#define REG_L1 state.gpr.l1.aword
|
||||
#define REG_L2 state.gpr.l2.aword
|
||||
#define REG_L3 state.gpr.l3.aword
|
||||
#define REG_L4 state.gpr.l4.aword
|
||||
#define REG_L5 state.gpr.l5.aword
|
||||
#define REG_L6 state.gpr.l6.aword
|
||||
#define REG_L7 state.gpr.l7.aword
|
||||
#define REG_L0 state.gpr.l0.aword
|
||||
#define REG_L1 state.gpr.l1.aword
|
||||
#define REG_L2 state.gpr.l2.aword
|
||||
#define REG_L3 state.gpr.l3.aword
|
||||
#define REG_L4 state.gpr.l4.aword
|
||||
#define REG_L5 state.gpr.l5.aword
|
||||
#define REG_L6 state.gpr.l6.aword
|
||||
#define REG_L7 state.gpr.l7.aword
|
||||
|
||||
#define REG_I0 state.gpr.i0.aword
|
||||
#define REG_I1 state.gpr.i1.aword
|
||||
#define REG_I2 state.gpr.i2.aword
|
||||
#define REG_I3 state.gpr.i3.aword
|
||||
#define REG_I4 state.gpr.i4.aword
|
||||
#define REG_I5 state.gpr.i5.aword
|
||||
#define REG_I6 state.gpr.i6.aword
|
||||
#define REG_I7 state.gpr.i7.aword
|
||||
#define REG_I0 state.gpr.i0.aword
|
||||
#define REG_I1 state.gpr.i1.aword
|
||||
#define REG_I2 state.gpr.i2.aword
|
||||
#define REG_I3 state.gpr.i3.aword
|
||||
#define REG_I4 state.gpr.i4.aword
|
||||
#define REG_I5 state.gpr.i5.aword
|
||||
#define REG_I6 state.gpr.i6.aword
|
||||
#define REG_I7 state.gpr.i7.aword
|
||||
|
||||
#define REG_O0 state.gpr.o0.aword
|
||||
#define REG_O1 state.gpr.o1.aword
|
||||
#define REG_O2 state.gpr.o2.aword
|
||||
#define REG_O3 state.gpr.o3.aword
|
||||
#define REG_O4 state.gpr.o4.aword
|
||||
#define REG_O5 state.gpr.o5.aword
|
||||
#define REG_O6 state.gpr.o6.aword
|
||||
#define REG_O7 state.gpr.o7.aword
|
||||
#define REG_O0 state.gpr.o0.aword
|
||||
#define REG_O1 state.gpr.o1.aword
|
||||
#define REG_O2 state.gpr.o2.aword
|
||||
#define REG_O3 state.gpr.o3.aword
|
||||
#define REG_O4 state.gpr.o4.aword
|
||||
#define REG_O5 state.gpr.o5.aword
|
||||
#define REG_O6 state.gpr.o6.aword
|
||||
#define REG_O7 state.gpr.o7.aword
|
||||
|
||||
#define REG_F0 state.fpreg.v[0].floats.elems[0]
|
||||
#define REG_F1 state.fpreg.v[0].floats.elems[1]
|
||||
#define REG_F2 state.fpreg.v[0].floats.elems[2]
|
||||
#define REG_F3 state.fpreg.v[0].floats.elems[3]
|
||||
#define REG_F0 state.fpreg.v[0].floats.elems[0]
|
||||
#define REG_F1 state.fpreg.v[0].floats.elems[1]
|
||||
#define REG_F2 state.fpreg.v[0].floats.elems[2]
|
||||
#define REG_F3 state.fpreg.v[0].floats.elems[3]
|
||||
|
||||
#define REG_D0 state.fpreg.v[0].doubles.elems[0]
|
||||
#define REG_D2 state.fpreg.v[0].doubles.elems[1]
|
||||
#define REG_D0 state.fpreg.v[0].doubles.elems[0]
|
||||
#define REG_D2 state.fpreg.v[0].doubles.elems[1]
|
||||
|
||||
// GSR Register
|
||||
#define GSR_ALIGN state.asr.gsr.align
|
||||
#define GSR_MASK state.asr.gsr.mask
|
||||
#define GSR_ALIGN state.asr.gsr.align
|
||||
#define GSR_MASK state.asr.gsr.mask
|
||||
|
||||
#define REG_Y state.asr.yreg.aword
|
||||
#define REG_Y state.asr.yreg.aword
|
||||
|
||||
#define FLAG_ICC_CF state.asr.ccr.icc.c
|
||||
#define FLAG_ICC_VF state.asr.ccr.icc.v
|
||||
#define FLAG_ICC_ZF state.asr.ccr.icc.z
|
||||
#define FLAG_ICC_NF state.asr.ccr.icc.n
|
||||
#define FLAG_ICC_CF state.asr.ccr.icc.c
|
||||
#define FLAG_ICC_VF state.asr.ccr.icc.v
|
||||
#define FLAG_ICC_ZF state.asr.ccr.icc.z
|
||||
#define FLAG_ICC_NF state.asr.ccr.icc.n
|
||||
|
||||
#define FLAG_XCC_CF state.asr.ccr.xcc.c
|
||||
#define FLAG_XCC_VF state.asr.ccr.xcc.v
|
||||
#define FLAG_XCC_ZF state.asr.ccr.xcc.z
|
||||
#define FLAG_XCC_NF state.asr.ccr.xcc.n
|
||||
#define FLAG_XCC_CF state.asr.ccr.xcc.c
|
||||
#define FLAG_XCC_VF state.asr.ccr.xcc.v
|
||||
#define FLAG_XCC_ZF state.asr.ccr.xcc.z
|
||||
#define FLAG_XCC_NF state.asr.ccr.xcc.n
|
||||
|
||||
#define REG_ICC state.asr.ccr.icc.flat
|
||||
#define REG_XCC state.asr.ccr.xcc.flat
|
||||
#define REG_CCC state.csr.ccc
|
||||
#define REG_ICC state.asr.ccr.icc.flat
|
||||
#define REG_XCC state.asr.ccr.xcc.flat
|
||||
#define REG_CCC state.csr.ccc
|
||||
|
||||
#define FSR_FCC_0 state.fsr.fcc0
|
||||
#define FSR_FCC_1 state.fsr.fcc1
|
||||
#define FSR_FCC_2 state.fsr.fcc2
|
||||
#define FSR_FCC_4 state.fsr.fcc4
|
||||
#define FSR_FCC_0 state.fsr.fcc0
|
||||
#define FSR_FCC_1 state.fsr.fcc1
|
||||
#define FSR_FCC_2 state.fsr.fcc2
|
||||
#define FSR_FCC_4 state.fsr.fcc4
|
||||
|
||||
#define FSR_CEXC state.fsr.cexc
|
||||
#define FSR_FTT state.fsr.ftt
|
||||
#define FSR_RD state.fsr.rd
|
||||
#define FSR_CEXC state.fsr.cexc
|
||||
#define FSR_FTT state.fsr.ftt
|
||||
#define FSR_RD state.fsr.rd
|
||||
|
||||
#define PSR_TPC state.psr.tpc
|
||||
#define PSR_TNPC state.psr.tnpc
|
||||
#define PSR_TSTATE state.psr.tstate
|
||||
#define PSR_TT state.psr.tt
|
||||
#define PSR_TBA state.psr.tba
|
||||
#define PSR_PSTATE state.psr.pstate
|
||||
#define PSR_TL state.psr.tl
|
||||
#define PSR_PIL state.psr.pil
|
||||
#define PSR_WSTATE state.psr.wstate
|
||||
#define PSR_CWP state.psr.cwp
|
||||
#define PSR_CANSAVE state.psr.cansave
|
||||
#define PSR_CANRESTORE state.psr.canrestore
|
||||
#define PSR_CLEANWIN state.psr.cleanwin
|
||||
#define PSR_OTHERWIN state.psr.otherwin
|
||||
#define PSR_GL state.psr.gl
|
||||
#define PSR_TPC state.psr.tpc
|
||||
#define PSR_TNPC state.psr.tnpc
|
||||
#define PSR_TSTATE state.psr.tstate
|
||||
#define PSR_TT state.psr.tt
|
||||
#define PSR_TBA state.psr.tba
|
||||
#define PSR_PSTATE state.psr.pstate
|
||||
#define PSR_TL state.psr.tl
|
||||
#define PSR_PIL state.psr.pil
|
||||
#define PSR_WSTATE state.psr.wstate
|
||||
#define PSR_CWP state.psr.cwp
|
||||
#define PSR_CANSAVE state.psr.cansave
|
||||
#define PSR_CANRESTORE state.psr.canrestore
|
||||
#define PSR_CLEANWIN state.psr.cleanwin
|
||||
#define PSR_OTHERWIN state.psr.otherwin
|
||||
#define PSR_GL state.psr.gl
|
||||
|
||||
#define ASR_Y state.asr.yreg.aword
|
||||
#define ASR_ASI state.asr.asi_flat
|
||||
#define ASR_FPRS state.asr.fprs_flat
|
||||
#define ASR_GSR state.asr.gsr.flat
|
||||
#define ASR_SOFTINT state.asr.softint
|
||||
#define ASR_STICK_CMPR state.asr.stick_cmpr
|
||||
#define ASR_PAUSE state.asr.pause
|
||||
#define ASR_Y state.asr.yreg.aword
|
||||
#define ASR_ASI state.asr.asi_flat
|
||||
#define ASR_FPRS state.asr.fprs_flat
|
||||
#define ASR_GSR state.asr.gsr.flat
|
||||
#define ASR_SOFTINT state.asr.softint
|
||||
#define ASR_STICK_CMPR state.asr.stick_cmpr
|
||||
#define ASR_PAUSE state.asr.pause
|
||||
|
||||
#define HYPER_CALL state.hyper_call
|
||||
#define INTERRUPT_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL state.hyper_call
|
||||
#define INTERRUPT_VECTOR state.hyper_call_vector
|
||||
#define HYPER_CALL_VECTOR state.hyper_call_vector
|
||||
|
||||
#if ADDRESS_SIZE_BITS == 64
|
||||
# define SPARC_STACKBIAS 0
|
||||
# define SPARC_STACKBIAS 0
|
||||
#else
|
||||
# define SPARC_STACKBIAS 0
|
||||
# define SPARC_STACKBIAS 0
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -136,7 +136,9 @@ namespace {
|
||||
// Takes the place of an unsupported instruction.
|
||||
DEF_SEM(HandleUnsupported) {
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction, kSPARC64EmulateInstruction));
|
||||
state, memory,
|
||||
SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction,
|
||||
kSPARC64EmulateInstruction));
|
||||
}
|
||||
|
||||
// Takes the place of an invalid instruction.
|
||||
@@ -145,12 +147,13 @@ DEF_SEM(HandleInvalidInstruction) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_HELPER(SAVE_WINDOW, RegisterWindow *window, RegisterWindow *&prev_window) -> void {
|
||||
DEF_HELPER(SAVE_WINDOW, RegisterWindow *window, RegisterWindow *&prev_window)
|
||||
->void {
|
||||
|
||||
// TODO(pag): These two lines should be uncommented for correctness, but then
|
||||
// they don't result in as nice bitcode in McSema :-(
|
||||
// window->prev_window = state.window;
|
||||
// state.window = window;
|
||||
// TODO(pag): These two lines should be uncommented for correctness, but then
|
||||
// they don't result in as nice bitcode in McSema :-(
|
||||
// window->prev_window = state.window;
|
||||
// state.window = window;
|
||||
|
||||
prev_window = window;
|
||||
|
||||
@@ -183,18 +186,18 @@ DEF_HELPER(SAVE_WINDOW, RegisterWindow *window, RegisterWindow *&prev_window) ->
|
||||
Write(REG_I7, REG_O7);
|
||||
}
|
||||
|
||||
DEF_HELPER(RESTORE_WINDOW, RegisterWindow *&prev_window) -> void {
|
||||
DEF_HELPER(RESTORE_WINDOW, RegisterWindow *&prev_window)->void {
|
||||
|
||||
const auto window = prev_window ? prev_window : state.window;
|
||||
if (!window) {
|
||||
memory = __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCWindowUnderflow);
|
||||
memory = __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCWindowUnderflow);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(pag): This next line should be uncommented for correctness, but then
|
||||
// it means not as nice bitcode for mcsema.
|
||||
// state.window = window->prev_window;
|
||||
// TODO(pag): This next line should be uncommented for correctness, but then
|
||||
// it means not as nice bitcode for mcsema.
|
||||
// state.window = window->prev_window;
|
||||
|
||||
// Move input register to output
|
||||
Write(REG_O0, REG_I0);
|
||||
@@ -231,17 +234,16 @@ DEF_HELPER(RESTORE_WINDOW, RegisterWindow *&prev_window) -> void {
|
||||
DEF_ISEL(UNSUPPORTED_INSTRUCTION) = HandleUnsupported;
|
||||
DEF_ISEL(INVALID_INSTRUCTION) = HandleInvalidInstruction;
|
||||
|
||||
#include "lib/Arch/SPARC32/Semantics/FLAGS.cpp"
|
||||
#include "lib/Arch/SPARC32/Semantics/COND.cpp"
|
||||
|
||||
#include "lib/Arch/SPARC32/Semantics/FLAGS.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/ADDRESS.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/BITBYTE.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/BINARY.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/DATAXFER.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/MISC.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/LOGICAL.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/BITBYTE.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/BRANCH.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/DATAXFER.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/FOP.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/VIS.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/LOGICAL.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/MISC.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/TRAP.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/VIS.cpp"
|
||||
#include "lib/Arch/SPARC64/Semantics/WRASR.cpp"
|
||||
|
||||
@@ -41,34 +41,29 @@ DEF_SEM(ALIGNADDRESS_LITTLE, R64 src1, R64 src2, R64W dst) {
|
||||
}
|
||||
|
||||
DEF_SEM(FALIGNDATAG, V128 src1, V128 src2, V128W dst) {
|
||||
|
||||
// extract F[rs1] and F[rs2] and concat them
|
||||
auto rs1 = UReadV8(src1);
|
||||
auto rs2 = UReadV8(src2);
|
||||
auto concat_vec = UClearV8(UReadV8(dst));
|
||||
_Pragma("unroll")
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
concat_vec = UInsertV8(
|
||||
concat_vec, i, UExtractV8(rs1, i));
|
||||
_Pragma("unroll") for (size_t i = 0; i < 8; ++i) {
|
||||
concat_vec = UInsertV8(concat_vec, i, UExtractV8(rs1, i));
|
||||
}
|
||||
_Pragma("unroll")
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
concat_vec = UInsertV8(
|
||||
concat_vec, i, UExtractV8(rs2, i));
|
||||
_Pragma("unroll") for (size_t i = 0; i < 8; ++i) {
|
||||
concat_vec = UInsertV8(concat_vec, i, UExtractV8(rs2, i));
|
||||
}
|
||||
|
||||
// Recover the vector from the GSR.align value
|
||||
auto align = Read(GSR_ALIGN);
|
||||
auto recv_vec = UClearV8(UReadV8(dst));
|
||||
_Pragma("unroll")
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
recv_vec = UInsertV8(
|
||||
recv_vec, i, UExtractV8(concat_vec, align + i));
|
||||
_Pragma("unroll") for (size_t i = 0; i < 8; ++i) {
|
||||
recv_vec = UInsertV8(recv_vec, i, UExtractV8(concat_vec, align + i));
|
||||
}
|
||||
UWriteV8(dst, recv_vec);
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(ALIGNADDRESS) = ALIGNADDRESS;
|
||||
DEF_ISEL(ALIGNADDRESS_LITTLE) = ALIGNADDRESS_LITTLE;
|
||||
|
||||
@@ -32,27 +32,31 @@ ALWAYS_INLINE static void WriteFlagsAddSub(State &state, T lhs, T rhs, T res) {
|
||||
}
|
||||
|
||||
template <typename Tag, typename T>
|
||||
ALWAYS_INLINE static void WriteXCCFlagsIncDec(State &state, T lhs, T rhs, T res) {
|
||||
ALWAYS_INLINE static void WriteXCCFlagsIncDec(State &state, T lhs, T rhs,
|
||||
T res) {
|
||||
FLAG_XCC_ZF = ZeroFlag(res);
|
||||
FLAG_XCC_NF = SignFlag(res);
|
||||
FLAG_XCC_VF = Overflow<Tag>::Flag(lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename Tag, typename T>
|
||||
ALWAYS_INLINE static void WriteICCFlagsIncDec(State &state, T lhs, T rhs, T res) {
|
||||
ALWAYS_INLINE static void WriteICCFlagsIncDec(State &state, T lhs, T rhs,
|
||||
T res) {
|
||||
FLAG_ICC_ZF = ZeroFlag(res);
|
||||
FLAG_ICC_NF = SignFlag(res);
|
||||
FLAG_ICC_VF = Overflow<Tag>::Flag(lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
ALWAYS_INLINE static void WriteICCFlagsAddSub(State &state, uint32_t lhs, uint32_t rhs, uint32_t res) {
|
||||
ALWAYS_INLINE static void WriteICCFlagsAddSub(State &state, uint32_t lhs,
|
||||
uint32_t rhs, uint32_t res) {
|
||||
FLAG_ICC_CF = Carry<Tag>::Flag(lhs, rhs, res);
|
||||
WriteICCFlagsIncDec<Tag>(state, lhs, rhs, res);
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
ALWAYS_INLINE static void WriteXCCFlagsAddSub(State &state, uint64_t lhs, uint64_t rhs, uint64_t res) {
|
||||
ALWAYS_INLINE static void WriteXCCFlagsAddSub(State &state, uint64_t lhs,
|
||||
uint64_t rhs, uint64_t res) {
|
||||
FLAG_XCC_CF = Carry<Tag>::Flag(lhs, rhs, res);
|
||||
WriteXCCFlagsIncDec<Tag>(state, lhs, rhs, res);
|
||||
}
|
||||
@@ -80,9 +84,8 @@ DEF_SEM(ADDCC, S1 src1, S2 src2, D dst) {
|
||||
auto rhs = Read(src2);
|
||||
auto res = UAdd(lhs, rhs);
|
||||
Write(dst, res);
|
||||
WriteICCFlagsAddSub<tag_add>(
|
||||
state, Literal<uint32_t>(lhs),
|
||||
Literal<uint32_t>(rhs), Literal<uint32_t>(res));
|
||||
WriteICCFlagsAddSub<tag_add>(state, Literal<uint32_t>(lhs),
|
||||
Literal<uint32_t>(rhs), Literal<uint32_t>(res));
|
||||
WriteXCCFlagsAddSub<tag_add>(state, lhs, rhs, res);
|
||||
return memory;
|
||||
}
|
||||
@@ -95,9 +98,9 @@ DEF_SEM(ADDCCC, S1 src1, S2 src2, D dst) {
|
||||
auto sum = UAdd(lhs, rhs);
|
||||
auto res = UAdd(sum, carry);
|
||||
Write(dst, res);
|
||||
WriteICCFlagsAddSub<tag_add>(
|
||||
state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs), static_cast<uint32_t>(sum));
|
||||
WriteICCFlagsAddSub<tag_add>(state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(sum));
|
||||
WriteXCCFlagsAddSub<tag_add>(state, lhs, rhs, res);
|
||||
return memory;
|
||||
}
|
||||
@@ -128,9 +131,9 @@ DEF_SEM(ADDXCC, S1 src1, S2 src2, D dst) {
|
||||
auto rhs = Read(src2);
|
||||
auto sum = UAdd(lhs, rhs);
|
||||
Write(dst, sum);
|
||||
WriteICCFlagsAddSub<tag_add>(
|
||||
state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs), static_cast<uint32_t>(sum));
|
||||
WriteICCFlagsAddSub<tag_add>(state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(sum));
|
||||
WriteXCCFlagsAddSub<tag_add>(state, lhs, rhs, sum);
|
||||
return memory;
|
||||
}
|
||||
@@ -143,9 +146,9 @@ DEF_SEM(ADDXCCC, S1 src1, S2 src2, D dst) {
|
||||
auto sum = UAdd(lhs, rhs);
|
||||
auto res = UAdd(sum, carry);
|
||||
Write(dst, res);
|
||||
WriteICCFlagsAddSub<tag_add>(
|
||||
state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs), static_cast<uint32_t>(res));
|
||||
WriteICCFlagsAddSub<tag_add>(state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(res));
|
||||
WriteXCCFlagsAddSub<tag_add>(state, lhs, rhs, res);
|
||||
return memory;
|
||||
}
|
||||
@@ -163,9 +166,9 @@ DEF_SEM(SUBCC, S1 src1, S2 src2, D dst) {
|
||||
auto rhs = Read(src2);
|
||||
auto res = USub(lhs, rhs);
|
||||
Write(dst, res);
|
||||
WriteICCFlagsAddSub<tag_sub>(
|
||||
state, static_cast<uint32_t>(lhs), static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(res));
|
||||
WriteICCFlagsAddSub<tag_sub>(state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(res));
|
||||
WriteXCCFlagsAddSub<tag_sub>(state, lhs, rhs, res);
|
||||
return memory;
|
||||
}
|
||||
@@ -189,9 +192,9 @@ DEF_SEM(SUBCCC, S1 src1, S2 src2, D dst) {
|
||||
auto sub = USub(lhs, rhs);
|
||||
auto res = USub(sub, carry);
|
||||
WriteZExt(dst, res);
|
||||
WriteICCFlagsAddSub<tag_sub>(
|
||||
state, static_cast<uint32_t>(lhs), static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(res));
|
||||
WriteICCFlagsAddSub<tag_sub>(state, static_cast<uint32_t>(lhs),
|
||||
static_cast<uint32_t>(rhs),
|
||||
static_cast<uint32_t>(res));
|
||||
WriteXCCFlagsAddSub<tag_sub>(state, lhs, rhs, res);
|
||||
return memory;
|
||||
}
|
||||
@@ -201,15 +204,16 @@ template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(TADDCC, S1 src1, S2 src2, D dst) {
|
||||
auto rs1 = Read(src1);
|
||||
auto rs2 = Read(src2);
|
||||
|
||||
// Check for the tag overflow
|
||||
auto tag_rs1 = UAnd(rs1, Literal<S1>(0x3));
|
||||
auto tag_rs2 = UAnd(rs2, Literal<S2>(0x3));
|
||||
auto tag_ov = UCmpNeq(UOr(tag_rs1, tag_rs2), 0);
|
||||
auto sum = UAdd(rs1, rs2);
|
||||
FLAG_ICC_VF = tag_ov; //|| Overflow<tag_add>::Flag(rs1, rs2, sum));
|
||||
FLAG_ICC_CF = Carry<tag_add>::Flag(
|
||||
static_cast<uint32_t>(rs1), static_cast<uint32_t>(rs2),
|
||||
static_cast<uint32_t>(sum));
|
||||
FLAG_ICC_VF = tag_ov; //|| Overflow<tag_add>::Flag(rs1, rs2, sum));
|
||||
FLAG_ICC_CF = Carry<tag_add>::Flag(static_cast<uint32_t>(rs1),
|
||||
static_cast<uint32_t>(rs2),
|
||||
static_cast<uint32_t>(sum));
|
||||
FLAG_ICC_ZF = ZeroFlag(static_cast<uint32_t>(sum));
|
||||
FLAG_ICC_NF = SignFlag(static_cast<uint32_t>(sum));
|
||||
WriteXCCFlagsAddSub<tag_add>(state, rs1, rs2, sum);
|
||||
@@ -221,6 +225,7 @@ template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(TADDCCTV, R8W cond, S1 src1, S2 src2, D dst) {
|
||||
auto rs1 = Read(src1);
|
||||
auto rs2 = Read(src2);
|
||||
|
||||
// Check for the tag overflow
|
||||
auto tag_rs1 = UAnd(rs1, Literal<S1>(0x3));
|
||||
auto tag_rs2 = UAnd(rs2, Literal<S2>(0x3));
|
||||
@@ -237,8 +242,7 @@ DEF_SEM(TADDCCTV, R8W cond, S1 src1, S2 src2, D dst) {
|
||||
Write(dst, sum);
|
||||
FLAG_ICC_VF = tag_ov;
|
||||
FLAG_ICC_CF = Carry<tag_add>::Flag(
|
||||
Literal<uint32_t>(rs1), Literal<uint32_t>(rs2),
|
||||
Literal<uint32_t>(sum));
|
||||
Literal<uint32_t>(rs1), Literal<uint32_t>(rs2), Literal<uint32_t>(sum));
|
||||
FLAG_ICC_ZF = ZeroFlag(static_cast<uint32_t>(sum));
|
||||
FLAG_ICC_NF = SignFlag(static_cast<uint32_t>(sum));
|
||||
WriteXCCFlagsAddSub<tag_add>(state, rs1, rs2, sum);
|
||||
@@ -249,15 +253,16 @@ template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(TSUBCC, S1 src1, S2 src2, D dst) {
|
||||
auto rs1 = Read(src1);
|
||||
auto rs2 = Read(src2);
|
||||
|
||||
// Check for the tag overflow
|
||||
auto tag_rs1 = UAnd(rs1, Literal<S1>(0x3));
|
||||
auto tag_rs2 = UAnd(rs2, Literal<S2>(0x3));
|
||||
auto tag_ov = UCmpNeq(UOr(tag_rs1, tag_rs2), 0);
|
||||
auto res = USub(rs1, rs2);
|
||||
FLAG_ICC_VF = tag_ov; //|| Overflow<tag_add>::Flag(rs1, rs2, sum));
|
||||
FLAG_ICC_CF = Carry<tag_sub>::Flag(
|
||||
static_cast<uint32_t>(rs1), static_cast<uint32_t>(rs2),
|
||||
static_cast<uint32_t>(res));
|
||||
FLAG_ICC_VF = tag_ov; //|| Overflow<tag_add>::Flag(rs1, rs2, sum));
|
||||
FLAG_ICC_CF = Carry<tag_sub>::Flag(static_cast<uint32_t>(rs1),
|
||||
static_cast<uint32_t>(rs2),
|
||||
static_cast<uint32_t>(res));
|
||||
FLAG_ICC_ZF = ZeroFlag(static_cast<uint32_t>(res));
|
||||
FLAG_ICC_NF = SignFlag(static_cast<uint32_t>(res));
|
||||
WriteXCCFlagsAddSub<tag_sub>(state, rs1, rs2, res);
|
||||
@@ -268,6 +273,7 @@ template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(TSUBCCTV, R8W cond, S1 src1, S2 src2, D dst) {
|
||||
auto rs1 = Read(src1);
|
||||
auto rs2 = Read(src2);
|
||||
|
||||
// Check for the tag overflow
|
||||
auto tag_rs1 = UAnd(rs1, Literal<S1>(0x3));
|
||||
auto tag_rs2 = UAnd(rs2, Literal<S2>(0x3));
|
||||
@@ -285,15 +291,14 @@ DEF_SEM(TSUBCCTV, R8W cond, S1 src1, S2 src2, D dst) {
|
||||
Write(dst, res);
|
||||
FLAG_ICC_VF = tag_ov;
|
||||
FLAG_ICC_CF = Carry<tag_sub>::Flag(
|
||||
Literal<uint32_t>(rs1), Literal<uint32_t>(rs2),
|
||||
Literal<uint32_t>(res));
|
||||
Literal<uint32_t>(rs1), Literal<uint32_t>(rs2), Literal<uint32_t>(res));
|
||||
FLAG_ICC_ZF = ZeroFlag(Literal<uint32_t>(res));
|
||||
FLAG_ICC_NF = SignFlag(Literal<uint32_t>(res));
|
||||
WriteXCCFlagsAddSub<tag_sub>(state, rs1, rs2, res);
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(ADD) = ADD<R64, R64, R64W>;
|
||||
DEF_ISEL(ADDC) = ADDC<R64, R64, R64W>;
|
||||
@@ -405,7 +410,8 @@ DEF_SEM(SDIV, S1 src1, S2 src2, D dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto lhs_wide = ZExt(lhs);
|
||||
auto y = Read(REG_Y);
|
||||
auto y_lhs_wide = Signed(UOr(decltype(lhs_wide)(UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto y_lhs_wide = Signed(
|
||||
UOr(decltype(lhs_wide)(UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto rhs = Signed(Read(src2));
|
||||
auto rhs_wide = SExt(rhs);
|
||||
auto quot = SDiv(y_lhs_wide, rhs_wide);
|
||||
@@ -418,8 +424,8 @@ DEF_SEM(SDIVCC, S1 src1, S2 src2, D dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto lhs_wide = ZExt(lhs);
|
||||
auto y = Read(REG_Y);
|
||||
auto y_lhs_wide = Signed(UOr(decltype(lhs_wide)(
|
||||
UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto y_lhs_wide = Signed(
|
||||
UOr(decltype(lhs_wide)(UShl(y, Literal<decltype(y)>(32))), lhs_wide));
|
||||
auto rhs = Read(src2);
|
||||
auto rhs_wide = SExt(rhs);
|
||||
auto quot = SDiv(y_lhs_wide, rhs_wide);
|
||||
@@ -490,7 +496,7 @@ DEF_SEM(UDIVX, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(SMUL) = SMUL<R64, R64, R64W>;
|
||||
DEF_ISEL(SMULcc) = SMULCC<R64, R64, R64W>;
|
||||
@@ -518,7 +524,8 @@ DEF_SEM(MULSCC_R32, R32 src1, R32 src2, R32W dest) {
|
||||
auto lsb_y = UAnd(y, Literal<decltype(y)>(0x1));
|
||||
auto masked_rs1 = UAnd(rs1, Literal<decltype(rs1)>(0xffffffff));
|
||||
auto masked_rs2 = UAnd(rs2, Literal<decltype(rs2)>(0xffffffff));
|
||||
auto new_rs2 = Select(UCmpEq(lsb_y, 0), Literal<decltype(rs2)>(0), masked_rs2);
|
||||
auto new_rs2 =
|
||||
Select(UCmpEq(lsb_y, 0), Literal<decltype(rs2)>(0), masked_rs2);
|
||||
|
||||
auto flag_nf = Literal<uint32_t>(Read(FLAG_ICC_NF));
|
||||
auto flag_vf = Literal<uint32_t>(Read(FLAG_ICC_VF));
|
||||
@@ -527,17 +534,19 @@ DEF_SEM(MULSCC_R32, R32 src1, R32 src2, R32W dest) {
|
||||
auto new_rs1 = UOr(UShr(masked_rs1, Literal<decltype(masked_rs1)>(1)),
|
||||
Literal<decltype(masked_rs1)>(shifted_flag));
|
||||
auto res = UAdd(new_rs1, new_rs2);
|
||||
|
||||
// Y register is shifted right by one bit, with the LSB of the unshifted
|
||||
// r[rs1] replacing the MSB of Y
|
||||
auto shifted_y = UShr(y, Literal<decltype(y)>(1));
|
||||
auto lsb_rs1 = UAnd(rs1, Literal<decltype(rs1)>(0x1));
|
||||
auto new_y = UOr(shifted_y, decltype(y)(UShl(lsb_rs1, Literal<decltype(lsb_rs1)>(31))));
|
||||
auto lsb_rs1 = UAnd(rs1, Literal<decltype(rs1)>(0x1));
|
||||
auto new_y = UOr(shifted_y,
|
||||
decltype(y)(UShl(lsb_rs1, Literal<decltype(lsb_rs1)>(31))));
|
||||
Write(REG_Y, new_y);
|
||||
Write(dest, res);
|
||||
|
||||
WriteICCFlagsAddSub<tag_add>(
|
||||
state, static_cast<uint32_t>(new_rs1),
|
||||
static_cast<uint32_t>(new_rs2), static_cast<uint32_t>(res));
|
||||
WriteICCFlagsAddSub<tag_add>(state, static_cast<uint32_t>(new_rs1),
|
||||
static_cast<uint32_t>(new_rs2),
|
||||
static_cast<uint32_t>(res));
|
||||
|
||||
// All undefined.
|
||||
FLAG_XCC_CF = 0;
|
||||
@@ -549,7 +558,6 @@ DEF_SEM(MULSCC_R32, R32 src1, R32 src2, R32W dest) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(MULScc) = MULSCC_R32;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
namespace {
|
||||
|
||||
|
||||
template<typename S1, typename S2, typename D>
|
||||
template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(BMASK, S1 src1, S2 src2, D dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
@@ -37,23 +37,22 @@ DEF_SEM(BSHUFFLE, V64 src1, V64 src2, V64W dst) {
|
||||
auto mask = Read(GSR_MASK);
|
||||
|
||||
auto num_elems = NumVectorElems(rs1_vec);
|
||||
_Pragma("unroll")
|
||||
for (size_t i = 0; i < NumVectorElems(dst_vec); ++i) {
|
||||
auto e = UShr(mask, decltype(mask)(28 - i*4));
|
||||
_Pragma("unroll") for (size_t i = 0; i < NumVectorElems(dst_vec); ++i) {
|
||||
auto e = UShr(mask, decltype(mask)(28 - i * 4));
|
||||
auto index = UXor(e, Literal<decltype(e)>(0xff));
|
||||
if(index >= num_elems) {
|
||||
dst_vec = UInsertV8(
|
||||
dst_vec, num_elems - i, UExtractV8(rs2_vec, (2*num_elems-1) - index));
|
||||
if (index >= num_elems) {
|
||||
dst_vec = UInsertV8(dst_vec, num_elems - i,
|
||||
UExtractV8(rs2_vec, (2 * num_elems - 1) - index));
|
||||
} else {
|
||||
dst_vec = UInsertV8(
|
||||
dst_vec, num_elems - i, UExtractV8(rs2_vec, (num_elems-1) - index));
|
||||
dst_vec = UInsertV8(dst_vec, num_elems - i,
|
||||
UExtractV8(rs2_vec, (num_elems - 1) - index));
|
||||
}
|
||||
}
|
||||
UWriteV8(dst, dst_vec);
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(BMASK) = BMASK<R64, R64, R64W>;
|
||||
DEF_ISEL(BSHUFFLE) = BSHUFFLE;
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(JMPL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
T dst_pc, T dst_npc) {
|
||||
DEF_SEM(JMPL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst, T dst_pc, T dst_npc) {
|
||||
Write(dst, Read(pc_of_jmp));
|
||||
Write(dst_pc, Read(new_pc));
|
||||
Write(dst_npc, Read(new_npc));
|
||||
@@ -29,8 +28,8 @@ DEF_SEM(JMPL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
|
||||
// This is a variation on JMPL that also stores the return address.
|
||||
template <typename T>
|
||||
DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
T dst_pc, T dst_npc, T return_pc_dst) {
|
||||
DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst, T dst_pc, T dst_npc,
|
||||
T return_pc_dst) {
|
||||
Write(dst, Read(pc_of_jmp));
|
||||
Write(dst_pc, Read(new_pc));
|
||||
Write(dst_npc, Read(new_npc));
|
||||
@@ -41,32 +40,28 @@ DEF_SEM(CALL, PC pc_of_jmp, PC new_pc, PC new_npc, T dst,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(BA, PC new_taken_pc, PC new_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(BA, PC new_taken_pc, PC new_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_taken_pc));
|
||||
Write(npc_dst, Read(new_taken_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(BN, PC new_not_taken_pc, PC new_not_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(BN, PC new_not_taken_pc, PC new_not_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_not_taken_pc));
|
||||
Write(npc_dst, Read(new_not_taken_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(FBA, PC new_taken_pc, PC new_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(FBA, PC new_taken_pc, PC new_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_taken_pc));
|
||||
Write(npc_dst, Read(new_taken_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DEF_SEM(FBN, PC new_not_taken_pc, PC new_not_taken_npc,
|
||||
T pc_dst, T npc_dst) {
|
||||
DEF_SEM(FBN, PC new_not_taken_pc, PC new_not_taken_npc, T pc_dst, T npc_dst) {
|
||||
Write(pc_dst, Read(new_not_taken_pc));
|
||||
Write(npc_dst, Read(new_not_taken_npc));
|
||||
return memory;
|
||||
@@ -88,10 +83,10 @@ DEF_SEM(RETURN, PC new_pc, PC new_npc, T dst_pc, T dst_npc,
|
||||
// is placed inside of a delay slot.
|
||||
#define MAKE_BRANCH(name, cond, cc) \
|
||||
namespace { \
|
||||
DEF_SEM(name ## cond ## _ ## cc, R8W branch_taken, PC new_taken_pc, PC new_taken_npc, \
|
||||
PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
DEF_SEM(name##cond##_##cc, R8W branch_taken, PC new_taken_pc, \
|
||||
PC new_taken_npc, PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
R64W pc_dst, R64W npc_dst) { \
|
||||
if (Cond ## cond ## _ ## cc(state)) { \
|
||||
if (Cond##cond##_##cc(state)) { \
|
||||
Write(branch_taken, true); \
|
||||
Write(pc_dst, Read(new_taken_pc)); \
|
||||
Write(npc_dst, Read(new_taken_npc)); \
|
||||
@@ -103,11 +98,11 @@ DEF_SEM(RETURN, PC new_pc, PC new_npc, T dst_pc, T dst_npc,
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## cond ## _ ## cc) = name ## cond ## _ ## cc;
|
||||
DEF_ISEL(name##cond##_##cc) = name##cond##_##cc;
|
||||
|
||||
DEF_SEM(UNSUPPORTED_DCTI) {
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCUnhandledDCTI);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCUnhandledDCTI);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -138,8 +133,8 @@ DEF_ISEL(FBN_fcc2) = FBN<R64W>;
|
||||
DEF_ISEL(FBN_fcc3) = FBN<R64W>;
|
||||
|
||||
#define MAKE_BRANCH_CC(name, cond) \
|
||||
MAKE_BRANCH(name, cond, icc) \
|
||||
MAKE_BRANCH(name, cond, xcc) \
|
||||
MAKE_BRANCH(name, cond, icc) \
|
||||
MAKE_BRANCH(name, cond, xcc)
|
||||
|
||||
MAKE_BRANCH_CC(B, NE)
|
||||
MAKE_BRANCH_CC(B, E)
|
||||
@@ -157,10 +152,10 @@ MAKE_BRANCH_CC(B, VC)
|
||||
MAKE_BRANCH_CC(B, VS)
|
||||
|
||||
#define MAKE_BRANCH_F(name, cond) \
|
||||
MAKE_BRANCH(name, cond, fcc0);\
|
||||
MAKE_BRANCH(name, cond, fcc1);\
|
||||
MAKE_BRANCH(name, cond, fcc2);\
|
||||
MAKE_BRANCH(name, cond, fcc3);\
|
||||
MAKE_BRANCH(name, cond, fcc0); \
|
||||
MAKE_BRANCH(name, cond, fcc1); \
|
||||
MAKE_BRANCH(name, cond, fcc2); \
|
||||
MAKE_BRANCH(name, cond, fcc3);
|
||||
|
||||
MAKE_BRANCH_F(FB, U);
|
||||
MAKE_BRANCH_F(FB, G);
|
||||
@@ -183,12 +178,12 @@ MAKE_BRANCH_F(FB, O);
|
||||
|
||||
#define MAKE_BRANCH(name, cond) \
|
||||
namespace { \
|
||||
template<typename S> \
|
||||
DEF_SEM(name ## cond, R8W branch_taken, S reg_cc, PC new_taken_pc, PC new_taken_npc, \
|
||||
PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
template <typename S> \
|
||||
DEF_SEM(name##cond, R8W branch_taken, S reg_cc, PC new_taken_pc, \
|
||||
PC new_taken_npc, PC new_not_taken_pc, PC new_not_taken_npc, \
|
||||
R64W pc_dst, R64W npc_dst) { \
|
||||
auto cc = Read(reg_cc); \
|
||||
if (CondR ## cond (state, cc)) { \
|
||||
if (CondR##cond(state, cc)) { \
|
||||
Write(branch_taken, true); \
|
||||
Write(pc_dst, Read(new_taken_pc)); \
|
||||
Write(npc_dst, Read(new_taken_npc)); \
|
||||
@@ -200,7 +195,7 @@ MAKE_BRANCH_F(FB, O);
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## cond ) = name ## cond <R64>;
|
||||
DEF_ISEL(name##cond) = name##cond<R64>;
|
||||
|
||||
MAKE_BRANCH(BR, Z)
|
||||
MAKE_BRANCH(BR, LEZ)
|
||||
|
||||
@@ -28,8 +28,8 @@ template <typename S, typename D>
|
||||
DEF_SEM(STA, R8 asi, S src, D dst) {
|
||||
WriteZExt(dst, Read(src));
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(STF, RF32 src, MF32W dst) {
|
||||
@@ -48,16 +48,16 @@ DEF_SEM(STFA, R8 asi, RF32 src, MF32W dst) {
|
||||
auto lhs = Read(src);
|
||||
Write(dst, lhs);
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(STDFA, R8 asi, RF64 src, MF64W dst) {
|
||||
auto lhs = Read(src);
|
||||
Write(dst, lhs);
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(STTW_R32, R32 src1, R32 src2, MV64W dst) {
|
||||
@@ -87,7 +87,7 @@ DEF_SEM(SETHI, S src, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
template<typename S1, typename S2, typename D>
|
||||
template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(SET, S1 src1, S2 src2, D dst) {
|
||||
const auto high_bits = Read(src1);
|
||||
const auto low_bits = Read(src2);
|
||||
@@ -113,7 +113,7 @@ DEF_SEM(SETHI_ADD, S1 src1, S2 src2, D1 dst1, D2 dst2) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(STB) = ST<R8, M8W>;
|
||||
DEF_ISEL(STH) = ST<R16, M16W>;
|
||||
@@ -153,8 +153,8 @@ template <typename S, typename D>
|
||||
DEF_SEM(LDUA, R8 asi, S src, D dst) {
|
||||
WriteZExt(dst, Read(src));
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
template <typename S, typename D>
|
||||
@@ -167,8 +167,8 @@ template <typename S, typename D>
|
||||
DEF_SEM(LDSA, R8 asi, S src, D dst) {
|
||||
WriteSExt(dst, Signed(Read(src)));
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(LDF, MV64 src, R32W dst) {
|
||||
@@ -187,16 +187,16 @@ DEF_SEM(LDFA, R8 asi, MV64 src, R32W dst) {
|
||||
auto vec = UReadV32(src);
|
||||
WriteZExt(dst, UExtractV32(vec, 0));
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(LDDFA, R8 asi, MV64 src, R64W dst) {
|
||||
auto vec = UReadV64(src);
|
||||
WriteZExt(dst, UExtractV64(vec, 0));
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(LDTW_IMMEXC, MV64 src_mem, R32W dst1, R32W dst2) {
|
||||
@@ -215,7 +215,7 @@ DEF_SEM(LDTW_R32EXC, MV64 src_mem, R32W dst1, R32W dst2) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(LDUB) = LDU<M8, R64W>;
|
||||
DEF_ISEL(LDUH) = LDU<M16, R64W>;
|
||||
@@ -261,8 +261,8 @@ DEF_SEM(LDSTUBA, R8 asi, M8W src_mem, R dst) {
|
||||
auto mem_val = Read(src_mem);
|
||||
WriteZExt(dst, mem_val);
|
||||
Write(src_mem, static_cast<uint8_t>(0xffu));
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
DEF_SEM(CASA, R32 src1, R32 src2, R32W dst) {
|
||||
@@ -291,11 +291,11 @@ DEF_SEM(SWAPA, R8 asi, M32W src, R64W dst) {
|
||||
WriteZExt(dst, old_src);
|
||||
WriteTrunc(src, old_dst);
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(LDSTUB) = LDSTUB<R64W>;
|
||||
DEF_ISEL(LDSTUBA) = LDSTUBA<R64W>;
|
||||
@@ -325,7 +325,7 @@ DEF_SEM(MOVN_xcc, S src, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(MOVA_icc) = MOVA_icc<R64, R64W>;
|
||||
DEF_ISEL(MOVA_xcc) = MOVA_xcc<R64, R64W>;
|
||||
@@ -335,17 +335,17 @@ DEF_ISEL(MOVN_xcc) = MOVN_xcc<R64, R64W>;
|
||||
#define MAKE_SEMANTICS(name, cond, cc) \
|
||||
namespace { \
|
||||
template <typename S, typename D> \
|
||||
DEF_SEM(name ## cond ## _ ## cc, S src, D dst) { \
|
||||
DEF_SEM(name##cond##_##cc, S src, D dst) { \
|
||||
auto new_value = Read(src); \
|
||||
auto old_value = Read(dst); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_value, \
|
||||
decltype(new_value)(old_value)); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = \
|
||||
Select(branch_taken, new_value, decltype(new_value)(old_value)); \
|
||||
WriteZExt(dst, value); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(MOV ## cond ## _ ## cc) = name ## cond ## _ ## cc<R64, R64W>;
|
||||
DEF_ISEL(MOV##cond##_##cc) = name##cond##_##cc<R64, R64W>;
|
||||
|
||||
#define MAKE_SEMANTICS_CC(name, cond) \
|
||||
MAKE_SEMANTICS(name, cond, icc) \
|
||||
@@ -394,18 +394,19 @@ MAKE_SEMANTICS_FCC(MOVF, O)
|
||||
|
||||
#define MAKE_SEMANTICS(name, cond) \
|
||||
namespace { \
|
||||
template <typename C, typename S, typename D> \
|
||||
DEF_SEM(name ## cond, C reg_cc, S src, D dst) { \
|
||||
template <typename C, typename S, typename D> \
|
||||
DEF_SEM(name##cond, C reg_cc, S src, D dst) { \
|
||||
auto new_value = Read(src); \
|
||||
auto old_value = Read(dst); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_value, decltype(new_value)(old_value)); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = \
|
||||
Select(cond_taken, new_value, decltype(new_value)(old_value)); \
|
||||
WriteZExt(dst, value); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## cond) = name ## cond<R64, R64, R64W>;
|
||||
DEF_ISEL(name##cond) = name##cond<R64, R64, R64W>;
|
||||
|
||||
MAKE_SEMANTICS(MOVR, Z)
|
||||
MAKE_SEMANTICS(MOVR, LEZ)
|
||||
@@ -454,7 +455,7 @@ DEF_SEM(FMoveNeverQuad, RF64 src, RF64W dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FMOVSA_icc) = FMoveAlwaysSingle;
|
||||
DEF_ISEL(FMOVSA_xcc) = FMoveAlwaysSingle;
|
||||
@@ -501,43 +502,40 @@ DEF_ISEL(FMOVQN_fcc3) = FMoveNeverQuad;
|
||||
|
||||
#define MAKE_SEMANTICS(name, cond, cc) \
|
||||
namespace { \
|
||||
DEF_SEM(FMOVS ## cond ## _ ## cc, RF32 src, RF32W dst) { \
|
||||
DEF_SEM(FMOVS##cond##_##cc, RF32 src, RF32W dst) { \
|
||||
auto new_val = Read(src); \
|
||||
auto old_val = Read(dst); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = Select(branch_taken, new_val, decltype(new_val)(old_val)); \
|
||||
Write(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(FMOVD ## cond ## _ ## cc, RF64 src, RF64W dst) { \
|
||||
auto new_val = Read(src); \
|
||||
auto old_val = Read(dst); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
DEF_SEM(FMOVD##cond##_##cc, RF64 src, RF64W dst) { \
|
||||
auto new_val = Read(src); \
|
||||
auto old_val = Read(dst); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = Select(branch_taken, new_val, decltype(new_val)(old_val)); \
|
||||
Write(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(FMOVQ ## cond ## _ ## cc, RF64 src, RF64W dst) { \
|
||||
auto new_val = Read(src); \
|
||||
auto old_val = Read(dst); \
|
||||
auto branch_taken = Cond ## cond ## _ ## cc(state); \
|
||||
auto value = Select(branch_taken, new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
DEF_SEM(FMOVQ##cond##_##cc, RF64 src, RF64W dst) { \
|
||||
auto new_val = Read(src); \
|
||||
auto old_val = Read(dst); \
|
||||
auto branch_taken = Cond##cond##_##cc(state); \
|
||||
auto value = Select(branch_taken, new_val, decltype(new_val)(old_val)); \
|
||||
Write(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(FMOVS ## cond ## _ ## cc) = FMOVS ## cond ## _ ## cc; \
|
||||
DEF_ISEL(FMOVD ## cond ## _ ## cc) = FMOVD ## cond ## _ ## cc; \
|
||||
DEF_ISEL(FMOVQ ## cond ## _ ## cc) = FMOVQ ## cond ## _ ## cc;
|
||||
DEF_ISEL(FMOVS##cond##_##cc) = FMOVS##cond##_##cc; \
|
||||
DEF_ISEL(FMOVD##cond##_##cc) = FMOVD##cond##_##cc; \
|
||||
DEF_ISEL(FMOVQ##cond##_##cc) = FMOVQ##cond##_##cc;
|
||||
|
||||
#define MAKE_SEMANTICS_CC(name, cond) \
|
||||
MAKE_SEMANTICS(name, cond, icc) \
|
||||
@@ -585,49 +583,43 @@ MAKE_SEMANTICS_FCC(FMOV, O)
|
||||
|
||||
#define MAKE_SEMANTICS(name, cond) \
|
||||
namespace { \
|
||||
DEF_SEM(name ## S ## cond, R64 reg_cc, V32 src, V32W dst) { \
|
||||
DEF_SEM(name##S##cond, R64 reg_cc, V32 src, V32W dst) { \
|
||||
auto new_val = FExtractV32(FReadV32(src), 0); \
|
||||
auto old_val = FExtractV32(FReadV32(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, \
|
||||
new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV32(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(name ## D ## cond, R64 reg_cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, \
|
||||
new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(name ## Q ## cond, R64 reg_cc, V64 src, V64W dst) { \
|
||||
DEF_SEM(name##D##cond, R64 reg_cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR ## cond(state, cc); \
|
||||
auto value = Select(cond_taken, \
|
||||
new_val, \
|
||||
decltype(new_val)(old_val)); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(name##Q##cond, R64 reg_cc, V64 src, V64W dst) { \
|
||||
auto new_val = FExtractV64(FReadV64(src), 0); \
|
||||
auto old_val = FExtractV64(FReadV64(dst), 0); \
|
||||
auto cc = Read(reg_cc); \
|
||||
auto cond_taken = CondR##cond(state, cc); \
|
||||
auto value = Select(cond_taken, new_val, decltype(new_val)(old_val)); \
|
||||
FWriteV64(dst, value); \
|
||||
WriteTrunc(FSR_CEXC, 0); \
|
||||
WriteTrunc(FSR_FTT, 0); \
|
||||
return memory; \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(name ## S ## cond) = name ## S ## cond; \
|
||||
DEF_ISEL(name ## D ## cond) = name ## D ## cond; \
|
||||
DEF_ISEL(name ## Q ## cond) = name ## Q ## cond;
|
||||
DEF_ISEL(name##S##cond) = name##S##cond; \
|
||||
DEF_ISEL(name##D##cond) = name##D##cond; \
|
||||
DEF_ISEL(name##Q##cond) = name##Q##cond;
|
||||
|
||||
MAKE_SEMANTICS(FMOVR, Z)
|
||||
MAKE_SEMANTICS(FMOVR, LEZ)
|
||||
@@ -635,4 +627,3 @@ MAKE_SEMANTICS(FMOVR, LZ)
|
||||
MAKE_SEMANTICS(FMOVR, NZ)
|
||||
MAKE_SEMANTICS(FMOVR, GZ)
|
||||
MAKE_SEMANTICS(FMOVR, GEZ)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ DEF_SEM(FMOVQ, RF64 src, RF64W dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FMOVS) = FMOVS;
|
||||
DEF_ISEL(FMOVD) = FMOVD;
|
||||
@@ -135,7 +135,7 @@ DEF_SEM(FNOT2D, RF64 src, RF64W dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FABSS) = FABSS;
|
||||
DEF_ISEL(FABSD) = FABSD;
|
||||
@@ -158,15 +158,15 @@ namespace {
|
||||
DEF_SEM(FADDS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sum = FAdd(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sum);
|
||||
return memory;
|
||||
@@ -175,15 +175,15 @@ DEF_SEM(FADDS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FADDD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sum = FAdd64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sum);
|
||||
return memory;
|
||||
@@ -192,15 +192,15 @@ DEF_SEM(FADDD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
DEF_SEM(FSUBS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sub = FSub32(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sub);
|
||||
return memory;
|
||||
@@ -209,15 +209,15 @@ DEF_SEM(FSUBS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FSUBD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto sub = FSub64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, sub);
|
||||
return memory;
|
||||
@@ -226,15 +226,15 @@ DEF_SEM(FSUBD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
DEF_SEM(FMULS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except =
|
||||
__remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul32(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
@@ -243,30 +243,32 @@ DEF_SEM(FMULS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FMULD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(FsMULD, RF32 src1, RF32 src2, RF64W dst) {
|
||||
DEF_SEM(FsMULD, RF32 src1, RF32 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto mul = FMul64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, mul);
|
||||
return memory;
|
||||
@@ -275,14 +277,15 @@ DEF_SEM(FsMULD, RF32 src1, RF32 src2, RF64W dst) {
|
||||
DEF_SEM(FDIVS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto div = FDiv32(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, div);
|
||||
return memory;
|
||||
@@ -291,20 +294,21 @@ DEF_SEM(FDIVS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
DEF_SEM(FDIVD, RF64 src1, RF64 src2, RF64W dst) {
|
||||
auto lhs = Read(src1);
|
||||
auto rhs = Read(src2);
|
||||
|
||||
// Test and clear the Floating point exception and prevent
|
||||
// recording of the instructions
|
||||
auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT);
|
||||
BarrierReorder();
|
||||
auto div = FDiv64(lhs, rhs);
|
||||
BarrierReorder();
|
||||
auto new_except = __remill_fpu_exception_test_and_clear(
|
||||
FE_ALL_EXCEPT, old_except);
|
||||
auto new_except =
|
||||
__remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, old_except);
|
||||
SetFPSRStatusFlags(state, new_except);
|
||||
Write(dst, div);
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FADDS) = FADDS;
|
||||
DEF_ISEL(FADDD) = FADDD;
|
||||
@@ -431,7 +435,7 @@ DEF_SEM(FNADDS, RF32 src1, RF32 src2, RF32W dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FSTOX) = FSTOX;
|
||||
DEF_ISEL(FSTOI) = FSTOI;
|
||||
@@ -466,23 +470,23 @@ DEF_ISEL(FNADDD) = FNADDD;
|
||||
|
||||
#define MAKE_COMPARE(fcc) \
|
||||
template <typename S> \
|
||||
void FCompare_ ## fcc(State &state, Memory *memory, \
|
||||
S val1, S val2, bool signal) { \
|
||||
void FCompare_##fcc(State &state, Memory *memory, S val1, S val2, \
|
||||
bool signal) { \
|
||||
if (std::isnan(val1) || std::isnan(val2)) { \
|
||||
Write(state.fsr.fcc, Literal<R8>(3)); \
|
||||
} else { \
|
||||
if (FCmpEq(val1, val2)) { \
|
||||
/* result = '00'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(0)); \
|
||||
/* result = '00'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(0)); \
|
||||
} else if (FCmpLt(val1, val2)) { \
|
||||
/* result = '01'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(1)); \
|
||||
} else { /* FCmpGt(val1, val2) */ \
|
||||
/* result = '01'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(1)); \
|
||||
} else { /* FCmpGt(val1, val2) */ \
|
||||
/* result = '10'; */ \
|
||||
Write(state.fsr.fcc, Literal<R8>(2)); \
|
||||
Write(state.fsr.fcc, Literal<R8>(2)); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
@@ -492,51 +496,51 @@ MAKE_COMPARE(fcc1)
|
||||
MAKE_COMPARE(fcc2)
|
||||
MAKE_COMPARE(fcc3)
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#undef MAKE_COMPARE
|
||||
|
||||
#define MAKE_SEMANTICS_FCMP(fcc) \
|
||||
DEF_SEM(FCMPS_ ## fcc, RF32 src1, RF32 src2) { \
|
||||
DEF_SEM(FCMPS_##fcc, RF32 src1, RF32 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPD_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
\
|
||||
DEF_SEM(FCMPD_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPQ_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
\
|
||||
DEF_SEM(FCMPQ_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
}
|
||||
|
||||
#define MAKE_SEMANTICS_FCMPE(fcc) \
|
||||
DEF_SEM(FCMPES_ ## fcc, RF32 src1, RF32 src2) { \
|
||||
DEF_SEM(FCMPES_##fcc, RF32 src1, RF32 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPED_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
\
|
||||
DEF_SEM(FCMPED_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
} \
|
||||
\
|
||||
DEF_SEM(FCMPEQ_ ## fcc, RF64 src1, RF64 src2) { \
|
||||
\
|
||||
DEF_SEM(FCMPEQ_##fcc, RF64 src1, RF64 src2) { \
|
||||
auto val1 = Read(src1); \
|
||||
auto val2 = Read(src2); \
|
||||
FCompare_ ## fcc(state, memory, val1, val2, false); \
|
||||
FCompare_##fcc(state, memory, val1, val2, false); \
|
||||
return memory; \
|
||||
}
|
||||
|
||||
@@ -552,7 +556,7 @@ MAKE_SEMANTICS_FCMPE(fcc1)
|
||||
MAKE_SEMANTICS_FCMPE(fcc2)
|
||||
MAKE_SEMANTICS_FCMPE(fcc3)
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#undef MAKE_SEMANTICS_FCMP
|
||||
#undef MAKE_SEMANTICS_FCMPE
|
||||
@@ -588,4 +592,3 @@ DEF_ISEL(FCMPEQ_fcc2) = FCMPEQ_fcc2;
|
||||
DEF_ISEL(FCMPES_fcc3) = FCMPES_fcc3;
|
||||
DEF_ISEL(FCMPED_fcc3) = FCMPED_fcc3;
|
||||
DEF_ISEL(FCMPEQ_fcc3) = FCMPEQ_fcc3;
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE void SetFlagsLogical(
|
||||
State &state, uint64_t lhs, uint64_t rhs, uint64_t res) {
|
||||
ALWAYS_INLINE void SetFlagsLogical(State &state, uint64_t lhs, uint64_t rhs,
|
||||
uint64_t res) {
|
||||
const auto res_32 = static_cast<uint32_t>(res);
|
||||
FLAG_ICC_CF = false;
|
||||
FLAG_ICC_ZF = ZeroFlag(res_32);
|
||||
@@ -32,7 +32,7 @@ ALWAYS_INLINE void SetFlagsLogical(
|
||||
FLAG_XCC_VF = false;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Logical Operations
|
||||
namespace {
|
||||
@@ -56,7 +56,7 @@ DEF_SEM(ANDCC, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// AND, ANDcc
|
||||
DEF_ISEL(AND) = AND<R64, R64, R64W>;
|
||||
@@ -83,7 +83,7 @@ DEF_SEM(ANDNCC, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// ANDN, ANDNcc
|
||||
DEF_ISEL(ANDN) = ANDN<R64, R64, R64W>;
|
||||
@@ -110,7 +110,7 @@ DEF_SEM(ORCC, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(OR) = OR<R64, R64, R64W>;
|
||||
DEF_ISEL(ORcc) = ORCC<R64, R64, R64W>;
|
||||
@@ -136,7 +136,7 @@ DEF_SEM(ORNCC, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(ORN) = ORN<R64, R64, R64W>;
|
||||
DEF_ISEL(ORNcc) = ORNCC<R64, R64, R64W>;
|
||||
@@ -162,7 +162,7 @@ DEF_SEM(XORCC, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(XOR) = XOR<R64, R64, R64W>;
|
||||
DEF_ISEL(XORcc) = XORCC<R64, R64, R64W>;
|
||||
@@ -188,7 +188,7 @@ DEF_SEM(XNORCC, S1 src1, S2 src2, D dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(XNOR) = XNOR<R64, R64, R64W>;
|
||||
DEF_ISEL(XNORcc) = XNORCC<R64, R64, R64W>;
|
||||
@@ -231,4 +231,3 @@ DEF_ISEL(SRA) = SRA<R32, I32, R64W>;
|
||||
DEF_ISEL(SLLX) = SLL<R64, I64, R64W>;
|
||||
DEF_ISEL(SRLX) = SRL<R64, I64, R64W>;
|
||||
DEF_ISEL(SRAX) = SRA<R64, I64, R64W>;
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ DEF_SEM(PREFETCH, M64 address, I32 fcn) {
|
||||
|
||||
DEF_SEM(PREFETCHA, R8 asi, M64 address, I32 fcn) {
|
||||
HYPER_CALL_VECTOR = Read(asi);
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::kSPARCSetAsiRegister);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCSetAsiRegister);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -71,7 +71,8 @@ DEF_ISEL(PREFETCHA) = PREFETCHA;
|
||||
namespace {
|
||||
|
||||
template <typename S1, typename S2, typename D>
|
||||
DEF_SEM(SAVE, S1 src1, S2 src2, D dst, RegisterWindow *window, RegisterWindow *&prev_window) {
|
||||
DEF_SEM(SAVE, S1 src1, S2 src2, D dst, RegisterWindow *window,
|
||||
RegisterWindow *&prev_window) {
|
||||
addr_t sp_base = Read(src1);
|
||||
addr_t sp_offset = Read(src2);
|
||||
addr_t new_sp = UAdd(sp_base, sp_offset);
|
||||
|
||||
@@ -22,47 +22,52 @@
|
||||
// is placed inside of a delay slot.
|
||||
#define MAKE_TRAP(cond, cc) \
|
||||
namespace { \
|
||||
DEF_SEM(T ## cond ## _ ## cc, R8W branch_taken, PC new_pc, PC new_npc, \
|
||||
I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) { \
|
||||
Write(branch_taken, Cond ## cond ## _ ## cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond ## cond; \
|
||||
DEF_SEM(T##cond##_##cc, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, \
|
||||
I32 vec_b, R64W pc_dst, R64W npc_dst) { \
|
||||
Write(branch_taken, Cond##cond##_##cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond##cond; \
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(T ## cond ## _sync ## _ ## cc, R8W branch_taken, PC new_pc, PC new_npc, \
|
||||
DEF_SEM(T##cond##_sync##_##cc, R8W branch_taken, PC new_pc, PC new_npc, \
|
||||
I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) { \
|
||||
Write(branch_taken, Cond ## cond ## _ ## cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond ## cond; \
|
||||
Write(branch_taken, Cond##cond##_##cc(state)); \
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCond##cond; \
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu); \
|
||||
return __remill_sync_hyper_call( \
|
||||
state, memory, SyncHyperCall::kSPARCTrapCond ## cond); \
|
||||
return __remill_sync_hyper_call(state, memory, \
|
||||
SyncHyperCall::kSPARCTrapCond##cond); \
|
||||
} \
|
||||
} \
|
||||
DEF_ISEL(T ## cond ## _ ## cc) = T ## cond ## _ ## cc; \
|
||||
DEF_ISEL(T ## cond ## _sync ## _ ## cc) = T ## cond ## _sync ## _ ## cc
|
||||
DEF_ISEL(T##cond##_##cc) = T##cond##_##cc; \
|
||||
DEF_ISEL(T##cond##_sync##_##cc) = T##cond##_sync##_##cc
|
||||
|
||||
namespace {
|
||||
|
||||
DEF_SEM(TA, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) {
|
||||
DEF_SEM(TA, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R64W pc_dst, R64W npc_dst) {
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCondA;
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu);
|
||||
Write(branch_taken, true);
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(TA_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) {
|
||||
DEF_SEM(TA_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R64W pc_dst, R64W npc_dst) {
|
||||
HYPER_CALL = AsyncHyperCall::kSPARCTrapCondA;
|
||||
HYPER_CALL_VECTOR = UAnd(UAdd(Read(vec_a), Read(vec_b)), 0x7fu);
|
||||
return __remill_sync_hyper_call(state, memory, SyncHyperCall::kSPARCTrapCondA);
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kSPARCTrapCondA);
|
||||
}
|
||||
|
||||
DEF_SEM(TN, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) {
|
||||
DEF_SEM(TN, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R64W pc_dst, R64W npc_dst) {
|
||||
Write(pc_dst, Read(new_pc));
|
||||
Write(npc_dst, Read(new_npc));
|
||||
return memory;
|
||||
}
|
||||
|
||||
DEF_SEM(TN_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b, R64W pc_dst, R64W npc_dst) {
|
||||
DEF_SEM(TN_sync, R8W branch_taken, PC new_pc, PC new_npc, I32 vec_a, I32 vec_b,
|
||||
R64W pc_dst, R64W npc_dst) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -128,4 +133,3 @@ DEF_SEM(ILLTRAP_ASYNC, I32 struct_size) {
|
||||
|
||||
DEF_ISEL(ILLTRAP_SYNC) = ILLTRAP_SYNC; // In a delay slot.
|
||||
DEF_ISEL(ILLTRAP_ASYNC) = ILLTRAP_ASYNC; // Not in a delay slot.
|
||||
|
||||
|
||||
@@ -54,27 +54,29 @@ DEF_SEM(EDGE8CC, R64 src1, R64 src2, R64W dst) {
|
||||
auto rs2 = Read(src2);
|
||||
auto imask = Literal<I64>(0x7);
|
||||
auto shift = Literal<I64>(3);
|
||||
auto omask= Literal<I8>(0xff);
|
||||
auto omask = Literal<I8>(0xff);
|
||||
|
||||
// l1 = rs1 & 0x7 rs[3:0]
|
||||
auto l1 = UAnd(rs1, imask);
|
||||
auto rs1_shifted = UShr(rs1, shift);
|
||||
|
||||
// l2 = rs2 & 0x7 rs[3:0]
|
||||
auto l2 = UAnd(rs2, imask);
|
||||
auto rs2_shifted = UShr(rs2, shift);
|
||||
auto left_edge = UShr(omask, decltype(omask)(l1));
|
||||
auto right_edge = UShl(omask, decltype(omask)(USub(imask, l2)));
|
||||
auto value = Select(
|
||||
UCmpEq(rs1_shifted, rs2_shifted),
|
||||
left_edge, UAnd(right_edge, left_edge));
|
||||
auto value = Select(UCmpEq(rs1_shifted, rs2_shifted), left_edge,
|
||||
UAnd(right_edge, left_edge));
|
||||
auto diff = USub(rs1, rs2);
|
||||
WriteICCFlagsAddSub<tag_sub>(state, Literal<uint32_t>(rs1), Literal<uint32_t>(rs2), Literal<uint32_t>(diff));
|
||||
WriteICCFlagsAddSub<tag_sub>(state, Literal<uint32_t>(rs1),
|
||||
Literal<uint32_t>(rs2), Literal<uint32_t>(diff));
|
||||
WriteXCCFlagsAddSub<tag_sub>(state, rs1, rs2, diff);
|
||||
WriteZExt(dst, value);
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(FORS) = PACK_ORS<V32W, V32W, V32W>;
|
||||
DEF_ISEL(FORD) = PACK_ORD<V64W, V64W, V64W>;
|
||||
@@ -89,17 +91,20 @@ namespace {
|
||||
DEF_SEM(IMPDEP1, I32 opf) {
|
||||
HYPER_CALL_VECTOR = Literal<decltype(state.hyper_call_vector)>(Read(opf));
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction, kSPARC64EmulateInstruction));
|
||||
state, memory,
|
||||
SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction,
|
||||
kSPARC64EmulateInstruction));
|
||||
}
|
||||
|
||||
DEF_SEM(IMPDEP2, I32 opf) {
|
||||
HYPER_CALL_VECTOR = Literal<decltype(state.hyper_call_vector)>(Read(opf));
|
||||
return __remill_sync_hyper_call(
|
||||
state, memory, SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction, kSPARC64EmulateInstruction));
|
||||
state, memory,
|
||||
SyncHyperCall::IF_32BIT_ELSE(kSPARC32EmulateInstruction,
|
||||
kSPARC64EmulateInstruction));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(IMPDEP1) = IMPDEP1;
|
||||
DEF_ISEL(IMPDEP2) = IMPDEP2;
|
||||
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
|
||||
#define MAKE_SEMANTICS_WR(op) \
|
||||
namespace { \
|
||||
DEF_SEM(WR ## op, R32 src1, I32 src2) { \
|
||||
auto lhs = Read(src1); \
|
||||
auto rhs = Read(src2); \
|
||||
auto res = UXor(lhs, rhs); \
|
||||
WriteZExt(ASR_ ## op, res); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(WR##op, R32 src1, I32 src2) { \
|
||||
auto lhs = Read(src1); \
|
||||
auto rhs = Read(src2); \
|
||||
auto res = UXor(lhs, rhs); \
|
||||
WriteZExt(ASR_##op, res); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_ISEL(WR ## op) = WR ## op;
|
||||
} \
|
||||
DEF_ISEL(WR##op) = WR##op;
|
||||
|
||||
MAKE_SEMANTICS_WR(Y)
|
||||
MAKE_SEMANTICS_WR(PAUSE)
|
||||
@@ -41,21 +41,21 @@ MAKE_SEMANTICS_WR(ASI)
|
||||
|
||||
#define MAKE_SEMANTICS_RD(op) \
|
||||
namespace { \
|
||||
DEF_SEM(RD ## op, R64W dst) { \
|
||||
auto asr = Read(ASR_ ## op); \
|
||||
WriteZExt(dst, asr); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_SEM(RD##op, R64W dst) { \
|
||||
auto asr = Read(ASR_##op); \
|
||||
WriteZExt(dst, asr); \
|
||||
return memory; \
|
||||
} \
|
||||
DEF_ISEL(RD ## op) = RD ## op;
|
||||
} \
|
||||
DEF_ISEL(RD##op) = RD##op;
|
||||
|
||||
MAKE_SEMANTICS_RD(Y)
|
||||
MAKE_SEMANTICS_RD(ASI)
|
||||
|
||||
//MAKE_SEMANTICS_RD(PC)
|
||||
MAKE_SEMANTICS_RD(FPRS)
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename R>
|
||||
@@ -192,7 +192,7 @@ DEF_SEM(WRPRGL, R64 src1, R src2) {
|
||||
WriteTrunc(PSR_GL, res);
|
||||
return memory;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(WRPRTPC) = WRPRTPC<R64>;
|
||||
DEF_ISEL(WRPRTPC_IMM) = WRPRTPC<I64>;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "XED.h"
|
||||
#include "remill/Arch/Instruction.h"
|
||||
#include "remill/Arch/Name.h"
|
||||
#include "remill/BC/ABI.h"
|
||||
@@ -37,8 +38,6 @@
|
||||
#include "remill/BC/Version.h"
|
||||
#include "remill/OS/OS.h"
|
||||
|
||||
#include "XED.h"
|
||||
|
||||
// clang-format off
|
||||
#define HAS_FEATURE_AVX 1
|
||||
#define HAS_FEATURE_AVX512 1
|
||||
@@ -809,7 +808,6 @@ class X86Arch final : public Arch {
|
||||
llvm::Function *bb_func) const override;
|
||||
|
||||
private:
|
||||
|
||||
X86Arch(void) = delete;
|
||||
};
|
||||
|
||||
@@ -1067,8 +1065,8 @@ bool X86Arch::DecodeInstruction(uint64_t address, std::string_view inst_bytes,
|
||||
|
||||
if (xed_decoded_inst_is_xacquire(xedd) ||
|
||||
xed_decoded_inst_is_xrelease(xedd)) {
|
||||
LOG(ERROR) << "Ignoring XACQUIRE/XRELEASE prefix at " << std::hex
|
||||
<< inst.pc << std::dec;
|
||||
LOG(ERROR) << "Ignoring XACQUIRE/XRELEASE prefix at " << std::hex << inst.pc
|
||||
<< std::dec;
|
||||
}
|
||||
|
||||
// Make sure we disallow decoding of AVX instructions when running with non-
|
||||
|
||||
@@ -165,11 +165,12 @@ DEF_HELPER(PopFromStack)->T {
|
||||
return val;
|
||||
}
|
||||
|
||||
DEF_HELPER(SquareRoot32, float32_t src_float) -> float32_t {
|
||||
DEF_HELPER(SquareRoot32, float32_t src_float)->float32_t {
|
||||
auto square_root = src_float;
|
||||
|
||||
// Special cases for invalid square root operations. See Intel manual, Table E-10.
|
||||
if (IsNaN(src_float)) {
|
||||
|
||||
// If src is SNaN, return the SNaN converted to a QNaN:
|
||||
if (IsSignalingNaN(src_float)) {
|
||||
nan32_t temp_nan = {src_float};
|
||||
@@ -181,6 +182,7 @@ DEF_HELPER(SquareRoot32, float32_t src_float) -> float32_t {
|
||||
square_root = src_float;
|
||||
}
|
||||
} else { // a number, that is, not a NaN
|
||||
|
||||
// A negative operand (except -0.0) results in the QNaN indefinite value.
|
||||
if (IsNegative(src_float) && src_float != -0.0) {
|
||||
uint32_t indef_qnan = 0xFFC00000U;
|
||||
|
||||
@@ -42,13 +42,13 @@ DEF_SEM(VPBROADCASTB, D dst, S1 src1) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
template<typename S2>
|
||||
template <typename S2>
|
||||
DEF_SEM(VINSERTF128, VV256W dst, V256 src1, S2 src2, I8 src3) {
|
||||
auto dst_vec = UReadV128(src1);
|
||||
auto src2_vec = UReadV128(src2);
|
||||
auto src3_i8 = Read(src3);
|
||||
auto i = static_cast<unsigned>(src3_i8 & 1u);
|
||||
dst_vec = UInsertV128(dst_vec, i, UExtractV128(src2_vec, 0));
|
||||
dst_vec = UInsertV128(dst_vec, i, UExtractV128(src2_vec, 0));
|
||||
UWriteV128(dst, dst_vec);
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Disable the "loop not unrolled warnings"
|
||||
#pragma clang diagnostic ignored "-Wpass-failed"
|
||||
|
||||
@@ -115,7 +116,7 @@ DEF_SEM(DoNothing) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
template <typename...Args>
|
||||
template <typename... Args>
|
||||
DEF_SEM(DoNothingWithParam, Args...) {
|
||||
return memory;
|
||||
}
|
||||
@@ -176,9 +177,11 @@ DEF_ISEL(XLAT) = DoXLAT;
|
||||
|
||||
DEF_ISEL(CPUID) = DoCPUID;
|
||||
|
||||
DEF_ISEL(UD0_GPR32_MEMd) = DoNothingWithParam<R32, M32, IF_32BIT_ELSE(R32W, R64W)>;
|
||||
DEF_ISEL(UD0_GPR32_MEMd) =
|
||||
DoNothingWithParam<R32, M32, IF_32BIT_ELSE(R32W, R64W)>;
|
||||
|
||||
DEF_ISEL(UD1_GPR32_MEMd) = DoNothingWithParam<R32, M32, IF_32BIT_ELSE(R32W, R64W)>;
|
||||
DEF_ISEL(UD1_GPR32_MEMd) =
|
||||
DoNothingWithParam<R32, M32, IF_32BIT_ELSE(R32W, R64W)>;
|
||||
|
||||
DEF_ISEL(UD2) = DoNothingWithParam<IF_32BIT_ELSE(R32W, R64W)>;
|
||||
|
||||
|
||||
@@ -1961,8 +1961,7 @@ DEF_SEM(PFMAX, D dst, S1 src_dst, S2 src) {
|
||||
auto src1 = FReadV32(src_dst);
|
||||
auto src2 = FReadV32(src);
|
||||
auto out = src1;
|
||||
_Pragma("unroll")
|
||||
for (auto i = 0u; i < 2; ++i) {
|
||||
_Pragma("unroll") for (auto i = 0u; i < 2; ++i) {
|
||||
auto s1_val = FExtractV32(src1, i);
|
||||
auto s2_val = FExtractV32(src2, i);
|
||||
if (!std::isunordered(s1_val, s2_val) && s2_val > s1_val) {
|
||||
@@ -1978,8 +1977,7 @@ DEF_SEM(PFMIN, D dst, S1 src_dst, S2 src) {
|
||||
auto src1 = FReadV32(src_dst);
|
||||
auto src2 = FReadV32(src);
|
||||
auto out = src1;
|
||||
_Pragma("unroll")
|
||||
for (auto i = 0u; i < 2; ++i) {
|
||||
_Pragma("unroll") for (auto i = 0u; i < 2; ++i) {
|
||||
auto s1_val = FExtractV32(src1, i);
|
||||
auto s2_val = FExtractV32(src2, i);
|
||||
if (!std::isunordered(s1_val, s2_val) && s2_val < s1_val) {
|
||||
@@ -1995,8 +1993,7 @@ DEF_SEM(PFCMPGT, D dst, S1 src_dst, S2 src) {
|
||||
auto src1 = FReadV32(src_dst);
|
||||
auto src2 = FReadV32(src);
|
||||
uint32v2_t out = {};
|
||||
_Pragma("unroll")
|
||||
for (auto i = 0u; i < 2; ++i) {
|
||||
_Pragma("unroll") for (auto i = 0u; i < 2; ++i) {
|
||||
auto s1_val = FExtractV32(src1, i);
|
||||
auto s2_val = FExtractV32(src2, i);
|
||||
if (!std::isunordered(s1_val, s2_val) && s1_val > s2_val) {
|
||||
@@ -2012,8 +2009,7 @@ DEF_SEM(PFCMPGE, D dst, S1 src_dst, S2 src) {
|
||||
auto src1 = FReadV32(src_dst);
|
||||
auto src2 = FReadV32(src);
|
||||
uint32v2_t out = {};
|
||||
_Pragma("unroll")
|
||||
for (auto i = 0u; i < 2; ++i) {
|
||||
_Pragma("unroll") for (auto i = 0u; i < 2; ++i) {
|
||||
auto s1_val = FExtractV32(src1, i);
|
||||
auto s2_val = FExtractV32(src2, i);
|
||||
if (!std::isunordered(s1_val, s2_val) && s1_val >= s2_val) {
|
||||
@@ -2029,8 +2025,7 @@ DEF_SEM(PFCMPEQ, D dst, S1 src_dst, S2 src) {
|
||||
auto src1 = FReadV32(src_dst);
|
||||
auto src2 = FReadV32(src);
|
||||
uint32v2_t out = {};
|
||||
_Pragma("unroll")
|
||||
for (auto i = 0u; i < 2; ++i) {
|
||||
_Pragma("unroll") for (auto i = 0u; i < 2; ++i) {
|
||||
auto s1_val = FExtractV32(src1, i);
|
||||
auto s2_val = FExtractV32(src2, i);
|
||||
if (!std::isunordered(s1_val, s2_val) && s1_val == s2_val) {
|
||||
@@ -2045,8 +2040,10 @@ template <typename D, typename S1, typename S2>
|
||||
DEF_SEM(PFRSQRT, D dst, S1, S2 src) {
|
||||
auto src2 = FReadV32(src);
|
||||
auto out = FClearV32(FReadV32(dst));
|
||||
out = FInsertV32(out, 0, FDiv(1.0f, SquareRoot32(memory, state, FExtractV32(src2, 0))));
|
||||
out = FInsertV32(out, 1, FDiv(1.0f, SquareRoot32(memory, state, FExtractV32(src2, 1))));
|
||||
out = FInsertV32(
|
||||
out, 0, FDiv(1.0f, SquareRoot32(memory, state, FExtractV32(src2, 0))));
|
||||
out = FInsertV32(
|
||||
out, 1, FDiv(1.0f, SquareRoot32(memory, state, FExtractV32(src2, 1))));
|
||||
FWriteV32(dst, out);
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -17,20 +17,20 @@
|
||||
namespace {
|
||||
|
||||
#define MAKE_CMPXCHG_XAX(xax, xax_write, xax_read) \
|
||||
template <typename D, typename S1, typename S2> \
|
||||
DEF_SEM(CMPXCHG_ ## xax, D dst, S1 src1, S2 src2) { \
|
||||
auto desired_val = Read(src2); \
|
||||
auto check_val = Read(REG_ ## xax_read); \
|
||||
auto prev_value = check_val; \
|
||||
auto swap_flag = UCmpXchg(dst, check_val, desired_val); \
|
||||
auto sub_res = USub(prev_value, check_val); \
|
||||
WriteFlagsAddSub<tag_sub>(state, prev_value, check_val, sub_res); \
|
||||
Write(FLAG_ZF, swap_flag); \
|
||||
if (!swap_flag) { \
|
||||
WriteZExt(REG_ ## xax_write, check_val); \
|
||||
} \
|
||||
return memory; \
|
||||
}
|
||||
template <typename D, typename S1, typename S2> \
|
||||
DEF_SEM(CMPXCHG_##xax, D dst, S1 src1, S2 src2) { \
|
||||
auto desired_val = Read(src2); \
|
||||
auto check_val = Read(REG_##xax_read); \
|
||||
auto prev_value = check_val; \
|
||||
auto swap_flag = UCmpXchg(dst, check_val, desired_val); \
|
||||
auto sub_res = USub(prev_value, check_val); \
|
||||
WriteFlagsAddSub<tag_sub>(state, prev_value, check_val, sub_res); \
|
||||
Write(FLAG_ZF, swap_flag); \
|
||||
if (!swap_flag) { \
|
||||
WriteZExt(REG_##xax_write, check_val); \
|
||||
} \
|
||||
return memory; \
|
||||
}
|
||||
|
||||
MAKE_CMPXCHG_XAX(AL, AL, AL)
|
||||
MAKE_CMPXCHG_XAX(AX, AX, AX)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Disable the "loop not unrolled warnings"
|
||||
#pragma clang diagnostic ignored "-Wpass-failed"
|
||||
|
||||
@@ -1631,6 +1632,7 @@ namespace {
|
||||
|
||||
template <typename D, typename S1>
|
||||
DEF_SEM(SQRTSS, D dst, S1 src1) {
|
||||
|
||||
// Extract a "single-precision" (32-bit) float from [31:0] of src1 vector:
|
||||
auto src_float = FExtractV32(FReadV32(src1), 0);
|
||||
|
||||
@@ -1640,7 +1642,7 @@ DEF_SEM(SQRTSS, D dst, S1 src1) {
|
||||
temp_vec = FInsertV32(temp_vec, 0, square_root);
|
||||
|
||||
// Write out the result and return memory state:
|
||||
FWriteV32(dst, temp_vec); // SSE: Writes to XMM, AVX: Zero-extends XMM.
|
||||
FWriteV32(dst, temp_vec); // SSE: Writes to XMM, AVX: Zero-extends XMM.
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -1681,6 +1683,7 @@ DEF_SEM(VSQRTSS, D dst, S1 src1, S2 src2) {
|
||||
|
||||
template <typename D, typename S1, typename S2>
|
||||
DEF_SEM(VRSQRTSS, D dst, S1 src1, S2 src2) {
|
||||
|
||||
// Extract the single-precision float from [31:0] of the src2 vector:
|
||||
auto src_float = FExtractV32(FReadV32(src2), 0);
|
||||
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ const std::string_view kReturnPCVariableName = "RETURN_PC";
|
||||
const std::string_view kBranchTakenVariableName = "BRANCH_TAKEN";
|
||||
|
||||
const std::string_view kInvalidInstructionISelName = "INVALID_INSTRUCTION";
|
||||
const std::string_view kUnsupportedInstructionISelName = "UNSUPPORTED_INSTRUCTION";
|
||||
const std::string_view kUnsupportedInstructionISelName =
|
||||
"UNSUPPORTED_INSTRUCTION";
|
||||
|
||||
} // namespace remill
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "remill/Arch/Arch.h"
|
||||
#include "remill/BC/ABI.h"
|
||||
#include "remill/BC/Compat/CallSite.h"
|
||||
#include "remill/BC/Compat/VectorType.h"
|
||||
#include "remill/BC/ABI.h"
|
||||
#include "remill/BC/Util.h"
|
||||
#include "remill/OS/FileSystem.h"
|
||||
|
||||
@@ -417,7 +417,7 @@ static void StreamCallOrInvokeToDOT(std::ostream &dot,
|
||||
LOG(ERROR) << "Encountered callsite that is not call nor invoke!";
|
||||
}
|
||||
|
||||
if(!cs.getCalledValue()->getName().empty()) {
|
||||
if (!cs.getCalledValue()->getName().empty()) {
|
||||
dot << cs.getCalledValue()->getName().str();
|
||||
} else {
|
||||
dot << cs.getCalledValue()->getValueID();
|
||||
@@ -1136,8 +1136,10 @@ VisitResult ForwardAliasVisitor::visitPHINode(llvm::PHINode &inst) {
|
||||
}
|
||||
|
||||
VisitResult ForwardAliasVisitor::visitCallInst(llvm::CallInst &inst) {
|
||||
|
||||
//const auto val = inst.getCalledOperand()->stripPointerCasts();
|
||||
const auto val = compat::llvm::CallSite(&inst).getCalledValue()->stripPointerCasts();
|
||||
const auto val =
|
||||
compat::llvm::CallSite(&inst).getCalledValue()->stripPointerCasts();
|
||||
if (auto const_val = llvm::dyn_cast<llvm::Constant>(val); const_val) {
|
||||
|
||||
// Don't let this affect anything.
|
||||
@@ -1189,7 +1191,8 @@ VisitResult ForwardAliasVisitor::visitCallInst(llvm::CallInst &inst) {
|
||||
}
|
||||
|
||||
VisitResult ForwardAliasVisitor::visitInvokeInst(llvm::InvokeInst &inst) {
|
||||
auto val = compat::llvm::CallSite(&inst).getCalledValue()->stripPointerCasts();
|
||||
auto val =
|
||||
compat::llvm::CallSite(&inst).getCalledValue()->stripPointerCasts();
|
||||
if (llvm::isa<llvm::InlineAsm>(val)) {
|
||||
live_args[&inst].set(); // Weird to invoke inline assembly.
|
||||
|
||||
|
||||
+161
-25
@@ -55,12 +55,12 @@ InstructionLifter::Impl::Impl(const Arch *arch_,
|
||||
unsupported_instruction(
|
||||
GetInstructionFunction(module, kUnsupportedInstructionISelName)) {
|
||||
|
||||
CHECK(invalid_instruction != nullptr)
|
||||
<< kInvalidInstructionISelName << " doesn't exist";
|
||||
CHECK(invalid_instruction != nullptr)
|
||||
<< kInvalidInstructionISelName << " doesn't exist";
|
||||
|
||||
CHECK(unsupported_instruction != nullptr)
|
||||
<< kUnsupportedInstructionISelName << " doesn't exist";
|
||||
}
|
||||
CHECK(unsupported_instruction != nullptr)
|
||||
<< kUnsupportedInstructionISelName << " doesn't exist";
|
||||
}
|
||||
|
||||
InstructionLifter::~InstructionLifter(void) {}
|
||||
|
||||
@@ -114,9 +114,11 @@ LiftStatus InstructionLifter::LiftIntoBlock(Instruction &arch_inst,
|
||||
}
|
||||
|
||||
llvm::IRBuilder<> ir(block);
|
||||
const auto mem_ptr_ref = LoadRegAddress(block, state_ptr, kMemoryVariableName);
|
||||
const auto mem_ptr_ref =
|
||||
LoadRegAddress(block, state_ptr, kMemoryVariableName);
|
||||
const auto pc_ref = LoadRegAddress(block, state_ptr, kPCVariableName);
|
||||
const auto next_pc_ref = LoadRegAddress(block, state_ptr, kNextPCVariableName);
|
||||
const auto next_pc_ref =
|
||||
LoadRegAddress(block, state_ptr, kNextPCVariableName);
|
||||
const auto next_pc = ir.CreateLoad(next_pc_ref);
|
||||
|
||||
// If this instruction appears within a delay slot, then we're going to assume
|
||||
@@ -140,9 +142,8 @@ LiftStatus InstructionLifter::LiftIntoBlock(Instruction &arch_inst,
|
||||
// the program counter in the semantics code.
|
||||
ir.CreateStore(next_pc, pc_ref);
|
||||
ir.CreateStore(
|
||||
ir.CreateAdd(next_pc,
|
||||
llvm::ConstantInt::get(impl->word_type,
|
||||
arch_inst.bytes.size())),
|
||||
ir.CreateAdd(next_pc, llvm::ConstantInt::get(impl->word_type,
|
||||
arch_inst.bytes.size())),
|
||||
next_pc_ref);
|
||||
}
|
||||
|
||||
@@ -217,9 +218,10 @@ LiftStatus InstructionLifter::LiftIntoBlock(Instruction &arch_inst,
|
||||
}
|
||||
|
||||
// Load the address of a register.
|
||||
llvm::Value *InstructionLifter::LoadRegAddress(
|
||||
llvm::BasicBlock *block, llvm::Value *state_ptr,
|
||||
std::string_view reg_name_) const {
|
||||
llvm::Value *
|
||||
InstructionLifter::LoadRegAddress(llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr,
|
||||
std::string_view reg_name_) const {
|
||||
const auto func = block->getParent();
|
||||
|
||||
// Invalidate the cache.
|
||||
@@ -231,8 +233,8 @@ llvm::Value *InstructionLifter::LoadRegAddress(
|
||||
}
|
||||
|
||||
std::string reg_name(reg_name_.data(), reg_name_.size());
|
||||
auto [reg_ptr_it, added] = impl->reg_ptr_cache.emplace(
|
||||
std::move(reg_name), nullptr);
|
||||
auto [reg_ptr_it, added] =
|
||||
impl->reg_ptr_cache.emplace(std::move(reg_name), nullptr);
|
||||
|
||||
if (reg_ptr_it->second) {
|
||||
(void) added;
|
||||
@@ -281,8 +283,7 @@ llvm::Value *InstructionLifter::LoadRegAddress(
|
||||
return reg_ptr;
|
||||
|
||||
} else {
|
||||
LOG(FATAL)
|
||||
<< "Could not locate variable or register " << reg_name_;
|
||||
LOG(FATAL) << "Could not locate variable or register " << reg_name_;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -294,9 +295,9 @@ void InstructionLifter::ClearCache(void) const {
|
||||
}
|
||||
|
||||
// Load the value of a register.
|
||||
llvm::Value *InstructionLifter::LoadRegValue(
|
||||
llvm::BasicBlock *block, llvm::Value *state_ptr,
|
||||
std::string_view reg_name) const {
|
||||
llvm::Value *InstructionLifter::LoadRegValue(llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr,
|
||||
std::string_view reg_name) const {
|
||||
auto ptr = LoadRegAddress(block, state_ptr, reg_name);
|
||||
CHECK_NOTNULL(ptr);
|
||||
auto ptr_ty = ptr->getType()->getPointerElementType();
|
||||
@@ -304,9 +305,10 @@ llvm::Value *InstructionLifter::LoadRegValue(
|
||||
}
|
||||
|
||||
// Return a register value, or zero.
|
||||
llvm::Value *InstructionLifter::LoadWordRegValOrZero(
|
||||
llvm::BasicBlock *block, llvm::Value *state_ptr,
|
||||
std::string_view reg_name, llvm::ConstantInt *zero) {
|
||||
llvm::Value *InstructionLifter::LoadWordRegValOrZero(llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr,
|
||||
std::string_view reg_name,
|
||||
llvm::ConstantInt *zero) {
|
||||
|
||||
if (reg_name.empty()) {
|
||||
return zero;
|
||||
@@ -630,6 +632,137 @@ InstructionLifter::LiftImmediateOperand(Instruction &inst, llvm::BasicBlock *,
|
||||
}
|
||||
}
|
||||
|
||||
// Lift an expression operand.
|
||||
llvm::Value *InstructionLifter::LiftExpressionOperand(Instruction &inst,
|
||||
llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr,
|
||||
llvm::Argument *arg,
|
||||
Operand &op) {
|
||||
auto val = LiftExpressionOperandRec(inst, block, state_ptr, arg, op.expr);
|
||||
llvm::Function *func = block->getParent();
|
||||
llvm::Module *module = func->getParent();
|
||||
const auto real_arg_type = arg->getType();
|
||||
|
||||
// LLVM on AArch64 and on amd64 Windows converts things like `RnW<uint64_t>`,
|
||||
// which is a struct containing a `uint64_t *`, into a `uintptr_t` when they
|
||||
// are being passed as arguments.
|
||||
auto arg_type = IntendedArgumentType(arg);
|
||||
|
||||
if (llvm::isa<llvm::PointerType>(arg_type)) {
|
||||
return ConvertToIntendedType(inst, op, block, val, real_arg_type);
|
||||
|
||||
} else {
|
||||
CHECK(arg_type->isIntegerTy() || arg_type->isFloatingPointTy())
|
||||
<< "Expected " << op.Serialize() << " to be an integral or float type "
|
||||
<< "for instruction at " << std::hex << inst.pc;
|
||||
|
||||
const llvm::DataLayout data_layout(module);
|
||||
auto val_type = val->getType();
|
||||
auto val_size = data_layout.getTypeAllocSizeInBits(val_type);
|
||||
auto arg_size = data_layout.getTypeAllocSizeInBits(arg_type);
|
||||
const auto word_size = impl->arch->address_size;
|
||||
|
||||
if (val_size < arg_size) {
|
||||
if (arg_type->isIntegerTy()) {
|
||||
CHECK(val_type->isIntegerTy())
|
||||
<< "Expected " << op.Serialize() << " to be an integral type "
|
||||
<< "for instruction at " << std::hex << inst.pc;
|
||||
|
||||
CHECK(word_size == arg_size)
|
||||
<< "Expected integer argument to be machine word size ("
|
||||
<< word_size << " bits) but is is " << arg_size << " instead "
|
||||
<< "in instruction at " << std::hex << inst.pc;
|
||||
|
||||
val = new llvm::ZExtInst(val, impl->word_type, "", block);
|
||||
|
||||
} else if (arg_type->isFloatingPointTy()) {
|
||||
CHECK(val_type->isFloatingPointTy())
|
||||
<< "Expected " << op.Serialize() << " to be a floating point type "
|
||||
<< "for instruction at " << std::hex << inst.pc;
|
||||
|
||||
val = new llvm::FPExtInst(val, arg_type, "", block);
|
||||
}
|
||||
|
||||
} else if (val_size > arg_size) {
|
||||
if (arg_type->isIntegerTy()) {
|
||||
CHECK(val_type->isIntegerTy())
|
||||
<< "Expected " << op.Serialize() << " to be an integral type "
|
||||
<< "for instruction at " << std::hex << inst.pc;
|
||||
|
||||
CHECK(word_size == arg_size)
|
||||
<< "Expected integer argument to be machine word size ("
|
||||
<< word_size << " bits) but is is " << arg_size << " instead "
|
||||
<< "in instruction at " << std::hex << inst.pc;
|
||||
|
||||
val = new llvm::TruncInst(val, arg_type, "", block);
|
||||
|
||||
} else if (arg_type->isFloatingPointTy()) {
|
||||
CHECK(val_type->isFloatingPointTy())
|
||||
<< "Expected " << op.Serialize() << " to be a floating point type "
|
||||
<< "for instruction at " << std::hex << inst.pc;
|
||||
|
||||
val = new llvm::FPTruncInst(val, arg_type, "", block);
|
||||
}
|
||||
}
|
||||
|
||||
return ConvertToIntendedType(inst, op, block, val, real_arg_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Lift an expression operand.
|
||||
llvm::Value *InstructionLifter::LiftExpressionOperandRec(
|
||||
Instruction &inst, llvm::BasicBlock *block, llvm::Value *state_ptr,
|
||||
llvm::Argument *arg, const OperandExpression *op) {
|
||||
if (auto llvm_op = std::get_if<LLVMOpExpr>(op)) {
|
||||
auto lhs =
|
||||
LiftExpressionOperandRec(inst, block, state_ptr, nullptr, llvm_op->op1);
|
||||
llvm::Value *rhs = nullptr;
|
||||
if (llvm_op->op2) {
|
||||
rhs = LiftExpressionOperandRec(inst, block, state_ptr, nullptr,
|
||||
llvm_op->op2);
|
||||
}
|
||||
llvm::IRBuilder<> ir(block);
|
||||
switch (llvm_op->llvm_opcode) {
|
||||
case llvm::Instruction::Add: return ir.CreateAdd(lhs, rhs);
|
||||
case llvm::Instruction::Sub: return ir.CreateSub(lhs, rhs);
|
||||
case llvm::Instruction::Mul: return ir.CreateMul(lhs, rhs);
|
||||
case llvm::Instruction::Shl: return ir.CreateShl(lhs, rhs);
|
||||
case llvm::Instruction::LShr: return ir.CreateLShr(lhs, rhs);
|
||||
case llvm::Instruction::AShr: return ir.CreateAShr(lhs, rhs);
|
||||
case llvm::Instruction::ZExt: return ir.CreateZExt(lhs, op->type);
|
||||
case llvm::Instruction::SExt: return ir.CreateSExt(lhs, op->type);
|
||||
case llvm::Instruction::Trunc: return ir.CreateTrunc(lhs, op->type);
|
||||
case llvm::Instruction::And: return ir.CreateAnd(lhs, rhs);
|
||||
case llvm::Instruction::Or: return ir.CreateOr(lhs, rhs);
|
||||
case llvm::Instruction::URem: return ir.CreateURem(lhs, rhs);
|
||||
case llvm::Instruction::Xor: return ir.CreateXor(lhs, rhs);
|
||||
default:
|
||||
LOG(FATAL) << "Invalid Expression "
|
||||
<< llvm::Instruction::getOpcodeName(llvm_op->llvm_opcode);
|
||||
return nullptr;
|
||||
}
|
||||
} else if (auto reg_op = std::get_if<const Register *>(op)) {
|
||||
if (!arg || !llvm::isa<llvm::PointerType>(arg->getType())) {
|
||||
return LoadRegValue(block, state_ptr, (*reg_op)->name);
|
||||
} else {
|
||||
return LoadRegAddress(block, state_ptr, (*reg_op)->name);
|
||||
}
|
||||
|
||||
} else if (auto ci_op = std::get_if<llvm::Constant *>(op)) {
|
||||
return *ci_op;
|
||||
|
||||
} else if (auto str_op = std::get_if<std::string>(op)) {
|
||||
if (!arg || !llvm::isa<llvm::PointerType>(arg->getType())) {
|
||||
return LoadRegValue(block, state_ptr, *str_op);
|
||||
} else {
|
||||
return LoadRegAddress(block, state_ptr, *str_op);
|
||||
}
|
||||
} else {
|
||||
LOG(FATAL) << "Uninitialized Operand Expression";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Zero-extend a value to be the machine word size.
|
||||
llvm::Value *InstructionLifter::LiftAddressOperand(Instruction &inst,
|
||||
llvm::BasicBlock *block,
|
||||
@@ -729,11 +862,14 @@ InstructionLifter::LiftOperand(Instruction &inst, llvm::BasicBlock *block,
|
||||
LOG(FATAL) << "Expected that a memory operand should be represented by "
|
||||
<< "machine word type. Argument type is "
|
||||
<< LLVMThingToString(arg_type) << " and word type is "
|
||||
<< LLVMThingToString(impl->word_type) << " in instruction at "
|
||||
<< std::hex << inst.pc;
|
||||
<< LLVMThingToString(impl->word_type)
|
||||
<< " in instruction at " << std::hex << inst.pc;
|
||||
}
|
||||
|
||||
return LiftAddressOperand(inst, block, state_ptr, arg, arch_op);
|
||||
|
||||
case Operand::kTypeExpression:
|
||||
return LiftExpressionOperand(inst, block, state_ptr, arg, arch_op);
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Got a unknown operand type of "
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <remill/BC/InstructionLifter.h>
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/IR/BasicBlock.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
@@ -33,8 +32,7 @@
|
||||
#include <llvm/Transforms/Scalar.h>
|
||||
#include <llvm/Transforms/Utils/Cloning.h>
|
||||
#include <llvm/Transforms/Utils/ValueMapper.h>
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <remill/BC/InstructionLifter.h>
|
||||
|
||||
#include <functional>
|
||||
#include <ios>
|
||||
@@ -76,9 +74,9 @@ class InstructionLifter::Impl {
|
||||
// clear out `reg_ptr_cache`.
|
||||
llvm::Function *last_func{nullptr};
|
||||
|
||||
llvm::Module * const module;
|
||||
llvm::Function * const invalid_instruction;
|
||||
llvm::Function * const unsupported_instruction;
|
||||
llvm::Module *const module;
|
||||
llvm::Function *const invalid_instruction;
|
||||
llvm::Function *const unsupported_instruction;
|
||||
};
|
||||
|
||||
} // namespace remill
|
||||
|
||||
+142
-101
@@ -16,15 +16,13 @@
|
||||
|
||||
#include <remill/BC/TraceLifter.h>
|
||||
|
||||
#include "InstructionLifter.h"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
namespace remill {
|
||||
namespace {
|
||||
#include "InstructionLifter.h"
|
||||
|
||||
} // namespace
|
||||
namespace remill {
|
||||
namespace {} // namespace
|
||||
|
||||
TraceManager::~TraceManager(void) {}
|
||||
|
||||
@@ -102,6 +100,7 @@ class TraceLifter::Impl {
|
||||
}
|
||||
|
||||
llvm::BasicBlock *GetOrCreateBranchNotTakenBlock(void) {
|
||||
CHECK(inst.branch_not_taken_pc != 0);
|
||||
inst_work_list.insert(inst.branch_not_taken_pc);
|
||||
return GetOrCreateBlock(inst.branch_not_taken_pc);
|
||||
}
|
||||
@@ -151,7 +150,8 @@ TraceLifter::Impl::Impl(InstructionLifter *inst_lifter_, TraceManager *manager_)
|
||||
intrinsics(inst_lifter.impl->intrinsics),
|
||||
context(inst_lifter.impl->word_type->getContext()),
|
||||
module(intrinsics->async_hyper_call->getParent()),
|
||||
addr_mask(arch->address_size >= 64 ? ~0ULL : (~0ULL >> arch->address_size)),
|
||||
addr_mask(arch->address_size >= 64 ? ~0ULL
|
||||
: (~0ULL >> arch->address_size)),
|
||||
manager(*manager_),
|
||||
func(nullptr),
|
||||
block(nullptr),
|
||||
@@ -300,8 +300,8 @@ bool TraceLifter::Impl::Lift(
|
||||
|
||||
if (auto entry_block = &(func->front())) {
|
||||
auto pc = LoadProgramCounterArg(func);
|
||||
auto next_pc_ref =
|
||||
inst_lifter.LoadRegAddress(entry_block, state_ptr, kNextPCVariableName);
|
||||
auto next_pc_ref = inst_lifter.LoadRegAddress(entry_block, state_ptr,
|
||||
kNextPCVariableName);
|
||||
|
||||
// Initialize `NEXT_PC`.
|
||||
(void) new llvm::StoreInst(pc, next_pc_ref, entry_block);
|
||||
@@ -407,48 +407,7 @@ bool TraceLifter::Impl::Lift(
|
||||
|
||||
case Instruction::kCategoryIndirectJump: {
|
||||
try_add_delay_slot(true, block);
|
||||
|
||||
// The trace manager might know about the targets of things like
|
||||
// jump tables, so we will let it tell us about those possibilities.
|
||||
std::unordered_map<uint64_t, llvm::BasicBlock *> devirt_targets;
|
||||
manager.ForEachDevirtualizedTarget(
|
||||
inst,
|
||||
[&](uint64_t target_addr, DevirtualizedTargetKind target_kind) {
|
||||
if (target_kind == DevirtualizedTargetKind::kTraceHead) {
|
||||
auto target_block =
|
||||
llvm::BasicBlock::Create(context, "", func);
|
||||
devirt_targets[target_addr] = target_block;
|
||||
|
||||
// Always add to the work list. This will cause us to lift
|
||||
// if we haven't, and guarantee that `get_trace_decl` returns
|
||||
// something.
|
||||
trace_work_list.insert(target_addr);
|
||||
auto target_trace = get_trace_decl(target_addr);
|
||||
AddTerminatingTailCall(target_block, target_trace);
|
||||
|
||||
} else {
|
||||
devirt_targets[target_addr] = GetOrCreateBlock(target_addr);
|
||||
inst_work_list.insert(target_addr);
|
||||
}
|
||||
});
|
||||
|
||||
if (devirt_targets.empty()) {
|
||||
AddTerminatingTailCall(block, intrinsics->jump);
|
||||
break;
|
||||
}
|
||||
|
||||
auto default_case = llvm::BasicBlock::Create(context, "", func);
|
||||
|
||||
auto pc = LoadProgramCounter(block);
|
||||
auto pc_type = pc->getType();
|
||||
auto dispatcher = llvm::SwitchInst::Create(
|
||||
pc, default_case, devirt_targets.size(), block);
|
||||
for (auto devirt_target : devirt_targets) {
|
||||
dispatcher->addCase(
|
||||
llvm::dyn_cast<llvm::ConstantInt>(llvm::ConstantInt::get(
|
||||
pc_type, devirt_target.first, false)),
|
||||
devirt_target.second);
|
||||
}
|
||||
AddTerminatingTailCall(block, intrinsics->jump);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -467,58 +426,46 @@ bool TraceLifter::Impl::Lift(
|
||||
LoadNextProgramCounterRef(fall_through_block);
|
||||
llvm::IRBuilder<> ir(fall_through_block);
|
||||
ir.CreateStore(ir.CreateLoad(ret_pc_ref), next_pc_ref);
|
||||
ir.CreateBr(GetOrCreateNextBlock());
|
||||
|
||||
// The trace manager might know about the targets of things like
|
||||
// virtual tables, so we will let it tell us about those possibilities.
|
||||
std::unordered_map<uint64_t, llvm::BasicBlock *> devirt_targets;
|
||||
manager.ForEachDevirtualizedTarget(
|
||||
inst,
|
||||
[&](uint64_t target_addr, DevirtualizedTargetKind target_kind) {
|
||||
if (target_kind == DevirtualizedTargetKind::kTraceLocal) {
|
||||
LOG(WARNING)
|
||||
<< "Ignoring trace-local target in devirtualizable call";
|
||||
return;
|
||||
}
|
||||
|
||||
auto target_block = llvm::BasicBlock::Create(context, "", func);
|
||||
devirt_targets[target_addr] = target_block;
|
||||
|
||||
// Always add to the work list. This will cause us to lift
|
||||
// if we haven't, and guarantee that `get_trace_decl` returns
|
||||
// something.
|
||||
trace_work_list.insert(target_addr);
|
||||
auto target_trace = get_trace_decl(target_addr);
|
||||
AddCall(target_block, target_trace);
|
||||
|
||||
llvm::BranchInst::Create(fall_through_block, target_block);
|
||||
});
|
||||
|
||||
if (devirt_targets.empty()) {
|
||||
AddCall(block, intrinsics->function_call);
|
||||
llvm::BranchInst::Create(fall_through_block, block);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto default_case = llvm::BasicBlock::Create(context, "", func);
|
||||
AddCall(default_case, intrinsics->function_call);
|
||||
llvm::BranchInst::Create(fall_through_block, default_case);
|
||||
|
||||
auto pc = LoadProgramCounter(block);
|
||||
auto pc_type = pc->getType();
|
||||
auto dispatcher = llvm::SwitchInst::Create(
|
||||
pc, default_case, devirt_targets.size(), block);
|
||||
for (auto devirt_target : devirt_targets) {
|
||||
dispatcher->addCase(
|
||||
llvm::dyn_cast<llvm::ConstantInt>(llvm::ConstantInt::get(
|
||||
pc_type, devirt_target.first, false)),
|
||||
devirt_target.second);
|
||||
}
|
||||
ir.CreateBr(GetOrCreateBranchNotTakenBlock());
|
||||
|
||||
AddCall(block, intrinsics->function_call);
|
||||
llvm::BranchInst::Create(fall_through_block, block);
|
||||
block = fall_through_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Instruction::kCategoryConditionalIndirectFunctionCall: {
|
||||
auto taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
auto not_taken_block = GetOrCreateBranchNotTakenBlock();
|
||||
const auto orig_not_taken_block = not_taken_block;
|
||||
|
||||
// If we might need to add delay slots, then try to lift the delayed
|
||||
// instruction on each side of the conditional branch, injecting in
|
||||
// new blocks (for the delayed instruction) between the branch
|
||||
// and its original targets.
|
||||
if (try_delay) {
|
||||
not_taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
|
||||
try_add_delay_slot(true, taken_block);
|
||||
try_add_delay_slot(false, not_taken_block);
|
||||
|
||||
llvm::BranchInst::Create(orig_not_taken_block, not_taken_block);
|
||||
}
|
||||
|
||||
llvm::BranchInst::Create(taken_block, not_taken_block,
|
||||
LoadBranchTaken(block), block);
|
||||
|
||||
AddCall(taken_block, intrinsics->function_call);
|
||||
|
||||
const auto ret_pc_ref = LoadReturnProgramCounterRef(taken_block);
|
||||
const auto next_pc_ref = LoadNextProgramCounterRef(taken_block);
|
||||
llvm::IRBuilder<> ir(taken_block);
|
||||
ir.CreateStore(ir.CreateLoad(ret_pc_ref), next_pc_ref);
|
||||
ir.CreateBr(orig_not_taken_block);
|
||||
block = orig_not_taken_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
// In the case of a direct function call, we try to handle the
|
||||
// pattern of a call to the next PC as a way of getting access to
|
||||
// an instruction pointer. It is the case where a call to the next
|
||||
@@ -528,8 +475,9 @@ bool TraceLifter::Impl::Lift(
|
||||
// that up when trying to lift it), or we'll just have a really big
|
||||
// trace for this function without sacrificing correctness.
|
||||
case Instruction::kCategoryDirectFunctionCall: {
|
||||
direct_func_call:
|
||||
try_add_delay_slot(true, block);
|
||||
if (inst.next_pc != inst.branch_taken_pc) {
|
||||
if (inst.branch_not_taken_pc != inst.branch_taken_pc) {
|
||||
trace_work_list.insert(inst.branch_taken_pc);
|
||||
auto target_trace = get_trace_decl(inst.branch_taken_pc);
|
||||
AddCall(block, target_trace);
|
||||
@@ -539,17 +487,59 @@ bool TraceLifter::Impl::Lift(
|
||||
const auto next_pc_ref = LoadNextProgramCounterRef(block);
|
||||
llvm::IRBuilder<> ir(block);
|
||||
ir.CreateStore(ir.CreateLoad(ret_pc_ref), next_pc_ref);
|
||||
ir.CreateBr(GetOrCreateNextBlock());
|
||||
ir.CreateBr(GetOrCreateBranchNotTakenBlock());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
case Instruction::kCategoryConditionalDirectFunctionCall: {
|
||||
if (inst.branch_not_taken_pc == inst.branch_taken_pc) {
|
||||
goto direct_func_call;
|
||||
}
|
||||
|
||||
auto taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
auto not_taken_block = GetOrCreateBranchNotTakenBlock();
|
||||
const auto orig_not_taken_block = not_taken_block;
|
||||
|
||||
// If we might need to add delay slots, then try to lift the delayed
|
||||
// instruction on each side of the conditional branch, injecting in
|
||||
// new blocks (for the delayed instruction) between the branch
|
||||
// and its original targets.
|
||||
if (try_delay) {
|
||||
not_taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
|
||||
try_add_delay_slot(true, taken_block);
|
||||
try_add_delay_slot(false, not_taken_block);
|
||||
|
||||
llvm::BranchInst::Create(orig_not_taken_block, not_taken_block);
|
||||
}
|
||||
|
||||
llvm::BranchInst::Create(taken_block, not_taken_block,
|
||||
LoadBranchTaken(block), block);
|
||||
|
||||
trace_work_list.insert(inst.branch_taken_pc);
|
||||
auto target_trace = get_trace_decl(inst.branch_taken_pc);
|
||||
|
||||
AddCall(taken_block, intrinsics->function_call);
|
||||
AddCall(taken_block, target_trace);
|
||||
|
||||
const auto ret_pc_ref = LoadReturnProgramCounterRef(taken_block);
|
||||
const auto next_pc_ref = LoadNextProgramCounterRef(taken_block);
|
||||
llvm::IRBuilder<> ir(taken_block);
|
||||
ir.CreateStore(ir.CreateLoad(ret_pc_ref), next_pc_ref);
|
||||
ir.CreateBr(orig_not_taken_block);
|
||||
block = orig_not_taken_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lift an async hyper call to check if it should do the hypercall.
|
||||
// If so, it will jump to the `do_hyper_call` block, otherwise it will
|
||||
// jump to the block associated with the next PC. In the case of the
|
||||
// `do_hyper_call` block, we assign it to `state.block`, then go
|
||||
// to `check_call_return` to add the hyper call into that block,
|
||||
// checking if the hyper call returns to the next PC or not.
|
||||
//
|
||||
// TODO(pag): Delay slots?
|
||||
case Instruction::kCategoryConditionalAsyncHyperCall: {
|
||||
auto do_hyper_call = llvm::BasicBlock::Create(context, "", func);
|
||||
llvm::BranchInst::Create(do_hyper_call, GetOrCreateNextBlock(),
|
||||
@@ -562,8 +552,8 @@ bool TraceLifter::Impl::Lift(
|
||||
check_call_return:
|
||||
do {
|
||||
auto pc = LoadProgramCounter(block);
|
||||
auto ret_pc =
|
||||
llvm::ConstantInt::get(inst_lifter.impl->word_type, inst.next_pc);
|
||||
auto ret_pc = llvm::ConstantInt::get(inst_lifter.impl->word_type,
|
||||
inst.next_pc);
|
||||
|
||||
llvm::IRBuilder<> ir(block);
|
||||
auto eq = ir.CreateICmpEQ(pc, ret_pc);
|
||||
@@ -580,6 +570,32 @@ bool TraceLifter::Impl::Lift(
|
||||
AddTerminatingTailCall(block, intrinsics->function_return);
|
||||
break;
|
||||
|
||||
case Instruction::kCategoryConditionalFunctionReturn: {
|
||||
auto taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
auto not_taken_block = GetOrCreateBranchNotTakenBlock();
|
||||
const auto orig_not_taken_block = not_taken_block;
|
||||
|
||||
// If we might need to add delay slots, then try to lift the delayed
|
||||
// instruction on each side of the conditional branch, injecting in
|
||||
// new blocks (for the delayed instruction) between the branch
|
||||
// and its original targets.
|
||||
if (try_delay) {
|
||||
not_taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
|
||||
try_add_delay_slot(true, taken_block);
|
||||
try_add_delay_slot(false, not_taken_block);
|
||||
|
||||
llvm::BranchInst::Create(orig_not_taken_block, not_taken_block);
|
||||
}
|
||||
|
||||
llvm::BranchInst::Create(taken_block, not_taken_block,
|
||||
LoadBranchTaken(block), block);
|
||||
|
||||
AddTerminatingTailCall(taken_block, intrinsics->function_return);
|
||||
block = orig_not_taken_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Instruction::kCategoryConditionalBranch: {
|
||||
auto taken_block = GetOrCreateBranchTakenBlock();
|
||||
auto not_taken_block = GetOrCreateBranchNotTakenBlock();
|
||||
@@ -607,6 +623,31 @@ bool TraceLifter::Impl::Lift(
|
||||
LoadBranchTaken(block), block);
|
||||
break;
|
||||
}
|
||||
case Instruction::kCategoryConditionalIndirectJump: {
|
||||
auto taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
auto not_taken_block = GetOrCreateBranchNotTakenBlock();
|
||||
const auto orig_not_taken_block = not_taken_block;
|
||||
|
||||
// If we might need to add delay slots, then try to lift the delayed
|
||||
// instruction on each side of the conditional branch, injecting in
|
||||
// new blocks (for the delayed instruction) between the branch
|
||||
// and its original targets.
|
||||
if (try_delay) {
|
||||
not_taken_block = llvm::BasicBlock::Create(context, "", func);
|
||||
|
||||
try_add_delay_slot(true, taken_block);
|
||||
try_add_delay_slot(false, not_taken_block);
|
||||
|
||||
llvm::BranchInst::Create(orig_not_taken_block, not_taken_block);
|
||||
}
|
||||
|
||||
llvm::BranchInst::Create(taken_block, not_taken_block,
|
||||
LoadBranchTaken(block), block);
|
||||
|
||||
AddTerminatingTailCall(taken_block, intrinsics->jump);
|
||||
block = orig_not_taken_block;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-21
@@ -115,8 +115,8 @@ llvm::CallInst *AddCall(llvm::BasicBlock *source_block,
|
||||
arg_types[kStatePointerArgNum] = args[kStatePointerArgNum]->getType();
|
||||
arg_types[kMemoryPointerArgNum] = args[kMemoryPointerArgNum]->getType();
|
||||
arg_types[kPCArgNum] = args[kPCArgNum]->getType();
|
||||
auto func_type = llvm::FunctionType::get(
|
||||
arg_types[kMemoryPointerArgNum], arg_types, false);
|
||||
auto func_type = llvm::FunctionType::get(arg_types[kMemoryPointerArgNum],
|
||||
arg_types, false);
|
||||
llvm::FunctionCallee callee(func_type, dest_func);
|
||||
return ir.CreateCall(callee, args);
|
||||
}
|
||||
@@ -164,8 +164,8 @@ llvm::Value *FindVarInFunction(llvm::BasicBlock *block, std::string_view name,
|
||||
|
||||
// Find a local variable defined in the entry block of the function. We use
|
||||
// this to find register variables.
|
||||
llvm::Value *FindVarInFunction(llvm::Function *function,
|
||||
std::string_view name_, bool allow_failure) {
|
||||
llvm::Value *FindVarInFunction(llvm::Function *function, std::string_view name_,
|
||||
bool allow_failure) {
|
||||
llvm::StringRef name(name_.data(), name_.size());
|
||||
if (!function->empty()) {
|
||||
for (auto &instr : function->getEntryBlock()) {
|
||||
@@ -356,8 +356,8 @@ std::unique_ptr<llvm::Module> LoadModuleFromFile(llvm::LLVMContext *context,
|
||||
|
||||
if (!module) {
|
||||
LOG_IF(FATAL, !allow_failure)
|
||||
<< "Unable to parse module file " << file_name_
|
||||
<< ": " << err.getMessage().str();
|
||||
<< "Unable to parse module file " << file_name_ << ": "
|
||||
<< err.getMessage().str();
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -461,6 +461,12 @@ namespace {
|
||||
# define REMILL_BUILD_SEMANTICS_DIR_X86
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_X86
|
||||
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_AARCH32
|
||||
# error \
|
||||
"Macro `REMILL_BUILD_SEMANTICS_DIR_AARCH32` must be defined to support AArch64 architecture."
|
||||
# define REMILL_BUILD_SEMANTICS_DIR_AARCH32
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_AARCH32
|
||||
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_AARCH64
|
||||
# error \
|
||||
"Macro `REMILL_BUILD_SEMANTICS_DIR_AARCH64` must be defined to support AArch64 architecture."
|
||||
@@ -468,13 +474,15 @@ namespace {
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_AARCH64
|
||||
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_SPARC32
|
||||
#error "Macro `REMILL_BUILD_SEMANTICS_DIR_SPARC32` must be defined to support the SPARC32 architectures."
|
||||
#define REMILL_BUILD_SEMANTICS_DIR_SPARC32
|
||||
# error \
|
||||
"Macro `REMILL_BUILD_SEMANTICS_DIR_SPARC32` must be defined to support the SPARC32 architectures."
|
||||
# define REMILL_BUILD_SEMANTICS_DIR_SPARC32
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_SPARC32
|
||||
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_SPARC64
|
||||
#error "Macro `REMILL_BUILD_SEMANTICS_DIR_SPARC64` must be defined to support the SPARC64 architectures."
|
||||
#define REMILL_BUILD_SEMANTICS_DIR_SPARC64
|
||||
# error \
|
||||
"Macro `REMILL_BUILD_SEMANTICS_DIR_SPARC64` must be defined to support the SPARC64 architectures."
|
||||
# define REMILL_BUILD_SEMANTICS_DIR_SPARC64
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_SPARC64
|
||||
|
||||
#ifndef REMILL_INSTALL_SEMANTICS_DIR
|
||||
@@ -487,15 +495,17 @@ namespace {
|
||||
#define MAJOR_MINOR S(LLVM_VERSION_MAJOR) "." S(LLVM_VERSION_MINOR)
|
||||
|
||||
static const char *gSemanticsSearchPaths[] = {
|
||||
// Derived from the build.
|
||||
REMILL_BUILD_SEMANTICS_DIR_X86 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_AARCH64 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_SPARC32 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_SPARC64 "\0",
|
||||
REMILL_INSTALL_SEMANTICS_DIR "\0",
|
||||
"/usr/local/share/remill/" MAJOR_MINOR "/semantics",
|
||||
"/usr/share/remill/" MAJOR_MINOR "/semantics",
|
||||
"/share/remill/" MAJOR_MINOR "/semantics",
|
||||
|
||||
// Derived from the build.
|
||||
REMILL_BUILD_SEMANTICS_DIR_X86 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_AARCH32 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_AARCH64 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_SPARC32 "\0",
|
||||
REMILL_BUILD_SEMANTICS_DIR_SPARC64 "\0",
|
||||
REMILL_INSTALL_SEMANTICS_DIR "\0",
|
||||
"/usr/local/share/remill/" MAJOR_MINOR "/semantics",
|
||||
"/usr/share/remill/" MAJOR_MINOR "/semantics",
|
||||
"/share/remill/" MAJOR_MINOR "/semantics",
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -2123,14 +2133,18 @@ BuildIndexes(const llvm::DataLayout &dl, llvm::Type *type, size_t offset,
|
||||
|
||||
indexes_out.push_back(llvm::ConstantInt::get(index_type, index, false));
|
||||
return BuildIndexes(dl, elem_type, offset, goal_offset, indexes_out);
|
||||
} else if (auto fvt_type = llvm::dyn_cast<llvm::FixedVectorType>(type); fvt_type) {
|
||||
} else if (auto fvt_type = llvm::dyn_cast<llvm::FixedVectorType>(type);
|
||||
fvt_type) {
|
||||
|
||||
// It is possible that this gets called on an unexpected type
|
||||
// such as FixedVectorType; if so, report the issue and fix if/when it
|
||||
// happens
|
||||
LOG(FATAL) << "Called BuildIndexes on unsupported type: "
|
||||
<< remill::LLVMThingToString(type);
|
||||
#if LLVM_VERSION_NUMBER >= LLVM_VERSION(11, 0)
|
||||
} else if (auto svt_type = llvm::dyn_cast<llvm::ScalableVectorType>(type); svt_type) {
|
||||
} else if (auto svt_type = llvm::dyn_cast<llvm::ScalableVectorType>(type);
|
||||
svt_type) {
|
||||
|
||||
// same as above, but for scalable vectors
|
||||
LOG(FATAL) << "Called BuildIndexes on unsupported type: "
|
||||
<< remill::LLVMThingToString(type);
|
||||
|
||||
@@ -142,8 +142,8 @@ extern "C" int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
DLOG(INFO) << "Serializing bitcode to " << FLAGS_bc_out;
|
||||
auto host_arch = remill::Arch::Build(
|
||||
&context, os_name, remill::GetArchName(REMILL_ARCH));
|
||||
auto host_arch =
|
||||
remill::Arch::Build(&context, os_name, remill::GetArchName(REMILL_ARCH));
|
||||
host_arch->PrepareModule(module.get());
|
||||
remill::StoreModuleToFile(module.get(), FLAGS_bc_out);
|
||||
|
||||
|
||||
+2
-2
@@ -142,8 +142,8 @@ extern "C" int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
DLOG(INFO) << "Serializing bitcode to " << FLAGS_bc_out;
|
||||
auto host_arch = remill::Arch::Build(
|
||||
&context, os_name, remill::GetArchName(REMILL_ARCH));
|
||||
auto host_arch =
|
||||
remill::Arch::Build(&context, os_name, remill::GetArchName(REMILL_ARCH));
|
||||
host_arch->PrepareModule(module.get());
|
||||
remill::StoreModuleToFile(module.get(), FLAGS_bc_out);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user