git subrepo pull --force deps/lightrec

subrepo:
  subdir:   "deps/lightrec"
  merged:   "aa2f992e"
upstream:
  origin:   "https://github.com/pcercuei/lightrec.git"
  branch:   "master"
  commit:   "aa2f992e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"
This commit is contained in:
Zachary Cook 2021-05-11 18:48:35 -04:00
parent 8ac3ca82f0
commit a388aacc8f
26 changed files with 2169 additions and 1564 deletions

View File

@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/pcercuei/lightrec.git
branch = master
commit = 16a375e4532106128a8d486276963801a0e03189
parent = 9f797430963d9cf0fcef7d963466f9cac7026de2
commit = aa2f992ed8c3236d1d952d72e3de8ea2b8d11af0
parent = bd765e2bf9b0f1e5bd788ebd867c9b1830ece001
method = merge
cmdver = 0.4.3

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.0)
project(lightrec LANGUAGES C VERSION 0.3)
project(lightrec LANGUAGES C VERSION 0.4)
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries")
if (NOT BUILD_SHARED_LIBS)
@ -29,7 +29,6 @@ set(HAS_DEFAULT_ELM ${CMAKE_COMPILER_IS_GNUCC})
list(APPEND LIGHTREC_SOURCES
blockcache.c
disassembler.c
emitter.c
interpreter.c
lightrec.c
@ -62,6 +61,16 @@ if (ENABLE_THREADED_COMPILER)
endif (NOT ENABLE_FIRST_PASS)
endif (ENABLE_THREADED_COMPILER)
option(OPT_REMOVE_DIV_BY_ZERO_SEQ "(optimization) Remove div-by-zero check sequence" ON)
option(OPT_REPLACE_MEMSET "(optimization) Detect and replace memset with host variant" ON)
option(OPT_DETECT_IMPOSSIBLE_BRANCHES "(optimization) Detect impossible branches" ON)
option(OPT_TRANSFORM_OPS "(optimization) Transform opcodes" ON)
option(OPT_LOCAL_BRANCHES "(optimization) Detect local branches" ON)
option(OPT_SWITCH_DELAY_SLOTS "(optimization) Switch delay slots" ON)
option(OPT_FLAG_STORES "(optimization) Flag stores that don't require invalidation" ON)
option(OPT_FLAG_MULT_DIV "(optimization) Flag MULT/DIV that only use one of HI/LO" ON)
option(OPT_EARLY_UNLOAD "(optimization) Unload registers early" ON)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_library(${PROJECT_NAME} ${LIGHTREC_SOURCES} ${LIGHTREC_HEADERS})
@ -74,6 +83,13 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
C_EXTENSIONS OFF
)
if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wno-parentheses)
endif()
if (CMAKE_C_COMPILER_ID STREQUAL "Clang")
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-initializer-overrides)
endif()
option(ENABLE_TINYMM "Enable optional libtinymm dependency" OFF)
if (ENABLE_TINYMM)
find_library(TINYMM_LIBRARIES tinymm REQUIRED)
@ -98,16 +114,8 @@ include_directories(${LIBLIGHTNING_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBLIGHTNING})
if (LOG_LEVEL STREQUAL Debug)
find_library(LIBOPCODES NAMES opcodes-multiarch opcodes)
find_path(LIBOPCODES_INCLUDE_DIR dis-asm.h)
if (NOT LIBOPCODES OR NOT LIBOPCODES_INCLUDE_DIR)
message(SEND_ERROR "Debug log level requires libopcodes (from binutils) to be installed.")
endif ()
set(ENABLE_DISASSEMBLER ON)
include_directories(${LIBOPCODES_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBOPCODES})
target_sources(${PROJECT_NAME} PRIVATE disassembler.c)
endif()
configure_file(config.h.cmakein config.h @ONLY)

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2015-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2015-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "blockcache.h"
@ -19,6 +10,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
/* Must be power of two */
#define LUT_SIZE 0x4000
@ -28,6 +20,11 @@ struct blockcache {
struct block * lut[LUT_SIZE];
};
u16 lightrec_get_lut_entry(const struct block *block)
{
return (kunseg(block->pc) >> 2) & (LUT_SIZE - 1);
}
struct block * lightrec_find_block(struct blockcache *cache, u32 pc)
{
struct block *block;
@ -42,22 +39,33 @@ struct block * lightrec_find_block(struct blockcache *cache, u32 pc)
return NULL;
}
struct block * lightrec_find_block_from_lut(struct blockcache *cache,
u16 lut_entry, u32 addr_in_block)
{
struct block *block;
u32 pc;
addr_in_block = kunseg(addr_in_block);
for (block = cache->lut[lut_entry]; block; block = block->next) {
pc = kunseg(block->pc);
if (addr_in_block >= pc &&
addr_in_block < pc + (block->nb_ops << 2))
return block;
}
return NULL;
}
void remove_from_code_lut(struct blockcache *cache, struct block *block)
{
struct lightrec_state *state = block->state;
const struct opcode *op;
u32 offset = lut_offset(block->pc);
/* Use state->get_next_block in the code LUT, which basically
* calls back get_next_block_func(), until the compiler
* overrides this. This is required, as a NULL value in the code
* LUT means an outdated block. */
state->code_lut[offset] = state->get_next_block;
for (op = block->opcode_list; op; op = op->next)
if (op->c.i.op == OP_META_SYNC)
state->code_lut[offset + op->offset] = NULL;
if (block->function) {
memset(&state->code_lut[offset], 0,
block->nb_ops * sizeof(*state->code_lut));
}
}
void lightrec_register_block(struct blockcache *cache, struct block *block)

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __BLOCKCACHE_H__
@ -20,6 +11,10 @@
struct blockcache;
struct block * lightrec_find_block(struct blockcache *cache, u32 pc);
struct block * lightrec_find_block_from_lut(struct blockcache *cache,
u16 lut_entry, u32 addr_in_block);
u16 lightrec_get_lut_entry(const struct block *block);
void lightrec_register_block(struct blockcache *cache, struct block *block);
void lightrec_unregister_block(struct blockcache *cache, struct block *block);

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_CONFIG_H__
@ -22,5 +13,15 @@
#cmakedefine01 HAS_DEFAULT_ELM
#cmakedefine01 OPT_REMOVE_DIV_BY_ZERO_SEQ
#cmakedefine01 OPT_REPLACE_MEMSET
#cmakedefine01 OPT_DETECT_IMPOSSIBLE_BRANCHES
#cmakedefine01 OPT_TRANSFORM_OPS
#cmakedefine01 OPT_LOCAL_BRANCHES
#cmakedefine01 OPT_SWITCH_DELAY_SLOTS
#cmakedefine01 OPT_FLAG_STORES
#cmakedefine01 OPT_FLAG_MULT_DIV
#cmakedefine01 OPT_EARLY_UNLOAD
#endif /* __LIGHTREC_CONFIG_H__ */

13
deps/lightrec/debug.h vendored
View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef DEBUG_H

View File

@ -1,142 +1,391 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "config.h"
#if ENABLE_DISASSEMBLER
#include <dis-asm.h>
#endif
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "disassembler.h"
#include "lightrec-private.h"
#include "memmanager.h"
#include "regcache.h"
static bool is_unconditional_jump(const struct opcode *op)
static const char *std_opcodes[] = {
[OP_J] = "j ",
[OP_JAL] = "jal ",
[OP_BEQ] = "beq ",
[OP_BNE] = "bne ",
[OP_BLEZ] = "blez ",
[OP_ADDI] = "addi ",
[OP_ADDIU] = "addiu ",
[OP_SLTI] = "slti ",
[OP_SLTIU] = "sltiu ",
[OP_ANDI] = "andi ",
[OP_ORI] = "ori ",
[OP_XORI] = "xori ",
[OP_LUI] = "lui ",
[OP_LB] = "lb ",
[OP_LH] = "lh ",
[OP_LWL] = "lwl ",
[OP_LW] = "lw ",
[OP_LBU] = "lbu ",
[OP_LHU] = "lhu ",
[OP_LWR] = "lwr ",
[OP_SB] = "sb ",
[OP_SH] = "sh ",
[OP_SWL] = "swl ",
[OP_SW] = "sw ",
[OP_SWR] = "swr ",
[OP_LWC2] = "lwc2 ",
[OP_SWC2] = "swc2 ",
[OP_META_BEQZ] = "beqz ",
[OP_META_BNEZ] = "bnez ",
};
static const char *special_opcodes[] = {
[OP_SPECIAL_SLL] = "sll ",
[OP_SPECIAL_SRL] = "srl ",
[OP_SPECIAL_SRA] = "sra ",
[OP_SPECIAL_SLLV] = "sllv ",
[OP_SPECIAL_SRLV] = "srlv ",
[OP_SPECIAL_SRAV] = "srav ",
[OP_SPECIAL_JR] = "jr ",
[OP_SPECIAL_JALR] = "jalr ",
[OP_SPECIAL_SYSCALL] = "syscall ",
[OP_SPECIAL_BREAK] = "break ",
[OP_SPECIAL_MFHI] = "mfhi ",
[OP_SPECIAL_MTHI] = "mthi ",
[OP_SPECIAL_MFLO] = "mflo ",
[OP_SPECIAL_MTLO] = "mtlo ",
[OP_SPECIAL_MULT] = "mult ",
[OP_SPECIAL_MULTU] = "multu ",
[OP_SPECIAL_DIV] = "div ",
[OP_SPECIAL_DIVU] = "divu ",
[OP_SPECIAL_ADD] = "add ",
[OP_SPECIAL_ADDU] = "addu ",
[OP_SPECIAL_SUB] = "sub ",
[OP_SPECIAL_SUBU] = "subu ",
[OP_SPECIAL_AND] = "and ",
[OP_SPECIAL_OR] = "or ",
[OP_SPECIAL_XOR] = "xor ",
[OP_SPECIAL_NOR] = "nor ",
[OP_SPECIAL_SLT] = "slt ",
[OP_SPECIAL_SLTU] = "sltu ",
};
static const char *regimm_opcodes[] = {
[OP_REGIMM_BLTZ] = "bltz ",
[OP_REGIMM_BGEZ] = "bgez ",
[OP_REGIMM_BLTZAL] = "bltzal ",
[OP_REGIMM_BGEZAL] = "bgezal ",
};
static const char *cp0_opcodes[] = {
[OP_CP0_MFC0] = "mfc0 ",
[OP_CP0_CFC0] = "cfc0 ",
[OP_CP0_MTC0] = "mtc0 ",
[OP_CP0_CTC0] = "ctc0 ",
[OP_CP0_RFE] = "rfe",
};
static const char *cp2_opcodes[] = {
[OP_CP2_BASIC_MFC2] = "mfc2 ",
[OP_CP2_BASIC_CFC2] = "cfc2 ",
[OP_CP2_BASIC_MTC2] = "mtc2 ",
[OP_CP2_BASIC_CTC2] = "ctc2 ",
};
static const char *opcode_flags[] = {
"switched branch/DS",
"unload Rs",
"unload Rt",
"unload Rd",
"sync point",
};
static const char *opcode_io_flags[] = {
"memory I/O",
"hardware I/O",
"self-modifying code",
"no invalidation",
};
static const char *opcode_branch_flags[] = {
"emulate branch",
"local branch",
};
static const char *opcode_multdiv_flags[] = {
"No LO",
"No HI",
"No div check",
};
static int print_flags(char *buf, size_t len, u16 flags,
const char **array, size_t array_size)
{
switch (op->i.op) {
const char *flag_name;
unsigned int i;
size_t count = 0, bytes;
bool first = true;
for (i = 0; i < array_size + ARRAY_SIZE(opcode_flags); i++) {
if (!(flags & BIT(i)))
continue;
if (i < ARRAY_SIZE(opcode_flags))
flag_name = opcode_flags[i];
else
flag_name = array[i - ARRAY_SIZE(opcode_flags)];
if (first)
bytes = snprintf(buf, len, "(%s", flag_name);
else
bytes = snprintf(buf, len, ", %s", flag_name);
first = false;
buf += bytes;
len -= bytes;
count += bytes;
}
if (!first)
count += snprintf(buf, len, ")");
else
*buf = '\0';
return count;
}
static int print_op_special(union code c, char *buf, size_t len,
const char ***flags_ptr, size_t *nb_flags)
{
switch (c.r.op) {
case OP_SPECIAL_SLL:
case OP_SPECIAL_SRL:
case OP_SPECIAL_SRA:
return snprintf(buf, len, "%s%s,%s,%u",
special_opcodes[c.r.op],
lightrec_reg_name(c.r.rd),
lightrec_reg_name(c.r.rt),
c.r.imm);
case OP_SPECIAL_SLLV:
case OP_SPECIAL_SRLV:
case OP_SPECIAL_SRAV:
case OP_SPECIAL_ADD:
case OP_SPECIAL_ADDU:
case OP_SPECIAL_SUB:
case OP_SPECIAL_SUBU:
case OP_SPECIAL_AND:
case OP_SPECIAL_OR:
case OP_SPECIAL_XOR:
case OP_SPECIAL_NOR:
case OP_SPECIAL_SLT:
case OP_SPECIAL_SLTU:
return snprintf(buf, len, "%s%s,%s,%s",
special_opcodes[c.r.op],
lightrec_reg_name(c.r.rd),
lightrec_reg_name(c.r.rt),
lightrec_reg_name(c.r.rs));
case OP_SPECIAL_JR:
case OP_SPECIAL_MTHI:
case OP_SPECIAL_MTLO:
return snprintf(buf, len, "%s%s",
special_opcodes[c.r.op],
lightrec_reg_name(c.r.rs));
case OP_SPECIAL_JALR:
return snprintf(buf, len, "%s%s,%s",
special_opcodes[c.r.op],
lightrec_reg_name(c.r.rd),
lightrec_reg_name(c.r.rt));
case OP_SPECIAL_SYSCALL:
case OP_SPECIAL_BREAK:
return snprintf(buf, len, "%s", special_opcodes[c.r.op]);
case OP_SPECIAL_MFHI:
case OP_SPECIAL_MFLO:
return snprintf(buf, len, "%s%s",
special_opcodes[c.r.op],
lightrec_reg_name(c.r.rd));
case OP_SPECIAL_MULT:
case OP_SPECIAL_MULTU:
case OP_SPECIAL_DIV:
case OP_SPECIAL_DIVU:
*flags_ptr = opcode_multdiv_flags;
*nb_flags = ARRAY_SIZE(opcode_multdiv_flags);
return snprintf(buf, len, "%s%s,%s,%s,%s",
special_opcodes[c.r.op],
lightrec_reg_name(get_mult_div_hi(c)),
lightrec_reg_name(get_mult_div_lo(c)),
lightrec_reg_name(c.r.rs),
lightrec_reg_name(c.r.rt));
default:
return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
}
}
static int print_op_cp(union code c, char *buf, size_t len, unsigned int cp)
{
if (cp == 2) {
switch (c.i.rs) {
case OP_CP0_MFC0:
case OP_CP0_CFC0:
case OP_CP0_MTC0:
case OP_CP0_CTC0:
return snprintf(buf, len, "%s%s,%u",
cp2_opcodes[c.i.rs],
lightrec_reg_name(c.i.rt),
c.r.rd);
default:
return snprintf(buf, len, "cp2 (0x%08x)", c.opcode);
}
} else {
switch (c.i.rs) {
case OP_CP0_MFC0:
case OP_CP0_CFC0:
case OP_CP0_MTC0:
case OP_CP0_CTC0:
return snprintf(buf, len, "%s%s,%u",
cp0_opcodes[c.i.rs],
lightrec_reg_name(c.i.rt),
c.r.rd);
case OP_CP0_RFE:
return snprintf(buf, len, "rfe ");
default:
return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
}
}
}
static int print_op(union code c, u32 pc, char *buf, size_t len,
const char ***flags_ptr, size_t *nb_flags)
{
if (c.opcode == 0)
return snprintf(buf, len, "nop ");
switch (c.i.op) {
case OP_SPECIAL:
return op->r.op == OP_SPECIAL_JR || op->r.op == OP_SPECIAL_JALR;
return print_op_special(c, buf, len, flags_ptr, nb_flags);
case OP_REGIMM:
*flags_ptr = opcode_branch_flags;
*nb_flags = ARRAY_SIZE(opcode_branch_flags);
return snprintf(buf, len, "%s%s,0x%x",
regimm_opcodes[c.i.rt],
lightrec_reg_name(c.i.rs),
pc + 4 + ((s16)c.i.imm << 2));
case OP_J:
case OP_JAL:
return true;
return snprintf(buf, len, "%s0x%x",
std_opcodes[c.i.op],
(pc & 0xf0000000) | (c.j.imm << 2));
case OP_BEQ:
case OP_BNE:
case OP_BLEZ:
return op->i.rs == op->i.rt;
case OP_REGIMM:
return (op->r.rt == OP_REGIMM_BGEZ ||
op->r.rt == OP_REGIMM_BGEZAL) && op->i.rs == 0;
case OP_BGTZ:
*flags_ptr = opcode_branch_flags;
*nb_flags = ARRAY_SIZE(opcode_branch_flags);
return snprintf(buf, len, "%s%s,%s,0x%x",
std_opcodes[c.i.op],
lightrec_reg_name(c.i.rs),
lightrec_reg_name(c.i.rt),
pc + 4 + ((s16)c.i.imm << 2));
case OP_ADDI:
case OP_ADDIU:
case OP_SLTI:
case OP_SLTIU:
case OP_ANDI:
case OP_ORI:
case OP_XORI:
return snprintf(buf, len, "%s%s,%s,0x%04hx",
std_opcodes[c.i.op],
lightrec_reg_name(c.i.rt),
lightrec_reg_name(c.i.rs),
(u16)c.i.imm);
case OP_LUI:
return snprintf(buf, len, "%s%s,0x%04hx",
std_opcodes[c.i.op],
lightrec_reg_name(c.i.rt),
(u16)c.i.imm);
case OP_CP0:
return print_op_cp(c, buf, len, 0);
case OP_CP2:
return print_op_cp(c, buf, len, 2);
case OP_LB:
case OP_LH:
case OP_LWL:
case OP_LW:
case OP_LBU:
case OP_LHU:
case OP_LWR:
case OP_SB:
case OP_SH:
case OP_SWL:
case OP_SW:
case OP_SWR:
*flags_ptr = opcode_io_flags;
*nb_flags = ARRAY_SIZE(opcode_io_flags);
return snprintf(buf, len, "%s%s,%hd(%s)",
std_opcodes[c.i.op],
lightrec_reg_name(c.i.rt),
(s16)c.i.imm,
lightrec_reg_name(c.i.rs));
case OP_LWC2:
case OP_SWC2:
*flags_ptr = opcode_io_flags;
*nb_flags = ARRAY_SIZE(opcode_io_flags);
return snprintf(buf, len, "%s%s,%hd(%s)",
std_opcodes[c.i.op],
lightrec_reg_name(c.i.rt),
(s16)c.i.imm,
lightrec_reg_name(c.i.rs));
case OP_META_BEQZ:
case OP_META_BNEZ:
*flags_ptr = opcode_branch_flags;
*nb_flags = ARRAY_SIZE(opcode_branch_flags);
return snprintf(buf, len, "%s%s,0x%x",
std_opcodes[c.i.op],
lightrec_reg_name(c.i.rs),
pc + 4 + ((s16)c.i.imm << 2));
case OP_META_MOV:
return snprintf(buf, len, "move %s,%s",
lightrec_reg_name(c.r.rd),
lightrec_reg_name(c.r.rs));
default:
return false;
return snprintf(buf, len, "unknown (0x%08x)", c.opcode);
}
}
static bool is_syscall(const struct opcode *op)
void lightrec_print_disassembly(const struct block *block, const u32 *code)
{
return (op->i.op == OP_SPECIAL && op->r.op == OP_SPECIAL_SYSCALL) ||
(op->i.op == OP_CP0 && (op->r.rs == OP_CP0_MTC0 ||
op->r.rs == OP_CP0_CTC0) &&
(op->r.rd == 12 || op->r.rd == 13));
}
void lightrec_free_opcode_list(struct lightrec_state *state, struct opcode *list)
{
struct opcode *next;
while (list) {
next = list->next;
lightrec_free(state, MEM_FOR_IR, sizeof(*list), list);
list = next;
}
}
struct opcode * lightrec_disassemble(struct lightrec_state *state,
const u32 *src, unsigned int *len)
{
struct opcode *head = NULL;
bool stop_next = false;
struct opcode *curr, *last;
const struct opcode *op;
const char **flags_ptr;
size_t nb_flags, count, count2;
char buf[256], buf2[256], buf3[256];
unsigned int i;
u32 pc, branch_pc;
for (i = 0, last = NULL; ; i++, last = curr) {
curr = lightrec_calloc(state, MEM_FOR_IR, sizeof(*curr));
if (!curr) {
pr_err("Unable to allocate memory\n");
lightrec_free_opcode_list(state, head);
return NULL;
for (i = 0; i < block->nb_ops; i++) {
op = &block->opcode_list[i];
branch_pc = get_branch_pc(block, i, 0);
pc = block->pc + (i << 2);
count = print_op((union code)code[i], pc, buf, sizeof(buf),
&flags_ptr, &nb_flags);
flags_ptr = NULL;
nb_flags = 0;
count2 = print_op(op->c, branch_pc, buf2, sizeof(buf2),
&flags_ptr, &nb_flags);
if (code[i] == op->c.opcode) {
*buf2 = '\0';
count2 = 0;
}
if (!last)
head = curr;
else
last->next = curr;
print_flags(buf3, sizeof(buf3), op->flags, flags_ptr, nb_flags);
/* TODO: Take care of endianness */
curr->opcode = LE32TOH(*src++);
curr->offset = i;
/* NOTE: The block disassembly ends after the opcode that
* follows an unconditional jump (delay slot) */
if (stop_next || is_syscall(curr))
break;
else if (is_unconditional_jump(curr))
stop_next = true;
}
if (len)
*len = (i + 1) * sizeof(u32);
return head;
}
unsigned int lightrec_cycles_of_opcode(union code code)
{
switch (code.i.op) {
case OP_META_REG_UNLOAD:
case OP_META_SYNC:
return 0;
default:
return 2;
printf("0x%08x (0x%x)\t%s%*c%s%*c%s\n", pc, i << 2,
buf, 30 - (int)count, ' ', buf2, 30 - (int)count2, ' ', buf3);
}
}
#if ENABLE_DISASSEMBLER
void lightrec_print_disassembly(const struct block *block,
const u32 *code, unsigned int length)
{
struct disassemble_info info;
unsigned int i;
memset(&info, 0, sizeof(info));
init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
info.buffer = (bfd_byte *) code;
info.buffer_vma = (bfd_vma)(uintptr_t) code;
info.buffer_length = length;
info.flavour = bfd_target_unknown_flavour;
info.arch = bfd_arch_mips;
info.mach = bfd_mach_mips3000;
disassemble_init_for_target(&info);
for (i = 0; i < length; i += 4) {
void print_insn_little_mips(bfd_vma, struct disassemble_info *);
putc('\t', stdout);
print_insn_little_mips((bfd_vma)(uintptr_t) code++, &info);
putc('\n', stdout);
}
}
#endif

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __DISASSEMBLER_H__
@ -22,24 +13,29 @@
#define __packed __attribute__((packed))
#endif
#define BIT(x) (1 << (x))
#define BIT(x) (1ULL << (x))
/* Flags for all opcodes */
#define LIGHTREC_NO_DS BIT(0)
#define LIGHTREC_UNLOAD_RS BIT(1)
#define LIGHTREC_UNLOAD_RT BIT(2)
#define LIGHTREC_UNLOAD_RD BIT(3)
#define LIGHTREC_SYNC BIT(4)
/* Flags for load/store opcodes */
#define LIGHTREC_DIRECT_IO BIT(1)
#define LIGHTREC_HW_IO BIT(2)
#define LIGHTREC_SMC BIT(3)
#define LIGHTREC_NO_INVALIDATE BIT(4)
#define LIGHTREC_DIRECT_IO BIT(5)
#define LIGHTREC_HW_IO BIT(6)
#define LIGHTREC_SMC BIT(7)
#define LIGHTREC_NO_INVALIDATE BIT(8)
/* Flags for branches */
#define LIGHTREC_EMULATE_BRANCH BIT(1)
#define LIGHTREC_LOCAL_BRANCH BIT(2)
#define LIGHTREC_EMULATE_BRANCH BIT(5)
#define LIGHTREC_LOCAL_BRANCH BIT(6)
/* Flags for div/mult opcodes */
#define LIGHTREC_NO_HI BIT(1)
#define LIGHTREC_NO_DIV_CHECK BIT(2)
#define LIGHTREC_NO_LO BIT(5)
#define LIGHTREC_NO_HI BIT(6)
#define LIGHTREC_NO_DIV_CHECK BIT(7)
struct block;
@ -77,13 +73,10 @@ enum standard_opcodes {
OP_LWC2 = 0x32,
OP_SWC2 = 0x3a,
OP_META_REG_UNLOAD = 0x11,
OP_META_BEQZ = 0x14,
OP_META_BNEZ = 0x15,
OP_META_MOV = 0x16,
OP_META_SYNC = 0x17,
};
enum special_opcodes {
@ -205,18 +198,8 @@ struct opcode {
struct opcode_j j;
};
u16 flags;
u16 offset;
struct opcode *next;
};
struct opcode * lightrec_disassemble(struct lightrec_state *state,
const u32 *src, unsigned int *len);
void lightrec_free_opcode_list(struct lightrec_state *state,
struct opcode *list);
unsigned int lightrec_cycles_of_opcode(union code code);
void lightrec_print_disassembly(const struct block *block,
const u32 *code, unsigned int length);
void lightrec_print_disassembly(const struct block *block, const u32 *code);
#endif /* __DISASSEMBLER_H__ */

1454
deps/lightrec/emitter.c vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __EMITTER_H__
@ -20,9 +11,7 @@
struct block;
struct opcode;
void lightrec_rec_opcode(const struct block *block,
const struct opcode *op, u32 pc);
void lightrec_emit_eob(const struct block *block,
const struct opcode *op, u32 pc);
void lightrec_rec_opcode(const struct block *block, u16 offset);
void lightrec_emit_eob(const struct block *block, u16 offset);
#endif /* __EMITTER_H__ */

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "disassembler.h"
@ -39,18 +30,45 @@ struct interpreter {
struct opcode *op;
u32 cycles;
bool delay_slot;
u16 offset;
};
static u32 int_get_branch_pc(const struct interpreter *inter)
{
return get_branch_pc(inter->block, inter->offset, 0);
}
static inline u32 int_get_ds_pc(const struct interpreter *inter, s16 imm)
{
return get_ds_pc(inter->block, inter->offset, imm);
}
static inline struct opcode *next_op(const struct interpreter *inter)
{
return &inter->block->opcode_list[inter->offset + 1];
}
static inline u32 execute(lightrec_int_func_t func, struct interpreter *inter)
{
return (*func)(inter);
}
static inline u32 lightrec_int_op(struct interpreter *inter)
{
return execute(int_standard[inter->op->i.op], inter);
}
static inline u32 jump_skip(struct interpreter *inter)
{
inter->op = inter->op->next;
inter->op = next_op(inter);
inter->offset++;
return execute(int_standard[inter->op->i.op], inter);
if (inter->op->flags & LIGHTREC_SYNC) {
inter->state->current_cycle += inter->cycles;
inter->cycles = 0;
}
return lightrec_int_op(inter);
}
static inline u32 jump_next(struct interpreter *inter)
@ -70,16 +88,12 @@ static inline u32 jump_after_branch(struct interpreter *inter)
if (unlikely(inter->delay_slot))
return 0;
inter->op = inter->op->next;
inter->op = next_op(inter);
inter->offset++;
return jump_skip(inter);
}
static inline u32 lightrec_int_op(struct interpreter *inter)
{
return execute(int_standard[inter->op->i.op], inter);
}
static void update_cycles_before_branch(struct interpreter *inter)
{
u32 cycles;
@ -89,7 +103,7 @@ static void update_cycles_before_branch(struct interpreter *inter)
if (has_delay_slot(inter->op->c) &&
!(inter->op->flags & LIGHTREC_NO_DS))
cycles += lightrec_cycles_of_opcode(inter->op->next->c);
cycles += lightrec_cycles_of_opcode(next_op(inter)->c);
inter->cycles += cycles;
inter->state->current_cycle += inter->cycles;
@ -131,7 +145,7 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
{
struct lightrec_state *state = inter->state;
u32 *reg_cache = state->native_reg_cache;
struct opcode new_op, *op = inter->op->next;
struct opcode new_op, *op = next_op(inter);
union code op_next;
struct interpreter inter2 = {
.state = state,
@ -228,8 +242,6 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
} else {
new_op.c = op_next;
new_op.flags = 0;
new_op.offset = 0;
new_op.next = NULL;
inter2.op = &new_op;
/* Execute the first opcode of the next block */
@ -243,8 +255,7 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
inter->cycles += lightrec_cycles_of_opcode(op_next);
}
} else {
next_pc = inter->block->pc
+ (inter->op->offset + 2) * sizeof(u32);
next_pc = int_get_ds_pc(inter, 2);
}
inter2.block = inter->block;
@ -291,8 +302,6 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
new_op.c = op_next;
new_op.flags = 0;
new_op.offset = sizeof(u32);
new_op.next = NULL;
inter2.op = &new_op;
inter2.block = NULL;
@ -316,7 +325,7 @@ static u32 int_unimplemented(struct interpreter *inter)
static u32 int_jump(struct interpreter *inter, bool link)
{
struct lightrec_state *state = inter->state;
u32 old_pc = inter->block->pc + inter->op->offset * sizeof(u32);
u32 old_pc = int_get_branch_pc(inter);
u32 pc = (old_pc & 0xf0000000) | (inter->op->j.imm << 2);
if (link)
@ -344,7 +353,7 @@ static u32 int_jumpr(struct interpreter *inter, u8 link_reg)
u32 old_pc, next_pc = state->native_reg_cache[inter->op->r.rs];
if (link_reg) {
old_pc = inter->block->pc + inter->op->offset * sizeof(u32);
old_pc = int_get_branch_pc(inter);
state->native_reg_cache[link_reg] = old_pc + 8;
}
@ -404,7 +413,7 @@ static u32 int_branch(struct interpreter *inter, u32 pc,
static u32 int_beq(struct interpreter *inter, bool bne)
{
u32 rs, rt, old_pc = inter->block->pc + inter->op->offset * sizeof(u32);
u32 rs, rt, old_pc = int_get_branch_pc(inter);
rs = inter->state->native_reg_cache[inter->op->i.rs];
rt = inter->state->native_reg_cache[inter->op->i.rt];
@ -424,7 +433,7 @@ static u32 int_BNE(struct interpreter *inter)
static u32 int_bgez(struct interpreter *inter, bool link, bool lt, bool regimm)
{
u32 old_pc = inter->block->pc + inter->op->offset * sizeof(u32);
u32 old_pc = int_get_branch_pc(inter);
s32 rs;
if (link)
@ -492,7 +501,7 @@ static u32 int_ctc(struct interpreter *inter)
* interrupt status. */
if (!(inter->op->flags & LIGHTREC_NO_DS) &&
op->i.op == OP_CP0 && (op->r.rd == 12 || op->r.rd == 13))
return inter->block->pc + (op->offset + 1) * sizeof(u32);
return int_get_ds_pc(inter, 1);
else
return jump_next(inter);
}
@ -520,7 +529,7 @@ static u32 int_CP(struct interpreter *inter)
const struct lightrec_cop_ops *ops;
const struct opcode *op = inter->op;
if ((op->j.imm >> 25) & 1)
if (op->i.op == OP_CP2)
ops = &state->ops.cop2_ops;
else
ops = &state->ops.cop0_ops;
@ -613,7 +622,7 @@ static u32 int_io(struct interpreter *inter, bool is_load)
val = lightrec_rw(inter->state, inter->op->c,
reg_cache[op->rs], reg_cache[op->rt],
&inter->op->flags);
&inter->op->flags, inter->block);
if (is_load && op->rt)
reg_cache[op->rt] = val;
@ -636,9 +645,9 @@ static u32 int_store(struct interpreter *inter)
lightrec_rw(inter->state, inter->op->c,
inter->state->native_reg_cache[inter->op->i.rs],
inter->state->native_reg_cache[inter->op->i.rt],
&inter->op->flags);
&inter->op->flags, inter->block);
next_pc = inter->block->pc + (inter->op->offset + 1) * 4;
next_pc = int_get_ds_pc(inter, 1);
/* Invalidate next PC, to force the rest of the block to be rebuilt */
lightrec_invalidate(inter->state, next_pc, 4);
@ -725,7 +734,7 @@ static u32 int_syscall_break(struct interpreter *inter)
else
inter->state->exit_flags |= LIGHTREC_EXIT_SYSCALL;
return inter->block->pc + inter->op->offset * sizeof(u32);
return int_get_ds_pc(inter, 0);
}
static u32 int_special_MFHI(struct interpreter *inter)
@ -773,11 +782,14 @@ static u32 int_special_MULT(struct interpreter *inter)
u32 *reg_cache = inter->state->native_reg_cache;
s32 rs = reg_cache[inter->op->r.rs];
s32 rt = reg_cache[inter->op->r.rt];
u8 reg_lo = get_mult_div_lo(inter->op->c);
u8 reg_hi = get_mult_div_hi(inter->op->c);
u64 res = (s64)rs * (s64)rt;
if (!(inter->op->flags & LIGHTREC_NO_HI))
reg_cache[REG_HI] = res >> 32;
reg_cache[REG_LO] = res;
reg_cache[reg_hi] = res >> 32;
if (!(inter->op->flags & LIGHTREC_NO_LO))
reg_cache[reg_lo] = res;
return jump_next(inter);
}
@ -787,11 +799,14 @@ static u32 int_special_MULTU(struct interpreter *inter)
u32 *reg_cache = inter->state->native_reg_cache;
u32 rs = reg_cache[inter->op->r.rs];
u32 rt = reg_cache[inter->op->r.rt];
u8 reg_lo = get_mult_div_lo(inter->op->c);
u8 reg_hi = get_mult_div_hi(inter->op->c);
u64 res = (u64)rs * (u64)rt;
if (!(inter->op->flags & LIGHTREC_NO_HI))
reg_cache[REG_HI] = res >> 32;
reg_cache[REG_LO] = res;
reg_cache[reg_hi] = res >> 32;
if (!(inter->op->flags & LIGHTREC_NO_LO))
reg_cache[reg_lo] = res;
return jump_next(inter);
}
@ -801,6 +816,8 @@ static u32 int_special_DIV(struct interpreter *inter)
u32 *reg_cache = inter->state->native_reg_cache;
s32 rs = reg_cache[inter->op->r.rs];
s32 rt = reg_cache[inter->op->r.rt];
u8 reg_lo = get_mult_div_lo(inter->op->c);
u8 reg_hi = get_mult_div_hi(inter->op->c);
u32 lo, hi;
if (rt == 0) {
@ -811,8 +828,10 @@ static u32 int_special_DIV(struct interpreter *inter)
hi = rs % rt;
}
reg_cache[REG_HI] = hi;
reg_cache[REG_LO] = lo;
if (!(inter->op->flags & LIGHTREC_NO_HI))
reg_cache[reg_hi] = hi;
if (!(inter->op->flags & LIGHTREC_NO_LO))
reg_cache[reg_lo] = lo;
return jump_next(inter);
}
@ -822,6 +841,8 @@ static u32 int_special_DIVU(struct interpreter *inter)
u32 *reg_cache = inter->state->native_reg_cache;
u32 rs = reg_cache[inter->op->r.rs];
u32 rt = reg_cache[inter->op->r.rt];
u8 reg_lo = get_mult_div_lo(inter->op->c);
u8 reg_hi = get_mult_div_hi(inter->op->c);
u32 lo, hi;
if (rt == 0) {
@ -832,8 +853,10 @@ static u32 int_special_DIVU(struct interpreter *inter)
hi = rs % rt;
}
reg_cache[REG_HI] = hi;
reg_cache[REG_LO] = lo;
if (!(inter->op->flags & LIGHTREC_NO_HI))
reg_cache[reg_hi] = hi;
if (!(inter->op->flags & LIGHTREC_NO_LO))
reg_cache[reg_lo] = lo;
return jump_next(inter);
}
@ -942,11 +965,6 @@ static u32 int_special_SLTU(struct interpreter *inter)
return jump_next(inter);
}
static u32 int_META_SKIP(struct interpreter *inter)
{
return jump_skip(inter);
}
static u32 int_META_MOV(struct interpreter *inter)
{
u32 *reg_cache = inter->state->native_reg_cache;
@ -958,14 +976,6 @@ static u32 int_META_MOV(struct interpreter *inter)
return jump_next(inter);
}
static u32 int_META_SYNC(struct interpreter *inter)
{
inter->state->current_cycle += inter->cycles;
inter->cycles = 0;
return jump_skip(inter);
}
static const lightrec_int_func_t int_standard[64] = {
SET_DEFAULT_ELM(int_standard, int_unimplemented),
[OP_SPECIAL] = int_SPECIAL,
@ -1001,11 +1011,9 @@ static const lightrec_int_func_t int_standard[64] = {
[OP_LWC2] = int_LWC2,
[OP_SWC2] = int_store,
[OP_META_REG_UNLOAD] = int_META_SKIP,
[OP_META_BEQZ] = int_BEQ,
[OP_META_BNEZ] = int_BNE,
[OP_META_MOV] = int_META_MOV,
[OP_META_SYNC] = int_META_SYNC,
};
static const lightrec_int_func_t int_special[64] = {
@ -1106,14 +1114,15 @@ static u32 int_CP2(struct interpreter *inter)
return int_CP(inter);
}
static u32 lightrec_emulate_block_list(struct block *block, struct opcode *op)
static u32 lightrec_emulate_block_list(struct block *block, u32 offset)
{
struct interpreter inter;
u32 pc;
inter.block = block;
inter.state = block->state;
inter.op = op;
inter.offset = offset;
inter.op = &block->opcode_list[offset];
inter.cycles = 0;
inter.delay_slot = false;
@ -1130,12 +1139,9 @@ static u32 lightrec_emulate_block_list(struct block *block, struct opcode *op)
u32 lightrec_emulate_block(struct block *block, u32 pc)
{
u32 offset = (kunseg(pc) - kunseg(block->pc)) >> 2;
struct opcode *op;
for (op = block->opcode_list;
op && (op->offset < offset); op = op->next);
if (op)
return lightrec_emulate_block_list(block, op);
if (offset < block->nb_ops)
return lightrec_emulate_block_list(block, offset);
pr_err("PC 0x%x is outside block at PC 0x%x\n", pc, block->pc);

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_INTERPRETER_H__

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2016-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2016-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_PRIVATE_H__
@ -56,12 +47,16 @@
#define BLOCK_SHOULD_RECOMPILE BIT(1)
#define BLOCK_FULLY_TAGGED BIT(2)
#define BLOCK_IS_DEAD BIT(3)
#define BLOCK_IS_MEMSET BIT(4)
#define RAM_SIZE 0x200000
#define BIOS_SIZE 0x80000
#define CODE_LUT_SIZE ((RAM_SIZE + BIOS_SIZE) >> 2)
#define REG_LO 32
#define REG_HI 33
/* Definition of jit_state_t (avoids inclusion of <lightning.h>) */
struct jit_node;
struct jit_state;
@ -101,17 +96,27 @@ struct lightrec_branch_target {
u32 offset;
};
enum c_wrappers {
C_WRAPPER_RW,
C_WRAPPER_RW_GENERIC,
C_WRAPPER_MFC,
C_WRAPPER_MTC,
C_WRAPPER_RFE,
C_WRAPPER_CP,
C_WRAPPER_SYSCALL,
C_WRAPPER_BREAK,
C_WRAPPERS_COUNT,
};
struct lightrec_state {
u32 native_reg_cache[34];
u32 next_pc;
u32 current_cycle;
u32 target_cycle;
u32 exit_flags;
struct block *dispatcher, *rw_wrapper, *rw_generic_wrapper,
*mfc_wrapper, *mtc_wrapper, *rfe_wrapper, *cp_wrapper,
*syscall_wrapper, *break_wrapper;
void *rw_func, *rw_generic_func, *mfc_func, *mtc_func, *rfe_func,
*cp_func, *syscall_func, *break_func;
u32 old_cycle_counter;
struct block *dispatcher, *c_wrapper_block;
void *c_wrapper, *c_wrappers[C_WRAPPERS_COUNT];
struct jit_node *branches[512];
struct lightrec_branch local_branches[512];
struct lightrec_branch_target targets[512];
@ -124,6 +129,7 @@ struct lightrec_state {
struct recompiler *rec;
struct reaper *reaper;
void (*eob_wrapper_func)(void);
void (*memset_func)(void);
void (*get_next_block)(void);
struct lightrec_ops ops;
unsigned int nb_precompile;
@ -137,7 +143,8 @@ struct lightrec_state {
};
u32 lightrec_rw(struct lightrec_state *state, union code op,
u32 addr, u32 data, u16 *flags);
u32 addr, u32 data, u16 *flags,
struct block *block);
void lightrec_free_block(struct block *block);
@ -159,6 +166,24 @@ static inline u32 lut_offset(u32 pc)
return (pc & (RAM_SIZE - 1)) >> 2; // RAM
}
static inline u32 get_ds_pc(const struct block *block, u16 offset, s16 imm)
{
u16 flags = block->opcode_list[offset].flags;
offset += !!(OPT_SWITCH_DELAY_SLOTS && (flags & LIGHTREC_NO_DS));
return block->pc + (offset + imm << 2);
}
static inline u32 get_branch_pc(const struct block *block, u16 offset, s16 imm)
{
u16 flags = block->opcode_list[offset].flags;
offset -= !!(OPT_SWITCH_DELAY_SLOTS && (flags & LIGHTREC_NO_DS));
return block->pc + (offset + imm << 2);
}
void lightrec_mtc(struct lightrec_state *state, union code op, u32 data);
u32 lightrec_mfc(struct lightrec_state *state, union code op);
@ -166,5 +191,18 @@ union code lightrec_read_opcode(struct lightrec_state *state, u32 pc);
struct block * lightrec_get_block(struct lightrec_state *state, u32 pc);
int lightrec_compile_block(struct block *block);
void lightrec_free_opcode_list(struct block *block);
unsigned int lightrec_cycles_of_opcode(union code code);
static inline u8 get_mult_div_lo(union code c)
{
return (OPT_FLAG_MULT_DIV && c.r.rd) ? c.r.rd : REG_LO;
}
static inline u8 get_mult_div_hi(union code c)
{
return (OPT_FLAG_MULT_DIV && c.r.imm) ? c.r.imm : REG_HI;
}
#endif /* __LIGHTREC_PRIVATE_H__ */

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "blockcache.h"
@ -98,11 +89,14 @@ static const struct lightrec_mem_map_ops lightrec_default_ops = {
.lw = lightrec_default_lw,
};
static void __segfault_cb(struct lightrec_state *state, u32 addr)
static void __segfault_cb(struct lightrec_state *state, u32 addr,
const struct block *block)
{
lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
pr_err("Segmentation fault in recompiled code: invalid "
"load/store at address 0x%08x\n", addr);
if (block)
pr_err("Was executing block PC 0x%08x\n", block->pc);
}
static void lightrec_swl(struct lightrec_state *state,
@ -196,10 +190,12 @@ static void lightrec_lwc2(struct lightrec_state *state, union code op,
}
static void lightrec_invalidate_map(struct lightrec_state *state,
const struct lightrec_mem_map *map, u32 addr)
const struct lightrec_mem_map *map, u32 addr, u32 len)
{
if (map == &state->maps[PSX_MAP_KERNEL_USER_RAM])
state->code_lut[lut_offset(addr)] = NULL;
if (map == &state->maps[PSX_MAP_KERNEL_USER_RAM]) {
memset(&state->code_lut[lut_offset(addr)], 0,
((len + 3) / 4) * sizeof(void *));
}
}
static const struct lightrec_mem_map *
@ -218,7 +214,7 @@ lightrec_get_map(struct lightrec_state *state, u32 kaddr)
}
u32 lightrec_rw(struct lightrec_state *state, union code op,
u32 addr, u32 data, u16 *flags)
u32 addr, u32 data, u16 *flags, struct block *block)
{
const struct lightrec_mem_map *map;
const struct lightrec_mem_map_ops *ops;
@ -230,7 +226,7 @@ u32 lightrec_rw(struct lightrec_state *state, union code op,
map = lightrec_get_map(state, kaddr);
if (!map) {
__segfault_cb(state, addr);
__segfault_cb(state, addr, block);
return 0;
}
@ -294,11 +290,13 @@ u32 lightrec_rw(struct lightrec_state *state, union code op,
}
static void lightrec_rw_helper(struct lightrec_state *state,
union code op, u16 *flags)
union code op, u16 *flags,
struct block *block)
{
u32 ret = lightrec_rw(state, op,
state->native_reg_cache[op.i.rs],
state->native_reg_cache[op.i.rt], flags);
state->native_reg_cache[op.i.rt], flags,
block);
switch (op.i.op) {
case OP_LB:
@ -317,20 +315,32 @@ static void lightrec_rw_helper(struct lightrec_state *state,
static void lightrec_rw_cb(struct lightrec_state *state, union code op)
{
lightrec_rw_helper(state, op, NULL);
lightrec_rw_helper(state, op, NULL, NULL);
}
static void lightrec_rw_generic_cb(struct lightrec_state *state,
struct opcode *op, struct block *block)
static void lightrec_rw_generic_cb(struct lightrec_state *state, u32 arg)
{
bool was_tagged = op->flags & (LIGHTREC_HW_IO | LIGHTREC_DIRECT_IO);
struct block *block;
struct opcode *op;
bool was_tagged;
u16 offset = (u16)arg;
lightrec_rw_helper(state, op->c, &op->flags);
block = lightrec_find_block_from_lut(state->block_cache,
arg >> 16, state->next_pc);
if (unlikely(!block)) {
pr_err("rw_generic: No block found in LUT for PC 0x%x offset 0x%x\n",
state->next_pc, offset);
return;
}
op = &block->opcode_list[offset];
was_tagged = op->flags & (LIGHTREC_HW_IO | LIGHTREC_DIRECT_IO);
lightrec_rw_helper(state, op->c, &op->flags, block);
if (!was_tagged) {
pr_debug("Opcode of block at PC 0x%08x offset 0x%x has been "
"tagged - flag for recompilation\n",
block->pc, op->offset << 2);
pr_debug("Opcode of block at PC 0x%08x has been tagged - flag "
"for recompilation\n", block->pc);
block->flags |= BLOCK_SHOULD_RECOMPILE;
}
@ -407,7 +417,7 @@ static void lightrec_cp_cb(struct lightrec_state *state, union code op)
{
void (*func)(struct lightrec_state *, u32);
if ((op.opcode >> 25) & 1)
if (op.i.op == OP_CP2)
func = state->ops.cop2_ops.op;
else
func = state->ops.cop0_ops.op;
@ -466,12 +476,17 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc)
for (;;) {
func = state->code_lut[lut_offset(pc)];
if (func && func != state->get_next_block)
return func;
break;
block = lightrec_get_block(state, pc);
if (unlikely(!block))
return NULL;
break;
if (OPT_REPLACE_MEMSET && (block->flags & BLOCK_IS_MEMSET)) {
func = state->memset_func;
break;
}
should_recompile = block->flags & BLOCK_SHOULD_RECOMPILE &&
!(block->flags & BLOCK_IS_DEAD);
@ -493,7 +508,7 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc)
func = block->function;
if (likely(func))
return func;
break;
/* Block wasn't compiled yet - run the interpreter */
if (!ENABLE_THREADED_COMPILER &&
@ -510,40 +525,26 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc)
}
if (state->exit_flags != LIGHTREC_EXIT_NORMAL ||
state->current_cycle >= state->target_cycle) {
state->next_pc = pc;
return NULL;
}
state->current_cycle >= state->target_cycle)
break;
}
}
static s32 c_generic_function_wrapper(struct lightrec_state *state,
s32 cycles_delta,
void (*f)(struct lightrec_state *,
struct opcode *,
struct block *),
struct opcode *op, struct block *block)
{
state->current_cycle = state->target_cycle - cycles_delta;
(*f)(state, op, block);
return state->target_cycle - state->current_cycle;
state->next_pc = pc;
return func;
}
static s32 c_function_wrapper(struct lightrec_state *state, s32 cycles_delta,
void (*f)(struct lightrec_state *, union code),
union code op)
void (*f)(struct lightrec_state *, u32 d),
u32 d)
{
state->current_cycle = state->target_cycle - cycles_delta;
(*f)(state, op);
(*f)(state, d);
return state->target_cycle - state->current_cycle;
}
static struct block * generate_wrapper(struct lightrec_state *state,
void *f, bool generic)
static struct block * generate_wrapper(struct lightrec_state *state)
{
struct block *block;
jit_state_t *_jit;
@ -594,14 +595,9 @@ static struct block * generate_wrapper(struct lightrec_state *state,
jit_prepare();
jit_pushargr(LIGHTREC_REG_STATE);
jit_pushargr(LIGHTREC_REG_CYCLE);
jit_pushargi((uintptr_t)f);
jit_pushargr(JIT_R0);
if (generic) {
jit_pushargr(JIT_R1);
jit_finishi(c_generic_function_wrapper);
} else {
jit_finishi(c_function_wrapper);
}
jit_pushargr(JIT_R1);
jit_finishi(c_function_wrapper);
#if __WORDSIZE == 64
jit_retval_i(LIGHTREC_REG_CYCLE);
@ -639,11 +635,40 @@ err_no_mem:
return NULL;
}
static u32 lightrec_memset(struct lightrec_state *state)
{
const struct lightrec_mem_map *map;
u32 pc, kunseg_pc = kunseg(state->native_reg_cache[4]);
u32 length = state->native_reg_cache[5] * 4;
map = lightrec_get_map(state, kunseg_pc);
if (!map) {
pr_err("Unable to find memory map for memset target address "
"0x%x\n", kunseg_pc);
return 0;
}
pc = kunseg_pc - map->pc;
while (map->mirror_of)
map = map->mirror_of;
pr_debug("Calling host memset, PC 0x%x (host address 0x%lx) for %u bytes\n",
kunseg_pc, (uintptr_t)map->address + pc, length);
memset((void *)map->address + pc, 0, length);
if (!state->invalidate_from_dma_only)
lightrec_invalidate_map(state, map, kunseg_pc, length);
/* Rough estimation of the number of cycles consumed */
return 8 + 5 * (length + 3 / 4);
}
static struct block * generate_dispatcher(struct lightrec_state *state)
{
struct block *block;
jit_state_t *_jit;
jit_node_t *to_end, *to_end2, *to_c, *loop, *addr, *addr2;
jit_node_t *to_end, *to_c, *loop, *addr, *addr2, *addr3;
unsigned int i;
u32 offset, ram_len;
jit_word_t code_size;
@ -682,10 +707,35 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
/* Call the block's code */
jit_jmpr(JIT_R0);
if (OPT_REPLACE_MEMSET) {
/* Blocks will jump here when they need to call
* lightrec_memset() */
addr3 = jit_indirect();
jit_prepare();
jit_pushargr(LIGHTREC_REG_STATE);
jit_finishi(lightrec_memset);
#if __WORDSIZE == 64
jit_ldxi_ui(JIT_V0, LIGHTREC_REG_STATE,
offsetof(struct lightrec_state, native_reg_cache[31]));
#else
jit_ldxi_i(JIT_V0, LIGHTREC_REG_STATE,
offsetof(struct lightrec_state, native_reg_cache[31]));
#endif
jit_retval(JIT_R0);
jit_subr(LIGHTREC_REG_CYCLE, LIGHTREC_REG_CYCLE, JIT_R0);
}
/* The block will jump here, with the number of cycles remaining in
* LIGHTREC_REG_CYCLE */
addr2 = jit_indirect();
/* Store back the next_pc to the lightrec_state structure */
offset = offsetof(struct lightrec_state, next_pc);
jit_stxi_i(offset, LIGHTREC_REG_STATE, JIT_V0);
/* Jump to end if state->target_cycle < state->current_cycle */
to_end = jit_blei(LIGHTREC_REG_CYCLE, 0);
@ -707,7 +757,7 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
/* Slow path: call C function get_next_block_func() */
jit_patch(to_c);
if (ENABLE_FIRST_PASS) {
if (ENABLE_FIRST_PASS || OPT_DETECT_IMPOSSIBLE_BRANCHES) {
/* We may call the interpreter - update state->current_cycle */
jit_ldxi_i(JIT_R2, LIGHTREC_REG_STATE,
offsetof(struct lightrec_state, target_cycle));
@ -728,7 +778,7 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
jit_finishi(&get_next_block_func);
jit_retval(JIT_R0);
if (ENABLE_FIRST_PASS) {
if (ENABLE_FIRST_PASS || OPT_DETECT_IMPOSSIBLE_BRANCHES) {
/* The interpreter may have updated state->current_cycle and
* state->target_cycle - recalc the delta */
jit_ldxi_i(JIT_R1, LIGHTREC_REG_STATE,
@ -741,18 +791,10 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
/* If we get non-NULL, loop */
jit_patch_at(jit_bnei(JIT_R0, 0), loop);
to_end2 = jit_jmpi();
/* When exiting, the recompiled code will jump to that address */
jit_note(__FILE__, __LINE__);
jit_patch(to_end);
/* Store back the next_pc to the lightrec_state structure */
offset = offsetof(struct lightrec_state, next_pc);
jit_stxi_i(offset, LIGHTREC_REG_STATE, JIT_V0);
jit_patch(to_end2);
jit_retr(LIGHTREC_REG_CYCLE);
jit_epilog();
@ -769,6 +811,8 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
block->code_size = code_size;
state->eob_wrapper_func = jit_address(addr2);
if (OPT_REPLACE_MEMSET)
state->memset_func = jit_address(addr3);
state->get_next_block = jit_address(addr);
if (ENABLE_DISASSEMBLER) {
@ -803,6 +847,58 @@ union code lightrec_read_opcode(struct lightrec_state *state, u32 pc)
return (union code) *code;
}
unsigned int lightrec_cycles_of_opcode(union code code)
{
return 2;
}
void lightrec_free_opcode_list(struct block *block)
{
lightrec_free(block->state, MEM_FOR_IR,
sizeof(*block->opcode_list) * block->nb_ops,
block->opcode_list);
}
static unsigned int lightrec_get_mips_block_len(const u32 *src)
{
unsigned int i;
union code c;
for (i = 1; ; i++) {
c.opcode = LE32TOH(*src++);
if (is_syscall(c))
return i;
if (is_unconditional_jump(c))
return i + 1;
}
}
static struct opcode * lightrec_disassemble(struct lightrec_state *state,
const u32 *src, unsigned int *len)
{
struct opcode *list;
unsigned int i, length;
length = lightrec_get_mips_block_len(src);
list = lightrec_malloc(state, MEM_FOR_IR, sizeof(*list) * length);
if (!list) {
pr_err("Unable to allocate memory\n");
return NULL;
}
for (i = 0; i < length; i++) {
list[i].opcode = LE32TOH(src[i]);
list[i].flags = 0;
}
*len = length * sizeof(u32);
return list;
}
static struct block * lightrec_precompile_block(struct lightrec_state *state,
u32 pc)
{
@ -857,16 +953,19 @@ static struct block * lightrec_precompile_block(struct lightrec_state *state,
if (ENABLE_DISASSEMBLER) {
pr_debug("Disassembled block at PC: 0x%x\n", block->pc);
lightrec_print_disassembly(block, code, length);
lightrec_print_disassembly(block, code);
}
pr_debug("Block size: %lu opcodes\n", block->nb_ops);
pr_debug("Block size: %hu opcodes\n", block->nb_ops);
/* If the first opcode is an 'impossible' branch, never compile the
* block */
if (should_emulate(list))
if (should_emulate(block->opcode_list))
block->flags |= BLOCK_NEVER_COMPILE;
if (OPT_REPLACE_MEMSET && (block->flags & BLOCK_IS_MEMSET))
state->code_lut[lut_offset(pc)] = state->memset_func;
block->hash = lightrec_calculate_block_hash(block);
pr_debug("Recompile count: %u\n", state->nb_precompile++);
@ -874,11 +973,14 @@ static struct block * lightrec_precompile_block(struct lightrec_state *state,
return block;
}
static bool lightrec_block_is_fully_tagged(struct block *block)
static bool lightrec_block_is_fully_tagged(const struct block *block)
{
struct opcode *op;
const struct opcode *op;
unsigned int i;
for (i = 0; i < block->nb_ops; i++) {
op = &block->opcode_list[i];
for (op = block->opcode_list; op; op = op->next) {
/* Verify that all load/stores of the opcode list
* Check all loads/stores of the opcode list and mark the
* block as fully compiled if they all have been tagged. */
@ -933,7 +1035,7 @@ int lightrec_compile_block(struct block *block)
bool skip_next = false;
jit_word_t code_size;
unsigned int i, j;
u32 next_pc, offset;
u32 offset;
fully_tagged = lightrec_block_is_fully_tagged(block);
if (fully_tagged)
@ -957,8 +1059,8 @@ int lightrec_compile_block(struct block *block)
start_of_block = jit_label();
for (elm = block->opcode_list; elm; elm = elm->next) {
next_pc = block->pc + elm->offset * sizeof(u32);
for (i = 0; i < block->nb_ops; i++) {
elm = &block->opcode_list[i];
if (skip_next) {
skip_next = false;
@ -969,11 +1071,12 @@ int lightrec_compile_block(struct block *block)
if (should_emulate(elm)) {
pr_debug("Branch at offset 0x%x will be emulated\n",
elm->offset << 2);
lightrec_emit_eob(block, elm, next_pc);
i << 2);
lightrec_emit_eob(block, i);
skip_next = !(elm->flags & LIGHTREC_NO_DS);
} else if (elm->opcode) {
lightrec_rec_opcode(block, elm, next_pc);
} else {
lightrec_rec_opcode(block, i);
skip_next = has_delay_slot(elm->c) &&
!(elm->flags & LIGHTREC_NO_DS);
#if _WIN32
@ -1086,7 +1189,7 @@ int lightrec_compile_block(struct block *block)
if (fully_tagged && !op_list_freed) {
pr_debug("Block PC 0x%08x is fully tagged"
" - free opcode list\n", block->pc);
lightrec_free_opcode_list(state, block->opcode_list);
lightrec_free_opcode_list(block);
block->opcode_list = NULL;
}
@ -1104,6 +1207,20 @@ int lightrec_compile_block(struct block *block)
return 0;
}
static void lightrec_print_info(struct lightrec_state *state)
{
if ((state->current_cycle & ~0xfffffff) != state->old_cycle_counter) {
pr_info("Lightrec RAM usage: IR %u KiB, CODE %u KiB, "
"MIPS %u KiB, TOTAL %u KiB, avg. IPI %f\n",
lightrec_get_mem_usage(MEM_FOR_IR) / 1024,
lightrec_get_mem_usage(MEM_FOR_CODE) / 1024,
lightrec_get_mem_usage(MEM_FOR_MIPS_CODE) / 1024,
lightrec_get_total_mem_usage() / 1024,
lightrec_get_average_ipi());
state->old_cycle_counter = state->current_cycle & ~0xfffffff;
}
}
u32 lightrec_execute(struct lightrec_state *state, u32 pc, u32 target_cycle)
{
s32 (*func)(void *, s32) = (void *)state->dispatcher->function;
@ -1117,6 +1234,7 @@ u32 lightrec_execute(struct lightrec_state *state, u32 pc, u32 target_cycle)
target_cycle = UINT_MAX;
state->target_cycle = target_cycle;
state->next_pc = pc;
block_trace = get_next_block_func(state, pc);
if (block_trace) {
@ -1130,6 +1248,9 @@ u32 lightrec_execute(struct lightrec_state *state, u32 pc, u32 target_cycle)
if (ENABLE_THREADED_COMPILER)
lightrec_reaper_reap(state->reaper);
if (LOG_LEVEL >= INFO_L)
lightrec_print_info(state);
return state->next_pc;
}
@ -1146,14 +1267,19 @@ u32 lightrec_run_interpreter(struct lightrec_state *state, u32 pc)
state->exit_flags = LIGHTREC_EXIT_NORMAL;
return lightrec_emulate_block(block, pc);
pc = lightrec_emulate_block(block, pc);
if (LOG_LEVEL >= INFO_L)
lightrec_print_info(state);
return pc;
}
void lightrec_free_block(struct block *block)
{
lightrec_unregister(MEM_FOR_MIPS_CODE, block->nb_ops * sizeof(u32));
if (block->opcode_list)
lightrec_free_opcode_list(block->state, block->opcode_list);
lightrec_free_opcode_list(block);
if (block->_jit)
_jit_destroy_state(block->_jit);
lightrec_unregister(MEM_FOR_CODE, block->code_size);
@ -1220,50 +1346,20 @@ struct lightrec_state * lightrec_init(char *argv0,
if (!state->dispatcher)
goto err_free_reaper;
state->rw_generic_wrapper = generate_wrapper(state,
lightrec_rw_generic_cb,
true);
if (!state->rw_generic_wrapper)
state->c_wrapper_block = generate_wrapper(state);
if (!state->c_wrapper_block)
goto err_free_dispatcher;
state->rw_wrapper = generate_wrapper(state, lightrec_rw_cb, false);
if (!state->rw_wrapper)
goto err_free_generic_rw_wrapper;
state->c_wrapper = state->c_wrapper_block->function;
state->mfc_wrapper = generate_wrapper(state, lightrec_mfc_cb, false);
if (!state->mfc_wrapper)
goto err_free_rw_wrapper;
state->mtc_wrapper = generate_wrapper(state, lightrec_mtc_cb, false);
if (!state->mtc_wrapper)
goto err_free_mfc_wrapper;
state->rfe_wrapper = generate_wrapper(state, lightrec_rfe_cb, false);
if (!state->rfe_wrapper)
goto err_free_mtc_wrapper;
state->cp_wrapper = generate_wrapper(state, lightrec_cp_cb, false);
if (!state->cp_wrapper)
goto err_free_rfe_wrapper;
state->syscall_wrapper = generate_wrapper(state, lightrec_syscall_cb,
false);
if (!state->syscall_wrapper)
goto err_free_cp_wrapper;
state->break_wrapper = generate_wrapper(state, lightrec_break_cb,
false);
if (!state->break_wrapper)
goto err_free_syscall_wrapper;
state->rw_generic_func = state->rw_generic_wrapper->function;
state->rw_func = state->rw_wrapper->function;
state->mfc_func = state->mfc_wrapper->function;
state->mtc_func = state->mtc_wrapper->function;
state->rfe_func = state->rfe_wrapper->function;
state->cp_func = state->cp_wrapper->function;
state->syscall_func = state->syscall_wrapper->function;
state->break_func = state->break_wrapper->function;
state->c_wrappers[C_WRAPPER_RW] = lightrec_rw_cb;
state->c_wrappers[C_WRAPPER_RW_GENERIC] = lightrec_rw_generic_cb;
state->c_wrappers[C_WRAPPER_MFC] = lightrec_mfc_cb;
state->c_wrappers[C_WRAPPER_MTC] = lightrec_mtc_cb;
state->c_wrappers[C_WRAPPER_RFE] = lightrec_rfe_cb;
state->c_wrappers[C_WRAPPER_CP] = lightrec_cp_cb;
state->c_wrappers[C_WRAPPER_SYSCALL] = lightrec_syscall_cb;
state->c_wrappers[C_WRAPPER_BREAK] = lightrec_break_cb;
map = &state->maps[PSX_MAP_BIOS];
state->offset_bios = (uintptr_t)map->address - map->pc;
@ -1279,22 +1375,17 @@ struct lightrec_state * lightrec_init(char *argv0,
state->maps[PSX_MAP_MIRROR3].address == map->address + 0x600000)
state->mirrors_mapped = true;
if (state->offset_bios == 0 &&
state->offset_scratch == 0 &&
state->offset_ram == 0 &&
state->mirrors_mapped) {
pr_info("Memory map is perfect. Emitted code will be best.\n");
} else {
pr_info("Memory map is sub-par. Emitted code will be slow.\n");
}
return state;
err_free_syscall_wrapper:
lightrec_free_block(state->syscall_wrapper);
err_free_cp_wrapper:
lightrec_free_block(state->cp_wrapper);
err_free_rfe_wrapper:
lightrec_free_block(state->rfe_wrapper);
err_free_mtc_wrapper:
lightrec_free_block(state->mtc_wrapper);
err_free_mfc_wrapper:
lightrec_free_block(state->mfc_wrapper);
err_free_rw_wrapper:
lightrec_free_block(state->rw_wrapper);
err_free_generic_rw_wrapper:
lightrec_free_block(state->rw_generic_wrapper);
err_free_dispatcher:
lightrec_free_block(state->dispatcher);
err_free_reaper:
@ -1330,14 +1421,7 @@ void lightrec_destroy(struct lightrec_state *state)
lightrec_free_regcache(state->reg_cache);
lightrec_free_block_cache(state->block_cache);
lightrec_free_block(state->dispatcher);
lightrec_free_block(state->rw_generic_wrapper);
lightrec_free_block(state->rw_wrapper);
lightrec_free_block(state->mfc_wrapper);
lightrec_free_block(state->mtc_wrapper);
lightrec_free_block(state->rfe_wrapper);
lightrec_free_block(state->cp_wrapper);
lightrec_free_block(state->syscall_wrapper);
lightrec_free_block(state->break_wrapper);
lightrec_free_block(state->c_wrapper_block);
finish_jit();
#if ENABLE_TINYMM
@ -1363,10 +1447,7 @@ void lightrec_invalidate(struct lightrec_state *state, u32 addr, u32 len)
/* Handle mirrors */
kaddr &= (state->maps[PSX_MAP_KERNEL_USER_RAM].length - 1);
for (; len > 4; len -= 4, kaddr += 4)
lightrec_invalidate_map(state, map, kaddr);
lightrec_invalidate_map(state, map, kaddr);
lightrec_invalidate_map(state, map, kaddr, len);
}
}

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2016-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2016-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_H__
@ -69,14 +60,6 @@ enum psx_map {
PSX_MAP_MIRROR3,
};
enum mem_type {
MEM_FOR_CODE,
MEM_FOR_MIPS_CODE,
MEM_FOR_IR,
MEM_FOR_LIGHTREC,
MEM_TYPE_END,
};
struct lightrec_mem_map_ops {
void (*sb)(struct lightrec_state *, u32 opcode,
void *host, u32 addr, u8 data);
@ -139,10 +122,6 @@ __api void lightrec_reset_cycle_count(struct lightrec_state *state, u32 cycles);
__api void lightrec_set_target_cycle_count(struct lightrec_state *state,
u32 cycles);
__api unsigned int lightrec_get_mem_usage(enum mem_type type);
__api unsigned int lightrec_get_total_mem_usage(void);
__api float lightrec_get_average_ipi(void);
#ifdef __cplusplus
};
#endif

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "config.h"

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __MEMMANAGER_H__
@ -17,6 +8,14 @@
#include "lightrec.h"
enum mem_type {
MEM_FOR_CODE,
MEM_FOR_MIPS_CODE,
MEM_FOR_IR,
MEM_FOR_LIGHTREC,
MEM_TYPE_END,
};
void * lightrec_malloc(struct lightrec_state *state,
enum mem_type type, unsigned int len);
void * lightrec_calloc(struct lightrec_state *state,
@ -27,4 +26,8 @@ void lightrec_free(struct lightrec_state *state,
void lightrec_register(enum mem_type type, unsigned int len);
void lightrec_unregister(enum mem_type type, unsigned int len);
unsigned int lightrec_get_mem_usage(enum mem_type type);
unsigned int lightrec_get_total_mem_usage(void);
float lightrec_get_average_ipi(void);
#endif /* __MEMMANAGER_H__ */

View File

@ -1,17 +1,9 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "config.h"
#include "disassembler.h"
#include "lightrec.h"
#include "memmanager.h"
@ -21,60 +13,89 @@
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define IF_OPT(opt, ptr) ((opt) ? (ptr) : NULL)
struct optimizer_list {
void (**optimizers)(struct opcode *);
unsigned int nb_optimizers;
};
bool opcode_reads_register(union code op, u8 reg)
bool is_unconditional_jump(union code c)
{
switch (c.i.op) {
case OP_SPECIAL:
return c.r.op == OP_SPECIAL_JR || c.r.op == OP_SPECIAL_JALR;
case OP_J:
case OP_JAL:
return true;
case OP_BEQ:
case OP_BLEZ:
return c.i.rs == c.i.rt;
case OP_REGIMM:
return (c.r.rt == OP_REGIMM_BGEZ ||
c.r.rt == OP_REGIMM_BGEZAL) && c.i.rs == 0;
default:
return false;
}
}
bool is_syscall(union code c)
{
return (c.i.op == OP_SPECIAL && c.r.op == OP_SPECIAL_SYSCALL) ||
(c.i.op == OP_CP0 && (c.r.rs == OP_CP0_MTC0 ||
c.r.rs == OP_CP0_CTC0) &&
(c.r.rd == 12 || c.r.rd == 13));
}
static u64 opcode_read_mask(union code op)
{
switch (op.i.op) {
case OP_SPECIAL:
switch (op.r.op) {
case OP_SPECIAL_SYSCALL:
case OP_SPECIAL_BREAK:
return false;
return 0;
case OP_SPECIAL_JR:
case OP_SPECIAL_JALR:
case OP_SPECIAL_MTHI:
case OP_SPECIAL_MTLO:
return op.r.rs == reg;
return BIT(op.r.rs);
case OP_SPECIAL_MFHI:
return reg == REG_HI;
return BIT(REG_HI);
case OP_SPECIAL_MFLO:
return reg == REG_LO;
return BIT(REG_LO);
case OP_SPECIAL_SLL:
case OP_SPECIAL_SRL:
case OP_SPECIAL_SRA:
return op.r.rt == reg;
return BIT(op.r.rt);
default:
return op.r.rs == reg || op.r.rt == reg;
return BIT(op.r.rs) | BIT(op.r.rt);
}
case OP_CP0:
switch (op.r.rs) {
case OP_CP0_MTC0:
case OP_CP0_CTC0:
return op.r.rt == reg;
return BIT(op.r.rt);
default:
return false;
return 0;
}
case OP_CP2:
if (op.r.op == OP_CP2_BASIC) {
switch (op.r.rs) {
case OP_CP2_BASIC_MTC2:
case OP_CP2_BASIC_CTC2:
return op.r.rt == reg;
return BIT(op.r.rt);
default:
return false;
break;
}
} else {
return false;
}
return 0;
case OP_J:
case OP_JAL:
case OP_LUI:
return false;
return 0;
case OP_BEQ:
case OP_BNE:
case OP_LWL:
@ -84,33 +105,45 @@ bool opcode_reads_register(union code op, u8 reg)
case OP_SWL:
case OP_SW:
case OP_SWR:
return op.i.rs == reg || op.i.rt == reg;
return BIT(op.i.rs) | BIT(op.i.rt);
default:
return op.i.rs == reg;
return BIT(op.i.rs);
}
}
bool opcode_writes_register(union code op, u8 reg)
static u64 opcode_write_mask(union code op)
{
u64 flags;
switch (op.i.op) {
case OP_SPECIAL:
switch (op.r.op) {
case OP_SPECIAL_JR:
case OP_SPECIAL_JALR:
case OP_SPECIAL_SYSCALL:
case OP_SPECIAL_BREAK:
return false;
return 0;
case OP_SPECIAL_MULT:
case OP_SPECIAL_MULTU:
case OP_SPECIAL_DIV:
case OP_SPECIAL_DIVU:
return reg == REG_LO || reg == REG_HI;
if (!OPT_FLAG_MULT_DIV)
return BIT(REG_LO) | BIT(REG_HI);
if (op.r.rd)
flags = BIT(op.r.rd);
else
flags = BIT(REG_LO);
if (op.r.imm)
flags |= BIT(op.r.imm);
else
flags |= BIT(REG_HI);
return flags;
case OP_SPECIAL_MTHI:
return reg == REG_HI;
return BIT(REG_HI);
case OP_SPECIAL_MTLO:
return reg == REG_LO;
return BIT(REG_LO);
default:
return op.r.rd == reg;
return BIT(op.r.rd);
}
case OP_ADDI:
case OP_ADDIU:
@ -127,34 +160,90 @@ bool opcode_writes_register(union code op, u8 reg)
case OP_LBU:
case OP_LHU:
case OP_LWR:
return op.i.rt == reg;
return BIT(op.i.rt);
case OP_JAL:
return BIT(31);
case OP_CP0:
switch (op.r.rs) {
case OP_CP0_MFC0:
case OP_CP0_CFC0:
return op.i.rt == reg;
return BIT(op.i.rt);
default:
return false;
return 0;
}
case OP_CP2:
if (op.r.op == OP_CP2_BASIC) {
switch (op.r.rs) {
case OP_CP2_BASIC_MFC2:
case OP_CP2_BASIC_CFC2:
return op.i.rt == reg;
return BIT(op.i.rt);
default:
return false;
break;
}
} else {
return false;
}
return 0;
case OP_REGIMM:
switch (op.r.rt) {
case OP_REGIMM_BLTZAL:
case OP_REGIMM_BGEZAL:
return BIT(31);
default:
return 0;
}
case OP_META_MOV:
return op.r.rd == reg;
return BIT(op.r.rd);
default:
return 0;
}
}
bool opcode_reads_register(union code op, u8 reg)
{
return opcode_read_mask(op) & BIT(reg);
}
bool opcode_writes_register(union code op, u8 reg)
{
return opcode_write_mask(op) & BIT(reg);
}
static bool opcode_is_load(union code op)
{
switch (op.i.op) {
case OP_LB:
case OP_LH:
case OP_LWL:
case OP_LW:
case OP_LBU:
case OP_LHU:
case OP_LWR:
case OP_LWC2:
return true;
default:
return false;
}
}
static bool opcode_is_store(union code op)
{
switch (op.i.op) {
case OP_SB:
case OP_SH:
case OP_SW:
case OP_SWL:
case OP_SWR:
case OP_SWC2:
return true;
default:
return false;
}
}
bool opcode_is_io(union code op)
{
return opcode_is_load(op) || opcode_is_store(op);
}
/* TODO: Complete */
static bool is_nop(union code op)
{
@ -481,43 +570,13 @@ static u32 lightrec_propagate_consts(union code c, u32 known, u32 *v)
return known;
}
static int lightrec_add_meta(struct block *block,
struct opcode *op, union code code)
{
struct opcode *meta;
meta = lightrec_malloc(block->state, MEM_FOR_IR, sizeof(*meta));
if (!meta)
return -ENOMEM;
meta->c = code;
meta->flags = 0;
if (op) {
meta->offset = op->offset;
meta->next = op->next;
op->next = meta;
} else {
meta->offset = 0;
meta->next = block->opcode_list;
block->opcode_list = meta;
}
return 0;
}
static int lightrec_add_sync(struct block *block, struct opcode *prev)
{
return lightrec_add_meta(block, prev, (union code){
.j.op = OP_META_SYNC,
});
}
static int lightrec_transform_ops(struct block *block)
{
struct opcode *list = block->opcode_list;
struct opcode *list;
unsigned int i;
for (; list; list = list->next) {
for (i = 0; i < block->nb_ops; i++) {
list = &block->opcode_list[i];
/* Transform all opcodes detected as useless to real NOPs
* (0x0: SLL r0, r0, #0) */
@ -604,20 +663,28 @@ static int lightrec_transform_ops(struct block *block)
static int lightrec_switch_delay_slots(struct block *block)
{
struct opcode *list, *prev;
struct opcode *list, *next = &block->opcode_list[0];
unsigned int i;
union code op, next_op;
u8 flags;
for (list = block->opcode_list, prev = NULL; list->next;
prev = list, list = list->next) {
union code op = list->c;
union code next_op = list->next->c;
for (i = 0; i < block->nb_ops - 1; i++) {
list = next;
next = &block->opcode_list[i + 1];
next_op = next->c;
op = list->c;
if (!has_delay_slot(op) ||
list->flags & (LIGHTREC_NO_DS | LIGHTREC_EMULATE_BRANCH) ||
op.opcode == 0)
op.opcode == 0 || next_op.opcode == 0)
continue;
if (prev && has_delay_slot(prev->c))
if (i && has_delay_slot(block->opcode_list[i - 1].c) &&
!(block->opcode_list[i - 1].flags & LIGHTREC_NO_DS))
continue;
if ((list->flags & LIGHTREC_SYNC) ||
(next->flags & LIGHTREC_SYNC))
continue;
switch (list->i.op) {
@ -671,27 +738,59 @@ static int lightrec_switch_delay_slots(struct block *block)
}
pr_debug("Swap branch and delay slot opcodes "
"at offsets 0x%x / 0x%x\n", list->offset << 2,
list->next->offset << 2);
"at offsets 0x%x / 0x%x\n",
i << 2, (i + 1) << 2);
flags = list->next->flags;
flags = next->flags;
list->c = next_op;
list->next->c = op;
list->next->flags = list->flags | LIGHTREC_NO_DS;
next->c = op;
next->flags = list->flags | LIGHTREC_NO_DS;
list->flags = flags | LIGHTREC_NO_DS;
list->offset++;
list->next->offset--;
}
return 0;
}
static int shrink_opcode_list(struct block *block, u16 new_size)
{
struct opcode *list;
if (new_size >= block->nb_ops) {
pr_err("Invalid shrink size (%u vs %u)\n",
new_size, block->nb_ops);
return -EINVAL;
}
list = lightrec_malloc(block->state, MEM_FOR_IR,
sizeof(*list) * new_size);
if (!list) {
pr_err("Unable to allocate memory\n");
return -ENOMEM;
}
memcpy(list, block->opcode_list, sizeof(*list) * new_size);
lightrec_free_opcode_list(block);
block->opcode_list = list;
block->nb_ops = new_size;
pr_debug("Shrunk opcode list of block PC 0x%08x to %u opcodes\n",
block->pc, new_size);
return 0;
}
static int lightrec_detect_impossible_branches(struct block *block)
{
struct opcode *op, *next;
struct opcode *op, *next = &block->opcode_list[0];
unsigned int i;
int ret = 0;
for (i = 0; i < block->nb_ops - 1; i++) {
op = next;
next = &block->opcode_list[i + 1];
for (op = block->opcode_list, next = op->next; next;
op = next, next = op->next) {
if (!has_delay_slot(op->c) ||
(!load_in_delay_slot(next->c) &&
!has_delay_slot(next->c) &&
@ -705,28 +804,33 @@ static int lightrec_detect_impossible_branches(struct block *block)
continue;
}
op->flags |= LIGHTREC_EMULATE_BRANCH;
if (op == block->opcode_list) {
pr_debug("First opcode of block PC 0x%08x is an impossible branch\n",
block->pc);
/* If the first opcode is an 'impossible' branch, we
* only keep the first two opcodes of the block (the
* branch itself + its delay slot) */
lightrec_free_opcode_list(block->state, next->next);
next->next = NULL;
block->nb_ops = 2;
if (block->nb_ops > 2)
ret = shrink_opcode_list(block, 2);
break;
}
op->flags |= LIGHTREC_EMULATE_BRANCH;
}
return 0;
return ret;
}
static int lightrec_local_branches(struct block *block)
{
struct opcode *list, *target, *prev;
struct opcode *list;
unsigned int i;
s32 offset;
int ret;
for (list = block->opcode_list; list; list = list->next) {
for (i = 0; i < block->nb_ops; i++) {
list = &block->opcode_list[i];
if (should_emulate(list))
continue;
@ -738,7 +842,7 @@ static int lightrec_local_branches(struct block *block)
case OP_REGIMM:
case OP_META_BEQZ:
case OP_META_BNEZ:
offset = list->offset + 1 + (s16)list->i.imm;
offset = i + 1 + (s16)list->i.imm;
if (offset >= 0 && offset < block->nb_ops)
break;
default: /* fall-through */
@ -747,37 +851,20 @@ static int lightrec_local_branches(struct block *block)
pr_debug("Found local branch to offset 0x%x\n", offset << 2);
for (target = block->opcode_list, prev = NULL;
target; prev = target, target = target->next) {
if (target->offset != offset ||
target->j.op == OP_META_SYNC)
continue;
if (should_emulate(target)) {
pr_debug("Branch target must be emulated"
" - skip\n");
break;
}
if (prev && has_delay_slot(prev->c)) {
pr_debug("Branch target is a delay slot"
" - skip\n");
break;
}
if (prev && prev->j.op != OP_META_SYNC) {
pr_debug("Adding sync before offset "
"0x%x\n", offset << 2);
ret = lightrec_add_sync(block, prev);
if (ret)
return ret;
prev->next->offset = target->offset;
}
list->flags |= LIGHTREC_LOCAL_BRANCH;
break;
if (should_emulate(&block->opcode_list[offset])) {
pr_debug("Branch target must be emulated - skip\n");
continue;
}
if (offset && has_delay_slot(block->opcode_list[offset - 1].c)) {
pr_debug("Branch target is a delay slot - skip\n");
continue;
}
pr_debug("Adding sync at offset 0x%x\n", offset << 2);
block->opcode_list[offset].flags |= LIGHTREC_SYNC;
list->flags |= LIGHTREC_LOCAL_BRANCH;
}
return 0;
@ -809,63 +896,57 @@ bool has_delay_slot(union code op)
}
}
bool should_emulate(struct opcode *list)
bool should_emulate(const struct opcode *list)
{
return has_delay_slot(list->c) &&
(list->flags & LIGHTREC_EMULATE_BRANCH);
}
static int lightrec_add_unload(struct block *block, struct opcode *op, u8 reg)
static void lightrec_add_unload(struct opcode *op, u8 reg)
{
return lightrec_add_meta(block, op, (union code){
.i.op = OP_META_REG_UNLOAD,
.i.rs = reg,
});
if (op->i.op == OP_SPECIAL && reg == op->r.rd)
op->flags |= LIGHTREC_UNLOAD_RD;
if (op->i.rs == reg)
op->flags |= LIGHTREC_UNLOAD_RS;
if (op->i.rt == reg)
op->flags |= LIGHTREC_UNLOAD_RT;
}
static int lightrec_early_unload(struct block *block)
{
struct opcode *list = block->opcode_list;
u8 i;
unsigned int i, offset;
struct opcode *op;
u8 reg;
for (i = 1; i < 34; i++) {
struct opcode *op, *last_r = NULL, *last_w = NULL;
unsigned int last_r_id = 0, last_w_id = 0, id = 0;
int ret;
for (reg = 1; reg < 34; reg++) {
int last_r_id = -1, last_w_id = -1;
for (op = list; op->next; op = op->next, id++) {
if (opcode_reads_register(op->c, i)) {
last_r = op;
last_r_id = id;
}
for (i = 0; i < block->nb_ops; i++) {
union code c = block->opcode_list[i].c;
if (opcode_writes_register(op->c, i)) {
last_w = op;
last_w_id = id;
}
if (opcode_reads_register(c, reg))
last_r_id = i;
if (opcode_writes_register(c, reg))
last_w_id = i;
}
if (last_w_id > last_r_id) {
if (has_delay_slot(last_w->c) &&
!(last_w->flags & LIGHTREC_NO_DS))
last_w = last_w->next;
if (last_w_id > last_r_id)
offset = (unsigned int)last_w_id;
else if (last_r_id >= 0)
offset = (unsigned int)last_r_id;
else
continue;
if (last_w->next) {
ret = lightrec_add_unload(block, last_w, i);
if (ret)
return ret;
}
} else if (last_r) {
if (has_delay_slot(last_r->c) &&
!(last_r->flags & LIGHTREC_NO_DS))
last_r = last_r->next;
op = &block->opcode_list[offset];
if (last_r->next) {
ret = lightrec_add_unload(block, last_r, i);
if (ret)
return ret;
}
}
if (has_delay_slot(op->c) && (op->flags & LIGHTREC_NO_DS))
offset++;
if (offset == block->nb_ops)
continue;
lightrec_add_unload(&block->opcode_list[offset], reg);
}
return 0;
@ -876,8 +957,11 @@ static int lightrec_flag_stores(struct block *block)
struct opcode *list;
u32 known = BIT(0);
u32 values[32] = { 0 };
unsigned int i;
for (i = 0; i < block->nb_ops; i++) {
list = &block->opcode_list[i];
for (list = block->opcode_list; list; list = list->next) {
/* Register $zero is always, well, zero */
known |= BIT(0);
values[0] = 0;
@ -918,13 +1002,29 @@ static int lightrec_flag_stores(struct block *block)
return 0;
}
static u8 get_mfhi_mflo_reg(const struct opcode *op, bool mflo)
static u8 get_mfhi_mflo_reg(const struct block *block, u16 offset,
const struct opcode *last,
u32 mask, bool sync, bool mflo)
{
const struct opcode *next;
u32 offset;
const struct opcode *op, *next = &block->opcode_list[offset];
u32 old_mask;
u8 reg2, reg = mflo ? REG_LO : REG_HI;
u16 branch_offset;
unsigned int i;
for (i = offset; i < block->nb_ops; i++) {
op = next;
next = &block->opcode_list[i + 1];
old_mask = mask;
/* If any other opcode writes or reads to the register
* we'd use, then we cannot use it anymore. */
mask |= opcode_read_mask(op->c);
mask |= opcode_write_mask(op->c);
if (op->flags & LIGHTREC_SYNC)
sync = true;
for (; op; op = op->next) {
switch (op->i.op) {
case OP_BEQ:
case OP_BNE:
@ -934,15 +1034,16 @@ static u8 get_mfhi_mflo_reg(const struct opcode *op, bool mflo)
case OP_META_BEQZ:
case OP_META_BNEZ:
/* TODO: handle backwards branches too */
if ((op->flags & LIGHTREC_LOCAL_BRANCH) &&
if (!last &&
(op->flags & LIGHTREC_LOCAL_BRANCH) &&
(s16)op->c.i.imm >= 0) {
offset = op->offset + 1 + (s16)op->c.i.imm;
branch_offset = i + 1 + (s16)op->c.i.imm
- !!(OPT_SWITCH_DELAY_SLOTS && (op->flags & LIGHTREC_NO_DS));
for (next = op; next->offset != offset;
next = next->next);
reg = get_mfhi_mflo_reg(next, mflo);
reg2 = get_mfhi_mflo_reg(op->next, mflo);
reg = get_mfhi_mflo_reg(block, branch_offset, NULL,
mask, sync, mflo);
reg2 = get_mfhi_mflo_reg(block, offset + 1, next,
mask, sync, mflo);
if (reg > 0 && reg == reg2)
return reg;
if (!reg && !reg2)
@ -969,26 +1070,37 @@ static u8 get_mfhi_mflo_reg(const struct opcode *op, bool mflo)
if (op->r.rs != 31)
return reg;
if (!(op->flags & LIGHTREC_NO_DS) &&
(op->next->i.op == OP_SPECIAL) &&
(!mflo && op->next->r.op == OP_SPECIAL_MFHI) ||
(mflo && op->next->r.op == OP_SPECIAL_MFLO))
return op->next->r.rd;
if (!sync &&
!(op->flags & LIGHTREC_NO_DS) &&
(next->i.op == OP_SPECIAL) &&
((!mflo && next->r.op == OP_SPECIAL_MFHI) ||
(mflo && next->r.op == OP_SPECIAL_MFLO)))
return next->r.rd;
return 0;
case OP_SPECIAL_JALR:
return reg;
case OP_SPECIAL_MFHI:
if (!mflo)
return op->r.rd;
if (!mflo) {
if (!sync && !(old_mask & BIT(op->r.rd)))
return op->r.rd;
else
return REG_HI;
}
continue;
case OP_SPECIAL_MFLO:
if (mflo)
return op->r.rd;
if (mflo) {
if (!sync && !(old_mask & BIT(op->r.rd)))
return op->r.rd;
else
return REG_LO;
}
continue;
default:
continue;
break;
}
/* fall-through */
default:
continue;
}
@ -997,13 +1109,66 @@ static u8 get_mfhi_mflo_reg(const struct opcode *op, bool mflo)
return reg;
}
static void lightrec_replace_lo_hi(struct block *block, u16 offset,
u16 last, bool lo)
{
unsigned int i;
u32 branch_offset;
/* This function will remove the following MFLO/MFHI. It must be called
* only if get_mfhi_mflo_reg() returned a non-zero value. */
for (i = offset; i < last; i++) {
struct opcode *op = &block->opcode_list[i];
switch (op->i.op) {
case OP_BEQ:
case OP_BNE:
case OP_BLEZ:
case OP_BGTZ:
case OP_REGIMM:
case OP_META_BEQZ:
case OP_META_BNEZ:
/* TODO: handle backwards branches too */
if ((op->flags & LIGHTREC_LOCAL_BRANCH) &&
(s16)op->c.i.imm >= 0) {
branch_offset = i + 1 + (s16)op->c.i.imm
- !!(OPT_SWITCH_DELAY_SLOTS && (op->flags & LIGHTREC_NO_DS));
lightrec_replace_lo_hi(block, branch_offset, last, lo);
lightrec_replace_lo_hi(block, i + 1, branch_offset, lo);
}
break;
case OP_SPECIAL:
if (lo && op->r.op == OP_SPECIAL_MFLO) {
pr_debug("Removing MFLO opcode at offset 0x%x\n",
i << 2);
op->opcode = 0;
return;
} else if (!lo && op->r.op == OP_SPECIAL_MFHI) {
pr_debug("Removing MFHI opcode at offset 0x%x\n",
i << 2);
op->opcode = 0;
return;
}
/* fall-through */
default:
break;
}
}
}
static int lightrec_flag_mults_divs(struct block *block)
{
struct opcode *list, *prev;
u8 reg_hi;
struct opcode *list;
u8 reg_hi, reg_lo;
unsigned int i;
for (i = 0; i < block->nb_ops - 1; i++) {
list = &block->opcode_list[i];
for (list = block->opcode_list, prev = NULL; list;
prev = list, list = list->next) {
if (list->i.op != OP_SPECIAL)
continue;
@ -1018,21 +1183,56 @@ static int lightrec_flag_mults_divs(struct block *block)
}
/* Don't support opcodes in delay slots */
if (prev && has_delay_slot(prev->c))
if ((i && has_delay_slot(block->opcode_list[i - 1].c)) ||
(list->flags & LIGHTREC_NO_DS))
continue;
reg_hi = get_mfhi_mflo_reg(list->next, false);
reg_lo = get_mfhi_mflo_reg(block, i + 1, NULL, 0, false, true);
if (reg_lo == 0) {
pr_debug("Mark MULT(U)/DIV(U) opcode at offset 0x%x as"
" not writing LO\n", i << 2);
list->flags |= LIGHTREC_NO_LO;
}
reg_hi = get_mfhi_mflo_reg(block, i + 1, NULL, 0, false, false);
if (reg_hi == 0) {
pr_debug("Mark MULT(U)/DIV(U) opcode at offset 0x%x as"
" 32-bit\n", list->offset << 2);
" not writing HI\n", i << 2);
list->flags |= LIGHTREC_NO_HI;
}
if (!reg_lo && !reg_hi) {
pr_debug("Both LO/HI unused in this block, they will "
"probably be used in parent block - removing "
"flags.\n");
list->flags &= ~(LIGHTREC_NO_LO | LIGHTREC_NO_HI);
}
if (reg_lo > 0 && reg_lo != REG_LO) {
pr_debug("Found register %s to hold LO (rs = %u, rt = %u)\n",
lightrec_reg_name(reg_lo), list->r.rs, list->r.rt);
lightrec_replace_lo_hi(block, i + 1, block->nb_ops, true);
list->r.rd = reg_lo;
} else {
list->r.rd = 0;
}
if (reg_hi > 0 && reg_hi != REG_HI) {
pr_debug("Found register %s to hold HI (rs = %u, rt = %u)\n",
lightrec_reg_name(reg_hi), list->r.rs, list->r.rt);
lightrec_replace_lo_hi(block, i + 1, block->nb_ops, false);
list->r.imm = reg_hi;
} else {
list->r.imm = 0;
}
}
return 0;
}
static bool remove_div_sequence(struct opcode *list)
static bool remove_div_sequence(struct block *block, unsigned int offset)
{
struct opcode *op;
unsigned int i, found = 0;
@ -1051,7 +1251,9 @@ static bool remove_div_sequence(struct opcode *list)
* and these sequences can be removed.
*/
for (op = list; op; op = op->next) {
for (i = offset; i < block->nb_ops; i++) {
op = &block->opcode_list[i];
if (!found) {
if (op->i.op == OP_SPECIAL &&
(op->r.op == OP_SPECIAL_DIV || op->r.op == OP_SPECIAL_DIVU))
@ -1061,7 +1263,7 @@ static bool remove_div_sequence(struct opcode *list)
/* BNE ???, zero, +8 */
found++;
} else {
list = list->next;
offset++;
}
} else if (found == 1 && !op->opcode) {
/* NOP */
@ -1098,13 +1300,10 @@ static bool remove_div_sequence(struct opcode *list)
found = 3;
pr_debug("Removing DIV%s sequence at offset 0x%x\n",
found == 9 ? "" : "U",
list->offset << 2);
found == 9 ? "" : "U", offset << 2);
for (i = 0; list && i < found; i++) {
list->opcode = 0;
list = list->next;
}
for (i = 0; i < found; i++)
block->opcode_list[offset + i].opcode = 0;
return true;
}
@ -1115,37 +1314,79 @@ static bool remove_div_sequence(struct opcode *list)
static int lightrec_remove_div_by_zero_check_sequence(struct block *block)
{
struct opcode *op;
unsigned int i;
for (i = 0; i < block->nb_ops; i++) {
op = &block->opcode_list[i];
for (op = block->opcode_list; op; op = op->next) {
if (op->i.op == OP_SPECIAL &&
(op->r.op == OP_SPECIAL_DIVU || op->r.op == OP_SPECIAL_DIV) &&
remove_div_sequence(op->next))
remove_div_sequence(block, i + 1))
op->flags |= LIGHTREC_NO_DIV_CHECK;
}
return 0;
}
static const u32 memset_code[] = {
0x10a00006, // beqz a1, 2f
0x24a2ffff, // addiu v0,a1,-1
0x2403ffff, // li v1,-1
0xac800000, // 1: sw zero,0(a0)
0x2442ffff, // addiu v0,v0,-1
0x1443fffd, // bne v0,v1, 1b
0x24840004, // addiu a0,a0,4
0x03e00008, // 2: jr ra
0x00000000, // nop
};
static int lightrec_replace_memset(struct block *block)
{
unsigned int i;
union code c;
for (i = 0; i < block->nb_ops; i++) {
c = block->opcode_list[i].c;
if (c.opcode != memset_code[i])
return 0;
if (i == ARRAY_SIZE(memset_code) - 1) {
/* success! */
pr_debug("Block at PC 0x%x is a memset\n", block->pc);
block->flags |= BLOCK_IS_MEMSET | BLOCK_NEVER_COMPILE;
/* Return non-zero to skip other optimizers. */
return 1;
}
}
return 0;
}
static int (*lightrec_optimizers[])(struct block *) = {
&lightrec_remove_div_by_zero_check_sequence,
&lightrec_detect_impossible_branches,
&lightrec_transform_ops,
&lightrec_local_branches,
&lightrec_switch_delay_slots,
&lightrec_flag_stores,
&lightrec_flag_mults_divs,
&lightrec_early_unload,
IF_OPT(OPT_REMOVE_DIV_BY_ZERO_SEQ, &lightrec_remove_div_by_zero_check_sequence),
IF_OPT(OPT_REPLACE_MEMSET, &lightrec_replace_memset),
IF_OPT(OPT_DETECT_IMPOSSIBLE_BRANCHES, &lightrec_detect_impossible_branches),
IF_OPT(OPT_TRANSFORM_OPS, &lightrec_transform_ops),
IF_OPT(OPT_LOCAL_BRANCHES, &lightrec_local_branches),
IF_OPT(OPT_SWITCH_DELAY_SLOTS, &lightrec_switch_delay_slots),
IF_OPT(OPT_FLAG_STORES, &lightrec_flag_stores),
IF_OPT(OPT_FLAG_MULT_DIV, &lightrec_flag_mults_divs),
IF_OPT(OPT_EARLY_UNLOAD, &lightrec_early_unload),
};
int lightrec_optimize(struct block *block)
{
unsigned int i;
int ret;
for (i = 0; i < ARRAY_SIZE(lightrec_optimizers); i++) {
int ret = lightrec_optimizers[i](block);
if (ret)
return ret;
if (lightrec_optimizers[i]) {
ret = (*lightrec_optimizers[i])(block);
if (ret)
return ret;
}
}
return 0;

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __OPTIMIZER_H__
@ -24,8 +15,11 @@ _Bool opcode_reads_register(union code op, u8 reg);
_Bool opcode_writes_register(union code op, u8 reg);
_Bool has_delay_slot(union code op);
_Bool load_in_delay_slot(union code op);
_Bool opcode_is_io(union code op);
_Bool is_unconditional_jump(union code c);
_Bool is_syscall(union code c);
_Bool should_emulate(struct opcode *op);
_Bool should_emulate(const struct opcode *op);
int lightrec_optimize(struct block *block);

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2020-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "blockcache.h"

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2020-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_REAPER_H__

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "debug.h"
@ -127,6 +118,8 @@ struct recompiler *lightrec_recompiler_init(struct lightrec_state *state)
goto err_mtx_destroy;
}
pr_info("Threaded recompiler started\n");
return rec;
err_mtx_destroy:
@ -263,8 +256,7 @@ void * lightrec_recompiler_run_first_pass(struct block *block, u32 *pc)
/* The block was already compiled but the opcode list
* didn't get freed yet - do it now */
lightrec_free_opcode_list(block->state,
block->opcode_list);
lightrec_free_opcode_list(block);
block->opcode_list = NULL;
}
}
@ -289,7 +281,7 @@ void * lightrec_recompiler_run_first_pass(struct block *block, u32 *pc)
pr_debug("Block PC 0x%08x is fully tagged"
" - free opcode list\n", block->pc);
lightrec_free_opcode_list(block->state, block->opcode_list);
lightrec_free_opcode_list(block);
block->opcode_list = NULL;
}

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_RECOMPILER_H__

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#include "debug.h"
@ -21,7 +12,8 @@
#include <stddef.h>
struct native_register {
bool used, loaded, dirty, output, extend, extended, locked;
bool used, loaded, dirty, output, extend, extended,
zero_extend, zero_extended, locked;
s8 emulated_register;
};
@ -79,6 +71,27 @@ static inline struct native_register * lightning_reg_to_lightrec(
}
}
u8 lightrec_get_reg_in_flags(struct regcache *cache, u8 jit_reg)
{
struct native_register *reg = lightning_reg_to_lightrec(cache, jit_reg);
u8 flags = 0;
if (reg->extended)
flags |= REG_EXT;
if (reg->zero_extended)
flags |= REG_ZEXT;
return flags;
}
void lightrec_set_reg_out_flags(struct regcache *cache, u8 jit_reg, u8 flags)
{
struct native_register *reg = lightning_reg_to_lightrec(cache, jit_reg);
reg->extend = flags & REG_EXT;
reg->zero_extend = flags & REG_ZEXT;
}
static struct native_register * alloc_temp(struct regcache *cache)
{
unsigned int i;
@ -157,6 +170,7 @@ static struct native_register * alloc_in_out(struct regcache *cache,
static void lightrec_discard_nreg(struct native_register *nreg)
{
nreg->extended = false;
nreg->zero_extended = false;
nreg->loaded = false;
nreg->output = false;
nreg->dirty = false;
@ -223,7 +237,8 @@ u8 lightrec_alloc_reg_temp(struct regcache *cache, jit_state_t *_jit)
return jit_reg;
}
u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit, u8 reg)
u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
u8 reg, u8 flags)
{
u8 jit_reg;
struct native_register *nreg = alloc_in_out(cache, reg, true);
@ -240,14 +255,16 @@ u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit, u8 reg)
if (nreg->emulated_register != reg)
lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
nreg->extend = false;
nreg->used = true;
nreg->output = true;
nreg->emulated_register = reg;
nreg->extend = flags & REG_EXT;
nreg->zero_extend = flags & REG_ZEXT;
return jit_reg;
}
u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit, u8 reg)
u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
u8 reg, u8 flags)
{
u8 jit_reg;
bool reg_changed;
@ -270,52 +287,49 @@ u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit, u8 reg)
s16 offset = offsetof(struct lightrec_state, native_reg_cache)
+ (reg << 2);
nreg->zero_extended = flags & REG_ZEXT;
nreg->extended = !nreg->zero_extended;
/* Load previous value from register cache */
#if __WORDSIZE == 64
if (nreg->zero_extended)
jit_ldxi_ui(jit_reg, LIGHTREC_REG_STATE, offset);
else
jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
#else
jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
#endif
nreg->loaded = true;
nreg->extended = true;
}
/* Clear register r0 before use */
if (reg == 0 && (!nreg->loaded || nreg->dirty)) {
jit_movi(jit_reg, 0);
nreg->extended = true;
nreg->zero_extended = true;
nreg->loaded = true;
}
nreg->used = true;
nreg->output = false;
nreg->emulated_register = reg;
return jit_reg;
}
u8 lightrec_alloc_reg_out_ext(struct regcache *cache, jit_state_t *_jit, u8 reg)
{
struct native_register *nreg;
u8 jit_reg;
jit_reg = lightrec_alloc_reg_out(cache, _jit, reg);
nreg = lightning_reg_to_lightrec(cache, jit_reg);
nreg->extend = true;
return jit_reg;
}
u8 lightrec_alloc_reg_in_ext(struct regcache *cache, jit_state_t *_jit, u8 reg)
{
struct native_register *nreg;
u8 jit_reg;
jit_reg = lightrec_alloc_reg_in(cache, _jit, reg);
nreg = lightning_reg_to_lightrec(cache, jit_reg);
#if __WORDSIZE == 64
if (!nreg->extended) {
if ((flags & REG_EXT) && !nreg->extended &&
(!nreg->zero_extended || !(flags & REG_ZEXT))) {
nreg->extended = true;
nreg->zero_extended = false;
#if __WORDSIZE == 64
jit_extr_i(jit_reg, jit_reg);
}
#endif
} else if (!(flags & REG_EXT) && (flags & REG_ZEXT) &&
!nreg->zero_extended) {
nreg->zero_extended = true;
nreg->extended = false;
#if __WORDSIZE == 64
jit_extr_ui(jit_reg, jit_reg);
#endif
}
return jit_reg;
}
@ -341,6 +355,7 @@ u8 lightrec_request_reg_in(struct regcache *cache, jit_state_t *_jit,
jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
nreg->extended = true;
nreg->zero_extended = false;
nreg->used = true;
nreg->loaded = true;
nreg->emulated_register = reg;
@ -353,8 +368,10 @@ static void free_reg(struct native_register *nreg)
/* Set output registers as dirty */
if (nreg->used && nreg->output && nreg->emulated_register > 0)
nreg->dirty = true;
if (nreg->output)
if (nreg->output) {
nreg->extended = nreg->extend;
nreg->zero_extended = nreg->zero_extend;
}
nreg->used = false;
}

View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __REGCACHE_H__
@ -22,8 +13,9 @@
#define LIGHTREC_REG_STATE (JIT_V(JIT_V_NUM - 1))
#define LIGHTREC_REG_CYCLE (JIT_V(JIT_V_NUM - 2))
#define REG_LO 32
#define REG_HI 33
/* Flags for lightrec_alloc_reg_in / lightrec_alloc_reg_out. */
#define REG_EXT BIT(0) /* register is sign-extended */
#define REG_ZEXT BIT(1) /* register is zero-extended */
struct register_value {
_Bool known;
@ -35,15 +27,17 @@ struct regcache;
u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg);
u8 lightrec_alloc_reg_temp(struct regcache *cache, jit_state_t *_jit);
u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit, u8 reg);
u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit, u8 reg);
u8 lightrec_alloc_reg_out_ext(struct regcache *cache,
jit_state_t *_jit, u8 reg);
u8 lightrec_alloc_reg_in_ext(struct regcache *cache, jit_state_t *_jit, u8 reg);
u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
u8 reg, u8 flags);
u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
u8 reg, u8 flags);
u8 lightrec_request_reg_in(struct regcache *cache, jit_state_t *_jit,
u8 reg, u8 jit_reg);
u8 lightrec_get_reg_in_flags(struct regcache *cache, u8 jit_reg);
void lightrec_set_reg_out_flags(struct regcache *cache, u8 jit_reg, u8 flags);
void lightrec_regcache_reset(struct regcache *cache);
void lightrec_lock_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg);

13
deps/lightrec/slist.h vendored
View File

@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Copyright (C) 2020-2021 Paul Cercueil <paul@crapouillou.net>
*/
#ifndef __LIGHTREC_SLIST_H__