mirror of https://github.com/xemu-project/xemu.git
Performance improvement Add pkt and insn to DisasContext Many functions need information from all 3 structures, so merge them together. 2) Bug fix Fix predicated assignment to .tmp and .cur 3) Performance improvement Add overrides for S2_asr_r_r_sat/S2_asl_r_r_sat These functions will not be handled by idef-parser 4-11) The final 8 patches improve change-of-flow handling. Currently, we set the PC to a new address before exiting a TB. The ultimate goal is to use direct block chaining. However, several steps are needed along the way. 4) When a packet has more than one change-of-flow (COF) instruction, only the first one taken is considered. The runtime bookkeeping is only needed when there is more than one COF instruction in a packet. 5, 6) Remove PC and next_PC from the runtime state and always use a translation-time constant. Note that next_PC is used by call instructions to set LR and by conditional COF instructions to set the fall-through address. 7, 8, 9) Add helper overrides for COF instructions. In particular, we must distinguish those that use a PC-relative address for the destination. These are candidates for direct block chaining later. 10) Use direct block chaining for packets that have a single PC-relative COF instruction. Instead of generating the code while processing the instruction, we record the effect in DisasContext and generate the code during gen_end_tb. 11) Use direct block chaining for tight loops. We look for TBs that end with an endloop0 that will branch back to the TB start address. 12-21) Instruction definition parser (idef-parser) from rev.ng Parses the instruction semantics and generates TCG -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEENjXHiM5iuR/UxZq0ewJE+xLeRCIFAmOc2BEACgkQewJE+xLe RCKqFwf/U/uWaQiF59OXyLHj9PR/bTf7PmZL12g8MTrntzmtIpRiTQb7ajJaLwyn TcCG9j9Ss6kWBq+LH5TBvstnSN9/3qEgnj2b26y6EAn85mSh6fai4foUPjXFUy7m 2Of0kuc2WKmwxN9C2iw6Hm6pbL3FSnYzKtBuSFzYyAIS0doLFT97zE97XnBtTQ4C 49JdNgQW9CKt7cCpKTcQA4N3ZO8LdARdvOtTShX1++qd4Trm0haTGRdaygSrTlS7 Eeqs4nbakKEE6VH2iltPGKX+KHbMCf2ZW7lefxHi+EuzE0DBIVoM64UnalyFfcSU hVMGF15HgAIAjecim0Y4AbPB/zVlEw== =PC9+ -----END PGP SIGNATURE----- Merge tag 'pull-hex-20221216-1' of https://github.com/quic/qemu into staging 1) Performance improvement Add pkt and insn to DisasContext Many functions need information from all 3 structures, so merge them together. 2) Bug fix Fix predicated assignment to .tmp and .cur 3) Performance improvement Add overrides for S2_asr_r_r_sat/S2_asl_r_r_sat These functions will not be handled by idef-parser 4-11) The final 8 patches improve change-of-flow handling. Currently, we set the PC to a new address before exiting a TB. The ultimate goal is to use direct block chaining. However, several steps are needed along the way. 4) When a packet has more than one change-of-flow (COF) instruction, only the first one taken is considered. The runtime bookkeeping is only needed when there is more than one COF instruction in a packet. 5, 6) Remove PC and next_PC from the runtime state and always use a translation-time constant. Note that next_PC is used by call instructions to set LR and by conditional COF instructions to set the fall-through address. 7, 8, 9) Add helper overrides for COF instructions. In particular, we must distinguish those that use a PC-relative address for the destination. These are candidates for direct block chaining later. 10) Use direct block chaining for packets that have a single PC-relative COF instruction. Instead of generating the code while processing the instruction, we record the effect in DisasContext and generate the code during gen_end_tb. 11) Use direct block chaining for tight loops. We look for TBs that end with an endloop0 that will branch back to the TB start address. 12-21) Instruction definition parser (idef-parser) from rev.ng Parses the instruction semantics and generates TCG # gpg: Signature made Fri 16 Dec 2022 20:41:53 GMT # gpg: using RSA key 3635C788CE62B91FD4C59AB47B0244FB12DE4422 # gpg: Good signature from "Taylor Simpson (Rock on) <tsimpson@quicinc.com>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 3635 C788 CE62 B91F D4C5 9AB4 7B02 44FB 12DE 4422 * tag 'pull-hex-20221216-1' of https://github.com/quic/qemu: (21 commits) target/hexagon: import additional tests target/hexagon: call idef-parser functions target/hexagon: import parser for idef-parser target/hexagon: import lexer for idef-parser target/hexagon: prepare input for the idef-parser target/hexagon: introduce new helper functions target/hexagon: make helper functions non-static target/hexagon: make slot number an unsigned target/hexagon: import README for idef-parser target/hexagon: update MAINTAINERS for idef-parser Hexagon (target/hexagon) Use direct block chaining for tight loops Hexagon (target/hexagon) Use direct block chaining for direct jump/branch Hexagon (target/hexagon) Add overrides for various forms of jump Hexagon (target/hexagon) Add overrides for compound compare and jump Hexagon (target/hexagon) Add overrides for direct call instructions Hexagon (target/hexagon) Remove next_PC from runtime state Hexagon (target/hexagon) Remove PC from the runtime state Hexagon (target/hexagon) Only use branch_taken when packet has multi cof Hexagon (target/hexagon) Add overrides for S2_asr_r_r_sat/S2_asl_r_r_sat Hexagon (target/hexagon) Fix predicated assignment to .tmp and .cur ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
4f9a4cd37e
|
@ -197,6 +197,8 @@ Hexagon TCG CPUs
|
||||||
M: Taylor Simpson <tsimpson@quicinc.com>
|
M: Taylor Simpson <tsimpson@quicinc.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: target/hexagon/
|
F: target/hexagon/
|
||||||
|
X: target/hexagon/idef-parser/
|
||||||
|
X: target/hexagon/gen_idef_parser_funcs.py
|
||||||
F: linux-user/hexagon/
|
F: linux-user/hexagon/
|
||||||
F: tests/tcg/hexagon/
|
F: tests/tcg/hexagon/
|
||||||
F: disas/hexagon.c
|
F: disas/hexagon.c
|
||||||
|
@ -204,6 +206,13 @@ F: configs/targets/hexagon-linux-user/default.mak
|
||||||
F: docker/dockerfiles/debian-hexagon-cross.docker
|
F: docker/dockerfiles/debian-hexagon-cross.docker
|
||||||
F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh
|
F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh
|
||||||
|
|
||||||
|
Hexagon idef-parser
|
||||||
|
M: Alessandro Di Federico <ale@rev.ng>
|
||||||
|
M: Anton Johansson <anjo@rev.ng>
|
||||||
|
S: Supported
|
||||||
|
F: target/hexagon/idef-parser/
|
||||||
|
F: target/hexagon/gen_idef_parser_funcs.py
|
||||||
|
|
||||||
HPPA (PA-RISC) TCG CPUs
|
HPPA (PA-RISC) TCG CPUs
|
||||||
M: Richard Henderson <richard.henderson@linaro.org>
|
M: Richard Henderson <richard.henderson@linaro.org>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
|
@ -321,3 +321,6 @@ option('profiler', type: 'boolean', value: false,
|
||||||
description: 'profiler support')
|
description: 'profiler support')
|
||||||
option('slirp_smbd', type : 'feature', value : 'auto',
|
option('slirp_smbd', type : 'feature', value : 'auto',
|
||||||
description: 'use smbd (at path --smbd=*) in slirp networking')
|
description: 'use smbd (at path --smbd=*) in slirp networking')
|
||||||
|
|
||||||
|
option('hexagon_idef_parser', type : 'boolean', value : true,
|
||||||
|
description: 'use idef-parser to automatically generate TCG code for the Hexagon frontend')
|
||||||
|
|
|
@ -27,6 +27,10 @@ Hexagon-specific code are
|
||||||
encode*.def Encoding patterns for each instruction
|
encode*.def Encoding patterns for each instruction
|
||||||
iclass.def Instruction class definitions used to determine
|
iclass.def Instruction class definitions used to determine
|
||||||
legal VLIW slots for each instruction
|
legal VLIW slots for each instruction
|
||||||
|
qemu/target/hexagon/idef-parser
|
||||||
|
Parser that, given the high-level definitions of an instruction,
|
||||||
|
produces a C function generating equivalent tiny code instructions.
|
||||||
|
See README.rst.
|
||||||
qemu/linux-user/hexagon
|
qemu/linux-user/hexagon
|
||||||
Helpers for loading the ELF file and making Linux system calls,
|
Helpers for loading the ELF file and making Linux system calls,
|
||||||
signals, etc
|
signals, etc
|
||||||
|
@ -47,6 +51,7 @@ header files in <BUILD_DIR>/target/hexagon
|
||||||
gen_tcg_funcs.py -> tcg_funcs_generated.c.inc
|
gen_tcg_funcs.py -> tcg_funcs_generated.c.inc
|
||||||
gen_tcg_func_table.py -> tcg_func_table_generated.c.inc
|
gen_tcg_func_table.py -> tcg_func_table_generated.c.inc
|
||||||
gen_helper_funcs.py -> helper_funcs_generated.c.inc
|
gen_helper_funcs.py -> helper_funcs_generated.c.inc
|
||||||
|
gen_idef_parser_funcs.py -> idef_parser_input.h
|
||||||
|
|
||||||
Qemu helper functions have 3 parts
|
Qemu helper functions have 3 parts
|
||||||
DEF_HELPER declaration indicates the signature of the helper
|
DEF_HELPER declaration indicates the signature of the helper
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "mmvec/mmvec.h"
|
#include "mmvec/mmvec.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
#include "hw/core/cpu.h"
|
#include "hw/core/cpu.h"
|
||||||
|
#include "hw/registerfields.h"
|
||||||
|
|
||||||
#define NUM_PREGS 4
|
#define NUM_PREGS 4
|
||||||
#define TOTAL_PER_THREAD_REGS 64
|
#define TOTAL_PER_THREAD_REGS 64
|
||||||
|
@ -78,7 +79,6 @@ typedef struct CPUArchState {
|
||||||
target_ulong gpr[TOTAL_PER_THREAD_REGS];
|
target_ulong gpr[TOTAL_PER_THREAD_REGS];
|
||||||
target_ulong pred[NUM_PREGS];
|
target_ulong pred[NUM_PREGS];
|
||||||
target_ulong branch_taken;
|
target_ulong branch_taken;
|
||||||
target_ulong next_PC;
|
|
||||||
|
|
||||||
/* For comparing with LLDB on target - see adjust_stack_ptrs function */
|
/* For comparing with LLDB on target - see adjust_stack_ptrs function */
|
||||||
target_ulong last_pc_dumped;
|
target_ulong last_pc_dumped;
|
||||||
|
@ -153,16 +153,18 @@ struct ArchCPU {
|
||||||
|
|
||||||
#include "cpu_bits.h"
|
#include "cpu_bits.h"
|
||||||
|
|
||||||
|
FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1)
|
||||||
|
|
||||||
static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, target_ulong *pc,
|
static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, target_ulong *pc,
|
||||||
target_ulong *cs_base, uint32_t *flags)
|
target_ulong *cs_base, uint32_t *flags)
|
||||||
{
|
{
|
||||||
|
uint32_t hex_flags = 0;
|
||||||
*pc = env->gpr[HEX_REG_PC];
|
*pc = env->gpr[HEX_REG_PC];
|
||||||
*cs_base = 0;
|
*cs_base = 0;
|
||||||
#ifdef CONFIG_USER_ONLY
|
if (*pc == env->gpr[HEX_REG_SA0]) {
|
||||||
*flags = 0;
|
hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1);
|
||||||
#else
|
}
|
||||||
#error System mode not supported on Hexagon yet
|
*flags = hex_flags;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cpu_mmu_index(CPUHexagonState *env, bool ifetch)
|
static inline int cpu_mmu_index(CPUHexagonState *env, bool ifetch)
|
||||||
|
|
|
@ -388,6 +388,7 @@ static void decode_set_insn_attr_fields(Packet *pkt)
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
|
|
||||||
pkt->pkt_has_cof = false;
|
pkt->pkt_has_cof = false;
|
||||||
|
pkt->pkt_has_multi_cof = false;
|
||||||
pkt->pkt_has_endloop = false;
|
pkt->pkt_has_endloop = false;
|
||||||
pkt->pkt_has_dczeroa = false;
|
pkt->pkt_has_dczeroa = false;
|
||||||
|
|
||||||
|
@ -412,13 +413,23 @@ static void decode_set_insn_attr_fields(Packet *pkt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->pkt_has_cof |= decode_opcode_can_jump(opcode);
|
if (decode_opcode_can_jump(opcode)) {
|
||||||
|
if (pkt->pkt_has_cof) {
|
||||||
|
pkt->pkt_has_multi_cof = true;
|
||||||
|
}
|
||||||
|
pkt->pkt_has_cof = true;
|
||||||
|
}
|
||||||
|
|
||||||
pkt->insn[i].is_endloop = decode_opcode_ends_loop(opcode);
|
pkt->insn[i].is_endloop = decode_opcode_ends_loop(opcode);
|
||||||
|
|
||||||
pkt->pkt_has_endloop |= pkt->insn[i].is_endloop;
|
pkt->pkt_has_endloop |= pkt->insn[i].is_endloop;
|
||||||
|
|
||||||
pkt->pkt_has_cof |= pkt->pkt_has_endloop;
|
if (pkt->pkt_has_endloop) {
|
||||||
|
if (pkt->pkt_has_cof) {
|
||||||
|
pkt->pkt_has_multi_cof = true;
|
||||||
|
}
|
||||||
|
pkt->pkt_has_cof = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
##
|
##
|
||||||
## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## This program is free software; you can redistribute it and/or modify
|
||||||
## it under the terms of the GNU General Public License as published by
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
@ -238,6 +238,17 @@ def gen_helper_function(f, tag, tagregs, tagimms):
|
||||||
gen_helper_arg_imm(f,immlett)
|
gen_helper_arg_imm(f,immlett)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
if (hex_common.need_pkt_has_multi_cof(tag)):
|
||||||
|
f.write(", uint32_t pkt_has_multi_cof")
|
||||||
|
|
||||||
|
if hex_common.need_PC(tag):
|
||||||
|
if i > 0: f.write(", ")
|
||||||
|
f.write("target_ulong PC")
|
||||||
|
i += 1
|
||||||
|
if hex_common.helper_needs_next_PC(tag):
|
||||||
|
if i > 0: f.write(", ")
|
||||||
|
f.write("target_ulong next_PC")
|
||||||
|
i += 1
|
||||||
if hex_common.need_slot(tag):
|
if hex_common.need_slot(tag):
|
||||||
if i > 0: f.write(", ")
|
if i > 0: f.write(", ")
|
||||||
f.write("uint32_t slot")
|
f.write("uint32_t slot")
|
||||||
|
@ -287,11 +298,24 @@ def main():
|
||||||
hex_common.read_attribs_file(sys.argv[2])
|
hex_common.read_attribs_file(sys.argv[2])
|
||||||
hex_common.read_overrides_file(sys.argv[3])
|
hex_common.read_overrides_file(sys.argv[3])
|
||||||
hex_common.read_overrides_file(sys.argv[4])
|
hex_common.read_overrides_file(sys.argv[4])
|
||||||
|
## Whether or not idef-parser is enabled is
|
||||||
|
## determined by the number of arguments to
|
||||||
|
## this script:
|
||||||
|
##
|
||||||
|
## 5 args. -> not enabled,
|
||||||
|
## 6 args. -> idef-parser enabled.
|
||||||
|
##
|
||||||
|
## The 6:th arg. then holds a list of the successfully
|
||||||
|
## parsed instructions.
|
||||||
|
is_idef_parser_enabled = len(sys.argv) > 6
|
||||||
|
if is_idef_parser_enabled:
|
||||||
|
hex_common.read_idef_parser_enabled_file(sys.argv[5])
|
||||||
hex_common.calculate_attribs()
|
hex_common.calculate_attribs()
|
||||||
tagregs = hex_common.get_tagregs()
|
tagregs = hex_common.get_tagregs()
|
||||||
tagimms = hex_common.get_tagimms()
|
tagimms = hex_common.get_tagimms()
|
||||||
|
|
||||||
with open(sys.argv[5], 'w') as f:
|
output_file = sys.argv[-1]
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
for tag in hex_common.tags:
|
for tag in hex_common.tags:
|
||||||
## Skip the priv instructions
|
## Skip the priv instructions
|
||||||
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
||||||
|
@ -308,6 +332,8 @@ def main():
|
||||||
continue
|
continue
|
||||||
if ( hex_common.skip_qemu_helper(tag) ):
|
if ( hex_common.skip_qemu_helper(tag) ):
|
||||||
continue
|
continue
|
||||||
|
if ( hex_common.is_idef_parser_enabled(tag) ):
|
||||||
|
continue
|
||||||
|
|
||||||
gen_helper_function(f, tag, tagregs, tagimms)
|
gen_helper_function(f, tag, tagregs, tagimms)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
##
|
##
|
||||||
## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## This program is free software; you can redistribute it and/or modify
|
||||||
## it under the terms of the GNU General Public License as published by
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
@ -82,15 +82,21 @@ def gen_helper_prototype(f, tag, tagregs, tagimms):
|
||||||
## Figure out how many arguments the helper will take
|
## Figure out how many arguments the helper will take
|
||||||
if (numscalarresults == 0):
|
if (numscalarresults == 0):
|
||||||
def_helper_size = len(regs)+len(imms)+numscalarreadwrite+1
|
def_helper_size = len(regs)+len(imms)+numscalarreadwrite+1
|
||||||
|
if hex_common.need_pkt_has_multi_cof(tag): def_helper_size += 1
|
||||||
if hex_common.need_part1(tag): def_helper_size += 1
|
if hex_common.need_part1(tag): def_helper_size += 1
|
||||||
if hex_common.need_slot(tag): def_helper_size += 1
|
if hex_common.need_slot(tag): def_helper_size += 1
|
||||||
|
if hex_common.need_PC(tag): def_helper_size += 1
|
||||||
|
if hex_common.helper_needs_next_PC(tag): def_helper_size += 1
|
||||||
f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag))
|
f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag))
|
||||||
## The return type is void
|
## The return type is void
|
||||||
f.write(', void' )
|
f.write(', void' )
|
||||||
else:
|
else:
|
||||||
def_helper_size = len(regs)+len(imms)+numscalarreadwrite
|
def_helper_size = len(regs)+len(imms)+numscalarreadwrite
|
||||||
|
if hex_common.need_pkt_has_multi_cof(tag): def_helper_size += 1
|
||||||
if hex_common.need_part1(tag): def_helper_size += 1
|
if hex_common.need_part1(tag): def_helper_size += 1
|
||||||
if hex_common.need_slot(tag): def_helper_size += 1
|
if hex_common.need_slot(tag): def_helper_size += 1
|
||||||
|
if hex_common.need_PC(tag): def_helper_size += 1
|
||||||
|
if hex_common.helper_needs_next_PC(tag): def_helper_size += 1
|
||||||
f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag))
|
f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag))
|
||||||
|
|
||||||
## Generate the qemu DEF_HELPER type for each result
|
## Generate the qemu DEF_HELPER type for each result
|
||||||
|
@ -126,7 +132,11 @@ def gen_helper_prototype(f, tag, tagregs, tagimms):
|
||||||
for immlett,bits,immshift in imms:
|
for immlett,bits,immshift in imms:
|
||||||
f.write(", s32")
|
f.write(", s32")
|
||||||
|
|
||||||
## Add the arguments for the instruction slot and part1 (if needed)
|
## Add the arguments for the instruction pkt_has_multi_cof, slot and
|
||||||
|
## part1 (if needed)
|
||||||
|
if hex_common.need_pkt_has_multi_cof(tag): f.write(', i32')
|
||||||
|
if hex_common.need_PC(tag): f.write(', i32')
|
||||||
|
if hex_common.helper_needs_next_PC(tag): f.write(', i32')
|
||||||
if hex_common.need_slot(tag): f.write(', i32' )
|
if hex_common.need_slot(tag): f.write(', i32' )
|
||||||
if hex_common.need_part1(tag): f.write(' , i32' )
|
if hex_common.need_part1(tag): f.write(' , i32' )
|
||||||
f.write(')\n')
|
f.write(')\n')
|
||||||
|
@ -136,11 +146,24 @@ def main():
|
||||||
hex_common.read_attribs_file(sys.argv[2])
|
hex_common.read_attribs_file(sys.argv[2])
|
||||||
hex_common.read_overrides_file(sys.argv[3])
|
hex_common.read_overrides_file(sys.argv[3])
|
||||||
hex_common.read_overrides_file(sys.argv[4])
|
hex_common.read_overrides_file(sys.argv[4])
|
||||||
|
## Whether or not idef-parser is enabled is
|
||||||
|
## determined by the number of arguments to
|
||||||
|
## this script:
|
||||||
|
##
|
||||||
|
## 5 args. -> not enabled,
|
||||||
|
## 6 args. -> idef-parser enabled.
|
||||||
|
##
|
||||||
|
## The 6:th arg. then holds a list of the successfully
|
||||||
|
## parsed instructions.
|
||||||
|
is_idef_parser_enabled = len(sys.argv) > 6
|
||||||
|
if is_idef_parser_enabled:
|
||||||
|
hex_common.read_idef_parser_enabled_file(sys.argv[5])
|
||||||
hex_common.calculate_attribs()
|
hex_common.calculate_attribs()
|
||||||
tagregs = hex_common.get_tagregs()
|
tagregs = hex_common.get_tagregs()
|
||||||
tagimms = hex_common.get_tagimms()
|
tagimms = hex_common.get_tagimms()
|
||||||
|
|
||||||
with open(sys.argv[5], 'w') as f:
|
output_file = sys.argv[-1]
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
for tag in hex_common.tags:
|
for tag in hex_common.tags:
|
||||||
## Skip the priv instructions
|
## Skip the priv instructions
|
||||||
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
||||||
|
@ -158,6 +181,8 @@ def main():
|
||||||
|
|
||||||
if ( hex_common.skip_qemu_helper(tag) ):
|
if ( hex_common.skip_qemu_helper(tag) ):
|
||||||
continue
|
continue
|
||||||
|
if ( hex_common.is_idef_parser_enabled(tag) ):
|
||||||
|
continue
|
||||||
|
|
||||||
gen_helper_prototype(f, tag, tagregs, tagimms)
|
gen_helper_prototype(f, tag, tagregs, tagimms)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
##
|
||||||
|
## Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program 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 General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
##
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
import hex_common
|
||||||
|
|
||||||
|
##
|
||||||
|
## Generate code to be fed to the idef_parser
|
||||||
|
##
|
||||||
|
## Consider A2_add:
|
||||||
|
##
|
||||||
|
## Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;}
|
||||||
|
##
|
||||||
|
## We produce:
|
||||||
|
##
|
||||||
|
## A2_add(RdV, in RsV, in RtV) {
|
||||||
|
## { RdV=RsV+RtV;}
|
||||||
|
## }
|
||||||
|
##
|
||||||
|
## A2_add represents the instruction tag. Then we have a list of TCGv
|
||||||
|
## that the code generated by the parser can expect in input. Some of
|
||||||
|
## them are inputs ("in" prefix), while some others are outputs.
|
||||||
|
##
|
||||||
|
def main():
|
||||||
|
hex_common.read_semantics_file(sys.argv[1])
|
||||||
|
hex_common.read_attribs_file(sys.argv[2])
|
||||||
|
hex_common.calculate_attribs()
|
||||||
|
tagregs = hex_common.get_tagregs()
|
||||||
|
tagimms = hex_common.get_tagimms()
|
||||||
|
|
||||||
|
with open(sys.argv[3], 'w') as f:
|
||||||
|
f.write('#include "macros.inc"\n\n')
|
||||||
|
|
||||||
|
for tag in hex_common.tags:
|
||||||
|
## Skip the priv instructions
|
||||||
|
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
||||||
|
continue
|
||||||
|
## Skip the guest instructions
|
||||||
|
if ( "A_GUEST" in hex_common.attribdict[tag] ) :
|
||||||
|
continue
|
||||||
|
## Skip instructions that saturate in a ternary expression
|
||||||
|
if ( tag in {'S2_asr_r_r_sat', 'S2_asl_r_r_sat'} ) :
|
||||||
|
continue
|
||||||
|
## Skip instructions using switch
|
||||||
|
if ( tag in {'S4_vrcrotate_acc', 'S4_vrcrotate'} ) :
|
||||||
|
continue
|
||||||
|
## Skip trap instructions
|
||||||
|
if ( tag in {'J2_trap0', 'J2_trap1'} ) :
|
||||||
|
continue
|
||||||
|
## Skip 128-bit instructions
|
||||||
|
if ( tag in {'A7_croundd_ri', 'A7_croundd_rr'} ) :
|
||||||
|
continue
|
||||||
|
if ( tag in {'M7_wcmpyrw', 'M7_wcmpyrwc',
|
||||||
|
'M7_wcmpyiw', 'M7_wcmpyiwc',
|
||||||
|
'M7_wcmpyrw_rnd', 'M7_wcmpyrwc_rnd',
|
||||||
|
'M7_wcmpyiw_rnd', 'M7_wcmpyiwc_rnd'} ) :
|
||||||
|
continue
|
||||||
|
## Skip interleave/deinterleave instructions
|
||||||
|
if ( tag in {'S2_interleave', 'S2_deinterleave'} ) :
|
||||||
|
continue
|
||||||
|
## Skip instructions using bit reverse
|
||||||
|
if ( tag in {'S2_brev', 'S2_brevp', 'S2_ct0', 'S2_ct1',
|
||||||
|
'S2_ct0p', 'S2_ct1p', 'A4_tlbmatch'} ) :
|
||||||
|
continue
|
||||||
|
## Skip other unsupported instructions
|
||||||
|
if ( tag == 'S2_cabacdecbin' or tag == 'A5_ACS' ) :
|
||||||
|
continue
|
||||||
|
if ( tag.startswith('Y') ) :
|
||||||
|
continue
|
||||||
|
if ( tag.startswith('V6_') ) :
|
||||||
|
continue
|
||||||
|
if ( tag.startswith('F') ) :
|
||||||
|
continue
|
||||||
|
if ( tag.endswith('_locked') ) :
|
||||||
|
continue
|
||||||
|
if ( "A_COF" in hex_common.attribdict[tag] ) :
|
||||||
|
continue
|
||||||
|
|
||||||
|
regs = tagregs[tag]
|
||||||
|
imms = tagimms[tag]
|
||||||
|
|
||||||
|
arguments = []
|
||||||
|
for regtype,regid,toss,numregs in regs:
|
||||||
|
prefix = "in " if hex_common.is_read(regid) else ""
|
||||||
|
|
||||||
|
is_pair = hex_common.is_pair(regid)
|
||||||
|
is_single_old = (hex_common.is_single(regid)
|
||||||
|
and hex_common.is_old_val(regtype, regid, tag))
|
||||||
|
is_single_new = (hex_common.is_single(regid)
|
||||||
|
and hex_common.is_new_val(regtype, regid, tag))
|
||||||
|
|
||||||
|
if is_pair or is_single_old:
|
||||||
|
arguments.append("%s%s%sV" % (prefix, regtype, regid))
|
||||||
|
elif is_single_new:
|
||||||
|
arguments.append("%s%s%sN" % (prefix, regtype, regid))
|
||||||
|
else:
|
||||||
|
print("Bad register parse: ",regtype,regid,toss,numregs)
|
||||||
|
|
||||||
|
for immlett,bits,immshift in imms:
|
||||||
|
arguments.append(hex_common.imm_name(immlett))
|
||||||
|
|
||||||
|
f.write("%s(%s) {\n" % (tag, ", ".join(arguments)))
|
||||||
|
f.write(" ");
|
||||||
|
if hex_common.need_ea(tag):
|
||||||
|
f.write("size4u_t EA; ");
|
||||||
|
f.write("%s\n" % hex_common.semdict[tag])
|
||||||
|
f.write("}\n\n")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
* Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -612,6 +612,409 @@
|
||||||
tcg_temp_free(tmp); \
|
tcg_temp_free(tmp); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_call(SHORTCODE) \
|
||||||
|
gen_call(ctx, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_callt(SHORTCODE) \
|
||||||
|
gen_cond_call(ctx, PuV, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J2_callf(SHORTCODE) \
|
||||||
|
gen_cond_call(ctx, PuV, TCG_COND_NE, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_endloop0(SHORTCODE) \
|
||||||
|
gen_endloop0(ctx)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compound compare and jump instructions
|
||||||
|
* Here is a primer to understand the tag names
|
||||||
|
*
|
||||||
|
* Comparison
|
||||||
|
* cmpeqi compare equal to an immediate
|
||||||
|
* cmpgti compare greater than an immediate
|
||||||
|
* cmpgtiu compare greater than an unsigned immediate
|
||||||
|
* cmpeqn1 compare equal to negative 1
|
||||||
|
* cmpgtn1 compare greater than negative 1
|
||||||
|
* cmpeq compare equal (two registers)
|
||||||
|
* cmpgtu compare greater than unsigned (two registers)
|
||||||
|
* tstbit0 test bit zero
|
||||||
|
*
|
||||||
|
* Condition
|
||||||
|
* tp0 p0 is true p0 = cmp.eq(r0,#5); if (p0.new) jump:nt address
|
||||||
|
* fp0 p0 is false p0 = cmp.eq(r0,#5); if (!p0.new) jump:nt address
|
||||||
|
* tp1 p1 is true p1 = cmp.eq(r0,#5); if (p1.new) jump:nt address
|
||||||
|
* fp1 p1 is false p1 = cmp.eq(r0,#5); if (!p1.new) jump:nt address
|
||||||
|
*
|
||||||
|
* Prediction (not modelled in qemu)
|
||||||
|
* _nt not taken
|
||||||
|
* _t taken
|
||||||
|
*/
|
||||||
|
#define fGEN_TCG_J4_cmpeq_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_EQ, RsV, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgt_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GT, RsV, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GTU, RsV, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_EQ, RsV, UiV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgti_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GT, RsV, UiV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GTU, RsV, UiV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_EQ, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_EQ, RsV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_GT, RsV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_GT, RsV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_tstbit0_tp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_tp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_fp0_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_NE, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_fp0_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_NE, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_tp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_tp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_fp1_jump_nt(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_NE, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_fp1_jump_t(SHORTCODE) \
|
||||||
|
gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_NE, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_jump(SHORTCODE) \
|
||||||
|
gen_jump(ctx, riV)
|
||||||
|
#define fGEN_TCG_J2_jumpr(SHORTCODE) \
|
||||||
|
gen_jumpr(ctx, RsV)
|
||||||
|
#define fGEN_TCG_J4_jumpseti(SHORTCODE) \
|
||||||
|
do { \
|
||||||
|
tcg_gen_movi_tl(RdV, UiV); \
|
||||||
|
gen_jump(ctx, riV); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define fGEN_TCG_cond_jumpt(COND) \
|
||||||
|
do { \
|
||||||
|
TCGv LSB = tcg_temp_new(); \
|
||||||
|
COND; \
|
||||||
|
gen_cond_jump(ctx, TCG_COND_EQ, LSB, riV); \
|
||||||
|
tcg_temp_free(LSB); \
|
||||||
|
} while (0)
|
||||||
|
#define fGEN_TCG_cond_jumpf(COND) \
|
||||||
|
do { \
|
||||||
|
TCGv LSB = tcg_temp_new(); \
|
||||||
|
COND; \
|
||||||
|
gen_cond_jump(ctx, TCG_COND_NE, LSB, riV); \
|
||||||
|
tcg_temp_free(LSB); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_jumpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumptpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumpf(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpf(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumpfpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpf(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumptnew(SHORTCODE) \
|
||||||
|
gen_cond_jump(ctx, TCG_COND_EQ, PuN, riV)
|
||||||
|
#define fGEN_TCG_J2_jumptnewpt(SHORTCODE) \
|
||||||
|
gen_cond_jump(ctx, TCG_COND_EQ, PuN, riV)
|
||||||
|
#define fGEN_TCG_J2_jumpfnewpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpf(fLSBNEW(PuN))
|
||||||
|
#define fGEN_TCG_J2_jumpfnew(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpf(fLSBNEW(PuN))
|
||||||
|
#define fGEN_TCG_J2_jumprz(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_NE, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprzpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_NE, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprnz(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_EQ, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprnzpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_EQ, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprgtez(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_GE, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprgtezpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_GE, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprltez(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_LE, LSB, RsV, 0))
|
||||||
|
#define fGEN_TCG_J2_jumprltezpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_LE, LSB, RsV, 0))
|
||||||
|
|
||||||
|
#define fGEN_TCG_cond_jumprt(COND) \
|
||||||
|
do { \
|
||||||
|
TCGv LSB = tcg_temp_new(); \
|
||||||
|
COND; \
|
||||||
|
gen_cond_jumpr(ctx, RsV, TCG_COND_EQ, LSB); \
|
||||||
|
tcg_temp_free(LSB); \
|
||||||
|
} while (0)
|
||||||
|
#define fGEN_TCG_cond_jumprf(COND) \
|
||||||
|
do { \
|
||||||
|
TCGv LSB = tcg_temp_new(); \
|
||||||
|
COND; \
|
||||||
|
gen_cond_jumpr(ctx, RsV, TCG_COND_NE, LSB); \
|
||||||
|
tcg_temp_free(LSB); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_jumprt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprt(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumprtpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprt(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumprf(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprf(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumprfpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprf(fLSBOLD(PuV))
|
||||||
|
#define fGEN_TCG_J2_jumprtnew(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprt(fLSBNEW(PuN))
|
||||||
|
#define fGEN_TCG_J2_jumprtnewpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprt(fLSBNEW(PuN))
|
||||||
|
#define fGEN_TCG_J2_jumprfnew(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprf(fLSBNEW(PuN))
|
||||||
|
#define fGEN_TCG_J2_jumprfnewpt(SHORTCODE) \
|
||||||
|
fGEN_TCG_cond_jumprf(fLSBNEW(PuN))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* New value compare & jump instructions
|
||||||
|
* if ([!]COND(r0.new, r1) jump:t address
|
||||||
|
* if ([!]COND(r0.new, #7) jump:t address
|
||||||
|
*/
|
||||||
|
#define fGEN_TCG_J4_cmpgt_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GT, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GT, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LE, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgt_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LE, NsN, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpeq_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_EQ, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_EQ, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_NE, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeq_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_NE, NsN, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmplt_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LT, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmplt_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LT, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmplt_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GE, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmplt_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GE, NsN, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqi_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, UiV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgti_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgti_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, UiV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpltu_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LTU, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpltu_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LTU, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpltu_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GEU, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpltu_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GEU, NsN, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_GTU, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_GTU, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_LEU, NsN, UiV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtui_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_LEU, NsN, UiV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GTU, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_GTU, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LEU, NsN, RtV, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtu_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmp_jumpnv(ctx, TCG_COND_LEU, NsN, RtV, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, -1, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, -1, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, -1, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpeqn1_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, -1, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, -1, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, -1, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, -1, riV)
|
||||||
|
#define fGEN_TCG_J4_cmpgtn1_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, -1, riV)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J4_tstbit0_t_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_testbit0_jumpnv(ctx, NsN, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_t_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_testbit0_jumpnv(ctx, NsN, TCG_COND_EQ, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_f_jumpnv_t(SHORTCODE) \
|
||||||
|
gen_testbit0_jumpnv(ctx, NsN, TCG_COND_NE, riV)
|
||||||
|
#define fGEN_TCG_J4_tstbit0_f_jumpnv_nt(SHORTCODE) \
|
||||||
|
gen_testbit0_jumpnv(ctx, NsN, TCG_COND_NE, riV)
|
||||||
|
|
||||||
|
/* r0 = r1 ; jump address */
|
||||||
|
#define fGEN_TCG_J4_jumpsetr(SHORTCODE) \
|
||||||
|
do { \
|
||||||
|
tcg_gen_mov_tl(RdV, RsV); \
|
||||||
|
gen_jump(ctx, riV); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_pause(SHORTCODE) \
|
||||||
|
do { \
|
||||||
|
uiV = uiV; \
|
||||||
|
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* r0 = asr(r1, r2):sat */
|
||||||
|
#define fGEN_TCG_S2_asr_r_r_sat(SHORTCODE) \
|
||||||
|
gen_asr_r_r_sat(RdV, RsV, RtV)
|
||||||
|
|
||||||
|
/* r0 = asl(r1, r2):sat */
|
||||||
|
#define fGEN_TCG_S2_asl_r_r_sat(SHORTCODE) \
|
||||||
|
gen_asl_r_r_sat(RdV, RsV, RtV)
|
||||||
|
|
||||||
/* Floating point */
|
/* Floating point */
|
||||||
#define fGEN_TCG_F2_conv_sf2df(SHORTCODE) \
|
#define fGEN_TCG_F2_conv_sf2df(SHORTCODE) \
|
||||||
gen_helper_conv_sf2df(RddV, cpu_env, RsV)
|
gen_helper_conv_sf2df(RddV, cpu_env, RsV)
|
||||||
|
@ -742,4 +1145,11 @@
|
||||||
RsV = RsV; \
|
RsV = RsV; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define fGEN_TCG_J2_trap0(SHORTCODE) \
|
||||||
|
do { \
|
||||||
|
uiV = uiV; \
|
||||||
|
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->pkt->pc); \
|
||||||
|
TCGv excp = tcg_constant_tl(HEX_EXCP_TRAP0); \
|
||||||
|
gen_helper_raise_exception(cpu_env, excp); \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -173,6 +173,18 @@ def genptr_decl(f, tag, regtype, regid, regno):
|
||||||
f.write(" ctx_future_vreg_off(ctx, %s%sN," % \
|
f.write(" ctx_future_vreg_off(ctx, %s%sN," % \
|
||||||
(regtype, regid))
|
(regtype, regid))
|
||||||
f.write(" 1, true);\n");
|
f.write(" 1, true);\n");
|
||||||
|
if 'A_CONDEXEC' in hex_common.attribdict[tag]:
|
||||||
|
f.write(" if (!is_vreg_preloaded(ctx, %s)) {\n" % (regN))
|
||||||
|
f.write(" intptr_t src_off =")
|
||||||
|
f.write(" offsetof(CPUHexagonState, VRegs[%s%sN]);\n"% \
|
||||||
|
(regtype, regid))
|
||||||
|
f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \
|
||||||
|
(regtype, regid))
|
||||||
|
f.write(" src_off,\n")
|
||||||
|
f.write(" sizeof(MMVector),\n")
|
||||||
|
f.write(" sizeof(MMVector));\n")
|
||||||
|
f.write(" }\n")
|
||||||
|
|
||||||
if (not hex_common.skip_qemu_helper(tag)):
|
if (not hex_common.skip_qemu_helper(tag)):
|
||||||
f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \
|
f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \
|
||||||
(regtype, regid))
|
(regtype, regid))
|
||||||
|
@ -561,11 +573,7 @@ def genptr_dst_write_opn(f,regtype, regid, tag):
|
||||||
## Generate the TCG code to call the helper
|
## Generate the TCG code to call the helper
|
||||||
## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;}
|
## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;}
|
||||||
## We produce:
|
## We produce:
|
||||||
## static void generate_A2_add()
|
## static void generate_A2_add(DisasContext *ctx)
|
||||||
## CPUHexagonState *env
|
|
||||||
## DisasContext *ctx,
|
|
||||||
## Insn *insn,
|
|
||||||
## Packet *pkt)
|
|
||||||
## {
|
## {
|
||||||
## TCGv RdV = tcg_temp_local_new();
|
## TCGv RdV = tcg_temp_local_new();
|
||||||
## const int RdN = insn->regno[0];
|
## const int RdN = insn->regno[0];
|
||||||
|
@ -584,12 +592,11 @@ def genptr_dst_write_opn(f,regtype, regid, tag):
|
||||||
## <GEN> is gen_helper_A2_add(RdV, cpu_env, RsV, RtV);
|
## <GEN> is gen_helper_A2_add(RdV, cpu_env, RsV, RtV);
|
||||||
##
|
##
|
||||||
def gen_tcg_func(f, tag, regs, imms):
|
def gen_tcg_func(f, tag, regs, imms):
|
||||||
f.write("static void generate_%s(\n" %tag)
|
f.write("static void generate_%s(DisasContext *ctx)\n" %tag)
|
||||||
f.write(" CPUHexagonState *env,\n")
|
|
||||||
f.write(" DisasContext *ctx,\n")
|
|
||||||
f.write(" Insn *insn,\n")
|
|
||||||
f.write(" Packet *pkt)\n")
|
|
||||||
f.write('{\n')
|
f.write('{\n')
|
||||||
|
|
||||||
|
f.write(" Insn *insn __attribute__((unused)) = ctx->insn;\n")
|
||||||
|
|
||||||
if hex_common.need_ea(tag): gen_decl_ea_tcg(f, tag)
|
if hex_common.need_ea(tag): gen_decl_ea_tcg(f, tag)
|
||||||
i=0
|
i=0
|
||||||
## Declare all the operands (regs and immediates)
|
## Declare all the operands (regs and immediates)
|
||||||
|
@ -609,16 +616,45 @@ def gen_tcg_func(f, tag, regs, imms):
|
||||||
if (hex_common.is_read(regid)):
|
if (hex_common.is_read(regid)):
|
||||||
genptr_src_read_opn(f,regtype,regid,tag)
|
genptr_src_read_opn(f,regtype,regid,tag)
|
||||||
|
|
||||||
if ( hex_common.skip_qemu_helper(tag) ):
|
if hex_common.is_idef_parser_enabled(tag):
|
||||||
|
declared = []
|
||||||
|
## Handle registers
|
||||||
|
for regtype,regid,toss,numregs in regs:
|
||||||
|
if (hex_common.is_pair(regid)
|
||||||
|
or (hex_common.is_single(regid)
|
||||||
|
and hex_common.is_old_val(regtype, regid, tag))):
|
||||||
|
declared.append("%s%sV" % (regtype, regid))
|
||||||
|
if regtype == "M":
|
||||||
|
declared.append("%s%sN" % (regtype, regid))
|
||||||
|
elif hex_common.is_new_val(regtype, regid, tag):
|
||||||
|
declared.append("%s%sN" % (regtype,regid))
|
||||||
|
else:
|
||||||
|
print("Bad register parse: ",regtype,regid,toss,numregs)
|
||||||
|
|
||||||
|
## Handle immediates
|
||||||
|
for immlett,bits,immshift in imms:
|
||||||
|
declared.append(hex_common.imm_name(immlett))
|
||||||
|
|
||||||
|
arguments = ", ".join(["ctx", "ctx->insn", "ctx->pkt"] + declared)
|
||||||
|
f.write(" emit_%s(%s);\n" % (tag, arguments))
|
||||||
|
|
||||||
|
elif ( hex_common.skip_qemu_helper(tag) ):
|
||||||
f.write(" fGEN_TCG_%s(%s);\n" % (tag, hex_common.semdict[tag]))
|
f.write(" fGEN_TCG_%s(%s);\n" % (tag, hex_common.semdict[tag]))
|
||||||
else:
|
else:
|
||||||
## Generate the call to the helper
|
## Generate the call to the helper
|
||||||
for immlett,bits,immshift in imms:
|
for immlett,bits,immshift in imms:
|
||||||
gen_helper_decl_imm(f,immlett)
|
gen_helper_decl_imm(f,immlett)
|
||||||
|
if hex_common.need_pkt_has_multi_cof(tag):
|
||||||
|
f.write(" TCGv pkt_has_multi_cof = ")
|
||||||
|
f.write("tcg_constant_tl(ctx->pkt->pkt_has_multi_cof);\n")
|
||||||
if hex_common.need_part1(tag):
|
if hex_common.need_part1(tag):
|
||||||
f.write(" TCGv part1 = tcg_constant_tl(insn->part1);\n")
|
f.write(" TCGv part1 = tcg_constant_tl(insn->part1);\n")
|
||||||
if hex_common.need_slot(tag):
|
if hex_common.need_slot(tag):
|
||||||
f.write(" TCGv slot = tcg_constant_tl(insn->slot);\n")
|
f.write(" TCGv slot = tcg_constant_tl(insn->slot);\n")
|
||||||
|
if hex_common.need_PC(tag):
|
||||||
|
f.write(" TCGv PC = tcg_constant_tl(ctx->pkt->pc);\n")
|
||||||
|
if hex_common.helper_needs_next_PC(tag):
|
||||||
|
f.write(" TCGv next_PC = tcg_constant_tl(ctx->next_PC);\n")
|
||||||
f.write(" gen_helper_%s(" % (tag))
|
f.write(" gen_helper_%s(" % (tag))
|
||||||
i=0
|
i=0
|
||||||
## If there is a scalar result, it is the return type
|
## If there is a scalar result, it is the return type
|
||||||
|
@ -647,6 +683,10 @@ def gen_tcg_func(f, tag, regs, imms):
|
||||||
for immlett,bits,immshift in imms:
|
for immlett,bits,immshift in imms:
|
||||||
gen_helper_call_imm(f,immlett)
|
gen_helper_call_imm(f,immlett)
|
||||||
|
|
||||||
|
if hex_common.need_pkt_has_multi_cof(tag):
|
||||||
|
f.write(", pkt_has_multi_cof")
|
||||||
|
if hex_common.need_PC(tag): f.write(", PC")
|
||||||
|
if hex_common.helper_needs_next_PC(tag): f.write(", next_PC")
|
||||||
if hex_common.need_slot(tag): f.write(", slot")
|
if hex_common.need_slot(tag): f.write(", slot")
|
||||||
if hex_common.need_part1(tag): f.write(", part1" )
|
if hex_common.need_part1(tag): f.write(", part1" )
|
||||||
f.write(");\n")
|
f.write(");\n")
|
||||||
|
@ -676,12 +716,27 @@ def main():
|
||||||
hex_common.read_overrides_file(sys.argv[3])
|
hex_common.read_overrides_file(sys.argv[3])
|
||||||
hex_common.read_overrides_file(sys.argv[4])
|
hex_common.read_overrides_file(sys.argv[4])
|
||||||
hex_common.calculate_attribs()
|
hex_common.calculate_attribs()
|
||||||
|
## Whether or not idef-parser is enabled is
|
||||||
|
## determined by the number of arguments to
|
||||||
|
## this script:
|
||||||
|
##
|
||||||
|
## 5 args. -> not enabled,
|
||||||
|
## 6 args. -> idef-parser enabled.
|
||||||
|
##
|
||||||
|
## The 6:th arg. then holds a list of the successfully
|
||||||
|
## parsed instructions.
|
||||||
|
is_idef_parser_enabled = len(sys.argv) > 6
|
||||||
|
if is_idef_parser_enabled:
|
||||||
|
hex_common.read_idef_parser_enabled_file(sys.argv[5])
|
||||||
tagregs = hex_common.get_tagregs()
|
tagregs = hex_common.get_tagregs()
|
||||||
tagimms = hex_common.get_tagimms()
|
tagimms = hex_common.get_tagimms()
|
||||||
|
|
||||||
with open(sys.argv[5], 'w') as f:
|
output_file = sys.argv[-1]
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
f.write("#ifndef HEXAGON_TCG_FUNCS_H\n")
|
f.write("#ifndef HEXAGON_TCG_FUNCS_H\n")
|
||||||
f.write("#define HEXAGON_TCG_FUNCS_H\n\n")
|
f.write("#define HEXAGON_TCG_FUNCS_H\n\n")
|
||||||
|
if is_idef_parser_enabled:
|
||||||
|
f.write("#include \"idef-generated-emitter.h.inc\"\n\n")
|
||||||
|
|
||||||
for tag in hex_common.tags:
|
for tag in hex_common.tags:
|
||||||
## Skip the priv instructions
|
## Skip the priv instructions
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
* Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -697,7 +697,7 @@ static inline void assert_vhist_tmp(DisasContext *ctx)
|
||||||
#define fGEN_TCG_NEWVAL_VEC_STORE(GET_EA, INC) \
|
#define fGEN_TCG_NEWVAL_VEC_STORE(GET_EA, INC) \
|
||||||
do { \
|
do { \
|
||||||
GET_EA; \
|
GET_EA; \
|
||||||
gen_vreg_store(ctx, insn, pkt, EA, OsN_off, insn->slot, true); \
|
gen_vreg_store(ctx, EA, OsN_off, insn->slot, true); \
|
||||||
INC; \
|
INC; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -736,7 +736,7 @@ static inline void assert_vhist_tmp(DisasContext *ctx)
|
||||||
PRED; \
|
PRED; \
|
||||||
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \
|
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \
|
||||||
tcg_temp_free(LSB); \
|
tcg_temp_free(LSB); \
|
||||||
gen_vreg_store(ctx, insn, pkt, EA, SRCOFF, insn->slot, ALIGN); \
|
gen_vreg_store(ctx, EA, SRCOFF, insn->slot, ALIGN); \
|
||||||
INC; \
|
INC; \
|
||||||
tcg_gen_br(end_label); \
|
tcg_gen_br(end_label); \
|
||||||
gen_set_label(false_label); \
|
gen_set_label(false_label); \
|
||||||
|
|
|
@ -29,8 +29,22 @@
|
||||||
#undef QEMU_GENERATE
|
#undef QEMU_GENERATE
|
||||||
#include "gen_tcg.h"
|
#include "gen_tcg.h"
|
||||||
#include "gen_tcg_hvx.h"
|
#include "gen_tcg_hvx.h"
|
||||||
|
#include "genptr.h"
|
||||||
|
|
||||||
static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot)
|
TCGv gen_read_reg(TCGv result, int num)
|
||||||
|
{
|
||||||
|
tcg_gen_mov_tl(result, hex_gpr[num]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TCGv gen_read_preg(TCGv pred, uint8_t num)
|
||||||
|
{
|
||||||
|
tcg_gen_mov_tl(pred, hex_pred[num]);
|
||||||
|
return pred;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gen_log_predicated_reg_write(int rnum, TCGv val,
|
||||||
|
uint32_t slot)
|
||||||
{
|
{
|
||||||
TCGv zero = tcg_constant_tl(0);
|
TCGv zero = tcg_constant_tl(0);
|
||||||
TCGv slot_mask = tcg_temp_new();
|
TCGv slot_mask = tcg_temp_new();
|
||||||
|
@ -53,7 +67,7 @@ static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot)
|
||||||
tcg_temp_free(slot_mask);
|
tcg_temp_free(slot_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_log_reg_write(int rnum, TCGv val)
|
void gen_log_reg_write(int rnum, TCGv val)
|
||||||
{
|
{
|
||||||
tcg_gen_mov_tl(hex_new_value[rnum], val);
|
tcg_gen_mov_tl(hex_new_value[rnum], val);
|
||||||
if (HEX_DEBUG) {
|
if (HEX_DEBUG) {
|
||||||
|
@ -62,7 +76,8 @@ static inline void gen_log_reg_write(int rnum, TCGv val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val, int slot)
|
static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val,
|
||||||
|
uint32_t slot)
|
||||||
{
|
{
|
||||||
TCGv val32 = tcg_temp_new();
|
TCGv val32 = tcg_temp_new();
|
||||||
TCGv zero = tcg_constant_tl(0);
|
TCGv zero = tcg_constant_tl(0);
|
||||||
|
@ -114,7 +129,7 @@ static void gen_log_reg_write_pair(int rnum, TCGv_i64 val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val)
|
void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val)
|
||||||
{
|
{
|
||||||
TCGv base_val = tcg_temp_new();
|
TCGv base_val = tcg_temp_new();
|
||||||
|
|
||||||
|
@ -272,7 +287,7 @@ static inline void gen_write_ctrl_reg_pair(DisasContext *ctx, int reg_num,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign)
|
TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign)
|
||||||
{
|
{
|
||||||
if (sign) {
|
if (sign) {
|
||||||
tcg_gen_sextract_tl(result, src, N * 8, 8);
|
tcg_gen_sextract_tl(result, src, N * 8, 8);
|
||||||
|
@ -282,7 +297,7 @@ static TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign)
|
TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign)
|
||||||
{
|
{
|
||||||
TCGv_i64 res64 = tcg_temp_new_i64();
|
TCGv_i64 res64 = tcg_temp_new_i64();
|
||||||
if (sign) {
|
if (sign) {
|
||||||
|
@ -296,7 +311,7 @@ static TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign)
|
TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign)
|
||||||
{
|
{
|
||||||
if (sign) {
|
if (sign) {
|
||||||
tcg_gen_sextract_tl(result, src, N * 16, 16);
|
tcg_gen_sextract_tl(result, src, N * 16, 16);
|
||||||
|
@ -306,12 +321,12 @@ static inline TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_set_half(int N, TCGv result, TCGv src)
|
void gen_set_half(int N, TCGv result, TCGv src)
|
||||||
{
|
{
|
||||||
tcg_gen_deposit_tl(result, result, src, N * 16, 16);
|
tcg_gen_deposit_tl(result, result, src, N * 16, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_set_half_i64(int N, TCGv_i64 result, TCGv src)
|
void gen_set_half_i64(int N, TCGv_i64 result, TCGv src)
|
||||||
{
|
{
|
||||||
TCGv_i64 src64 = tcg_temp_new_i64();
|
TCGv_i64 src64 = tcg_temp_new_i64();
|
||||||
tcg_gen_extu_i32_i64(src64, src);
|
tcg_gen_extu_i32_i64(src64, src);
|
||||||
|
@ -319,7 +334,7 @@ static inline void gen_set_half_i64(int N, TCGv_i64 result, TCGv src)
|
||||||
tcg_temp_free_i64(src64);
|
tcg_temp_free_i64(src64);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src)
|
void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src)
|
||||||
{
|
{
|
||||||
TCGv_i64 src64 = tcg_temp_new_i64();
|
TCGv_i64 src64 = tcg_temp_new_i64();
|
||||||
tcg_gen_extu_i32_i64(src64, src);
|
tcg_gen_extu_i32_i64(src64, src);
|
||||||
|
@ -394,60 +409,60 @@ static inline void gen_store_conditional8(DisasContext *ctx,
|
||||||
tcg_gen_movi_tl(hex_llsc_addr, ~0);
|
tcg_gen_movi_tl(hex_llsc_addr, ~0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store32(TCGv vaddr, TCGv src, int width, int slot)
|
void gen_store32(TCGv vaddr, TCGv src, int width, uint32_t slot)
|
||||||
{
|
{
|
||||||
tcg_gen_mov_tl(hex_store_addr[slot], vaddr);
|
tcg_gen_mov_tl(hex_store_addr[slot], vaddr);
|
||||||
tcg_gen_movi_tl(hex_store_width[slot], width);
|
tcg_gen_movi_tl(hex_store_width[slot], width);
|
||||||
tcg_gen_mov_tl(hex_store_val32[slot], src);
|
tcg_gen_mov_tl(hex_store_val32[slot], src);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, int slot)
|
void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot)
|
||||||
{
|
{
|
||||||
gen_store32(vaddr, src, 1, slot);
|
gen_store32(vaddr, src, 1, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, int slot)
|
void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot)
|
||||||
{
|
{
|
||||||
TCGv tmp = tcg_constant_tl(src);
|
TCGv tmp = tcg_constant_tl(src);
|
||||||
gen_store1(cpu_env, vaddr, tmp, slot);
|
gen_store1(cpu_env, vaddr, tmp, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, int slot)
|
void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot)
|
||||||
{
|
{
|
||||||
gen_store32(vaddr, src, 2, slot);
|
gen_store32(vaddr, src, 2, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, int slot)
|
void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot)
|
||||||
{
|
{
|
||||||
TCGv tmp = tcg_constant_tl(src);
|
TCGv tmp = tcg_constant_tl(src);
|
||||||
gen_store2(cpu_env, vaddr, tmp, slot);
|
gen_store2(cpu_env, vaddr, tmp, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, int slot)
|
void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot)
|
||||||
{
|
{
|
||||||
gen_store32(vaddr, src, 4, slot);
|
gen_store32(vaddr, src, 4, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, int slot)
|
void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot)
|
||||||
{
|
{
|
||||||
TCGv tmp = tcg_constant_tl(src);
|
TCGv tmp = tcg_constant_tl(src);
|
||||||
gen_store4(cpu_env, vaddr, tmp, slot);
|
gen_store4(cpu_env, vaddr, tmp, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, int slot)
|
void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, uint32_t slot)
|
||||||
{
|
{
|
||||||
tcg_gen_mov_tl(hex_store_addr[slot], vaddr);
|
tcg_gen_mov_tl(hex_store_addr[slot], vaddr);
|
||||||
tcg_gen_movi_tl(hex_store_width[slot], 8);
|
tcg_gen_movi_tl(hex_store_width[slot], 8);
|
||||||
tcg_gen_mov_i64(hex_store_val64[slot], src);
|
tcg_gen_mov_i64(hex_store_val64[slot], src);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, int slot)
|
void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot)
|
||||||
{
|
{
|
||||||
TCGv_i64 tmp = tcg_constant_i64(src);
|
TCGv_i64 tmp = tcg_constant_i64(src);
|
||||||
gen_store8(cpu_env, vaddr, tmp, slot);
|
gen_store8(cpu_env, vaddr, tmp, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TCGv gen_8bitsof(TCGv result, TCGv value)
|
TCGv gen_8bitsof(TCGv result, TCGv value)
|
||||||
{
|
{
|
||||||
TCGv zero = tcg_constant_tl(0);
|
TCGv zero = tcg_constant_tl(0);
|
||||||
TCGv ones = tcg_constant_tl(0xff);
|
TCGv ones = tcg_constant_tl(0xff);
|
||||||
|
@ -456,6 +471,392 @@ static TCGv gen_8bitsof(TCGv result, TCGv value)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_write_new_pc_addr(DisasContext *ctx, TCGv addr,
|
||||||
|
TCGCond cond, TCGv pred)
|
||||||
|
{
|
||||||
|
TCGLabel *pred_false = NULL;
|
||||||
|
if (cond != TCG_COND_ALWAYS) {
|
||||||
|
pred_false = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(cond, pred, 0, pred_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->pkt->pkt_has_multi_cof) {
|
||||||
|
/* If there are multiple branches in a packet, ignore the second one */
|
||||||
|
tcg_gen_movcond_tl(TCG_COND_NE, hex_gpr[HEX_REG_PC],
|
||||||
|
hex_branch_taken, tcg_constant_tl(0),
|
||||||
|
hex_gpr[HEX_REG_PC], addr);
|
||||||
|
tcg_gen_movi_tl(hex_branch_taken, 1);
|
||||||
|
} else {
|
||||||
|
tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cond != TCG_COND_ALWAYS) {
|
||||||
|
gen_set_label(pred_false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_write_new_pc_pcrel(DisasContext *ctx, int pc_off,
|
||||||
|
TCGCond cond, TCGv pred)
|
||||||
|
{
|
||||||
|
target_ulong dest = ctx->pkt->pc + pc_off;
|
||||||
|
if (ctx->pkt->pkt_has_multi_cof) {
|
||||||
|
gen_write_new_pc_addr(ctx, tcg_constant_tl(dest), cond, pred);
|
||||||
|
} else {
|
||||||
|
/* Defer this jump to the end of the TB */
|
||||||
|
ctx->branch_cond = TCG_COND_ALWAYS;
|
||||||
|
if (pred != NULL) {
|
||||||
|
ctx->branch_cond = cond;
|
||||||
|
tcg_gen_mov_tl(hex_branch_taken, pred);
|
||||||
|
}
|
||||||
|
ctx->branch_dest = dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_set_usr_field(int field, TCGv val)
|
||||||
|
{
|
||||||
|
tcg_gen_deposit_tl(hex_new_value[HEX_REG_USR], hex_new_value[HEX_REG_USR],
|
||||||
|
val,
|
||||||
|
reg_field_info[field].offset,
|
||||||
|
reg_field_info[field].width);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_set_usr_fieldi(int field, int x)
|
||||||
|
{
|
||||||
|
if (reg_field_info[field].width == 1) {
|
||||||
|
target_ulong bit = 1 << reg_field_info[field].offset;
|
||||||
|
if ((x & 1) == 1) {
|
||||||
|
tcg_gen_ori_tl(hex_new_value[HEX_REG_USR],
|
||||||
|
hex_new_value[HEX_REG_USR],
|
||||||
|
bit);
|
||||||
|
} else {
|
||||||
|
tcg_gen_andi_tl(hex_new_value[HEX_REG_USR],
|
||||||
|
hex_new_value[HEX_REG_USR],
|
||||||
|
~bit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TCGv val = tcg_constant_tl(x);
|
||||||
|
gen_set_usr_field(field, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_compare(TCGCond cond, TCGv res, TCGv arg1, TCGv arg2)
|
||||||
|
{
|
||||||
|
TCGv one = tcg_constant_tl(0xff);
|
||||||
|
TCGv zero = tcg_constant_tl(0);
|
||||||
|
|
||||||
|
tcg_gen_movcond_tl(cond, res, arg1, arg2, one, zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cond_jumpr(DisasContext *ctx, TCGv dst_pc,
|
||||||
|
TCGCond cond, TCGv pred)
|
||||||
|
{
|
||||||
|
gen_write_new_pc_addr(ctx, dst_pc, cond, pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cond_jump(DisasContext *ctx, TCGCond cond, TCGv pred,
|
||||||
|
int pc_off)
|
||||||
|
{
|
||||||
|
gen_write_new_pc_pcrel(ctx, pc_off, cond, pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmp_jmp(DisasContext *ctx,
|
||||||
|
int pnum, TCGCond cond1, TCGv arg1, TCGv arg2,
|
||||||
|
TCGCond cond2, int pc_off)
|
||||||
|
{
|
||||||
|
if (ctx->insn->part1) {
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
gen_compare(cond1, pred, arg1, arg2);
|
||||||
|
gen_log_pred_write(ctx, pnum, pred);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
} else {
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
tcg_gen_mov_tl(pred, hex_new_pred_value[pnum]);
|
||||||
|
gen_cond_jump(ctx, cond2, pred, pc_off);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmp_jmp_t(DisasContext *ctx,
|
||||||
|
int pnum, TCGCond cond, TCGv arg1, TCGv arg2,
|
||||||
|
int pc_off)
|
||||||
|
{
|
||||||
|
gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, arg2, TCG_COND_EQ, pc_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmp_jmp_f(DisasContext *ctx,
|
||||||
|
int pnum, TCGCond cond, TCGv arg1, TCGv arg2,
|
||||||
|
int pc_off)
|
||||||
|
{
|
||||||
|
gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, arg2, TCG_COND_NE, pc_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmpi_jmp_t(DisasContext *ctx,
|
||||||
|
int pnum, TCGCond cond, TCGv arg1, int arg2,
|
||||||
|
int pc_off)
|
||||||
|
{
|
||||||
|
TCGv tmp = tcg_constant_tl(arg2);
|
||||||
|
gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, tmp, TCG_COND_EQ, pc_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmpi_jmp_f(DisasContext *ctx,
|
||||||
|
int pnum, TCGCond cond, TCGv arg1, int arg2,
|
||||||
|
int pc_off)
|
||||||
|
{
|
||||||
|
TCGv tmp = tcg_constant_tl(arg2);
|
||||||
|
gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, tmp, TCG_COND_NE, pc_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmp_n1_jmp_t(DisasContext *ctx, int pnum, TCGCond cond,
|
||||||
|
TCGv arg, int pc_off)
|
||||||
|
{
|
||||||
|
gen_cmpnd_cmpi_jmp_t(ctx, pnum, cond, arg, -1, pc_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_cmp_n1_jmp_f(DisasContext *ctx, int pnum, TCGCond cond,
|
||||||
|
TCGv arg, int pc_off)
|
||||||
|
{
|
||||||
|
gen_cmpnd_cmpi_jmp_f(ctx, pnum, cond, arg, -1, pc_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpnd_tstbit0_jmp(DisasContext *ctx,
|
||||||
|
int pnum, TCGv arg, TCGCond cond, int pc_off)
|
||||||
|
{
|
||||||
|
if (ctx->insn->part1) {
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
tcg_gen_andi_tl(pred, arg, 1);
|
||||||
|
gen_8bitsof(pred, pred);
|
||||||
|
gen_log_pred_write(ctx, pnum, pred);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
} else {
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
tcg_gen_mov_tl(pred, hex_new_pred_value[pnum]);
|
||||||
|
gen_cond_jump(ctx, cond, pred, pc_off);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_testbit0_jumpnv(DisasContext *ctx,
|
||||||
|
TCGv arg, TCGCond cond, int pc_off)
|
||||||
|
{
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
tcg_gen_andi_tl(pred, arg, 1);
|
||||||
|
gen_cond_jump(ctx, cond, pred, pc_off);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_jump(DisasContext *ctx, int pc_off)
|
||||||
|
{
|
||||||
|
gen_write_new_pc_pcrel(ctx, pc_off, TCG_COND_ALWAYS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_jumpr(DisasContext *ctx, TCGv new_pc)
|
||||||
|
{
|
||||||
|
gen_write_new_pc_addr(ctx, new_pc, TCG_COND_ALWAYS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_call(DisasContext *ctx, int pc_off)
|
||||||
|
{
|
||||||
|
TCGv next_PC =
|
||||||
|
tcg_constant_tl(ctx->pkt->pc + ctx->pkt->encod_pkt_size_in_bytes);
|
||||||
|
gen_log_reg_write(HEX_REG_LR, next_PC);
|
||||||
|
gen_write_new_pc_pcrel(ctx, pc_off, TCG_COND_ALWAYS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cond_call(DisasContext *ctx, TCGv pred,
|
||||||
|
TCGCond cond, int pc_off)
|
||||||
|
{
|
||||||
|
TCGv next_PC;
|
||||||
|
TCGv lsb = tcg_temp_local_new();
|
||||||
|
TCGLabel *skip = gen_new_label();
|
||||||
|
tcg_gen_andi_tl(lsb, pred, 1);
|
||||||
|
gen_write_new_pc_pcrel(ctx, pc_off, cond, lsb);
|
||||||
|
tcg_gen_brcondi_tl(cond, lsb, 0, skip);
|
||||||
|
tcg_temp_free(lsb);
|
||||||
|
next_PC =
|
||||||
|
tcg_constant_tl(ctx->pkt->pc + ctx->pkt->encod_pkt_size_in_bytes);
|
||||||
|
gen_log_reg_write(HEX_REG_LR, next_PC);
|
||||||
|
gen_set_label(skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_endloop0(DisasContext *ctx)
|
||||||
|
{
|
||||||
|
TCGv lpcfg = tcg_temp_local_new();
|
||||||
|
|
||||||
|
GET_USR_FIELD(USR_LPCFG, lpcfg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if (lpcfg == 1) {
|
||||||
|
* hex_new_pred_value[3] = 0xff;
|
||||||
|
* hex_pred_written |= 1 << 3;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
TCGLabel *label1 = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_NE, lpcfg, 1, label1);
|
||||||
|
{
|
||||||
|
tcg_gen_movi_tl(hex_new_pred_value[3], 0xff);
|
||||||
|
tcg_gen_ori_tl(hex_pred_written, hex_pred_written, 1 << 3);
|
||||||
|
}
|
||||||
|
gen_set_label(label1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if (lpcfg) {
|
||||||
|
* SET_USR_FIELD(USR_LPCFG, lpcfg - 1);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
TCGLabel *label2 = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_EQ, lpcfg, 0, label2);
|
||||||
|
{
|
||||||
|
tcg_gen_subi_tl(lpcfg, lpcfg, 1);
|
||||||
|
SET_USR_FIELD(USR_LPCFG, lpcfg);
|
||||||
|
}
|
||||||
|
gen_set_label(label2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're in a tight loop, we'll do this at the end of the TB to take
|
||||||
|
* advantage of direct block chaining.
|
||||||
|
*/
|
||||||
|
if (!ctx->is_tight_loop) {
|
||||||
|
/*
|
||||||
|
* if (hex_gpr[HEX_REG_LC0] > 1) {
|
||||||
|
* PC = hex_gpr[HEX_REG_SA0];
|
||||||
|
* hex_new_value[HEX_REG_LC0] = hex_gpr[HEX_REG_LC0] - 1;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
TCGLabel *label3 = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, label3);
|
||||||
|
{
|
||||||
|
gen_jumpr(ctx, hex_gpr[HEX_REG_SA0]);
|
||||||
|
tcg_gen_subi_tl(hex_new_value[HEX_REG_LC0],
|
||||||
|
hex_gpr[HEX_REG_LC0], 1);
|
||||||
|
}
|
||||||
|
gen_set_label(label3);
|
||||||
|
}
|
||||||
|
|
||||||
|
tcg_temp_free(lpcfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmp_jumpnv(DisasContext *ctx,
|
||||||
|
TCGCond cond, TCGv val, TCGv src, int pc_off)
|
||||||
|
{
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
tcg_gen_setcond_tl(cond, pred, val, src);
|
||||||
|
gen_cond_jump(ctx, TCG_COND_EQ, pred, pc_off);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_cmpi_jumpnv(DisasContext *ctx,
|
||||||
|
TCGCond cond, TCGv val, int src, int pc_off)
|
||||||
|
{
|
||||||
|
TCGv pred = tcg_temp_new();
|
||||||
|
tcg_gen_setcondi_tl(cond, pred, val, src);
|
||||||
|
gen_cond_jump(ctx, TCG_COND_EQ, pred, pc_off);
|
||||||
|
tcg_temp_free(pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shift left with saturation */
|
||||||
|
static void gen_shl_sat(TCGv dst, TCGv src, TCGv shift_amt)
|
||||||
|
{
|
||||||
|
TCGv sh32 = tcg_temp_new();
|
||||||
|
TCGv dst_sar = tcg_temp_new();
|
||||||
|
TCGv ovf = tcg_temp_new();
|
||||||
|
TCGv satval = tcg_temp_new();
|
||||||
|
TCGv min = tcg_constant_tl(0x80000000);
|
||||||
|
TCGv max = tcg_constant_tl(0x7fffffff);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Possible values for shift_amt are 0 .. 64
|
||||||
|
* We need special handling for values above 31
|
||||||
|
*
|
||||||
|
* sh32 = shift & 31;
|
||||||
|
* dst = sh32 == shift ? src : 0;
|
||||||
|
* dst <<= sh32;
|
||||||
|
* dst_sar = dst >> sh32;
|
||||||
|
* satval = src < 0 ? min : max;
|
||||||
|
* if (dst_asr != src) {
|
||||||
|
* usr.OVF |= 1;
|
||||||
|
* dst = satval;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
tcg_gen_andi_tl(sh32, shift_amt, 31);
|
||||||
|
tcg_gen_movcond_tl(TCG_COND_EQ, dst, sh32, shift_amt,
|
||||||
|
src, tcg_constant_tl(0));
|
||||||
|
tcg_gen_shl_tl(dst, dst, sh32);
|
||||||
|
tcg_gen_sar_tl(dst_sar, dst, sh32);
|
||||||
|
tcg_gen_movcond_tl(TCG_COND_LT, satval, src, tcg_constant_tl(0), min, max);
|
||||||
|
|
||||||
|
tcg_gen_setcond_tl(TCG_COND_NE, ovf, dst_sar, src);
|
||||||
|
tcg_gen_shli_tl(ovf, ovf, reg_field_info[USR_OVF].offset);
|
||||||
|
tcg_gen_or_tl(hex_new_value[HEX_REG_USR], hex_new_value[HEX_REG_USR], ovf);
|
||||||
|
|
||||||
|
tcg_gen_movcond_tl(TCG_COND_EQ, dst, dst_sar, src, dst, satval);
|
||||||
|
|
||||||
|
tcg_temp_free(sh32);
|
||||||
|
tcg_temp_free(dst_sar);
|
||||||
|
tcg_temp_free(ovf);
|
||||||
|
tcg_temp_free(satval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_sar(TCGv dst, TCGv src, TCGv shift_amt)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Shift arithmetic right
|
||||||
|
* Robust when shift_amt is >31 bits
|
||||||
|
*/
|
||||||
|
TCGv tmp = tcg_temp_new();
|
||||||
|
tcg_gen_umin_tl(tmp, shift_amt, tcg_constant_tl(31));
|
||||||
|
tcg_gen_sar_tl(dst, src, tmp);
|
||||||
|
tcg_temp_free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bidirectional shift right with saturation */
|
||||||
|
static void gen_asr_r_r_sat(TCGv RdV, TCGv RsV, TCGv RtV)
|
||||||
|
{
|
||||||
|
TCGv shift_amt = tcg_temp_local_new();
|
||||||
|
TCGLabel *positive = gen_new_label();
|
||||||
|
TCGLabel *done = gen_new_label();
|
||||||
|
|
||||||
|
tcg_gen_sextract_i32(shift_amt, RtV, 0, 7);
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_GE, shift_amt, 0, positive);
|
||||||
|
|
||||||
|
/* Negative shift amount => shift left */
|
||||||
|
tcg_gen_neg_tl(shift_amt, shift_amt);
|
||||||
|
gen_shl_sat(RdV, RsV, shift_amt);
|
||||||
|
tcg_gen_br(done);
|
||||||
|
|
||||||
|
gen_set_label(positive);
|
||||||
|
/* Positive shift amount => shift right */
|
||||||
|
gen_sar(RdV, RsV, shift_amt);
|
||||||
|
|
||||||
|
gen_set_label(done);
|
||||||
|
|
||||||
|
tcg_temp_free(shift_amt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bidirectional shift left with saturation */
|
||||||
|
static void gen_asl_r_r_sat(TCGv RdV, TCGv RsV, TCGv RtV)
|
||||||
|
{
|
||||||
|
TCGv shift_amt = tcg_temp_local_new();
|
||||||
|
TCGLabel *positive = gen_new_label();
|
||||||
|
TCGLabel *done = gen_new_label();
|
||||||
|
|
||||||
|
tcg_gen_sextract_i32(shift_amt, RtV, 0, 7);
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_GE, shift_amt, 0, positive);
|
||||||
|
|
||||||
|
/* Negative shift amount => shift right */
|
||||||
|
tcg_gen_neg_tl(shift_amt, shift_amt);
|
||||||
|
gen_sar(RdV, RsV, shift_amt);
|
||||||
|
tcg_gen_br(done);
|
||||||
|
|
||||||
|
gen_set_label(positive);
|
||||||
|
/* Positive shift amount => shift left */
|
||||||
|
gen_shl_sat(RdV, RsV, shift_amt);
|
||||||
|
|
||||||
|
gen_set_label(done);
|
||||||
|
|
||||||
|
tcg_temp_free(shift_amt);
|
||||||
|
}
|
||||||
|
|
||||||
static intptr_t vreg_src_off(DisasContext *ctx, int num)
|
static intptr_t vreg_src_off(DisasContext *ctx, int num)
|
||||||
{
|
{
|
||||||
intptr_t offset = offsetof(CPUHexagonState, VRegs[num]);
|
intptr_t offset = offsetof(CPUHexagonState, VRegs[num]);
|
||||||
|
@ -551,13 +952,13 @@ static void gen_vreg_load(DisasContext *ctx, intptr_t dstoff, TCGv src,
|
||||||
tcg_temp_free_i64(tmp);
|
tcg_temp_free_i64(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_vreg_store(DisasContext *ctx, Insn *insn, Packet *pkt,
|
static void gen_vreg_store(DisasContext *ctx, TCGv EA, intptr_t srcoff,
|
||||||
TCGv EA, intptr_t srcoff, int slot, bool aligned)
|
int slot, bool aligned)
|
||||||
{
|
{
|
||||||
intptr_t dstoff = offsetof(CPUHexagonState, vstore[slot].data);
|
intptr_t dstoff = offsetof(CPUHexagonState, vstore[slot].data);
|
||||||
intptr_t maskoff = offsetof(CPUHexagonState, vstore[slot].mask);
|
intptr_t maskoff = offsetof(CPUHexagonState, vstore[slot].mask);
|
||||||
|
|
||||||
if (is_gather_store_insn(insn, pkt)) {
|
if (is_gather_store_insn(ctx)) {
|
||||||
TCGv sl = tcg_constant_tl(slot);
|
TCGv sl = tcg_constant_tl(slot);
|
||||||
gen_helper_gather_store(cpu_env, EA, sl);
|
gen_helper_gather_store(cpu_env, EA, sl);
|
||||||
return;
|
return;
|
||||||
|
@ -626,12 +1027,148 @@ static void vec_to_qvec(size_t size, intptr_t dstoff, intptr_t srcoff)
|
||||||
tcg_temp_free_i64(mask);
|
tcg_temp_free_i64(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void probe_noshuf_load(TCGv va, int s, int mi)
|
void probe_noshuf_load(TCGv va, int s, int mi)
|
||||||
{
|
{
|
||||||
TCGv size = tcg_constant_tl(s);
|
TCGv size = tcg_constant_tl(s);
|
||||||
TCGv mem_idx = tcg_constant_tl(mi);
|
TCGv mem_idx = tcg_constant_tl(mi);
|
||||||
gen_helper_probe_noshuf_load(cpu_env, va, size, mem_idx);
|
gen_helper_probe_noshuf_load(cpu_env, va, size, mem_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: Since this function might branch, `val` is
|
||||||
|
* required to be a `tcg_temp_local`.
|
||||||
|
*/
|
||||||
|
void gen_set_usr_field_if(int field, TCGv val)
|
||||||
|
{
|
||||||
|
/* Sets the USR field if `val` is non-zero */
|
||||||
|
if (reg_field_info[field].width == 1) {
|
||||||
|
TCGv tmp = tcg_temp_new();
|
||||||
|
tcg_gen_extract_tl(tmp, val, 0, reg_field_info[field].width);
|
||||||
|
tcg_gen_shli_tl(tmp, tmp, reg_field_info[field].offset);
|
||||||
|
tcg_gen_or_tl(hex_new_value[HEX_REG_USR],
|
||||||
|
hex_new_value[HEX_REG_USR],
|
||||||
|
tmp);
|
||||||
|
tcg_temp_free(tmp);
|
||||||
|
} else {
|
||||||
|
TCGLabel *skip_label = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_EQ, val, 0, skip_label);
|
||||||
|
gen_set_usr_field(field, val);
|
||||||
|
gen_set_label(skip_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_sat_i32(TCGv dest, TCGv source, int width)
|
||||||
|
{
|
||||||
|
TCGv max_val = tcg_constant_tl((1 << (width - 1)) - 1);
|
||||||
|
TCGv min_val = tcg_constant_tl(-(1 << (width - 1)));
|
||||||
|
tcg_gen_smin_tl(dest, source, max_val);
|
||||||
|
tcg_gen_smax_tl(dest, dest, min_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_sat_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width)
|
||||||
|
{
|
||||||
|
gen_sat_i32(dest, source, width);
|
||||||
|
tcg_gen_setcond_tl(TCG_COND_NE, ovfl, source, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_satu_i32(TCGv dest, TCGv source, int width)
|
||||||
|
{
|
||||||
|
TCGv max_val = tcg_constant_tl((1 << width) - 1);
|
||||||
|
TCGv zero = tcg_constant_tl(0);
|
||||||
|
tcg_gen_movcond_tl(TCG_COND_GTU, dest, source, max_val, max_val, source);
|
||||||
|
tcg_gen_movcond_tl(TCG_COND_LT, dest, source, zero, zero, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_satu_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width)
|
||||||
|
{
|
||||||
|
gen_satu_i32(dest, source, width);
|
||||||
|
tcg_gen_setcond_tl(TCG_COND_NE, ovfl, source, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_sat_i64(TCGv_i64 dest, TCGv_i64 source, int width)
|
||||||
|
{
|
||||||
|
TCGv_i64 max_val = tcg_constant_i64((1LL << (width - 1)) - 1LL);
|
||||||
|
TCGv_i64 min_val = tcg_constant_i64(-(1LL << (width - 1)));
|
||||||
|
tcg_gen_smin_i64(dest, source, max_val);
|
||||||
|
tcg_gen_smax_i64(dest, dest, min_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_sat_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width)
|
||||||
|
{
|
||||||
|
TCGv_i64 ovfl_64;
|
||||||
|
gen_sat_i64(dest, source, width);
|
||||||
|
ovfl_64 = tcg_temp_new_i64();
|
||||||
|
tcg_gen_setcond_i64(TCG_COND_NE, ovfl_64, dest, source);
|
||||||
|
tcg_gen_trunc_i64_tl(ovfl, ovfl_64);
|
||||||
|
tcg_temp_free_i64(ovfl_64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_satu_i64(TCGv_i64 dest, TCGv_i64 source, int width)
|
||||||
|
{
|
||||||
|
TCGv_i64 max_val = tcg_constant_i64((1LL << width) - 1LL);
|
||||||
|
TCGv_i64 zero = tcg_constant_i64(0);
|
||||||
|
tcg_gen_movcond_i64(TCG_COND_GTU, dest, source, max_val, max_val, source);
|
||||||
|
tcg_gen_movcond_i64(TCG_COND_LT, dest, source, zero, zero, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_satu_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width)
|
||||||
|
{
|
||||||
|
TCGv_i64 ovfl_64;
|
||||||
|
gen_satu_i64(dest, source, width);
|
||||||
|
ovfl_64 = tcg_temp_new_i64();
|
||||||
|
tcg_gen_setcond_i64(TCG_COND_NE, ovfl_64, dest, source);
|
||||||
|
tcg_gen_trunc_i64_tl(ovfl, ovfl_64);
|
||||||
|
tcg_temp_free_i64(ovfl_64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implements the fADDSAT64 macro in TCG */
|
||||||
|
void gen_add_sat_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
|
||||||
|
{
|
||||||
|
TCGv_i64 sum = tcg_temp_local_new_i64();
|
||||||
|
TCGv_i64 xor = tcg_temp_new_i64();
|
||||||
|
TCGv_i64 cond1 = tcg_temp_new_i64();
|
||||||
|
TCGv_i64 cond2 = tcg_temp_local_new_i64();
|
||||||
|
TCGv_i64 cond3 = tcg_temp_new_i64();
|
||||||
|
TCGv_i64 mask = tcg_constant_i64(0x8000000000000000ULL);
|
||||||
|
TCGv_i64 max_pos = tcg_constant_i64(0x7FFFFFFFFFFFFFFFLL);
|
||||||
|
TCGv_i64 max_neg = tcg_constant_i64(0x8000000000000000LL);
|
||||||
|
TCGv_i64 zero = tcg_constant_i64(0);
|
||||||
|
TCGLabel *no_ovfl_label = gen_new_label();
|
||||||
|
TCGLabel *ovfl_label = gen_new_label();
|
||||||
|
TCGLabel *ret_label = gen_new_label();
|
||||||
|
|
||||||
|
tcg_gen_add_i64(sum, a, b);
|
||||||
|
tcg_gen_xor_i64(xor, a, b);
|
||||||
|
|
||||||
|
/* if (xor & mask) */
|
||||||
|
tcg_gen_and_i64(cond1, xor, mask);
|
||||||
|
tcg_temp_free_i64(xor);
|
||||||
|
tcg_gen_brcondi_i64(TCG_COND_NE, cond1, 0, no_ovfl_label);
|
||||||
|
tcg_temp_free_i64(cond1);
|
||||||
|
|
||||||
|
/* else if ((a ^ sum) & mask) */
|
||||||
|
tcg_gen_xor_i64(cond2, a, sum);
|
||||||
|
tcg_gen_and_i64(cond2, cond2, mask);
|
||||||
|
tcg_gen_brcondi_i64(TCG_COND_NE, cond2, 0, ovfl_label);
|
||||||
|
tcg_temp_free_i64(cond2);
|
||||||
|
/* fallthrough to no_ovfl_label branch */
|
||||||
|
|
||||||
|
/* if branch */
|
||||||
|
gen_set_label(no_ovfl_label);
|
||||||
|
tcg_gen_mov_i64(ret, sum);
|
||||||
|
tcg_gen_br(ret_label);
|
||||||
|
|
||||||
|
/* else if branch */
|
||||||
|
gen_set_label(ovfl_label);
|
||||||
|
tcg_gen_and_i64(cond3, sum, mask);
|
||||||
|
tcg_temp_free_i64(mask);
|
||||||
|
tcg_temp_free_i64(sum);
|
||||||
|
tcg_gen_movcond_i64(TCG_COND_NE, ret, cond3, zero, max_pos, max_neg);
|
||||||
|
tcg_temp_free_i64(cond3);
|
||||||
|
SET_USR_FIELD(USR_OVF, 1);
|
||||||
|
|
||||||
|
gen_set_label(ret_label);
|
||||||
|
}
|
||||||
|
|
||||||
#include "tcg_funcs_generated.c.inc"
|
#include "tcg_funcs_generated.c.inc"
|
||||||
#include "tcg_func_table_generated.c.inc"
|
#include "tcg_func_table_generated.c.inc"
|
||||||
|
|
|
@ -19,7 +19,43 @@
|
||||||
#define HEXAGON_GENPTR_H
|
#define HEXAGON_GENPTR_H
|
||||||
|
|
||||||
#include "insn.h"
|
#include "insn.h"
|
||||||
|
#include "tcg/tcg.h"
|
||||||
|
#include "translate.h"
|
||||||
|
|
||||||
extern const SemanticInsn opcode_genptr[];
|
extern const SemanticInsn opcode_genptr[];
|
||||||
|
|
||||||
|
void gen_store32(TCGv vaddr, TCGv src, int width, uint32_t slot);
|
||||||
|
void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot);
|
||||||
|
void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot);
|
||||||
|
void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot);
|
||||||
|
void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, uint32_t slot);
|
||||||
|
void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot);
|
||||||
|
void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot);
|
||||||
|
void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot);
|
||||||
|
void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot);
|
||||||
|
TCGv gen_read_reg(TCGv result, int num);
|
||||||
|
TCGv gen_read_preg(TCGv pred, uint8_t num);
|
||||||
|
void gen_log_reg_write(int rnum, TCGv val);
|
||||||
|
void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val);
|
||||||
|
void gen_set_usr_field(int field, TCGv val);
|
||||||
|
void gen_set_usr_fieldi(int field, int x);
|
||||||
|
void gen_set_usr_field_if(int field, TCGv val);
|
||||||
|
void gen_sat_i32(TCGv dest, TCGv source, int width);
|
||||||
|
void gen_sat_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width);
|
||||||
|
void gen_satu_i32(TCGv dest, TCGv source, int width);
|
||||||
|
void gen_satu_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width);
|
||||||
|
void gen_sat_i64(TCGv_i64 dest, TCGv_i64 source, int width);
|
||||||
|
void gen_sat_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width);
|
||||||
|
void gen_satu_i64(TCGv_i64 dest, TCGv_i64 source, int width);
|
||||||
|
void gen_satu_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width);
|
||||||
|
void gen_add_sat_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b);
|
||||||
|
TCGv gen_8bitsof(TCGv result, TCGv value);
|
||||||
|
void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src);
|
||||||
|
TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign);
|
||||||
|
TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign);
|
||||||
|
TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign);
|
||||||
|
void gen_set_half(int N, TCGv result, TCGv src);
|
||||||
|
void gen_set_half_i64(int N, TCGv_i64 result, TCGv src);
|
||||||
|
void probe_noshuf_load(TCGv va, int s, int mi);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,7 @@ macros = {} # macro -> macro information...
|
||||||
attribinfo = {} # Register information and misc
|
attribinfo = {} # Register information and misc
|
||||||
tags = [] # list of all tags
|
tags = [] # list of all tags
|
||||||
overrides = {} # tags with helper overrides
|
overrides = {} # tags with helper overrides
|
||||||
|
idef_parser_enabled = {} # tags enabled for idef-parser
|
||||||
|
|
||||||
# We should do this as a hash for performance,
|
# We should do this as a hash for performance,
|
||||||
# but to keep order let's keep it as a list.
|
# but to keep order let's keep it as a list.
|
||||||
|
@ -66,6 +67,19 @@ def add_qemu_macro_attrib(name, attrib):
|
||||||
macros[name].attribs.add(attrib)
|
macros[name].attribs.add(attrib)
|
||||||
|
|
||||||
immextre = re.compile(r'f(MUST_)?IMMEXT[(]([UuSsRr])')
|
immextre = re.compile(r'f(MUST_)?IMMEXT[(]([UuSsRr])')
|
||||||
|
|
||||||
|
def is_cond_jump(tag):
|
||||||
|
if tag == 'J2_rte':
|
||||||
|
return False
|
||||||
|
if ('A_HWLOOP0_END' in attribdict[tag] or
|
||||||
|
'A_HWLOOP1_END' in attribdict[tag]):
|
||||||
|
return False
|
||||||
|
return \
|
||||||
|
re.compile(r"(if.*fBRANCH)|(if.*fJUMPR)").search(semdict[tag]) != None
|
||||||
|
|
||||||
|
def is_cond_call(tag):
|
||||||
|
return re.compile(r"(if.*fCALL)").search(semdict[tag]) != None
|
||||||
|
|
||||||
def calculate_attribs():
|
def calculate_attribs():
|
||||||
add_qemu_macro_attrib('fREAD_PC', 'A_IMPLICIT_READS_PC')
|
add_qemu_macro_attrib('fREAD_PC', 'A_IMPLICIT_READS_PC')
|
||||||
add_qemu_macro_attrib('fTRAP', 'A_IMPLICIT_READS_PC')
|
add_qemu_macro_attrib('fTRAP', 'A_IMPLICIT_READS_PC')
|
||||||
|
@ -96,6 +110,11 @@ def calculate_attribs():
|
||||||
for regtype, regid, toss, numregs in regs:
|
for regtype, regid, toss, numregs in regs:
|
||||||
if regtype == "P" and is_written(regid):
|
if regtype == "P" and is_written(regid):
|
||||||
attribdict[tag].add('A_WRITES_PRED_REG')
|
attribdict[tag].add('A_WRITES_PRED_REG')
|
||||||
|
# Mark conditional jumps and calls
|
||||||
|
# Not all instructions are properly marked with A_CONDEXEC
|
||||||
|
for tag in tags:
|
||||||
|
if is_cond_jump(tag) or is_cond_call(tag):
|
||||||
|
attribdict[tag].add('A_CONDEXEC')
|
||||||
|
|
||||||
def SEMANTICS(tag, beh, sem):
|
def SEMANTICS(tag, beh, sem):
|
||||||
#print tag,beh,sem
|
#print tag,beh,sem
|
||||||
|
@ -194,7 +213,8 @@ def is_new_val(regtype, regid, tag):
|
||||||
return regtype+regid+'N' in semdict[tag]
|
return regtype+regid+'N' in semdict[tag]
|
||||||
|
|
||||||
def need_slot(tag):
|
def need_slot(tag):
|
||||||
if ('A_CONDEXEC' in attribdict[tag] or
|
if (('A_CONDEXEC' in attribdict[tag] and
|
||||||
|
'A_JUMP' not in attribdict[tag]) or
|
||||||
'A_STORE' in attribdict[tag] or
|
'A_STORE' in attribdict[tag] or
|
||||||
'A_LOAD' in attribdict[tag]):
|
'A_LOAD' in attribdict[tag]):
|
||||||
return 1
|
return 1
|
||||||
|
@ -207,6 +227,15 @@ def need_part1(tag):
|
||||||
def need_ea(tag):
|
def need_ea(tag):
|
||||||
return re.compile(r"\bEA\b").search(semdict[tag])
|
return re.compile(r"\bEA\b").search(semdict[tag])
|
||||||
|
|
||||||
|
def need_PC(tag):
|
||||||
|
return 'A_IMPLICIT_READS_PC' in attribdict[tag]
|
||||||
|
|
||||||
|
def helper_needs_next_PC(tag):
|
||||||
|
return 'A_CALL' in attribdict[tag]
|
||||||
|
|
||||||
|
def need_pkt_has_multi_cof(tag):
|
||||||
|
return 'A_COF' in attribdict[tag]
|
||||||
|
|
||||||
def skip_qemu_helper(tag):
|
def skip_qemu_helper(tag):
|
||||||
return tag in overrides.keys()
|
return tag in overrides.keys()
|
||||||
|
|
||||||
|
@ -217,6 +246,9 @@ def is_tmp_result(tag):
|
||||||
def is_new_result(tag):
|
def is_new_result(tag):
|
||||||
return ('A_CVI_NEW' in attribdict[tag])
|
return ('A_CVI_NEW' in attribdict[tag])
|
||||||
|
|
||||||
|
def is_idef_parser_enabled(tag):
|
||||||
|
return tag in idef_parser_enabled
|
||||||
|
|
||||||
def imm_name(immlett):
|
def imm_name(immlett):
|
||||||
return "%siV" % immlett
|
return "%siV" % immlett
|
||||||
|
|
||||||
|
@ -248,3 +280,9 @@ def read_overrides_file(name):
|
||||||
continue
|
continue
|
||||||
tag = overridere.findall(line)[0]
|
tag = overridere.findall(line)[0]
|
||||||
overrides[tag] = True
|
overrides[tag] = True
|
||||||
|
|
||||||
|
def read_idef_parser_enabled_file(name):
|
||||||
|
global idef_parser_enabled
|
||||||
|
with open(name, "r") as idef_parser_enabled_file:
|
||||||
|
lines = idef_parser_enabled_file.read().strip().split("\n")
|
||||||
|
idef_parser_enabled = set(lines)
|
||||||
|
|
|
@ -0,0 +1,722 @@
|
||||||
|
Hexagon ISA instruction definitions to tinycode generator compiler
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
idef-parser is a small compiler able to translate the Hexagon ISA description
|
||||||
|
language into tinycode generator code, that can be easily integrated into QEMU.
|
||||||
|
|
||||||
|
Compilation Example
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
To better understand the scope of the idef-parser, we'll explore an applicative
|
||||||
|
example. Let's start by one of the simplest Hexagon instruction: the ``add``.
|
||||||
|
|
||||||
|
The ISA description language represents the ``add`` instruction as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
A2_add(RdV, in RsV, in RtV) {
|
||||||
|
{ RdV=RsV+RtV;}
|
||||||
|
}
|
||||||
|
|
||||||
|
idef-parser will compile the above code into the following code:
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
/* A2_add */
|
||||||
|
void emit_A2_add(DisasContext *ctx, Insn *insn, Packet *pkt, TCGv_i32 RdV,
|
||||||
|
TCGv_i32 RsV, TCGv_i32 RtV)
|
||||||
|
/* { RdV=RsV+RtV;} */
|
||||||
|
{
|
||||||
|
TCGv_i32 tmp_0 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_add_i32(tmp_0, RsV, RtV);
|
||||||
|
tcg_gen_mov_i32(RdV, tmp_0);
|
||||||
|
tcg_temp_free_i32(tmp_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
The output of the compilation process will be a function, containing the
|
||||||
|
tinycode generator code, implementing the correct semantics. That function will
|
||||||
|
not access any global variable, because all the accessed data structures will be
|
||||||
|
passed explicitly as function parameters. Among the passed parameters we will
|
||||||
|
have TCGv (tinycode variables) representing the input and output registers of
|
||||||
|
the architecture, integers representing the immediates that come from the code,
|
||||||
|
and other data structures which hold information about the disassemblation
|
||||||
|
context (``DisasContext`` struct).
|
||||||
|
|
||||||
|
Let's begin by describing the input code. The ``add`` instruction is associated
|
||||||
|
with a unique identifier, in this case ``A2_add``, which allows to distinguish
|
||||||
|
variants of the same instruction, and expresses the class to which the
|
||||||
|
instruction belongs, in this case ``A2`` corresponds to the Hexagon
|
||||||
|
``ALU32/ALU`` instruction subclass.
|
||||||
|
|
||||||
|
After the instruction identifier, we have a series of parameters that represents
|
||||||
|
TCG variables that will be passed to the generated function. Parameters marked
|
||||||
|
with ``in`` are already initialized, while the others are output parameters.
|
||||||
|
|
||||||
|
We will leverage this information to infer several information:
|
||||||
|
|
||||||
|
- Fill in the output function signature with the correct TCGv registers
|
||||||
|
- Fill in the output function signature with the immediate integers
|
||||||
|
- Keep track of which registers, among the declared one, have been
|
||||||
|
initialized
|
||||||
|
|
||||||
|
Let's now observe the actual instruction description code, in this case:
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
{ RdV=RsV+RtV;}
|
||||||
|
|
||||||
|
This code is composed by a subset of the C syntax, and is the result of the
|
||||||
|
application of some macro definitions contained in the ``macros.h`` file.
|
||||||
|
|
||||||
|
This file is used to reduce the complexity of the input language where complex
|
||||||
|
variants of similar constructs can be mapped to a unique primitive, so that the
|
||||||
|
idef-parser has to handle a lower number of computation primitives.
|
||||||
|
|
||||||
|
As you may notice, the description code modifies the registers which have been
|
||||||
|
declared by the declaration statements. In this case all the three registers
|
||||||
|
will be declared, ``RsV`` and ``RtV`` will also be read and ``RdV`` will be
|
||||||
|
written.
|
||||||
|
|
||||||
|
Now let's have a quick look at the generated code, line by line.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
TCGv_i32 tmp_0 = tcg_temp_new_i32();
|
||||||
|
|
||||||
|
This code starts by declaring a temporary TCGv to hold the result from the sum
|
||||||
|
operation.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
tcg_gen_add_i32(tmp_0, RsV, RtV);
|
||||||
|
|
||||||
|
Then, we are generating the sum tinycode operator between the selected
|
||||||
|
registers, storing the result in the just declared temporary.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
tcg_gen_mov_i32(RdV, tmp_0);
|
||||||
|
|
||||||
|
The result of the addition is now stored in the temporary, we move it into the
|
||||||
|
correct destination register. This code may seem inefficient, but QEMU will
|
||||||
|
perform some optimizations on the tinycode, reducing the unnecessary copy.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
tcg_temp_free_i32(tmp_0);
|
||||||
|
|
||||||
|
Finally, we free the temporary we used to hold the addition result.
|
||||||
|
|
||||||
|
Parser Input
|
||||||
|
------------
|
||||||
|
|
||||||
|
Before moving on to the structure of idef-parser itself, let us spend some words
|
||||||
|
on its' input. There are two preprocessing steps applied to the generated
|
||||||
|
instruction semantics in ``semantics_generated.pyinc`` that we need to consider.
|
||||||
|
Firstly,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gen_idef_parser_funcs.py
|
||||||
|
|
||||||
|
which takes instruction semantics in ``semantics_generated.pyinc`` to C-like
|
||||||
|
pseudo code, output into ``idef_parser_input.h.inc``. For instance, the
|
||||||
|
``J2_jumpr`` instruction which jumps to an address stored in a register
|
||||||
|
argument. This is instruction is defined as
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
SEMANTICS( \
|
||||||
|
"J2_jumpr", \
|
||||||
|
"jumpr Rs32", \
|
||||||
|
"""{fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}""" \
|
||||||
|
)
|
||||||
|
|
||||||
|
in ``semantics_generated.pyinc``. Running ``gen_idef_parser_funcs.py``
|
||||||
|
we obtain the pseudo code
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
J2_jumpr(in RsV) {
|
||||||
|
{fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}
|
||||||
|
}
|
||||||
|
|
||||||
|
with macros such as ``fJUMPR`` intact.
|
||||||
|
|
||||||
|
The second step is to expand macros into a form suitable for our parser.
|
||||||
|
These macros are defined in ``idef-parser/macros.inc`` and the step is
|
||||||
|
carried out by the ``prepare`` script which runs the C preprocessor on
|
||||||
|
``idef_parser_input.h.inc`` to produce
|
||||||
|
``idef_parser_input.preprocessed.h.inc``.
|
||||||
|
|
||||||
|
To finish the above example, after preprocessing ``J2_jumpr`` we obtain
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
J2_jumpr(in RsV) {
|
||||||
|
{(PC = RsV);}
|
||||||
|
}
|
||||||
|
|
||||||
|
where ``fJUMPR(RsN,RsV,COF_TYPE_JUMPR);`` was expanded to ``(PC = RsV)``,
|
||||||
|
signifying a write to the Program Counter ``PC``. Note, that ``PC`` in
|
||||||
|
this expression is not a variable in the strict C sense since it is not
|
||||||
|
declared anywhere, but rather a symbol which is easy to match in
|
||||||
|
idef-parser later on.
|
||||||
|
|
||||||
|
Parser Structure
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The idef-parser is built using the ``flex`` and ``bison``.
|
||||||
|
|
||||||
|
``flex`` is used to split the input string into tokens, each described using a
|
||||||
|
regular expression. The token description is contained in the
|
||||||
|
``idef-parser.lex`` source file. The flex-generated scanner takes care also to
|
||||||
|
extract from the input text other meaningful information, e.g., the numerical
|
||||||
|
value in case of an immediate constant, and decorates the token with the
|
||||||
|
extracted information.
|
||||||
|
|
||||||
|
``bison`` is used to generate the actual parser, starting from the parsing
|
||||||
|
description contained in the ``idef-parser.y`` file. The generated parser
|
||||||
|
executes the ``main`` function at the end of the ``idef-parser.y`` file, which
|
||||||
|
opens input and output files, creates the parsing context, and eventually calls
|
||||||
|
the ``yyparse()`` function, which starts the execution of the LALR(1) parser
|
||||||
|
(see `Wikipedia <https://en.wikipedia.org/wiki/LALR_parser>`__ for more
|
||||||
|
information about LALR parsing techniques). The LALR(1) parser, whenever it has
|
||||||
|
to shift a token, calls the ``yylex()`` function, which is defined by the
|
||||||
|
flex-generated code, and reads the input file returning the next scanned token.
|
||||||
|
|
||||||
|
The tokens are mapped on the source language grammar, defined in the
|
||||||
|
``idef-parser.y`` file to build a unique syntactic tree, according to the
|
||||||
|
specified operator precedences and associativity rules.
|
||||||
|
|
||||||
|
The grammar describes the whole file which contains the Hexagon instruction
|
||||||
|
descriptions, therefore it starts from the ``input`` nonterminal, which is a
|
||||||
|
list of instructions, each instruction is represented by the following grammar
|
||||||
|
rule, representing the structure of the input file shown above:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
instruction : INAME arguments code
|
||||||
|
| error
|
||||||
|
|
||||||
|
arguments : '(' ')'
|
||||||
|
| '(' argument_list ')';
|
||||||
|
|
||||||
|
argument_list : argument_decl ',' argument_list
|
||||||
|
| argument_decl
|
||||||
|
|
||||||
|
argument_decl : REG
|
||||||
|
| PRED
|
||||||
|
| IN REG
|
||||||
|
| IN PRED
|
||||||
|
| IMM
|
||||||
|
| var
|
||||||
|
;
|
||||||
|
|
||||||
|
code : '{' statements '}'
|
||||||
|
|
||||||
|
statements : statements statement
|
||||||
|
| statement
|
||||||
|
|
||||||
|
statement : control_statement
|
||||||
|
| var_decl ';'
|
||||||
|
| rvalue ';'
|
||||||
|
| code_block
|
||||||
|
| ';'
|
||||||
|
|
||||||
|
code_block : '{' statements '}'
|
||||||
|
| '{' '}'
|
||||||
|
|
||||||
|
With this initial portion of the grammar we are defining the instruction, its'
|
||||||
|
arguments, and its' statements. Each argument is defined by the
|
||||||
|
``argument_decl`` rule, and can be either
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Description Example
|
||||||
|
----------------------------------------
|
||||||
|
output register RsV
|
||||||
|
output predicate register P0
|
||||||
|
input register in RsV
|
||||||
|
input predicate register in P0
|
||||||
|
immediate value 1234
|
||||||
|
local variable EA
|
||||||
|
|
||||||
|
Note, the only local variable allowed to be used as an argument is the effective
|
||||||
|
address ``EA``. Similarly, each statement can be a ``control_statement``, a
|
||||||
|
variable declaration such as ``int a;``, a code block, which is just a
|
||||||
|
bracket-enclosed list of statements, a ``';'``, which is a ``nop`` instruction,
|
||||||
|
and an ``rvalue ';'``.
|
||||||
|
|
||||||
|
Expressions
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
Allowed in the input code are C language expressions with a few exceptions
|
||||||
|
to simplify parsing. For instance, variable names such as ``RdV``, ``RssV``,
|
||||||
|
``PdV``, ``CsV``, and other idiomatic register names from Hexagon, are
|
||||||
|
reserved specifically for register arguments. These arguments then map to
|
||||||
|
``TCGv_i32`` or ``TCGv_i64`` depending on the register size. Similarly, ``UiV``,
|
||||||
|
``riV``, etc. refer to immediate arguments and will map to C integers.
|
||||||
|
|
||||||
|
Also, as mentioned earlier, the names ``PC``, ``SP``, ``FP``, etc. are used to
|
||||||
|
refer to Hexagon registers such as the program counter, stack pointer, and frame
|
||||||
|
pointer seen here. Writes to these registers then correspond to assignments
|
||||||
|
``PC = ...``, and reads correspond to uses of the variable ``PC``.
|
||||||
|
|
||||||
|
Moreover, another example of one such exception is the selective expansion of
|
||||||
|
macros present in ``macros.h``. As an example, consider the ``fABS`` macro which
|
||||||
|
in plain C is defined as
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
#define fABS(A) (((A) < 0) ? (-(A)) : (A))
|
||||||
|
|
||||||
|
and returns the absolute value of the argument ``A``. This macro is not included
|
||||||
|
in ``idef-parser/macros.inc`` and as such is not expanded and kept as a "call"
|
||||||
|
``fABS(...)``. Reason being, that ``fABS`` is easier to match and map to
|
||||||
|
``tcg_gen_abs_<width>``, compared to the full ternary expression above. Loads of
|
||||||
|
macros in ``macros.h`` are kept unexpanded to aid in parsing, as seen in the
|
||||||
|
example above, for more information see ``idef-parser/idef-parser.lex``.
|
||||||
|
|
||||||
|
Finally, in mapping these input expressions to tinycode generators, idef-parser
|
||||||
|
tries to perform as much as possible in plain C. Such as, performing binary
|
||||||
|
operations in C instead of tinycode generators, thus effectively constant
|
||||||
|
folding the expression.
|
||||||
|
|
||||||
|
Variables and Variable Declarations
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Similarly to C, variables in the input code must be explicitly declared, such as
|
||||||
|
``int var1;`` which declares an uninitialized variable ``var1``. Initialization
|
||||||
|
``int var2 = 0;`` is also allowed and behaves as expected. In tinycode
|
||||||
|
generators the previous declarations are mapped to
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
int var1; -> TCGv_i32 var1 = tcg_temp_local_new_i32();
|
||||||
|
|
||||||
|
int var2 = 0; -> TCGv_i32 var1 = tcg_temp_local_new_i32();
|
||||||
|
tcg_gen_movi_i32(j, ((int64_t) 0ULL));
|
||||||
|
|
||||||
|
which are later automatically freed at the end of the function they're declared
|
||||||
|
in. Contrary to C, we only allow variables to be declared with an integer type
|
||||||
|
specified in the following table (without permutation of keywords)
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
type bit-width signedness
|
||||||
|
----------------------------------------------------------
|
||||||
|
int 32 signed
|
||||||
|
signed
|
||||||
|
signed int
|
||||||
|
|
||||||
|
unsigned 32 unsigned
|
||||||
|
unsigned int
|
||||||
|
|
||||||
|
long 64 signed
|
||||||
|
long int
|
||||||
|
signed long
|
||||||
|
signed long int
|
||||||
|
|
||||||
|
unsigned long 64 unsigned
|
||||||
|
unsigned long int
|
||||||
|
|
||||||
|
long long 64 signed
|
||||||
|
long long int
|
||||||
|
signed long long
|
||||||
|
signed long long int
|
||||||
|
|
||||||
|
unsigned long long 64 unsigned
|
||||||
|
unsigned long long int
|
||||||
|
|
||||||
|
size[1,2,4,8][s,u]_t 8-64 signed or unsigned
|
||||||
|
|
||||||
|
In idef-parser, variable names are matched by a generic ``VARID`` token,
|
||||||
|
which will feature the variable name as a decoration. For a variable declaration
|
||||||
|
idef-parser calls ``gen_varid_allocate`` with the ``VARID`` token to save the
|
||||||
|
name, size, and bit width of the newly declared variable. In addition, this
|
||||||
|
function also ensures that variables aren't declared multiple times, and prints
|
||||||
|
and error message if that is the case. Upon use of a variable, the ``VARID``
|
||||||
|
token is used to lookup the size and bit width of the variable.
|
||||||
|
|
||||||
|
Type System
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
idef-parser features a simple type system which is used to correctly implement
|
||||||
|
the signedness and bit width of the operations.
|
||||||
|
|
||||||
|
The type of each ``rvalue`` is determined by two attributes: its bit width
|
||||||
|
(``unsigned bit_width``) and its signedness (``HexSignedness signedness``).
|
||||||
|
|
||||||
|
For each operation, the type of ``rvalue``\ s influence the way in which the
|
||||||
|
operands are handled and emitted. For example a right shift between signed
|
||||||
|
operators will be an arithmetic shift, while one between unsigned operators
|
||||||
|
will be a logical shift. If one of the two operands is signed, and the other
|
||||||
|
is unsigned, the operation will be signed.
|
||||||
|
|
||||||
|
The bit width also influences the outcome of the operations, in particular while
|
||||||
|
the input languages features a fine granularity type system, with types of 8,
|
||||||
|
16, 32, 64 (and more for vectorial instructions) bits, the tinycode only
|
||||||
|
features 32 and 64 bit widths. We propagate as much as possible the fine
|
||||||
|
granularity type, until the value has to be used inside an operation between
|
||||||
|
``rvalue``\ s; in that case if one of the two operands is greater than 32 bits
|
||||||
|
we promote the whole operation to 64 bit, taking care of properly extending the
|
||||||
|
two operands. Fortunately, the most critical instructions already feature
|
||||||
|
explicit casts and zero/sign extensions which are properly propagated down to
|
||||||
|
our parser.
|
||||||
|
|
||||||
|
The combination of ``rvalue``\ s are handled through the use of the
|
||||||
|
``gen_bin_op`` and ``gen_bin_cmp`` helper functions. These two functions handle
|
||||||
|
the appropriate compile-time or run-time emission of operations to perform the
|
||||||
|
required computation.
|
||||||
|
|
||||||
|
Control Statements
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``control_statement``\ s are all the statements which modify the order of
|
||||||
|
execution of the generated code according to input parameters. They are expanded
|
||||||
|
by the following grammar rule:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
control_statement : frame_check
|
||||||
|
| cancel_statement
|
||||||
|
| if_statement
|
||||||
|
| for_statement
|
||||||
|
| fpart1_statement
|
||||||
|
|
||||||
|
``if_statement``\ s require the emission of labels and branch instructions which
|
||||||
|
effectively perform conditional jumps (``tcg_gen_brcondi``) according to the
|
||||||
|
value of an expression. Note, the tinycode generators we produce for conditional
|
||||||
|
statements do not perfectly mirror what would be expected in C, for instance we
|
||||||
|
do not reproduce short-circuiting of the ``&&`` operator, and use of the ``||``
|
||||||
|
operator is disallowed. All the predicated instructions, and in general all the
|
||||||
|
instructions where there could be alternative values assigned to an ``lvalue``,
|
||||||
|
like C-style ternary expressions:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
rvalue : rvalue QMARK rvalue COLON rvalue
|
||||||
|
|
||||||
|
are handled using the conditional move tinycode instruction
|
||||||
|
(``tcg_gen_movcond``), which avoids the additional complexity of managing labels
|
||||||
|
and jumps.
|
||||||
|
|
||||||
|
Instead, regarding the ``for`` loops, exploiting the fact that they always
|
||||||
|
iterate on immediate values, therefore their iteration ranges are always known
|
||||||
|
at compile time, we implemented those emitting plain C ``for`` loops. This is
|
||||||
|
possible because the loops will be executed in the QEMU code, leading to the
|
||||||
|
consequential unrolling of the for loop, since the tinycode generator
|
||||||
|
instructions will be executed multiple times, and the respective generated
|
||||||
|
tinycode will represent the unrolled execution of the loop.
|
||||||
|
|
||||||
|
Parsing Context
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
All the helper functions in ``idef-parser.y`` carry two fixed parameters, which
|
||||||
|
are the parsing context ``c`` and the ``YYLLOC`` location information. The
|
||||||
|
context is explicitly passed to all the functions because the parser we generate
|
||||||
|
is a reentrant one, meaning that it does not have any global variable, and
|
||||||
|
therefore the instruction compilation could easily be parallelized in the
|
||||||
|
future. Finally for each rule we propagate information about the location of the
|
||||||
|
involved tokens to generate pretty error reporting, able to highlight the
|
||||||
|
portion of the input code which generated each error.
|
||||||
|
|
||||||
|
Debugging
|
||||||
|
---------
|
||||||
|
|
||||||
|
Developing the idef-parser can lead to two types of errors: compile-time errors
|
||||||
|
and parsing errors.
|
||||||
|
|
||||||
|
Compile-time errors in Bison-generated parsers are usually due to conflicts in
|
||||||
|
the described grammar. Conflicts forbid the grammar to produce a unique
|
||||||
|
derivation tree, thus must be solved (except for the dangling else problem,
|
||||||
|
which is marked as expected through the ``%expect 1`` Bison option).
|
||||||
|
|
||||||
|
For solving conflicts you need a basic understanding of `shift-reduce conflicts
|
||||||
|
<https://www.gnu.org/software/Bison/manual/html_node/Shift_002fReduce.html>`__
|
||||||
|
and `reduce-reduce conflicts
|
||||||
|
<https://www.gnu.org/software/Bison/manual/html_node/Reduce_002fReduce.html>`__,
|
||||||
|
then, if you are using a Bison version > 3.7.1 you can ask Bison to generate
|
||||||
|
some counterexamples which highlight ambiguous derivations, passing the
|
||||||
|
``-Wcex`` option to Bison. In general shift/reduce conflicts are solved by
|
||||||
|
redesigning the grammar in an unambiguous way or by setting the token priority
|
||||||
|
correctly, while reduce/reduce conflicts are solved by redesigning the
|
||||||
|
interested part of the grammar.
|
||||||
|
|
||||||
|
Run-time errors can be divided between lexing and parsing errors, lexing errors
|
||||||
|
are hard to detect, since the ``var`` token will catch everything which is not
|
||||||
|
catched by other tokens, but easy to fix, because most of the time a simple
|
||||||
|
regex editing will be enough.
|
||||||
|
|
||||||
|
idef-parser features a fancy parsing error reporting scheme, which for each
|
||||||
|
parsing error reports the fragment of the input text which was involved in the
|
||||||
|
parsing rule that generated an error.
|
||||||
|
|
||||||
|
Implementing an instruction goes through several sequential steps, here are some
|
||||||
|
suggestions to make each instruction proceed to the next step.
|
||||||
|
|
||||||
|
- not-emitted
|
||||||
|
|
||||||
|
Means that the parsing of the input code relative to that instruction failed,
|
||||||
|
this could be due to a lexical error or to some mismatch between the order of
|
||||||
|
valid tokens and a parser rule. You should check that tokens are correctly
|
||||||
|
identified and mapped, and that there is a rule matching the token sequence
|
||||||
|
that you need to parse.
|
||||||
|
|
||||||
|
- emitted
|
||||||
|
|
||||||
|
This instruction class contains all the instructions which are emitted but
|
||||||
|
fail to compile when included in QEMU. The compilation errors are shown by
|
||||||
|
the QEMU building process and will lead to fixing the bug. Most common
|
||||||
|
errors regard the mismatch of parameters for tinycode generator functions,
|
||||||
|
which boil down to errors in the idef-parser type system.
|
||||||
|
|
||||||
|
- compiled
|
||||||
|
|
||||||
|
These instruction generate valid tinycode generator code, which however fail
|
||||||
|
the QEMU or the harness tests, these cases must be handled manually by
|
||||||
|
looking into the failing tests and looking at the generated tinycode
|
||||||
|
generator instruction and at the generated tinycode itself. Tip: handle the
|
||||||
|
failing harness tests first, because they usually feature only a single
|
||||||
|
instruction, thus will require less execution trace navigation. If a
|
||||||
|
multi-threaded test fail, fixing all the other tests will be the easier
|
||||||
|
option, hoping that the multi-threaded one will be indirectly fixed.
|
||||||
|
|
||||||
|
An example of debugging this type of failure is provided in the following
|
||||||
|
section.
|
||||||
|
|
||||||
|
- tests-passed
|
||||||
|
|
||||||
|
This is the final goal for each instruction, meaning that the instruction
|
||||||
|
passes the test suite.
|
||||||
|
|
||||||
|
Another approach to fix QEMU system test, where many instructions might fail, is
|
||||||
|
to compare the execution trace of your implementation with the reference
|
||||||
|
implementations already present in QEMU. To do so you should obtain a QEMU build
|
||||||
|
where the instruction pass the test, and run it with the following command:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo unshare -p sudo -u <USER> bash -c \
|
||||||
|
'env -i <qemu-hexagon full path> -d cpu <TEST>'
|
||||||
|
|
||||||
|
And do the same for your implementation, the generated execution traces will be
|
||||||
|
inherently aligned and can be inspected for behavioral differences using the
|
||||||
|
``diff`` tool.
|
||||||
|
|
||||||
|
Example of debugging erroneous tinycode generator code
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The goal of this section is to provide a complete example of debugging
|
||||||
|
incorrectly emitted tinycode generator for a single instruction.
|
||||||
|
|
||||||
|
Let's first introduce a bug in the tinycode generator of the ``A2_add``
|
||||||
|
instruction,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
void emit_A2_add(DisasContext *ctx, Insn *insn, Packet *pkt, TCGv_i32 RdV,
|
||||||
|
TCGv_i32 RsV, TCGv_i32 RtV)
|
||||||
|
/* RdV=RsV+RtV;} */
|
||||||
|
{
|
||||||
|
TCGv_i32 tmp_0 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_add_i32(tmp_0, RsV, RsV);
|
||||||
|
tcg_gen_mov_i32(RdV, tmp_0);
|
||||||
|
tcg_temp_free_i32(tmp_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Here the bug, albeit hard to spot, is in ``tcg_gen_add_i32(tmp_0, RsV, RsV);``
|
||||||
|
where we compute ``RsV + RsV`` instead of ``RsV + RtV``, as would be expected.
|
||||||
|
This particular bug is a bit tricky to pinpoint when debugging, since the
|
||||||
|
``A2_add`` instruction is so ubiquitous. As a result, pretty much all tests will
|
||||||
|
fail and therefore not provide a lot of information about the bug.
|
||||||
|
|
||||||
|
For example, let's run the ``check-tcg`` tests
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
make check-tcg TIMEOUT=1200 \
|
||||||
|
DOCKER_IMAGE=debian-hexagon-cross \
|
||||||
|
ENGINE=podman V=1 \
|
||||||
|
DOCKER_CROSS_CC_GUEST=hexagon-unknown-linux-musl-clang
|
||||||
|
|
||||||
|
In the output, we find a failure in the very first test case ``float_convs``
|
||||||
|
due to a segmentation fault. Similarly, all harness and libc tests will fail as
|
||||||
|
well. At this point we have no clue where the actual bug lies, and need to start
|
||||||
|
ruling out instructions. As such a good starting point is to utilize the debug
|
||||||
|
options ``-d in_asm,cpu`` of QEMU to inspect the Hexagon instructions being run,
|
||||||
|
alongside the CPU state. We additionally need a working version of the emulator
|
||||||
|
to compare our buggy CPU state against, running
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
meson configure -Dhexagon_idef_parser_enabled=false
|
||||||
|
|
||||||
|
will disable the idef-parser for all instructions and fallback on manual
|
||||||
|
tinycode generator overrides, or on helper function implementations. Recompiling
|
||||||
|
gives us ``qemu-hexagon`` which passes all tests. If ``qemu-heaxgon-buggy`` is
|
||||||
|
our binary with the incorrect tinycode generators, we can compare the CPU state
|
||||||
|
between the two versions
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
./qemu-hexagon-buggy -d in_asm,cpu float_convs &> out_buggy
|
||||||
|
./qemu-hexagon -d in_asm,cpu float_convs &> out_working
|
||||||
|
|
||||||
|
Looking at ``diff -u out_buggy out_working`` shows us that the CPU state begins
|
||||||
|
to diverge on line 141, with an incorrect value in the ``R1`` register
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
@@ -138,7 +138,7 @@
|
||||||
|
|
||||||
|
General Purpose Registers = {
|
||||||
|
r0 = 0x4100f9c0
|
||||||
|
- r1 = 0x00042108
|
||||||
|
+ r1 = 0x00000000
|
||||||
|
r2 = 0x00021084
|
||||||
|
r3 = 0x00000000
|
||||||
|
r4 = 0x00000000
|
||||||
|
|
||||||
|
If we also look into ``out_buggy`` directly we can inspect the input assembly
|
||||||
|
which the caused the incorrect CPU state, around line 141 we find
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
116 | ----------------
|
||||||
|
117 | IN: _start_c
|
||||||
|
118 | 0x000210b0: 0xa09dc002 { allocframe(R29,#0x10):raw }
|
||||||
|
... | ...
|
||||||
|
137 | 0x000210fc: 0x5a00c4aa { call PC+2388 }
|
||||||
|
138 |
|
||||||
|
139 | General Purpose Registers = {
|
||||||
|
140 | r0 = 0x4100fa70
|
||||||
|
141 | r1 = 0x00042108
|
||||||
|
142 | r2 = 0x00021084
|
||||||
|
143 | r3 = 0x00000000
|
||||||
|
|
||||||
|
Importantly, we see some Hexagon assembly followed by a dump of the CPU state,
|
||||||
|
now the CPU state is actually dumped before the input assembly above is ran.
|
||||||
|
As such, we are actually interested in the instructions ran before this.
|
||||||
|
|
||||||
|
Scrolling up a bit, we find
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
54 | ----------------
|
||||||
|
55 | IN: _start
|
||||||
|
56 | 0x00021088: 0x6a09c002 { R2 = C9/pc }
|
||||||
|
57 | 0x0002108c: 0xbfe2ff82 { R2 = add(R2,#0xfffffffc) }
|
||||||
|
58 | 0x00021090: 0x9182c001 { R1 = memw(R2+#0x0) }
|
||||||
|
59 | 0x00021094: 0xf302c101 { R1 = add(R2,R1) }
|
||||||
|
60 | 0x00021098: 0x7800c01e { R30 = #0x0 }
|
||||||
|
61 | 0x0002109c: 0x707dc000 { R0 = R29 }
|
||||||
|
62 | 0x000210a0: 0x763dfe1d { R29 = and(R29,#0xfffffff0) }
|
||||||
|
63 | 0x000210a4: 0xa79dfdfe { memw(R29+#0xfffffff8) = R29 }
|
||||||
|
64 | 0x000210a8: 0xbffdff1d { R29 = add(R29,#0xfffffff8) }
|
||||||
|
65 | 0x000210ac: 0x5a00c002 { call PC+4 }
|
||||||
|
66 |
|
||||||
|
67 | General Purpose Registers = {
|
||||||
|
68 | r0 = 0x00000000
|
||||||
|
69 | r1 = 0x00000000
|
||||||
|
70 | r2 = 0x00000000
|
||||||
|
71 | r3 = 0x00000000
|
||||||
|
|
||||||
|
Remember, the instructions on lines 56-65 are ran on the CPU state shown below
|
||||||
|
instructions, and as the CPU state has not diverged at this point, we know the
|
||||||
|
starting state is accurate. The bug must then lie within the instructions shown
|
||||||
|
here. Next we may notice that ``R1`` is only touched by lines 57 and 58, that is
|
||||||
|
by
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
58 | 0x00021090: 0x9182c001 { R1 = memw(R2+#0x0) }
|
||||||
|
59 | 0x00021094: 0xf302c101 { R1 = add(R2,R1) }
|
||||||
|
|
||||||
|
Therefore, we are either dealing with an correct load instruction
|
||||||
|
``R1 = memw(R2+#0x0)`` or with an incorrect add ``R1 = add(R2,R1)``. At this
|
||||||
|
point it might be easy enough to go directly to the emitted code for the
|
||||||
|
instructions mentioned and look for bugs, but we could also run
|
||||||
|
``./qemu-heaxgon -d op,in_asm float_conv`` where we find for the following
|
||||||
|
tinycode for the Hexagon ``add`` instruction
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
---- 00021094
|
||||||
|
mov_i32 pkt_has_store_s1,$0x0
|
||||||
|
add_i32 tmp0,r2,r2
|
||||||
|
mov_i32 loc2,tmp0
|
||||||
|
mov_i32 new_r1,loc2
|
||||||
|
mov_i32 r1,new_r1
|
||||||
|
|
||||||
|
Here we have finally located our bug ``add_i32 tmp0,r2,r2``.
|
||||||
|
|
||||||
|
Limitations and Future Development
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The main limitation of the current parser is given by the syntax-driven nature
|
||||||
|
of the Bison-generated parsers. This has the severe implication of only being
|
||||||
|
able to generate code in the order of evaluation of the various rules, without,
|
||||||
|
in any case, being able to backtrack and alter the generated code.
|
||||||
|
|
||||||
|
An example limitation is highlighted by this statement of the input language:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{ (PsV==0xff) ? (PdV=0xff) : (PdV=0x00); }
|
||||||
|
|
||||||
|
This ternary assignment, when written in this form requires us to emit some
|
||||||
|
proper control flow statements, which emit a jump to the first or to the second
|
||||||
|
code block, whose implementation is extremely convoluted, because when matching
|
||||||
|
the ternary assignment, the code evaluating the two assignments will be already
|
||||||
|
generated.
|
||||||
|
|
||||||
|
Instead we pre-process that statement, making it become:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{ PdV = ((PsV==0xff)) ? 0xff : 0x00; }
|
||||||
|
|
||||||
|
Which can be easily matched by the following parser rules:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
statement | rvalue ';'
|
||||||
|
|
||||||
|
rvalue : rvalue QMARK rvalue COLON rvalue
|
||||||
|
| rvalue EQ rvalue
|
||||||
|
| LPAR rvalue RPAR
|
||||||
|
| assign_statement
|
||||||
|
| IMM
|
||||||
|
|
||||||
|
assign_statement : pred ASSIGN rvalue
|
||||||
|
|
||||||
|
Another example that highlight the limitation of the flex/bison parser can be
|
||||||
|
found even in the add operation we already saw:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
TCGv_i32 tmp_0 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_add_i32(tmp_0, RsV, RtV);
|
||||||
|
tcg_gen_mov_i32(RdV, tmp_0);
|
||||||
|
|
||||||
|
The fact that we cannot directly use ``RdV`` as the destination of the sum is a
|
||||||
|
consequence of the syntax-driven nature of the parser. In fact when we parse the
|
||||||
|
assignment, the ``rvalue`` token, representing the sum has already been reduced,
|
||||||
|
and thus its code emitted and unchangeable. We rely on the fact that QEMU will
|
||||||
|
optimize our code reducing the useless move operations and the relative
|
||||||
|
temporaries.
|
||||||
|
|
||||||
|
A possible improvement of the parser regards the support for vectorial
|
||||||
|
instructions and floating point instructions, which will require the extension
|
||||||
|
of the scanner, the parser, and a partial re-design of the type system, allowing
|
||||||
|
to build the vectorial semantics over the available vectorial tinycode generator
|
||||||
|
primitives.
|
||||||
|
|
||||||
|
A more radical improvement will use the parser, not to generate directly the
|
||||||
|
tinycode generator code, but to generate an intermediate representation like the
|
||||||
|
LLVM IR, which in turn could be compiled using the clang TCG backend. That code
|
||||||
|
could be furtherly optimized, overcoming the limitations of the syntax-driven
|
||||||
|
parsing and could lead to a more optimized generated code.
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IDEF_PARSER_H
|
||||||
|
#define IDEF_PARSER_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#define TCGV_NAME_SIZE 7
|
||||||
|
#define MAX_WRITTEN_REGS 32
|
||||||
|
#define OFFSET_STR_LEN 32
|
||||||
|
#define ALLOC_LIST_LEN 32
|
||||||
|
#define ALLOC_NAME_SIZE 32
|
||||||
|
#define INIT_LIST_LEN 32
|
||||||
|
#define OUT_BUF_LEN (1024 * 1024)
|
||||||
|
#define SIGNATURE_BUF_LEN (128 * 1024)
|
||||||
|
#define HEADER_BUF_LEN (128 * 1024)
|
||||||
|
|
||||||
|
/* Variadic macros to wrap the buffer printing functions */
|
||||||
|
#define EMIT(c, ...) \
|
||||||
|
do { \
|
||||||
|
g_string_append_printf((c)->out_str, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EMIT_SIG(c, ...) \
|
||||||
|
do { \
|
||||||
|
g_string_append_printf((c)->signature_str, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EMIT_HEAD(c, ...) \
|
||||||
|
do { \
|
||||||
|
g_string_append_printf((c)->header_str, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of register, assigned to the HexReg.type field
|
||||||
|
*/
|
||||||
|
typedef enum { GENERAL_PURPOSE, CONTROL, MODIFIER, DOTNEW } HexRegType;
|
||||||
|
|
||||||
|
typedef enum { UNKNOWN_SIGNEDNESS, SIGNED, UNSIGNED } HexSignedness;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the REG tokens, identifying registers
|
||||||
|
*/
|
||||||
|
typedef struct HexReg {
|
||||||
|
uint8_t id; /**< Identifier of the register */
|
||||||
|
HexRegType type; /**< Type of the register */
|
||||||
|
unsigned bit_width; /**< Bit width of the reg, 32 or 64 bits */
|
||||||
|
} HexReg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure, identifying a TCGv temporary value
|
||||||
|
*/
|
||||||
|
typedef struct HexTmp {
|
||||||
|
unsigned index; /**< Index of the TCGv temporary value */
|
||||||
|
} HexTmp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of the possible immediated, an immediate is a value which is known
|
||||||
|
* at tinycode generation time, e.g. an integer value, not a TCGv
|
||||||
|
*/
|
||||||
|
enum ImmUnionTag {
|
||||||
|
I,
|
||||||
|
VARIABLE,
|
||||||
|
VALUE,
|
||||||
|
QEMU_TMP,
|
||||||
|
IMM_PC,
|
||||||
|
IMM_NPC,
|
||||||
|
IMM_CONSTEXT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the IMM token, identifying an immediate constant
|
||||||
|
*/
|
||||||
|
typedef struct HexImm {
|
||||||
|
union {
|
||||||
|
char id; /**< Identifier, used when type is VARIABLE */
|
||||||
|
uint64_t value; /**< Immediate value, used when type is VALUE */
|
||||||
|
uint64_t index; /**< Index, used when type is QEMU_TMP */
|
||||||
|
};
|
||||||
|
enum ImmUnionTag type; /**< Type of the immediate */
|
||||||
|
} HexImm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the PRED token, identifying a predicate
|
||||||
|
*/
|
||||||
|
typedef struct HexPred {
|
||||||
|
char id; /**< Identifier of the predicate */
|
||||||
|
} HexPred;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the SAT token, identifying the saturate operator
|
||||||
|
* Note: All saturates are assumed to implicitly set overflow.
|
||||||
|
*/
|
||||||
|
typedef struct HexSat {
|
||||||
|
HexSignedness signedness; /**< Signedness of the sat. op. */
|
||||||
|
} HexSat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the CAST token, identifying the cast operator
|
||||||
|
*/
|
||||||
|
typedef struct HexCast {
|
||||||
|
unsigned bit_width; /**< Bit width of the cast operator */
|
||||||
|
HexSignedness signedness; /**< Unsigned flag for the cast operator */
|
||||||
|
} HexCast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the EXTRACT token, identifying the cast operator
|
||||||
|
*/
|
||||||
|
typedef struct HexExtract {
|
||||||
|
unsigned bit_width; /**< Bit width of the extract operator */
|
||||||
|
unsigned storage_bit_width; /**< Actual bit width of the extract operator */
|
||||||
|
HexSignedness signedness; /**< Unsigned flag for the extract operator */
|
||||||
|
} HexExtract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the MPY token, identifying the fMPY multiplication
|
||||||
|
* operator
|
||||||
|
*/
|
||||||
|
typedef struct HexMpy {
|
||||||
|
unsigned first_bit_width; /**< Bit width of 1st operand of fMPY */
|
||||||
|
unsigned second_bit_width; /**< Bit width of 2nd operand of fMPY */
|
||||||
|
HexSignedness first_signedness; /**< Signedness of 1st operand of fMPY */
|
||||||
|
HexSignedness second_signedness; /**< Signedness of 2nd operand of fMPY */
|
||||||
|
} HexMpy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the VARID token, identifying declared variables
|
||||||
|
* of the input language
|
||||||
|
*/
|
||||||
|
typedef struct HexVar {
|
||||||
|
GString *name; /**< Name of the VARID variable */
|
||||||
|
} HexVar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure uniquely identifying a declared VARID variable, used for
|
||||||
|
* keeping track of declared variable, so that any variable is declared only
|
||||||
|
* once, and its properties are propagated through all the subsequent instances
|
||||||
|
* of that variable
|
||||||
|
*/
|
||||||
|
typedef struct Var {
|
||||||
|
GString *name; /**< Name of the VARID variable */
|
||||||
|
uint8_t bit_width; /**< Bit width of the VARID variable */
|
||||||
|
HexSignedness signedness; /**< Unsigned flag for the VARID var */
|
||||||
|
} Var;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of the possible rvalue types, used in the HexValue.type field
|
||||||
|
*/
|
||||||
|
typedef enum RvalueUnionTag {
|
||||||
|
REGISTER, REGISTER_ARG, TEMP, IMMEDIATE, PREDICATE, VARID
|
||||||
|
} RvalueUnionTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semantic record of the rvalue token, identifying any numeric value,
|
||||||
|
* immediate or register based. The rvalue tokens are combined together
|
||||||
|
* through the use of several operators, to encode expressions
|
||||||
|
*/
|
||||||
|
typedef struct HexValue {
|
||||||
|
union {
|
||||||
|
HexReg reg; /**< rvalue of register type */
|
||||||
|
HexTmp tmp; /**< rvalue of temporary type */
|
||||||
|
HexImm imm; /**< rvalue of immediate type */
|
||||||
|
HexPred pred; /**< rvalue of predicate type */
|
||||||
|
HexVar var; /**< rvalue of declared variable type */
|
||||||
|
};
|
||||||
|
RvalueUnionTag type; /**< Type of the rvalue */
|
||||||
|
unsigned bit_width; /**< Bit width of the rvalue */
|
||||||
|
HexSignedness signedness; /**< Unsigned flag for the rvalue */
|
||||||
|
bool is_dotnew; /**< rvalue of predicate type is dotnew? */
|
||||||
|
bool is_manual; /**< Opt out of automatic freeing of params */
|
||||||
|
} HexValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of ternary operator
|
||||||
|
*/
|
||||||
|
typedef enum TernaryState { IN_LEFT, IN_RIGHT } TernaryState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure used to handle side effects inside ternary operators
|
||||||
|
*/
|
||||||
|
typedef struct Ternary {
|
||||||
|
TernaryState state;
|
||||||
|
HexValue cond;
|
||||||
|
} Ternary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator type, used for referencing the correct operator when calling the
|
||||||
|
* gen_bin_op() function, which in turn will generate the correct code to
|
||||||
|
* execute the operation between the two rvalues
|
||||||
|
*/
|
||||||
|
typedef enum OpType {
|
||||||
|
ADD_OP, SUB_OP, MUL_OP, ASL_OP, ASR_OP, LSR_OP, ANDB_OP, ORB_OP,
|
||||||
|
XORB_OP, ANDL_OP, MINI_OP, MAXI_OP
|
||||||
|
} OpType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure including instruction specific information, to be cleared
|
||||||
|
* out after the compilation of each instruction
|
||||||
|
*/
|
||||||
|
typedef struct Inst {
|
||||||
|
GString *name; /**< Name of the compiled instruction */
|
||||||
|
char *code_begin; /**< Beginning of instruction input code */
|
||||||
|
char *code_end; /**< End of instruction input code */
|
||||||
|
unsigned tmp_count; /**< Index of the last declared TCGv temp */
|
||||||
|
unsigned qemu_tmp_count; /**< Index of the last declared int temp */
|
||||||
|
unsigned if_count; /**< Index of the last declared if label */
|
||||||
|
unsigned error_count; /**< Number of generated errors */
|
||||||
|
GArray *allocated; /**< Allocated declaredVARID vars */
|
||||||
|
GArray *init_list; /**< List of initialized registers */
|
||||||
|
GArray *strings; /**< Strings allocated by the instruction */
|
||||||
|
} Inst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure representing the whole translation context, which in a
|
||||||
|
* reentrant flex/bison parser just like ours is passed between the scanner
|
||||||
|
* and the parser, holding all the necessary information to perform the
|
||||||
|
* parsing, this data structure survives between the compilation of different
|
||||||
|
* instructions
|
||||||
|
*/
|
||||||
|
typedef struct Context {
|
||||||
|
void *scanner; /**< Reentrant parser state pointer */
|
||||||
|
char *input_buffer; /**< Buffer containing the input code */
|
||||||
|
GString *out_str; /**< String containing the output code */
|
||||||
|
GString *signature_str; /**< String containing the signatures code */
|
||||||
|
GString *header_str; /**< String containing the header code */
|
||||||
|
FILE *defines_file; /**< FILE * of the generated header */
|
||||||
|
FILE *output_file; /**< FILE * of the C output file */
|
||||||
|
FILE *enabled_file; /**< FILE * of the list of enabled inst */
|
||||||
|
GArray *ternary; /**< Array to track nesting of ternary ops */
|
||||||
|
unsigned total_insn; /**< Number of instructions in input file */
|
||||||
|
unsigned implemented_insn; /**< Instruction compiled without errors */
|
||||||
|
Inst inst; /**< Parsing data of the current inst */
|
||||||
|
} Context;
|
||||||
|
|
||||||
|
#endif /* IDEF_PARSER_H */
|
|
@ -0,0 +1,471 @@
|
||||||
|
%option noyywrap noinput nounput
|
||||||
|
%option 8bit reentrant bison-bridge
|
||||||
|
%option warn nodefault
|
||||||
|
%option bison-locations
|
||||||
|
|
||||||
|
%{
|
||||||
|
/*
|
||||||
|
* Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "hex_regs.h"
|
||||||
|
|
||||||
|
#include "idef-parser.h"
|
||||||
|
#include "idef-parser.tab.h"
|
||||||
|
|
||||||
|
/* Keep track of scanner position for error message printout */
|
||||||
|
#define YY_USER_ACTION yylloc->first_column = yylloc->last_column; \
|
||||||
|
for (int i = 0; yytext[i] != '\0'; i++) { \
|
||||||
|
yylloc->last_column++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Error Counter */
|
||||||
|
int error_count;
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
/* Definitions */
|
||||||
|
DIGIT [0-9]
|
||||||
|
LOWER_ID [a-z]
|
||||||
|
UPPER_ID [A-Z]
|
||||||
|
ID LOWER_ID|UPPER_ID
|
||||||
|
INST_NAME [A-Z]+[0-9]_([A-Za-z]|[0-9]|_)+
|
||||||
|
HEX_DIGIT [0-9a-fA-F]
|
||||||
|
REG_ID_32 e|s|d|t|u|v|x|y
|
||||||
|
REG_ID_64 ee|ss|dd|tt|uu|vv|xx|yy
|
||||||
|
SYS_ID_32 s|d
|
||||||
|
SYS_ID_64 ss|dd
|
||||||
|
PRED_ID d|s|t|u|v|e|x|x
|
||||||
|
IMM_ID r|s|S|u|U
|
||||||
|
VAR_ID [a-zA-Z_][a-zA-Z0-9_]*
|
||||||
|
SIGN_ID s|u
|
||||||
|
STRING_LIT \"(\\.|[^"\\])*\"
|
||||||
|
|
||||||
|
/* Tokens */
|
||||||
|
%%
|
||||||
|
|
||||||
|
[ \t\f\v]+ { /* Ignore whitespaces. */ }
|
||||||
|
[\n\r]+ { /* Ignore newlines. */ }
|
||||||
|
^#.*$ { /* Ignore linemarkers. */ }
|
||||||
|
|
||||||
|
{INST_NAME} { yylval->string = g_string_new(yytext);
|
||||||
|
return INAME; }
|
||||||
|
"fFLOAT" |
|
||||||
|
"fUNFLOAT" |
|
||||||
|
"fDOUBLE" |
|
||||||
|
"fUNDOUBLE" |
|
||||||
|
"0.0" |
|
||||||
|
"0x1.0p52" |
|
||||||
|
"0x1.0p-52" { return FAIL; }
|
||||||
|
"in" { return IN; }
|
||||||
|
"R"{REG_ID_32}"V" {
|
||||||
|
yylval->rvalue.type = REGISTER_ARG;
|
||||||
|
yylval->rvalue.reg.type = GENERAL_PURPOSE;
|
||||||
|
yylval->rvalue.reg.id = yytext[1];
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return REG; }
|
||||||
|
"R"{REG_ID_64}"V" {
|
||||||
|
yylval->rvalue.type = REGISTER_ARG;
|
||||||
|
yylval->rvalue.reg.type = GENERAL_PURPOSE;
|
||||||
|
yylval->rvalue.reg.id = yytext[1];
|
||||||
|
yylval->rvalue.reg.bit_width = 64;
|
||||||
|
yylval->rvalue.bit_width = 64;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return REG; }
|
||||||
|
"MuV" {
|
||||||
|
yylval->rvalue.type = REGISTER_ARG;
|
||||||
|
yylval->rvalue.reg.type = MODIFIER;
|
||||||
|
yylval->rvalue.reg.id = 'u';
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return REG; }
|
||||||
|
"C"{REG_ID_32}"V" {
|
||||||
|
yylval->rvalue.type = REGISTER_ARG;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = yytext[1];
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return REG; }
|
||||||
|
"C"{REG_ID_64}"V" {
|
||||||
|
yylval->rvalue.type = REGISTER_ARG;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = yytext[1];
|
||||||
|
yylval->rvalue.reg.bit_width = 64;
|
||||||
|
yylval->rvalue.bit_width = 64;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return REG; }
|
||||||
|
{IMM_ID}"iV" {
|
||||||
|
yylval->rvalue.type = IMMEDIATE;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
yylval->rvalue.imm.type = VARIABLE;
|
||||||
|
yylval->rvalue.imm.id = yytext[0];
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
return IMM; }
|
||||||
|
"P"{PRED_ID}"V" {
|
||||||
|
yylval->rvalue.type = PREDICATE;
|
||||||
|
yylval->rvalue.pred.id = yytext[1];
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return PRED; }
|
||||||
|
"P"{PRED_ID}"N" {
|
||||||
|
yylval->rvalue.type = PREDICATE;
|
||||||
|
yylval->rvalue.pred.id = yytext[1];
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = true;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
return PRED; }
|
||||||
|
"IV1DEAD()" |
|
||||||
|
"fPAUSE(uiV);" { return ';'; }
|
||||||
|
"+=" { return INC; }
|
||||||
|
"-=" { return DEC; }
|
||||||
|
"++" { return PLUSPLUS; }
|
||||||
|
"&=" { return ANDA; }
|
||||||
|
"|=" { return ORA; }
|
||||||
|
"^=" { return XORA; }
|
||||||
|
"<<" { return ASL; }
|
||||||
|
">>" { return ASR; }
|
||||||
|
">>>" { return LSR; }
|
||||||
|
"==" { return EQ; }
|
||||||
|
"!=" { return NEQ; }
|
||||||
|
"<=" { return LTE; }
|
||||||
|
">=" { return GTE; }
|
||||||
|
"&&" { return ANDL; }
|
||||||
|
"else" { return ELSE; }
|
||||||
|
"for" { return FOR; }
|
||||||
|
"fREAD_IREG" { return ICIRC; }
|
||||||
|
"fPART1" { return PART1; }
|
||||||
|
"if" { return IF; }
|
||||||
|
"fFRAME_SCRAMBLE" { return FSCR; }
|
||||||
|
"fFRAME_UNSCRAMBLE" { return FSCR; }
|
||||||
|
"fFRAMECHECK" { return FCHK; }
|
||||||
|
"Constant_extended" { return CONSTEXT; }
|
||||||
|
"fCL1_"{DIGIT} { return LOCNT; }
|
||||||
|
"fbrev" { return BREV; }
|
||||||
|
"fSXTN" { return SXT; }
|
||||||
|
"fZXTN" { return ZXT; }
|
||||||
|
"fDF_MAX" |
|
||||||
|
"fSF_MAX" |
|
||||||
|
"fMAX" { return MAX; }
|
||||||
|
"fDF_MIN" |
|
||||||
|
"fSF_MIN" |
|
||||||
|
"fMIN" { return MIN; }
|
||||||
|
"fABS" { return ABS; }
|
||||||
|
"fRNDN" { return ROUND; }
|
||||||
|
"fCRND" { return CROUND; }
|
||||||
|
"fCRNDN" { return CROUND; }
|
||||||
|
"fPM_CIRI" { return CIRCADD; }
|
||||||
|
"fPM_CIRR" { return CIRCADD; }
|
||||||
|
"fCOUNTONES_"{DIGIT} { return COUNTONES; }
|
||||||
|
"fSATN" { yylval->sat.signedness = SIGNED;
|
||||||
|
return SAT; }
|
||||||
|
"fSATUN" { yylval->sat.signedness = UNSIGNED;
|
||||||
|
return SAT; }
|
||||||
|
"fCONSTLL" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fSE32_64" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fCAST4_4u" { yylval->cast.bit_width = 32;
|
||||||
|
yylval->cast.signedness = UNSIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fCAST4_8s" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fCAST4_8u" { return CAST4_8U; }
|
||||||
|
"fCAST4u" { yylval->cast.bit_width = 32;
|
||||||
|
yylval->cast.signedness = UNSIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fNEWREG" |
|
||||||
|
"fCAST4_4s" |
|
||||||
|
"fCAST4s" { yylval->cast.bit_width = 32;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fCAST8_8u" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = UNSIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fCAST8u" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = UNSIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fCAST8_8s" |
|
||||||
|
"fCAST8s" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fGETBIT" { yylval->extract.bit_width = 1;
|
||||||
|
yylval->extract.storage_bit_width = 1;
|
||||||
|
yylval->extract.signedness = UNSIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fGETBYTE" { yylval->extract.bit_width = 8;
|
||||||
|
yylval->extract.storage_bit_width = 8;
|
||||||
|
yylval->extract.signedness = SIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fGETUBYTE" { yylval->extract.bit_width = 8;
|
||||||
|
yylval->extract.storage_bit_width = 8;
|
||||||
|
yylval->extract.signedness = UNSIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fGETHALF" { yylval->extract.bit_width = 16;
|
||||||
|
yylval->extract.storage_bit_width = 16;
|
||||||
|
yylval->extract.signedness = SIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fGETUHALF" { yylval->extract.bit_width = 16;
|
||||||
|
yylval->extract.storage_bit_width = 16;
|
||||||
|
yylval->extract.signedness = UNSIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fGETWORD" { yylval->extract.bit_width = 32;
|
||||||
|
yylval->extract.storage_bit_width = 64;
|
||||||
|
yylval->extract.signedness = SIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fGETUWORD" { yylval->extract.bit_width = 32;
|
||||||
|
yylval->extract.storage_bit_width = 64;
|
||||||
|
yylval->extract.signedness = UNSIGNED;
|
||||||
|
return EXTRACT; }
|
||||||
|
"fEXTRACTU_RANGE" { return EXTRANGE; }
|
||||||
|
"fSETBIT" { yylval->cast.bit_width = 1;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return DEPOSIT; }
|
||||||
|
"fSETBYTE" { yylval->cast.bit_width = 8;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return DEPOSIT; }
|
||||||
|
"fSETHALF" { yylval->cast.bit_width = 16;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return SETHALF; }
|
||||||
|
"fSETWORD" { yylval->cast.bit_width = 32;
|
||||||
|
yylval->cast.signedness = SIGNED;
|
||||||
|
return DEPOSIT; }
|
||||||
|
"fINSERT_BITS" { return INSBITS; }
|
||||||
|
"fSETBITS" { return SETBITS; }
|
||||||
|
"fMPY16UU" { yylval->mpy.first_bit_width = 16;
|
||||||
|
yylval->mpy.second_bit_width = 16;
|
||||||
|
yylval->mpy.first_signedness = UNSIGNED;
|
||||||
|
yylval->mpy.second_signedness = UNSIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fMPY16SU" { yylval->mpy.first_bit_width = 16;
|
||||||
|
yylval->mpy.second_bit_width = 16;
|
||||||
|
yylval->mpy.first_signedness = SIGNED;
|
||||||
|
yylval->mpy.second_signedness = UNSIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fMPY16SS" { yylval->mpy.first_bit_width = 16;
|
||||||
|
yylval->mpy.second_bit_width = 16;
|
||||||
|
yylval->mpy.first_signedness = SIGNED;
|
||||||
|
yylval->mpy.second_signedness = SIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fMPY32UU" { yylval->mpy.first_bit_width = 32;
|
||||||
|
yylval->mpy.second_bit_width = 32;
|
||||||
|
yylval->mpy.first_signedness = UNSIGNED;
|
||||||
|
yylval->mpy.second_signedness = UNSIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fMPY32SU" { yylval->mpy.first_bit_width = 32;
|
||||||
|
yylval->mpy.second_bit_width = 32;
|
||||||
|
yylval->mpy.first_signedness = SIGNED;
|
||||||
|
yylval->mpy.second_signedness = UNSIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fSFMPY" |
|
||||||
|
"fMPY32SS" { yylval->mpy.first_bit_width = 32;
|
||||||
|
yylval->mpy.second_bit_width = 32;
|
||||||
|
yylval->mpy.first_signedness = SIGNED;
|
||||||
|
yylval->mpy.second_signedness = SIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fMPY3216SS" { yylval->mpy.first_bit_width = 32;
|
||||||
|
yylval->mpy.second_bit_width = 16;
|
||||||
|
yylval->mpy.first_signedness = SIGNED;
|
||||||
|
yylval->mpy.second_signedness = SIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fMPY3216SU" { yylval->mpy.first_bit_width = 32;
|
||||||
|
yylval->mpy.second_bit_width = 16;
|
||||||
|
yylval->mpy.first_signedness = SIGNED;
|
||||||
|
yylval->mpy.second_signedness = UNSIGNED;
|
||||||
|
return MPY; }
|
||||||
|
"fNEWREG_ST" |
|
||||||
|
"fIMMEXT" |
|
||||||
|
"fMUST_IMMEXT" |
|
||||||
|
"fPASS" |
|
||||||
|
"fECHO" { return IDENTITY; }
|
||||||
|
"(size8u_t)" { yylval->cast.bit_width = 64;
|
||||||
|
yylval->cast.signedness = UNSIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"(unsigned int)" { yylval->cast.bit_width = 32;
|
||||||
|
yylval->cast.signedness = UNSIGNED;
|
||||||
|
return CAST; }
|
||||||
|
"fREAD_PC()" |
|
||||||
|
"PC" { return PC; }
|
||||||
|
"fREAD_NPC()" |
|
||||||
|
"NPC" { return NPC; }
|
||||||
|
"fGET_LPCFG" |
|
||||||
|
"USR.LPCFG" { return LPCFG; }
|
||||||
|
"LOAD_CANCEL(EA)" { return LOAD_CANCEL; }
|
||||||
|
"STORE_CANCEL(EA)" |
|
||||||
|
"CANCEL" { return CANCEL; }
|
||||||
|
"N"{LOWER_ID}"N" { yylval->rvalue.type = REGISTER_ARG;
|
||||||
|
yylval->rvalue.reg.type = DOTNEW;
|
||||||
|
yylval->rvalue.reg.id = yytext[1];
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_SP()" |
|
||||||
|
"SP" { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = GENERAL_PURPOSE;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_SP;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_FP()" |
|
||||||
|
"FP" { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = GENERAL_PURPOSE;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_FP;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_LR()" |
|
||||||
|
"LR" { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = GENERAL_PURPOSE;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_LR;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_GP()" |
|
||||||
|
"GP" { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_GP;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_LC"[01] { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_LC0
|
||||||
|
+ (yytext[8] - '0') * 2;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"LC"[01] { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_LC0
|
||||||
|
+ (yytext[2] - '0') * 2;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_SA"[01] { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_SA0
|
||||||
|
+ (yytext[8] - '0') * 2;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"SA"[01] { yylval->rvalue.type = REGISTER;
|
||||||
|
yylval->rvalue.reg.type = CONTROL;
|
||||||
|
yylval->rvalue.reg.id = HEX_REG_SA0
|
||||||
|
+ (yytext[2] - '0') * 2;
|
||||||
|
yylval->rvalue.reg.bit_width = 32;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
return REG; }
|
||||||
|
"fREAD_P0()" { yylval->rvalue.type = PREDICATE;
|
||||||
|
yylval->rvalue.pred.id = '0';
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
return PRED; }
|
||||||
|
[pP]{DIGIT} { yylval->rvalue.type = PREDICATE;
|
||||||
|
yylval->rvalue.pred.id = yytext[1];
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = false;
|
||||||
|
return PRED; }
|
||||||
|
[pP]{DIGIT}[nN] { yylval->rvalue.type = PREDICATE;
|
||||||
|
yylval->rvalue.pred.id = yytext[1];
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.is_dotnew = true;
|
||||||
|
return PRED; }
|
||||||
|
"fLSBNEW" { return LSBNEW; }
|
||||||
|
"N" { yylval->rvalue.type = IMMEDIATE;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.imm.type = VARIABLE;
|
||||||
|
yylval->rvalue.imm.id = 'N';
|
||||||
|
return IMM; }
|
||||||
|
"i" { yylval->rvalue.type = IMMEDIATE;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
yylval->rvalue.imm.type = I;
|
||||||
|
return IMM; }
|
||||||
|
{SIGN_ID} { if (yytext[0] == 'u') {
|
||||||
|
yylval->signedness = UNSIGNED;
|
||||||
|
} else {
|
||||||
|
yylval->signedness = SIGNED;
|
||||||
|
}
|
||||||
|
return SIGN;
|
||||||
|
}
|
||||||
|
"0x"{HEX_DIGIT}+ |
|
||||||
|
{DIGIT}+ { yylval->rvalue.type = IMMEDIATE;
|
||||||
|
yylval->rvalue.bit_width = 32;
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
yylval->rvalue.imm.type = VALUE;
|
||||||
|
yylval->rvalue.imm.value = strtoull(yytext, NULL, 0);
|
||||||
|
return IMM; }
|
||||||
|
"0x"{HEX_DIGIT}+"ULL" |
|
||||||
|
{DIGIT}+"ULL" { yylval->rvalue.type = IMMEDIATE;
|
||||||
|
yylval->rvalue.bit_width = 64;
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
yylval->rvalue.imm.type = VALUE;
|
||||||
|
yylval->rvalue.imm.value = strtoull(yytext, NULL, 0);
|
||||||
|
return IMM; }
|
||||||
|
"fLOAD" { return LOAD; }
|
||||||
|
"fSTORE" { return STORE; }
|
||||||
|
"fROTL" { return ROTL; }
|
||||||
|
"fCARRY_FROM_ADD" { return CARRY_FROM_ADD; }
|
||||||
|
"fADDSAT64" { return ADDSAT64; }
|
||||||
|
"size"[1248][us]"_t" { /* Handles "size_t" variants of int types */
|
||||||
|
const unsigned int bits_per_byte = 8;
|
||||||
|
const unsigned int bytes = yytext[4] - '0';
|
||||||
|
yylval->rvalue.bit_width = bits_per_byte * bytes;
|
||||||
|
if (yytext[5] == 'u') {
|
||||||
|
yylval->rvalue.signedness = UNSIGNED;
|
||||||
|
} else {
|
||||||
|
yylval->rvalue.signedness = SIGNED;
|
||||||
|
}
|
||||||
|
return TYPE_SIZE_T; }
|
||||||
|
"unsigned" { return TYPE_UNSIGNED; }
|
||||||
|
"long" { return TYPE_LONG; }
|
||||||
|
"int" { return TYPE_INT; }
|
||||||
|
"const" { /* Emit no token */ }
|
||||||
|
{VAR_ID} { /* Variable name, we adopt the C names convention */
|
||||||
|
yylval->rvalue.type = VARID;
|
||||||
|
yylval->rvalue.var.name = g_string_new(yytext);
|
||||||
|
/* Default to an unknown signedness and 0 width. */
|
||||||
|
yylval->rvalue.bit_width = 0;
|
||||||
|
yylval->rvalue.signedness = UNKNOWN_SIGNEDNESS;
|
||||||
|
return VAR; }
|
||||||
|
"fatal("{STRING_LIT}")" { /* Emit no token */ }
|
||||||
|
"fHINTJR(RsV)" { /* Emit no token */ }
|
||||||
|
. { return yytext[0]; }
|
||||||
|
|
||||||
|
%%
|
|
@ -0,0 +1,965 @@
|
||||||
|
%{
|
||||||
|
/*
|
||||||
|
* Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "idef-parser.h"
|
||||||
|
#include "parser-helpers.h"
|
||||||
|
#include "idef-parser.tab.h"
|
||||||
|
#include "idef-parser.yy.h"
|
||||||
|
|
||||||
|
/* Uncomment this to disable yyasserts */
|
||||||
|
/* #define NDEBUG */
|
||||||
|
|
||||||
|
#define ERR_LINE_CONTEXT 40
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%lex-param {void *scanner}
|
||||||
|
%parse-param {void *scanner}
|
||||||
|
%parse-param {Context *c}
|
||||||
|
|
||||||
|
%define parse.error verbose
|
||||||
|
%define parse.lac full
|
||||||
|
%define api.pure full
|
||||||
|
|
||||||
|
%locations
|
||||||
|
|
||||||
|
%union {
|
||||||
|
GString *string;
|
||||||
|
HexValue rvalue;
|
||||||
|
HexSat sat;
|
||||||
|
HexCast cast;
|
||||||
|
HexExtract extract;
|
||||||
|
HexMpy mpy;
|
||||||
|
HexSignedness signedness;
|
||||||
|
int index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tokens */
|
||||||
|
%start input
|
||||||
|
|
||||||
|
%expect 1
|
||||||
|
|
||||||
|
%token IN INAME VAR
|
||||||
|
%token ABS CROUND ROUND CIRCADD COUNTONES INC DEC ANDA ORA XORA PLUSPLUS ASL
|
||||||
|
%token ASR LSR EQ NEQ LTE GTE MIN MAX ANDL FOR ICIRC IF MUN FSCR FCHK SXT
|
||||||
|
%token ZXT CONSTEXT LOCNT BREV SIGN LOAD STORE PC NPC LPCFG
|
||||||
|
%token LOAD_CANCEL CANCEL IDENTITY PART1 ROTL INSBITS SETBITS EXTRANGE
|
||||||
|
%token CAST4_8U FAIL CARRY_FROM_ADD ADDSAT64 LSBNEW
|
||||||
|
%token TYPE_SIZE_T TYPE_INT TYPE_SIGNED TYPE_UNSIGNED TYPE_LONG
|
||||||
|
|
||||||
|
%token <rvalue> REG IMM PRED
|
||||||
|
%token <index> ELSE
|
||||||
|
%token <mpy> MPY
|
||||||
|
%token <sat> SAT
|
||||||
|
%token <cast> CAST DEPOSIT SETHALF
|
||||||
|
%token <extract> EXTRACT
|
||||||
|
%type <string> INAME
|
||||||
|
%type <rvalue> rvalue lvalue VAR assign_statement var var_decl var_type
|
||||||
|
%type <rvalue> FAIL
|
||||||
|
%type <rvalue> TYPE_SIGNED TYPE_UNSIGNED TYPE_INT TYPE_LONG TYPE_SIZE_T
|
||||||
|
%type <index> if_stmt IF
|
||||||
|
%type <signedness> SIGN
|
||||||
|
|
||||||
|
/* Operator Precedences */
|
||||||
|
%left MIN MAX
|
||||||
|
%left '('
|
||||||
|
%left ','
|
||||||
|
%left '='
|
||||||
|
%right CIRCADD
|
||||||
|
%right INC DEC ANDA ORA XORA
|
||||||
|
%left '?' ':'
|
||||||
|
%left ANDL
|
||||||
|
%left '|'
|
||||||
|
%left '^' ANDOR
|
||||||
|
%left '&'
|
||||||
|
%left EQ NEQ
|
||||||
|
%left '<' '>' LTE GTE
|
||||||
|
%left ASL ASR LSR
|
||||||
|
%right ABS
|
||||||
|
%left '-' '+'
|
||||||
|
%left '*' '/' '%' MPY
|
||||||
|
%right '~' '!'
|
||||||
|
%left '['
|
||||||
|
%right CAST
|
||||||
|
%right LOCNT BREV
|
||||||
|
|
||||||
|
/* Bison Grammar */
|
||||||
|
%%
|
||||||
|
|
||||||
|
/* Input file containing the description of each hexagon instruction */
|
||||||
|
input : instructions
|
||||||
|
{
|
||||||
|
YYACCEPT;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
instructions : instruction instructions
|
||||||
|
| %empty
|
||||||
|
;
|
||||||
|
|
||||||
|
instruction : INAME
|
||||||
|
{
|
||||||
|
gen_inst(c, $1);
|
||||||
|
}
|
||||||
|
arguments
|
||||||
|
{
|
||||||
|
EMIT_SIG(c, ")");
|
||||||
|
EMIT_HEAD(c, "{\n");
|
||||||
|
}
|
||||||
|
code
|
||||||
|
{
|
||||||
|
gen_inst_code(c, &@1);
|
||||||
|
}
|
||||||
|
| error /* Recover gracefully after instruction compilation error */
|
||||||
|
{
|
||||||
|
free_instruction(c);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
arguments : '(' ')'
|
||||||
|
| '(' argument_list ')';
|
||||||
|
|
||||||
|
argument_list : argument_decl ',' argument_list
|
||||||
|
| argument_decl
|
||||||
|
;
|
||||||
|
|
||||||
|
var : VAR
|
||||||
|
{
|
||||||
|
track_string(c, $1.var.name);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here the integer types are defined from valid combinations of
|
||||||
|
* `signed`, `unsigned`, `int`, and `long` tokens. The `signed`
|
||||||
|
* and `unsigned` tokens are here assumed to always be placed
|
||||||
|
* first in the type declaration, which is not the case in
|
||||||
|
* normal C. Similarly, `int` is assumed to always be placed
|
||||||
|
* last in the type.
|
||||||
|
*/
|
||||||
|
type_int : TYPE_INT
|
||||||
|
| TYPE_SIGNED
|
||||||
|
| TYPE_SIGNED TYPE_INT;
|
||||||
|
type_uint : TYPE_UNSIGNED
|
||||||
|
| TYPE_UNSIGNED TYPE_INT;
|
||||||
|
type_ulonglong : TYPE_UNSIGNED TYPE_LONG TYPE_LONG
|
||||||
|
| TYPE_UNSIGNED TYPE_LONG TYPE_LONG TYPE_INT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here the various valid int types defined above specify
|
||||||
|
* their `signedness` and `bit_width`. The LP64 convention
|
||||||
|
* is assumed where longs are 64-bit, long longs are then
|
||||||
|
* assumed to also be 64-bit.
|
||||||
|
*/
|
||||||
|
var_type : TYPE_SIZE_T
|
||||||
|
{
|
||||||
|
yyassert(c, &@1, $1.bit_width <= 64,
|
||||||
|
"Variables with size > 64-bit are not supported!");
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| type_int
|
||||||
|
{
|
||||||
|
$$.signedness = SIGNED;
|
||||||
|
$$.bit_width = 32;
|
||||||
|
}
|
||||||
|
| type_uint
|
||||||
|
{
|
||||||
|
$$.signedness = UNSIGNED;
|
||||||
|
$$.bit_width = 32;
|
||||||
|
}
|
||||||
|
| type_ulonglong
|
||||||
|
{
|
||||||
|
$$.signedness = UNSIGNED;
|
||||||
|
$$.bit_width = 64;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Rule to capture declarations of VARs */
|
||||||
|
var_decl : var_type IMM
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Rule to capture "int i;" declarations since "i" is special
|
||||||
|
* and assumed to be always be IMM. Moreover, "i" is only
|
||||||
|
* assumed to be used in for-loops.
|
||||||
|
*
|
||||||
|
* Therefore we want to NOP these declarations.
|
||||||
|
*/
|
||||||
|
yyassert(c, &@2, $2.imm.type == I,
|
||||||
|
"Variable declaration with immedaties only allowed"
|
||||||
|
" for the loop induction variable \"i\"");
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
| var_type var
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Allocate new variable, this checks that it hasn't already
|
||||||
|
* been declared.
|
||||||
|
*/
|
||||||
|
gen_varid_allocate(c, &@1, &$2, $1.bit_width, $1.signedness);
|
||||||
|
/* Copy var for variable name */
|
||||||
|
$$ = $2;
|
||||||
|
/* Copy type info from var_type */
|
||||||
|
$$.signedness = $1.signedness;
|
||||||
|
$$.bit_width = $1.bit_width;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Return the modified registers list */
|
||||||
|
code : '{' statements '}'
|
||||||
|
{
|
||||||
|
c->inst.code_begin = c->input_buffer + @2.first_column - 1;
|
||||||
|
c->inst.code_end = c->input_buffer + @2.last_column - 1;
|
||||||
|
}
|
||||||
|
| '{'
|
||||||
|
{
|
||||||
|
/* Nop */
|
||||||
|
}
|
||||||
|
'}'
|
||||||
|
;
|
||||||
|
|
||||||
|
argument_decl : REG
|
||||||
|
{
|
||||||
|
emit_arg(c, &@1, &$1);
|
||||||
|
/* Enqueue register into initialization list */
|
||||||
|
g_array_append_val(c->inst.init_list, $1);
|
||||||
|
}
|
||||||
|
| PRED
|
||||||
|
{
|
||||||
|
emit_arg(c, &@1, &$1);
|
||||||
|
/* Enqueue predicate into initialization list */
|
||||||
|
g_array_append_val(c->inst.init_list, $1);
|
||||||
|
}
|
||||||
|
| IN REG
|
||||||
|
{
|
||||||
|
emit_arg(c, &@2, &$2);
|
||||||
|
}
|
||||||
|
| IN PRED
|
||||||
|
{
|
||||||
|
emit_arg(c, &@2, &$2);
|
||||||
|
}
|
||||||
|
| IMM
|
||||||
|
{
|
||||||
|
EMIT_SIG(c, ", int %ciV", $1.imm.id);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
code_block : '{' statements '}'
|
||||||
|
| '{' '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
/* A list of one or more statements */
|
||||||
|
statements : statements statement
|
||||||
|
| statement
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Statements can be assignment (rvalue ';'), control or memory statements */
|
||||||
|
statement : control_statement
|
||||||
|
| var_decl ';'
|
||||||
|
| rvalue ';'
|
||||||
|
{
|
||||||
|
gen_rvalue_free(c, &@1, &$1);
|
||||||
|
}
|
||||||
|
| code_block
|
||||||
|
| ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
assign_statement : lvalue '=' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
gen_assign(c, &@1, &$1, &$3);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| var_decl '=' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
gen_assign(c, &@1, &$1, &$3);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| lvalue INC rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
HexValue tmp = gen_bin_op(c, &@1, ADD_OP, &$1, &$3);
|
||||||
|
gen_assign(c, &@1, &$1, &tmp);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| lvalue DEC rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
HexValue tmp = gen_bin_op(c, &@1, SUB_OP, &$1, &$3);
|
||||||
|
gen_assign(c, &@1, &$1, &tmp);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| lvalue ANDA rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
HexValue tmp = gen_bin_op(c, &@1, ANDB_OP, &$1, &$3);
|
||||||
|
gen_assign(c, &@1, &$1, &tmp);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| lvalue ORA rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
HexValue tmp = gen_bin_op(c, &@1, ORB_OP, &$1, &$3);
|
||||||
|
gen_assign(c, &@1, &$1, &tmp);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| lvalue XORA rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
HexValue tmp = gen_bin_op(c, &@1, XORB_OP, &$1, &$3);
|
||||||
|
gen_assign(c, &@1, &$1, &tmp);
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| PRED '=' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
gen_pred_assign(c, &@1, &$1, &$3);
|
||||||
|
}
|
||||||
|
| IMM '=' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
yyassert(c, &@1, $3.type == IMMEDIATE,
|
||||||
|
"Cannot assign non-immediate to immediate!");
|
||||||
|
yyassert(c, &@1, $1.imm.type == VARIABLE,
|
||||||
|
"Cannot assign to non-variable!");
|
||||||
|
/* Assign to the function argument */
|
||||||
|
OUT(c, &@1, &$1, " = ", &$3, ";\n");
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| PC '=' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
$3 = gen_rvalue_truncate(c, &@1, &$3);
|
||||||
|
$3 = rvalue_materialize(c, &@1, &$3);
|
||||||
|
OUT(c, &@1, "gen_write_new_pc(", &$3, ");\n");
|
||||||
|
gen_rvalue_free(c, &@1, &$3); /* Free temporary value */
|
||||||
|
}
|
||||||
|
| LOAD '(' IMM ',' IMM ',' SIGN ',' var ',' lvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @12.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
yyassert(c, &@1, $3.imm.value == 1,
|
||||||
|
"LOAD of arrays not supported!");
|
||||||
|
gen_load(c, &@1, &$5, $7, &$9, &$11);
|
||||||
|
}
|
||||||
|
| STORE '(' IMM ',' IMM ',' var ',' rvalue ')'
|
||||||
|
/* Store primitive */
|
||||||
|
{
|
||||||
|
@1.last_column = @10.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
yyassert(c, &@1, $3.imm.value == 1,
|
||||||
|
"STORE of arrays not supported!");
|
||||||
|
gen_store(c, &@1, &$5, &$7, &$9);
|
||||||
|
}
|
||||||
|
| LPCFG '=' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
$3 = gen_rvalue_truncate(c, &@1, &$3);
|
||||||
|
$3 = rvalue_materialize(c, &@1, &$3);
|
||||||
|
OUT(c, &@1, "SET_USR_FIELD(USR_LPCFG, ", &$3, ");\n");
|
||||||
|
gen_rvalue_free(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| DEPOSIT '(' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
gen_deposit_op(c, &@1, &$5, &$7, &$3, &$1);
|
||||||
|
}
|
||||||
|
| SETHALF '(' rvalue ',' lvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
gen_sethalf(c, &@1, &$1, &$3, &$5, &$7);
|
||||||
|
}
|
||||||
|
| SETBITS '(' rvalue ',' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @10.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
gen_setbits(c, &@1, &$3, &$5, &$7, &$9);
|
||||||
|
}
|
||||||
|
| INSBITS '(' lvalue ',' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @10.last_column;
|
||||||
|
yyassert(c, &@1, !is_inside_ternary(c),
|
||||||
|
"Assignment side-effect not modeled!");
|
||||||
|
gen_rdeposit_op(c, &@1, &$3, &$9, &$7, &$5);
|
||||||
|
}
|
||||||
|
| IDENTITY '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
$$ = $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
control_statement : frame_check
|
||||||
|
| cancel_statement
|
||||||
|
| if_statement
|
||||||
|
| for_statement
|
||||||
|
| fpart1_statement
|
||||||
|
;
|
||||||
|
|
||||||
|
frame_check : FCHK '(' rvalue ',' rvalue ')' ';'
|
||||||
|
{
|
||||||
|
gen_rvalue_free(c, &@1, &$3);
|
||||||
|
gen_rvalue_free(c, &@1, &$5);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
cancel_statement : LOAD_CANCEL
|
||||||
|
{
|
||||||
|
gen_load_cancel(c, &@1);
|
||||||
|
}
|
||||||
|
| CANCEL
|
||||||
|
{
|
||||||
|
gen_cancel(c, &@1);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
if_statement : if_stmt
|
||||||
|
{
|
||||||
|
/* Fix else label */
|
||||||
|
OUT(c, &@1, "gen_set_label(if_label_", &$1, ");\n");
|
||||||
|
}
|
||||||
|
| if_stmt ELSE
|
||||||
|
{
|
||||||
|
@1.last_column = @2.last_column;
|
||||||
|
$2 = gen_if_else(c, &@1, $1);
|
||||||
|
}
|
||||||
|
statement
|
||||||
|
{
|
||||||
|
OUT(c, &@1, "gen_set_label(if_label_", &$2, ");\n");
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
for_statement : FOR '(' IMM '=' IMM ';' IMM '<' IMM ';' IMM PLUSPLUS ')'
|
||||||
|
{
|
||||||
|
yyassert(c, &@3,
|
||||||
|
$3.imm.type == I &&
|
||||||
|
$7.imm.type == I &&
|
||||||
|
$11.imm.type == I,
|
||||||
|
"Loop induction variable must be \"i\"");
|
||||||
|
@1.last_column = @13.last_column;
|
||||||
|
OUT(c, &@1, "for (int ", &$3, " = ", &$5, "; ",
|
||||||
|
&$7, " < ", &$9);
|
||||||
|
OUT(c, &@1, "; ", &$11, "++) {\n");
|
||||||
|
}
|
||||||
|
code_block
|
||||||
|
{
|
||||||
|
OUT(c, &@1, "}\n");
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
fpart1_statement : PART1
|
||||||
|
{
|
||||||
|
OUT(c, &@1, "if (insn->part1) {\n");
|
||||||
|
}
|
||||||
|
'(' statements ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
OUT(c, &@1, "return; }\n");
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
if_stmt : IF '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$1 = gen_if_cond(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
statement
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
rvalue : FAIL
|
||||||
|
{
|
||||||
|
yyassert(c, &@1, false, "Encountered a FAIL token as rvalue.\n");
|
||||||
|
}
|
||||||
|
| assign_statement
|
||||||
|
| REG
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| IMM
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| PRED
|
||||||
|
{
|
||||||
|
$$ = gen_rvalue_pred(c, &@1, &$1);
|
||||||
|
}
|
||||||
|
| PC
|
||||||
|
{
|
||||||
|
/* Read PC from the CR */
|
||||||
|
HexValue rvalue;
|
||||||
|
memset(&rvalue, 0, sizeof(HexValue));
|
||||||
|
rvalue.type = IMMEDIATE;
|
||||||
|
rvalue.imm.type = IMM_PC;
|
||||||
|
rvalue.bit_width = 32;
|
||||||
|
rvalue.signedness = UNSIGNED;
|
||||||
|
$$ = rvalue;
|
||||||
|
}
|
||||||
|
| NPC
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NPC is only read from CALLs, so we can hardcode it
|
||||||
|
* at translation time
|
||||||
|
*/
|
||||||
|
HexValue rvalue;
|
||||||
|
memset(&rvalue, 0, sizeof(HexValue));
|
||||||
|
rvalue.type = IMMEDIATE;
|
||||||
|
rvalue.imm.type = IMM_NPC;
|
||||||
|
rvalue.bit_width = 32;
|
||||||
|
rvalue.signedness = UNSIGNED;
|
||||||
|
$$ = rvalue;
|
||||||
|
}
|
||||||
|
| CONSTEXT
|
||||||
|
{
|
||||||
|
HexValue rvalue;
|
||||||
|
memset(&rvalue, 0, sizeof(HexValue));
|
||||||
|
rvalue.type = IMMEDIATE;
|
||||||
|
rvalue.imm.type = IMM_CONSTEXT;
|
||||||
|
rvalue.signedness = UNSIGNED;
|
||||||
|
rvalue.is_dotnew = false;
|
||||||
|
rvalue.is_manual = false;
|
||||||
|
$$ = rvalue;
|
||||||
|
}
|
||||||
|
| var
|
||||||
|
{
|
||||||
|
$$ = gen_rvalue_var(c, &@1, &$1);
|
||||||
|
}
|
||||||
|
| MPY '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_rvalue_mpy(c, &@1, &$1, &$3, &$5);
|
||||||
|
}
|
||||||
|
| rvalue '+' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, ADD_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue '-' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, SUB_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue '*' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, MUL_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue ASL rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, ASL_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue ASR rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
assert_signedness(c, &@1, $1.signedness);
|
||||||
|
if ($1.signedness == UNSIGNED) {
|
||||||
|
$$ = gen_bin_op(c, &@1, LSR_OP, &$1, &$3);
|
||||||
|
} else if ($1.signedness == SIGNED) {
|
||||||
|
$$ = gen_bin_op(c, &@1, ASR_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| rvalue LSR rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, LSR_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue '&' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, ANDB_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue '|' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, ORB_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue '^' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, XORB_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue ANDL rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, ANDL_OP, &$1, &$3);
|
||||||
|
}
|
||||||
|
| MIN '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, MINI_OP, &$3, &$5);
|
||||||
|
}
|
||||||
|
| MAX '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_op(c, &@1, MAXI_OP, &$3, &$5);
|
||||||
|
}
|
||||||
|
| '~' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @2.last_column;
|
||||||
|
$$ = gen_rvalue_not(c, &@1, &$2);
|
||||||
|
}
|
||||||
|
| '!' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @2.last_column;
|
||||||
|
$$ = gen_rvalue_notl(c, &@1, &$2);
|
||||||
|
}
|
||||||
|
| SAT '(' IMM ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_rvalue_sat(c, &@1, &$1, &$3, &$5);
|
||||||
|
}
|
||||||
|
| CAST rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @2.last_column;
|
||||||
|
/* Assign target signedness */
|
||||||
|
$2.signedness = $1.signedness;
|
||||||
|
$$ = gen_cast_op(c, &@1, &$2, $1.bit_width, $1.signedness);
|
||||||
|
}
|
||||||
|
| rvalue EQ rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_EQ, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue NEQ rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_NE, &$1, &$3);
|
||||||
|
}
|
||||||
|
| rvalue '<' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
|
||||||
|
assert_signedness(c, &@1, $1.signedness);
|
||||||
|
assert_signedness(c, &@1, $3.signedness);
|
||||||
|
if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_LTU, &$1, &$3);
|
||||||
|
} else {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_LT, &$1, &$3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| rvalue '>' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
|
||||||
|
assert_signedness(c, &@1, $1.signedness);
|
||||||
|
assert_signedness(c, &@1, $3.signedness);
|
||||||
|
if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_GTU, &$1, &$3);
|
||||||
|
} else {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_GT, &$1, &$3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| rvalue LTE rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
|
||||||
|
assert_signedness(c, &@1, $1.signedness);
|
||||||
|
assert_signedness(c, &@1, $3.signedness);
|
||||||
|
if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_LEU, &$1, &$3);
|
||||||
|
} else {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_LE, &$1, &$3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| rvalue GTE rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @3.last_column;
|
||||||
|
|
||||||
|
assert_signedness(c, &@1, $1.signedness);
|
||||||
|
assert_signedness(c, &@1, $3.signedness);
|
||||||
|
if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_GEU, &$1, &$3);
|
||||||
|
} else {
|
||||||
|
$$ = gen_bin_cmp(c, &@1, TCG_COND_GE, &$1, &$3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| rvalue '?'
|
||||||
|
{
|
||||||
|
$1.is_manual = true;
|
||||||
|
Ternary t = { 0 };
|
||||||
|
t.state = IN_LEFT;
|
||||||
|
t.cond = $1;
|
||||||
|
g_array_append_val(c->ternary, t);
|
||||||
|
}
|
||||||
|
rvalue ':'
|
||||||
|
{
|
||||||
|
Ternary *t = &g_array_index(c->ternary, Ternary,
|
||||||
|
c->ternary->len - 1);
|
||||||
|
t->state = IN_RIGHT;
|
||||||
|
}
|
||||||
|
rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @5.last_column;
|
||||||
|
$$ = gen_rvalue_ternary(c, &@1, &$1, &$4, &$7);
|
||||||
|
}
|
||||||
|
| FSCR '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
$$ = gen_rvalue_fscr(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| SXT '(' rvalue ',' IMM ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
yyassert(c, &@1, $5.type == IMMEDIATE &&
|
||||||
|
$5.imm.type == VALUE,
|
||||||
|
"SXT expects immediate values\n");
|
||||||
|
$$ = gen_extend_op(c, &@1, &$3, $5.imm.value, &$7, SIGNED);
|
||||||
|
}
|
||||||
|
| ZXT '(' rvalue ',' IMM ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
yyassert(c, &@1, $5.type == IMMEDIATE &&
|
||||||
|
$5.imm.type == VALUE,
|
||||||
|
"ZXT expects immediate values\n");
|
||||||
|
$$ = gen_extend_op(c, &@1, &$3, $5.imm.value, &$7, UNSIGNED);
|
||||||
|
}
|
||||||
|
| '(' rvalue ')'
|
||||||
|
{
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
| ABS rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @2.last_column;
|
||||||
|
$$ = gen_rvalue_abs(c, &@1, &$2);
|
||||||
|
}
|
||||||
|
| CROUND '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_convround_n(c, &@1, &$3, &$5);
|
||||||
|
}
|
||||||
|
| CROUND '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
$$ = gen_convround(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| ROUND '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_round(c, &@1, &$3, &$5);
|
||||||
|
}
|
||||||
|
| '-' rvalue
|
||||||
|
{
|
||||||
|
@1.last_column = @2.last_column;
|
||||||
|
$$ = gen_rvalue_neg(c, &@1, &$2);
|
||||||
|
}
|
||||||
|
| ICIRC '(' rvalue ')' ASL IMM
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_tmp(c, &@1, 32, UNSIGNED);
|
||||||
|
OUT(c, &@1, "gen_read_ireg(", &$$, ", ", &$3, ", ", &$6, ");\n");
|
||||||
|
gen_rvalue_free(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| CIRCADD '(' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
gen_circ_op(c, &@1, &$3, &$5, &$7);
|
||||||
|
}
|
||||||
|
| LOCNT '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
/* Leading ones count */
|
||||||
|
$$ = gen_locnt_op(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| COUNTONES '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
/* Ones count */
|
||||||
|
$$ = gen_ctpop_op(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| LPCFG
|
||||||
|
{
|
||||||
|
$$ = gen_tmp_value(c, &@1, "0", 32, UNSIGNED);
|
||||||
|
OUT(c, &@1, "GET_USR_FIELD(USR_LPCFG, ", &$$, ");\n");
|
||||||
|
}
|
||||||
|
| EXTRACT '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_extract_op(c, &@1, &$5, &$3, &$1);
|
||||||
|
}
|
||||||
|
| EXTRANGE '(' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
yyassert(c, &@1, $5.type == IMMEDIATE &&
|
||||||
|
$5.imm.type == VALUE &&
|
||||||
|
$7.type == IMMEDIATE &&
|
||||||
|
$7.imm.type == VALUE,
|
||||||
|
"Range extract needs immediate values!\n");
|
||||||
|
$$ = gen_rextract_op(c,
|
||||||
|
&@1,
|
||||||
|
&$3,
|
||||||
|
$7.imm.value,
|
||||||
|
$5.imm.value - $7.imm.value + 1);
|
||||||
|
}
|
||||||
|
| CAST4_8U '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
$$ = gen_rvalue_truncate(c, &@1, &$3);
|
||||||
|
$$.signedness = UNSIGNED;
|
||||||
|
$$ = rvalue_materialize(c, &@1, &$$);
|
||||||
|
$$ = gen_rvalue_extend(c, &@1, &$$);
|
||||||
|
}
|
||||||
|
| BREV '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
$$ = gen_rvalue_brev(c, &@1, &$3);
|
||||||
|
}
|
||||||
|
| ROTL '(' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @6.last_column;
|
||||||
|
$$ = gen_rotl(c, &@1, &$3, &$5);
|
||||||
|
}
|
||||||
|
| ADDSAT64 '(' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
gen_addsat64(c, &@1, &$3, &$5, &$7);
|
||||||
|
}
|
||||||
|
| CARRY_FROM_ADD '(' rvalue ',' rvalue ',' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @8.last_column;
|
||||||
|
$$ = gen_carry_from_add(c, &@1, &$3, &$5, &$7);
|
||||||
|
}
|
||||||
|
| LSBNEW '(' rvalue ')'
|
||||||
|
{
|
||||||
|
@1.last_column = @4.last_column;
|
||||||
|
HexValue one = gen_imm_value(c, &@1, 1, 32, UNSIGNED);
|
||||||
|
$$ = gen_bin_op(c, &@1, ANDB_OP, &$3, &one);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
lvalue : FAIL
|
||||||
|
{
|
||||||
|
@1.last_column = @1.last_column;
|
||||||
|
yyassert(c, &@1, false, "Encountered a FAIL token as lvalue.\n");
|
||||||
|
}
|
||||||
|
| REG
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| var
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc != 5) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Semantics: Hexagon ISA to tinycode generator compiler\n\n");
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: ./semantics IDEFS EMITTER_C EMITTER_H "
|
||||||
|
"ENABLED_INSTRUCTIONS_LIST\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARG_INDEX_ARGV0 = 0,
|
||||||
|
ARG_INDEX_IDEFS,
|
||||||
|
ARG_INDEX_EMITTER_C,
|
||||||
|
ARG_INDEX_EMITTER_H,
|
||||||
|
ARG_INDEX_ENABLED_INSTRUCTIONS_LIST
|
||||||
|
};
|
||||||
|
|
||||||
|
FILE *enabled_file = fopen(argv[ARG_INDEX_ENABLED_INSTRUCTIONS_LIST], "w");
|
||||||
|
|
||||||
|
FILE *output_file = fopen(argv[ARG_INDEX_EMITTER_C], "w");
|
||||||
|
fputs("#include \"qemu/osdep.h\"\n", output_file);
|
||||||
|
fputs("#include \"qemu/log.h\"\n", output_file);
|
||||||
|
fputs("#include \"cpu.h\"\n", output_file);
|
||||||
|
fputs("#include \"internal.h\"\n", output_file);
|
||||||
|
fputs("#include \"tcg/tcg-op.h\"\n", output_file);
|
||||||
|
fputs("#include \"insn.h\"\n", output_file);
|
||||||
|
fputs("#include \"opcodes.h\"\n", output_file);
|
||||||
|
fputs("#include \"translate.h\"\n", output_file);
|
||||||
|
fputs("#define QEMU_GENERATE\n", output_file);
|
||||||
|
fputs("#include \"genptr.h\"\n", output_file);
|
||||||
|
fputs("#include \"tcg/tcg.h\"\n", output_file);
|
||||||
|
fputs("#include \"macros.h\"\n", output_file);
|
||||||
|
fprintf(output_file, "#include \"%s\"\n", argv[ARG_INDEX_EMITTER_H]);
|
||||||
|
|
||||||
|
FILE *defines_file = fopen(argv[ARG_INDEX_EMITTER_H], "w");
|
||||||
|
assert(defines_file != NULL);
|
||||||
|
fputs("#ifndef HEX_EMITTER_H\n", defines_file);
|
||||||
|
fputs("#define HEX_EMITTER_H\n", defines_file);
|
||||||
|
fputs("\n", defines_file);
|
||||||
|
fputs("#include \"insn.h\"\n\n", defines_file);
|
||||||
|
|
||||||
|
/* Parser input file */
|
||||||
|
Context context = { 0 };
|
||||||
|
context.defines_file = defines_file;
|
||||||
|
context.output_file = output_file;
|
||||||
|
context.enabled_file = enabled_file;
|
||||||
|
/* Initialize buffers */
|
||||||
|
context.out_str = g_string_new(NULL);
|
||||||
|
context.signature_str = g_string_new(NULL);
|
||||||
|
context.header_str = g_string_new(NULL);
|
||||||
|
context.ternary = g_array_new(FALSE, TRUE, sizeof(Ternary));
|
||||||
|
/* Read input file */
|
||||||
|
FILE *input_file = fopen(argv[ARG_INDEX_IDEFS], "r");
|
||||||
|
fseek(input_file, 0L, SEEK_END);
|
||||||
|
long input_size = ftell(input_file);
|
||||||
|
context.input_buffer = (char *) calloc(input_size + 1, sizeof(char));
|
||||||
|
fseek(input_file, 0L, SEEK_SET);
|
||||||
|
size_t read_chars = fread(context.input_buffer,
|
||||||
|
sizeof(char),
|
||||||
|
input_size,
|
||||||
|
input_file);
|
||||||
|
if (read_chars != (size_t) input_size) {
|
||||||
|
fprintf(stderr, "Error: an error occurred while reading input file!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
yylex_init(&context.scanner);
|
||||||
|
YY_BUFFER_STATE buffer;
|
||||||
|
buffer = yy_scan_string(context.input_buffer, context.scanner);
|
||||||
|
/* Start the parsing procedure */
|
||||||
|
yyparse(context.scanner, &context);
|
||||||
|
if (context.implemented_insn != context.total_insn) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Warning: %d/%d meta instructions have been implemented!\n",
|
||||||
|
context.implemented_insn,
|
||||||
|
context.total_insn);
|
||||||
|
}
|
||||||
|
fputs("#endif " START_COMMENT " HEX_EMITTER_h " END_COMMENT "\n",
|
||||||
|
defines_file);
|
||||||
|
/* Cleanup */
|
||||||
|
yy_delete_buffer(buffer, context.scanner);
|
||||||
|
yylex_destroy(context.scanner);
|
||||||
|
free(context.input_buffer);
|
||||||
|
g_string_free(context.out_str, TRUE);
|
||||||
|
g_string_free(context.signature_str, TRUE);
|
||||||
|
g_string_free(context.header_str, TRUE);
|
||||||
|
g_array_free(context.ternary, TRUE);
|
||||||
|
fclose(output_file);
|
||||||
|
fclose(input_file);
|
||||||
|
fclose(defines_file);
|
||||||
|
fclose(enabled_file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copy rules */
|
||||||
|
#define fLSBOLD(VAL) (fGETBIT(0, VAL))
|
||||||
|
#define fSATH(VAL) fSATN(16, VAL)
|
||||||
|
#define fSATUH(VAL) fSATUN(16, VAL)
|
||||||
|
#define fVSATH(VAL) fVSATN(16, VAL)
|
||||||
|
#define fVSATUH(VAL) fVSATUN(16, VAL)
|
||||||
|
#define fSATUB(VAL) fSATUN(8, VAL)
|
||||||
|
#define fSATB(VAL) fSATN(8, VAL)
|
||||||
|
#define fVSATUB(VAL) fVSATUN(8, VAL)
|
||||||
|
#define fVSATB(VAL) fVSATN(8, VAL)
|
||||||
|
#define fCALL(A) fWRITE_LR(fREAD_NPC()); fWRITE_NPC(A);
|
||||||
|
#define fCALLR(A) fWRITE_LR(fREAD_NPC()); fWRITE_NPC(A);
|
||||||
|
#define fCAST2_8s(A) fSXTN(16, 64, A)
|
||||||
|
#define fCAST2_8u(A) fZXTN(16, 64, A)
|
||||||
|
#define fVSATW(A) fVSATN(32, fCAST8_8s(A))
|
||||||
|
#define fSATW(A) fSATN(32, fCAST8_8s(A))
|
||||||
|
#define fVSAT(A) fVSATN(32, A)
|
||||||
|
#define fSAT(A) fSATN(32, A)
|
||||||
|
|
||||||
|
/* Ease parsing */
|
||||||
|
#define f8BITSOF(VAL) ((VAL) ? 0xff : 0x00)
|
||||||
|
#define fREAD_GP() (Constant_extended ? (0) : GP)
|
||||||
|
#define fCLIP(DST, SRC, U) (DST = fMIN((1 << U) - 1, fMAX(SRC, -(1 << U))))
|
||||||
|
#define fBIDIR_ASHIFTL(SRC, SHAMT, REGSTYPE) \
|
||||||
|
((SHAMT > 0) ? \
|
||||||
|
(fCAST##REGSTYPE##s(SRC) << SHAMT) : \
|
||||||
|
(fCAST##REGSTYPE##s(SRC) >> -SHAMT))
|
||||||
|
|
||||||
|
#define fBIDIR_LSHIFTL(SRC, SHAMT, REGSTYPE) \
|
||||||
|
((SHAMT > 0) ? \
|
||||||
|
(fCAST##REGSTYPE##u(SRC) << SHAMT) : \
|
||||||
|
(fCAST##REGSTYPE##u(SRC) >>> -SHAMT))
|
||||||
|
|
||||||
|
#define fBIDIR_ASHIFTR(SRC, SHAMT, REGSTYPE) \
|
||||||
|
((SHAMT > 0) ? \
|
||||||
|
(fCAST##REGSTYPE##s(SRC) >> SHAMT) : \
|
||||||
|
(fCAST##REGSTYPE##s(SRC) << -SHAMT))
|
||||||
|
|
||||||
|
#define fBIDIR_SHIFTR(SRC, SHAMT, REGSTYPE) \
|
||||||
|
(((SHAMT) < 0) ? ((fCAST##REGSTYPE(SRC) << ((-(SHAMT)) - 1)) << 1) \
|
||||||
|
: (fCAST##REGSTYPE(SRC) >> (SHAMT)))
|
||||||
|
|
||||||
|
#define fBIDIR_LSHIFTR(SRC, SHAMT, REGSTYPE) \
|
||||||
|
fBIDIR_SHIFTR(SRC, SHAMT, REGSTYPE##u)
|
||||||
|
|
||||||
|
#define fSATVALN(N, VAL) \
|
||||||
|
fSET_OVERFLOW( \
|
||||||
|
((VAL) < 0) ? (-(1LL << ((N) - 1))) : ((1LL << ((N) - 1)) - 1) \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define fSAT_ORIG_SHL(A, ORIG_REG) \
|
||||||
|
(((fCAST4s((fSAT(A)) ^ (fCAST4s(ORIG_REG)))) < 0) \
|
||||||
|
? fSATVALN(32, (fCAST4s(ORIG_REG))) \
|
||||||
|
: ((((ORIG_REG) > 0) && ((A) == 0)) ? fSATVALN(32, (ORIG_REG)) \
|
||||||
|
: fSAT(A)))
|
||||||
|
|
||||||
|
#define fBIDIR_ASHIFTR_SAT(SRC, SHAMT, REGSTYPE) \
|
||||||
|
(((SHAMT) < 0) ? fSAT_ORIG_SHL((fCAST##REGSTYPE##s(SRC) \
|
||||||
|
<< ((-(SHAMT)) - 1)) << 1, (SRC)) \
|
||||||
|
: (fCAST##REGSTYPE##s(SRC) >> (SHAMT)))
|
||||||
|
|
||||||
|
#define fBIDIR_ASHIFTL_SAT(SRC, SHAMT, REGSTYPE) \
|
||||||
|
(((SHAMT) < 0) \
|
||||||
|
? ((fCAST##REGSTYPE##s(SRC) >> ((-(SHAMT)) - 1)) >> 1) \
|
||||||
|
: fSAT_ORIG_SHL(fCAST##REGSTYPE##s(SRC) << (SHAMT), (SRC)))
|
||||||
|
|
||||||
|
#define fEXTRACTU_BIDIR(INREG, WIDTH, OFFSET) \
|
||||||
|
(fZXTN(WIDTH, 32, fBIDIR_LSHIFTR((INREG), (OFFSET), 4_8)))
|
||||||
|
|
||||||
|
/* Least significant bit operations */
|
||||||
|
#define fLSBNEW0 fLSBNEW(P0N)
|
||||||
|
#define fLSBNEW1 fLSBNEW(P1N)
|
||||||
|
#define fLSBOLDNOT(VAL) fGETBIT(0, ~VAL)
|
||||||
|
#define fLSBNEWNOT(PRED) (fLSBNEW(~PRED))
|
||||||
|
#define fLSBNEW0NOT fLSBNEW(~P0N)
|
||||||
|
#define fLSBNEW1NOT fLSBNEW(~P1N)
|
||||||
|
|
||||||
|
/* Assignments */
|
||||||
|
#define fPCALIGN(IMM) (IMM = IMM & ~3)
|
||||||
|
#define fWRITE_LR(A) (LR = A)
|
||||||
|
#define fWRITE_FP(A) (FP = A)
|
||||||
|
#define fWRITE_SP(A) (SP = A)
|
||||||
|
/*
|
||||||
|
* Note: There is a rule in the parser that matches `PC = ...` and emits
|
||||||
|
* a call to `gen_write_new_pc`. We need to call `gen_write_new_pc` to
|
||||||
|
* get the correct semantics when there are multiple stores in a packet.
|
||||||
|
*/
|
||||||
|
#define fBRANCH(LOC, TYPE) (PC = LOC)
|
||||||
|
#define fJUMPR(REGNO, TARGET, TYPE) (PC = TARGET)
|
||||||
|
#define fWRITE_LOOP_REGS0(START, COUNT) SA0 = START; (LC0 = COUNT)
|
||||||
|
#define fWRITE_LOOP_REGS1(START, COUNT) SA1 = START; (LC1 = COUNT)
|
||||||
|
#define fWRITE_LC0(VAL) (LC0 = VAL)
|
||||||
|
#define fWRITE_LC1(VAL) (LC1 = VAL)
|
||||||
|
#define fSET_LPCFG(VAL) (USR.LPCFG = VAL)
|
||||||
|
#define fWRITE_P0(VAL) P0 = VAL;
|
||||||
|
#define fWRITE_P1(VAL) P1 = VAL;
|
||||||
|
#define fWRITE_P3(VAL) P3 = VAL;
|
||||||
|
#define fEA_RI(REG, IMM) (EA = REG + IMM)
|
||||||
|
#define fEA_RRs(REG, REG2, SCALE) (EA = REG + (REG2 << SCALE))
|
||||||
|
#define fEA_IRs(IMM, REG, SCALE) (EA = IMM + (REG << SCALE))
|
||||||
|
#define fEA_IMM(IMM) (EA = IMM)
|
||||||
|
#define fEA_REG(REG) (EA = REG)
|
||||||
|
#define fEA_BREVR(REG) (EA = fbrev(REG))
|
||||||
|
#define fEA_GPI(IMM) (EA = fREAD_GP() + IMM)
|
||||||
|
#define fPM_I(REG, IMM) (REG = REG + IMM)
|
||||||
|
#define fPM_M(REG, MVAL) (REG = REG + MVAL)
|
||||||
|
#define fWRITE_NPC(VAL) (PC = VAL)
|
||||||
|
|
||||||
|
/* Unary operators */
|
||||||
|
#define fROUND(A) (A + 0x8000)
|
||||||
|
|
||||||
|
/* Binary operators */
|
||||||
|
#define fSCALE(N, A) (A << N)
|
||||||
|
#define fASHIFTR(SRC, SHAMT, REGSTYPE) (fCAST##REGSTYPE##s(SRC) >> SHAMT)
|
||||||
|
#define fLSHIFTR(SRC, SHAMT, REGSTYPE) (SRC >>> SHAMT)
|
||||||
|
#define fROTL(SRC, SHAMT, REGSTYPE) fROTL(SRC, SHAMT)
|
||||||
|
#define fASHIFTL(SRC, SHAMT, REGSTYPE) (fCAST##REGSTYPE##s(SRC) << SHAMT)
|
||||||
|
|
||||||
|
/* Include fHIDE macros which hide type declarations */
|
||||||
|
#define fHIDE(A) A
|
||||||
|
|
||||||
|
/* Purge non-relavant parts */
|
||||||
|
#define fBRANCH_SPECULATE_STALL(A, B, C, D, E)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,376 @@
|
||||||
|
/*
|
||||||
|
* Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARSER_HELPERS_H
|
||||||
|
#define PARSER_HELPERS_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "tcg/tcg-cond.h"
|
||||||
|
|
||||||
|
#include "idef-parser.tab.h"
|
||||||
|
#include "idef-parser.yy.h"
|
||||||
|
#include "idef-parser.h"
|
||||||
|
|
||||||
|
/* Decomment this to disable yyasserts */
|
||||||
|
/* #define NDEBUG */
|
||||||
|
|
||||||
|
#define ERR_LINE_CONTEXT 40
|
||||||
|
|
||||||
|
#define START_COMMENT "/" "*"
|
||||||
|
#define END_COMMENT "*" "/"
|
||||||
|
|
||||||
|
void yyerror(YYLTYPE *locp,
|
||||||
|
yyscan_t scanner __attribute__((unused)),
|
||||||
|
Context *c,
|
||||||
|
const char *s);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define yyassert(context, locp, condition, msg) \
|
||||||
|
if (!(condition)) { \
|
||||||
|
yyerror(locp, (context)->scanner, (context), (msg)); \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool is_direct_predicate(HexValue *value);
|
||||||
|
|
||||||
|
bool is_inside_ternary(Context *c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
void str_print(Context *c, YYLTYPE *locp, const char *string);
|
||||||
|
|
||||||
|
void uint8_print(Context *c, YYLTYPE *locp, uint8_t *num);
|
||||||
|
|
||||||
|
void uint64_print(Context *c, YYLTYPE *locp, uint64_t *num);
|
||||||
|
|
||||||
|
void int_print(Context *c, YYLTYPE *locp, int *num);
|
||||||
|
|
||||||
|
void uint_print(Context *c, YYLTYPE *locp, unsigned *num);
|
||||||
|
|
||||||
|
void tmp_print(Context *c, YYLTYPE *locp, HexTmp *tmp);
|
||||||
|
|
||||||
|
void pred_print(Context *c, YYLTYPE *locp, HexPred *pred, bool is_dotnew);
|
||||||
|
|
||||||
|
void reg_compose(Context *c, YYLTYPE *locp, HexReg *reg, char reg_id[5]);
|
||||||
|
|
||||||
|
void reg_print(Context *c, YYLTYPE *locp, HexReg *reg);
|
||||||
|
|
||||||
|
void imm_print(Context *c, YYLTYPE *locp, HexImm *imm);
|
||||||
|
|
||||||
|
void var_print(Context *c, YYLTYPE *locp, HexVar *var);
|
||||||
|
|
||||||
|
void rvalue_print(Context *c, YYLTYPE *locp, void *pointer);
|
||||||
|
|
||||||
|
void out_assert(Context *c, YYLTYPE *locp, void *dummy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies output code buffer into stdout
|
||||||
|
*/
|
||||||
|
void commit(Context *c);
|
||||||
|
|
||||||
|
#define OUT_IMPL(c, locp, x) \
|
||||||
|
_Generic(*(x), \
|
||||||
|
char: str_print, \
|
||||||
|
uint8_t: uint8_print, \
|
||||||
|
uint64_t: uint64_print, \
|
||||||
|
int: int_print, \
|
||||||
|
unsigned: uint_print, \
|
||||||
|
HexValue: rvalue_print, \
|
||||||
|
default: out_assert \
|
||||||
|
)(c, locp, x);
|
||||||
|
|
||||||
|
/* FOREACH macro */
|
||||||
|
#define FE_1(c, locp, WHAT, X) WHAT(c, locp, X)
|
||||||
|
#define FE_2(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_1(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_3(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_2(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_4(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_3(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_5(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_4(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_6(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_5(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_7(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_6(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_8(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_7(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
#define FE_9(c, locp, WHAT, X, ...) \
|
||||||
|
WHAT(c, locp, X)FE_8(c, locp, WHAT, __VA_ARGS__)
|
||||||
|
/* repeat as needed */
|
||||||
|
|
||||||
|
#define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, NAME, ...) NAME
|
||||||
|
|
||||||
|
#define FOR_EACH(c, locp, action, ...) \
|
||||||
|
do { \
|
||||||
|
GET_MACRO(__VA_ARGS__, \
|
||||||
|
FE_9, \
|
||||||
|
FE_8, \
|
||||||
|
FE_7, \
|
||||||
|
FE_6, \
|
||||||
|
FE_5, \
|
||||||
|
FE_4, \
|
||||||
|
FE_3, \
|
||||||
|
FE_2, \
|
||||||
|
FE_1)(c, locp, action, \
|
||||||
|
__VA_ARGS__) \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define OUT(c, locp, ...) FOR_EACH((c), (locp), OUT_IMPL, __VA_ARGS__)
|
||||||
|
|
||||||
|
const char *cmp_swap(Context *c, YYLTYPE *locp, const char *type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary values creation
|
||||||
|
*/
|
||||||
|
|
||||||
|
HexValue gen_tmp(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
unsigned bit_width,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
HexValue gen_tmp_value(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
const char *value,
|
||||||
|
unsigned bit_width,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
HexValue gen_imm_value(Context *c __attribute__((unused)),
|
||||||
|
YYLTYPE *locp,
|
||||||
|
int value,
|
||||||
|
unsigned bit_width,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
HexValue gen_imm_qemu_tmp(Context *c, YYLTYPE *locp, unsigned bit_width,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
void gen_rvalue_free(Context *c, YYLTYPE *locp, HexValue *rvalue);
|
||||||
|
|
||||||
|
HexValue rvalue_materialize(Context *c, YYLTYPE *locp, HexValue *rvalue);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_extend(Context *c, YYLTYPE *locp, HexValue *rvalue);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_truncate(Context *c, YYLTYPE *locp, HexValue *rvalue);
|
||||||
|
|
||||||
|
void gen_varid_allocate(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *varid,
|
||||||
|
unsigned bit_width,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code generation functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
HexValue gen_bin_cmp(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
TCGCond type,
|
||||||
|
HexValue *op1,
|
||||||
|
HexValue *op2);
|
||||||
|
|
||||||
|
HexValue gen_bin_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
OpType type,
|
||||||
|
HexValue *op1,
|
||||||
|
HexValue *op2);
|
||||||
|
|
||||||
|
HexValue gen_cast_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src,
|
||||||
|
unsigned target_width,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gen_extend_op extends a region of src_width_ptr bits stored in a
|
||||||
|
* value_ptr to the size of dst_width. Note: src_width_ptr is a
|
||||||
|
* HexValue * to handle the special case where it is unknown at
|
||||||
|
* translation time.
|
||||||
|
*/
|
||||||
|
HexValue gen_extend_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src_width,
|
||||||
|
unsigned dst_width,
|
||||||
|
HexValue *value,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
void gen_rdeposit_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *dst,
|
||||||
|
HexValue *value,
|
||||||
|
HexValue *begin,
|
||||||
|
HexValue *width);
|
||||||
|
|
||||||
|
void gen_deposit_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *dst,
|
||||||
|
HexValue *value,
|
||||||
|
HexValue *index,
|
||||||
|
HexCast *cast);
|
||||||
|
|
||||||
|
HexValue gen_rextract_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src,
|
||||||
|
unsigned begin,
|
||||||
|
unsigned width);
|
||||||
|
|
||||||
|
HexValue gen_extract_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src,
|
||||||
|
HexValue *index,
|
||||||
|
HexExtract *extract);
|
||||||
|
|
||||||
|
HexValue gen_read_reg(Context *c, YYLTYPE *locp, HexValue *reg);
|
||||||
|
|
||||||
|
void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value);
|
||||||
|
|
||||||
|
void gen_assign(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *dst,
|
||||||
|
HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_convround(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src);
|
||||||
|
|
||||||
|
HexValue gen_round(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src,
|
||||||
|
HexValue *position);
|
||||||
|
|
||||||
|
HexValue gen_convround_n(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *src,
|
||||||
|
HexValue *pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circular addressing mode with auto-increment
|
||||||
|
*/
|
||||||
|
void gen_circ_op(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *addr,
|
||||||
|
HexValue *increment,
|
||||||
|
HexValue *modifier);
|
||||||
|
|
||||||
|
HexValue gen_locnt_op(Context *c, YYLTYPE *locp, HexValue *src);
|
||||||
|
|
||||||
|
HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src);
|
||||||
|
|
||||||
|
HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *n);
|
||||||
|
|
||||||
|
HexValue gen_deinterleave(Context *c, YYLTYPE *locp, HexValue *mixed);
|
||||||
|
|
||||||
|
HexValue gen_interleave(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *odd,
|
||||||
|
HexValue *even);
|
||||||
|
|
||||||
|
HexValue gen_carry_from_add(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *op1,
|
||||||
|
HexValue *op2,
|
||||||
|
HexValue *op3);
|
||||||
|
|
||||||
|
void gen_addsat64(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexValue *dst,
|
||||||
|
HexValue *op1,
|
||||||
|
HexValue *op2);
|
||||||
|
|
||||||
|
void gen_inst(Context *c, GString *iname);
|
||||||
|
|
||||||
|
void gen_inst_init_args(Context *c, YYLTYPE *locp);
|
||||||
|
|
||||||
|
void gen_inst_code(Context *c, YYLTYPE *locp);
|
||||||
|
|
||||||
|
void gen_pred_assign(Context *c, YYLTYPE *locp, HexValue *left_pred,
|
||||||
|
HexValue *right_pred);
|
||||||
|
|
||||||
|
void gen_cancel(Context *c, YYLTYPE *locp);
|
||||||
|
|
||||||
|
void gen_load_cancel(Context *c, YYLTYPE *locp);
|
||||||
|
|
||||||
|
void gen_load(Context *c, YYLTYPE *locp, HexValue *size,
|
||||||
|
HexSignedness signedness, HexValue *ea, HexValue *dst);
|
||||||
|
|
||||||
|
void gen_store(Context *c, YYLTYPE *locp, HexValue *size, HexValue *ea,
|
||||||
|
HexValue *src);
|
||||||
|
|
||||||
|
void gen_sethalf(Context *c, YYLTYPE *locp, HexCast *sh, HexValue *n,
|
||||||
|
HexValue *dst, HexValue *value);
|
||||||
|
|
||||||
|
void gen_setbits(Context *c, YYLTYPE *locp, HexValue *hi, HexValue *lo,
|
||||||
|
HexValue *dst, HexValue *value);
|
||||||
|
|
||||||
|
unsigned gen_if_cond(Context *c, YYLTYPE *locp, HexValue *cond);
|
||||||
|
|
||||||
|
unsigned gen_if_else(Context *c, YYLTYPE *locp, unsigned index);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_pred(Context *c, YYLTYPE *locp, HexValue *pred);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_var(Context *c, YYLTYPE *locp, HexValue *var);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_mpy(Context *c, YYLTYPE *locp, HexMpy *mpy, HexValue *op1,
|
||||||
|
HexValue *op2);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_not(Context *c, YYLTYPE *locp, HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_notl(Context *c, YYLTYPE *locp, HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_sat(Context *c, YYLTYPE *locp, HexSat *sat, HexValue *n,
|
||||||
|
HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_fscr(Context *c, YYLTYPE *locp, HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_abs(Context *c, YYLTYPE *locp, HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_neg(Context *c, YYLTYPE *locp, HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_brev(Context *c, YYLTYPE *locp, HexValue *value);
|
||||||
|
|
||||||
|
HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond,
|
||||||
|
HexValue *true_branch, HexValue *false_branch);
|
||||||
|
|
||||||
|
const char *cond_to_str(TCGCond cond);
|
||||||
|
|
||||||
|
void emit_header(Context *c);
|
||||||
|
|
||||||
|
void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg);
|
||||||
|
|
||||||
|
void emit_footer(Context *c);
|
||||||
|
|
||||||
|
void track_string(Context *c, GString *s);
|
||||||
|
|
||||||
|
void free_variables(Context *c, YYLTYPE *locp);
|
||||||
|
|
||||||
|
void free_instruction(Context *c);
|
||||||
|
|
||||||
|
void assert_signedness(Context *c,
|
||||||
|
YYLTYPE *locp,
|
||||||
|
HexSignedness signedness);
|
||||||
|
|
||||||
|
#endif /* PARSER_HELPERS_h */
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright(c) 2019-2021 rev.ng Labs Srl. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# Run the preprocessor and drop comments
|
||||||
|
cpp "$@"
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
* Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -28,10 +28,7 @@ struct Instruction;
|
||||||
struct Packet;
|
struct Packet;
|
||||||
struct DisasContext;
|
struct DisasContext;
|
||||||
|
|
||||||
typedef void (*SemanticInsn)(CPUHexagonState *env,
|
typedef void (*SemanticInsn)(struct DisasContext *ctx);
|
||||||
struct DisasContext *ctx,
|
|
||||||
struct Instruction *insn,
|
|
||||||
struct Packet *pkt);
|
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
SemanticInsn generate; /* pointer to genptr routine */
|
SemanticInsn generate; /* pointer to genptr routine */
|
||||||
|
@ -57,9 +54,11 @@ typedef struct Instruction Insn;
|
||||||
struct Packet {
|
struct Packet {
|
||||||
uint16_t num_insns;
|
uint16_t num_insns;
|
||||||
uint16_t encod_pkt_size_in_bytes;
|
uint16_t encod_pkt_size_in_bytes;
|
||||||
|
uint32_t pc;
|
||||||
|
|
||||||
/* Pre-decodes about COF */
|
/* Pre-decodes about COF */
|
||||||
bool pkt_has_cof; /* Has any change-of-flow */
|
bool pkt_has_cof; /* Has any change-of-flow */
|
||||||
|
bool pkt_has_multi_cof; /* Has more than one change-of-flow */
|
||||||
bool pkt_has_endloop;
|
bool pkt_has_endloop;
|
||||||
|
|
||||||
bool pkt_has_dczeroa;
|
bool pkt_has_dczeroa;
|
||||||
|
|
|
@ -94,9 +94,9 @@
|
||||||
*/
|
*/
|
||||||
#define CHECK_NOSHUF(VA, SIZE) \
|
#define CHECK_NOSHUF(VA, SIZE) \
|
||||||
do { \
|
do { \
|
||||||
if (insn->slot == 0 && pkt->pkt_has_store_s1) { \
|
if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \
|
||||||
probe_noshuf_load(VA, SIZE, ctx->mem_idx); \
|
probe_noshuf_load(VA, SIZE, ctx->mem_idx); \
|
||||||
process_store(ctx, pkt, 1); \
|
process_store(ctx, 1); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -105,12 +105,12 @@
|
||||||
TCGLabel *label = gen_new_label(); \
|
TCGLabel *label = gen_new_label(); \
|
||||||
tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, label); \
|
tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, label); \
|
||||||
GET_EA; \
|
GET_EA; \
|
||||||
if (insn->slot == 0 && pkt->pkt_has_store_s1) { \
|
if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \
|
||||||
probe_noshuf_load(EA, SIZE, ctx->mem_idx); \
|
probe_noshuf_load(EA, SIZE, ctx->mem_idx); \
|
||||||
} \
|
} \
|
||||||
gen_set_label(label); \
|
gen_set_label(label); \
|
||||||
if (insn->slot == 0 && pkt->pkt_has_store_s1) { \
|
if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \
|
||||||
process_store(ctx, pkt, 1); \
|
process_store(ctx, 1); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -197,12 +197,21 @@
|
||||||
#define MEM_STORE8(VA, DATA, SLOT) log_store64(env, VA, DATA, 8, SLOT)
|
#define MEM_STORE8(VA, DATA, SLOT) log_store64(env, VA, DATA, 8, SLOT)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef QEMU_GENERATE
|
||||||
|
static inline void gen_cancel(uint32_t slot)
|
||||||
|
{
|
||||||
|
tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, 1 << slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CANCEL gen_cancel(slot);
|
||||||
|
#else
|
||||||
#define CANCEL cancel_slot(env, slot)
|
#define CANCEL cancel_slot(env, slot)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define LOAD_CANCEL(EA) do { CANCEL; } while (0)
|
#define LOAD_CANCEL(EA) do { CANCEL; } while (0)
|
||||||
|
|
||||||
#ifdef QEMU_GENERATE
|
#ifdef QEMU_GENERATE
|
||||||
static inline void gen_pred_cancel(TCGv pred, int slot_num)
|
static inline void gen_pred_cancel(TCGv pred, uint32_t slot_num)
|
||||||
{
|
{
|
||||||
TCGv slot_mask = tcg_temp_new();
|
TCGv slot_mask = tcg_temp_new();
|
||||||
TCGv tmp = tcg_temp_new();
|
TCGv tmp = tcg_temp_new();
|
||||||
|
@ -398,16 +407,16 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift)
|
||||||
#else
|
#else
|
||||||
#define fREAD_GP() READ_REG(HEX_REG_GP)
|
#define fREAD_GP() READ_REG(HEX_REG_GP)
|
||||||
#endif
|
#endif
|
||||||
#define fREAD_PC() (READ_REG(HEX_REG_PC))
|
#define fREAD_PC() (PC)
|
||||||
|
|
||||||
#define fREAD_NPC() (env->next_PC & (0xfffffffe))
|
#define fREAD_NPC() (next_PC & (0xfffffffe))
|
||||||
|
|
||||||
#define fREAD_P0() (READ_PREG(0))
|
#define fREAD_P0() (READ_PREG(0))
|
||||||
#define fREAD_P3() (READ_PREG(3))
|
#define fREAD_P3() (READ_PREG(3))
|
||||||
|
|
||||||
#define fCHECK_PCALIGN(A)
|
#define fCHECK_PCALIGN(A)
|
||||||
|
|
||||||
#define fWRITE_NPC(A) write_new_pc(env, A)
|
#define fWRITE_NPC(A) write_new_pc(env, pkt_has_multi_cof != 0, A)
|
||||||
|
|
||||||
#define fBRANCH(LOC, TYPE) fWRITE_NPC(LOC)
|
#define fBRANCH(LOC, TYPE) fWRITE_NPC(LOC)
|
||||||
#define fJUMPR(REGNO, TARGET, TYPE) fBRANCH(TARGET, COF_TYPE_JUMPR)
|
#define fJUMPR(REGNO, TARGET, TYPE) fBRANCH(TARGET, COF_TYPE_JUMPR)
|
||||||
|
|
|
@ -21,6 +21,7 @@ hex_common_py = 'hex_common.py'
|
||||||
attribs_def = meson.current_source_dir() / 'attribs_def.h.inc'
|
attribs_def = meson.current_source_dir() / 'attribs_def.h.inc'
|
||||||
gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h'
|
gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h'
|
||||||
gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h'
|
gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h'
|
||||||
|
idef_parser_dir = meson.current_source_dir() / 'idef-parser'
|
||||||
|
|
||||||
#
|
#
|
||||||
# Step 1
|
# Step 1
|
||||||
|
@ -42,10 +43,7 @@ hexagon_ss.add(semantics_generated)
|
||||||
# Step 2
|
# Step 2
|
||||||
# We use Python scripts to generate the following files
|
# We use Python scripts to generate the following files
|
||||||
# shortcode_generated.h.inc
|
# shortcode_generated.h.inc
|
||||||
# helper_protos_generated.h.inc
|
|
||||||
# tcg_funcs_generated.c.inc
|
|
||||||
# tcg_func_table_generated.c.inc
|
# tcg_func_table_generated.c.inc
|
||||||
# helper_funcs_generated.c.inc
|
|
||||||
# printinsn_generated.h.inc
|
# printinsn_generated.h.inc
|
||||||
# op_regs_generated.h.inc
|
# op_regs_generated.h.inc
|
||||||
# op_attribs_generated.h.inc
|
# op_attribs_generated.h.inc
|
||||||
|
@ -60,24 +58,6 @@ shortcode_generated = custom_target(
|
||||||
)
|
)
|
||||||
hexagon_ss.add(shortcode_generated)
|
hexagon_ss.add(shortcode_generated)
|
||||||
|
|
||||||
helper_protos_generated = custom_target(
|
|
||||||
'helper_protos_generated.h.inc',
|
|
||||||
output: 'helper_protos_generated.h.inc',
|
|
||||||
depends: [semantics_generated],
|
|
||||||
depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
|
|
||||||
command: [python, files('gen_helper_protos.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'],
|
|
||||||
)
|
|
||||||
hexagon_ss.add(helper_protos_generated)
|
|
||||||
|
|
||||||
tcg_funcs_generated = custom_target(
|
|
||||||
'tcg_funcs_generated.c.inc',
|
|
||||||
output: 'tcg_funcs_generated.c.inc',
|
|
||||||
depends: [semantics_generated],
|
|
||||||
depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
|
|
||||||
command: [python, files('gen_tcg_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'],
|
|
||||||
)
|
|
||||||
hexagon_ss.add(tcg_funcs_generated)
|
|
||||||
|
|
||||||
tcg_func_table_generated = custom_target(
|
tcg_func_table_generated = custom_target(
|
||||||
'tcg_func_table_generated.c.inc',
|
'tcg_func_table_generated.c.inc',
|
||||||
output: 'tcg_func_table_generated.c.inc',
|
output: 'tcg_func_table_generated.c.inc',
|
||||||
|
@ -87,15 +67,6 @@ tcg_func_table_generated = custom_target(
|
||||||
)
|
)
|
||||||
hexagon_ss.add(tcg_func_table_generated)
|
hexagon_ss.add(tcg_func_table_generated)
|
||||||
|
|
||||||
helper_funcs_generated = custom_target(
|
|
||||||
'helper_funcs_generated.c.inc',
|
|
||||||
output: 'helper_funcs_generated.c.inc',
|
|
||||||
depends: [semantics_generated],
|
|
||||||
depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
|
|
||||||
command: [python, files('gen_helper_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'],
|
|
||||||
)
|
|
||||||
hexagon_ss.add(helper_funcs_generated)
|
|
||||||
|
|
||||||
printinsn_generated = custom_target(
|
printinsn_generated = custom_target(
|
||||||
'printinsn_generated.h.inc',
|
'printinsn_generated.h.inc',
|
||||||
output: 'printinsn_generated.h.inc',
|
output: 'printinsn_generated.h.inc',
|
||||||
|
@ -179,4 +150,131 @@ hexagon_ss.add(files(
|
||||||
'mmvec/system_ext_mmvec.c',
|
'mmvec/system_ext_mmvec.c',
|
||||||
))
|
))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Step 4.5
|
||||||
|
# We use flex/bison based idef-parser to generate TCG code for a lot
|
||||||
|
# of instructions. idef-parser outputs
|
||||||
|
# idef-generated-emitter.c
|
||||||
|
# idef-generated-emitter.h.inc
|
||||||
|
# idef-generated-enabled-instructions
|
||||||
|
#
|
||||||
|
idef_parser_enabled = get_option('hexagon_idef_parser')
|
||||||
|
if idef_parser_enabled and 'hexagon-linux-user' in target_dirs
|
||||||
|
idef_parser_input_generated = custom_target(
|
||||||
|
'idef_parser_input.h.inc',
|
||||||
|
output: 'idef_parser_input.h.inc',
|
||||||
|
depends: [semantics_generated],
|
||||||
|
depend_files: [hex_common_py],
|
||||||
|
command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, attribs_def, '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
preprocessed_idef_parser_input_generated = custom_target(
|
||||||
|
'idef_parser_input.preprocessed.h.inc',
|
||||||
|
output: 'idef_parser_input.preprocessed.h.inc',
|
||||||
|
input: idef_parser_input_generated,
|
||||||
|
depend_files: [idef_parser_dir / 'macros.inc'],
|
||||||
|
command: [idef_parser_dir / 'prepare', '@INPUT@', '-I' + idef_parser_dir, '-o', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
flex = generator(
|
||||||
|
find_program('flex'),
|
||||||
|
output: ['@BASENAME@.yy.c', '@BASENAME@.yy.h'],
|
||||||
|
arguments: ['-o', '@OUTPUT0@', '--header-file=@OUTPUT1@', '@INPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
bison = generator(
|
||||||
|
find_program('bison'),
|
||||||
|
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
|
||||||
|
arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@']
|
||||||
|
)
|
||||||
|
|
||||||
|
glib_dep = dependency('glib-2.0', native: true)
|
||||||
|
|
||||||
|
idef_parser = executable(
|
||||||
|
'idef-parser',
|
||||||
|
[flex.process(idef_parser_dir / 'idef-parser.lex'),
|
||||||
|
bison.process(idef_parser_dir / 'idef-parser.y'),
|
||||||
|
idef_parser_dir / 'parser-helpers.c'],
|
||||||
|
include_directories: ['idef-parser', '../../include/'],
|
||||||
|
dependencies: [glib_dep],
|
||||||
|
c_args: ['-Wextra'],
|
||||||
|
native: true
|
||||||
|
)
|
||||||
|
|
||||||
|
idef_generated_tcg = custom_target(
|
||||||
|
'idef-generated-tcg',
|
||||||
|
output: ['idef-generated-emitter.c',
|
||||||
|
'idef-generated-emitter.h.inc',
|
||||||
|
'idef-generated-enabled-instructions'],
|
||||||
|
input: preprocessed_idef_parser_input_generated,
|
||||||
|
depend_files: [hex_common_py],
|
||||||
|
command: [idef_parser, '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', '@OUTPUT2@']
|
||||||
|
)
|
||||||
|
|
||||||
|
indent = find_program('indent', required: false)
|
||||||
|
if indent.found()
|
||||||
|
idef_generated_tcg_c = custom_target(
|
||||||
|
'indent',
|
||||||
|
input: idef_generated_tcg[0],
|
||||||
|
output: 'idef-generated-emitter.indented.c',
|
||||||
|
command: [indent, '-linux', '@INPUT@', '-o', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
else
|
||||||
|
idef_generated_tcg_c = custom_target(
|
||||||
|
'copy',
|
||||||
|
input: idef_generated_tcg[0],
|
||||||
|
output: 'idef-generated-emitter.indented.c',
|
||||||
|
command: ['cp', '@INPUT@', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
idef_generated_list = idef_generated_tcg[2].full_path()
|
||||||
|
|
||||||
|
hexagon_ss.add(idef_generated_tcg_c)
|
||||||
|
|
||||||
|
# Setup input and dependencies for the next step, this depends on whether or
|
||||||
|
# not idef-parser is enabled
|
||||||
|
helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg]
|
||||||
|
helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list]
|
||||||
|
else
|
||||||
|
# Setup input and dependencies for the next step, this depends on whether or
|
||||||
|
# not idef-parser is enabled
|
||||||
|
helper_dep = [semantics_generated]
|
||||||
|
helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h]
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# Step 5
|
||||||
|
# We use Python scripts to generate the following files
|
||||||
|
# helper_protos_generated.h.inc
|
||||||
|
# helper_funcs_generated.c.inc
|
||||||
|
# tcg_funcs_generated.c.inc
|
||||||
|
#
|
||||||
|
helper_protos_generated = custom_target(
|
||||||
|
'helper_protos_generated.h.inc',
|
||||||
|
output: 'helper_protos_generated.h.inc',
|
||||||
|
depends: helper_dep,
|
||||||
|
depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
|
||||||
|
command: [python, files('gen_helper_protos.py'), helper_in, '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
hexagon_ss.add(helper_protos_generated)
|
||||||
|
|
||||||
|
helper_funcs_generated = custom_target(
|
||||||
|
'helper_funcs_generated.c.inc',
|
||||||
|
output: 'helper_funcs_generated.c.inc',
|
||||||
|
depends: helper_dep,
|
||||||
|
depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
|
||||||
|
command: [python, files('gen_helper_funcs.py'), helper_in, '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
hexagon_ss.add(helper_funcs_generated)
|
||||||
|
|
||||||
|
tcg_funcs_generated = custom_target(
|
||||||
|
'tcg_funcs_generated.c.inc',
|
||||||
|
output: 'tcg_funcs_generated.c.inc',
|
||||||
|
depends: helper_dep,
|
||||||
|
depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
|
||||||
|
command: [python, files('gen_tcg_funcs.py'), helper_in, '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
hexagon_ss.add(tcg_funcs_generated)
|
||||||
|
|
||||||
target_arch += {'hexagon': hexagon_ss}
|
target_arch += {'hexagon': hexagon_ss}
|
||||||
|
|
|
@ -288,7 +288,7 @@
|
||||||
#endif
|
#endif
|
||||||
#ifdef QEMU_GENERATE
|
#ifdef QEMU_GENERATE
|
||||||
#define fSTOREMMV(EA, SRC) \
|
#define fSTOREMMV(EA, SRC) \
|
||||||
gen_vreg_store(ctx, insn, pkt, EA, SRC##_off, insn->slot, true)
|
gen_vreg_store(ctx, EA, SRC##_off, insn->slot, true)
|
||||||
#endif
|
#endif
|
||||||
#ifdef QEMU_GENERATE
|
#ifdef QEMU_GENERATE
|
||||||
#define fSTOREMMVQ(EA, SRC, MASK) \
|
#define fSTOREMMVQ(EA, SRC, MASK) \
|
||||||
|
@ -300,7 +300,7 @@
|
||||||
#endif
|
#endif
|
||||||
#ifdef QEMU_GENERATE
|
#ifdef QEMU_GENERATE
|
||||||
#define fSTOREMMVU(EA, SRC) \
|
#define fSTOREMMVU(EA, SRC) \
|
||||||
gen_vreg_store(ctx, insn, pkt, EA, SRC##_off, insn->slot, false)
|
gen_vreg_store(ctx, EA, SRC##_off, insn->slot, false)
|
||||||
#endif
|
#endif
|
||||||
#define fVFOREACH(WIDTH, VAR) for (VAR = 0; VAR < fVELEM(WIDTH); VAR++)
|
#define fVFOREACH(WIDTH, VAR) for (VAR = 0; VAR < fVELEM(WIDTH); VAR++)
|
||||||
#define fVARRAY_ELEMENT_ACCESS(ARRAY, TYPE, INDEX) \
|
#define fVARRAY_ELEMENT_ACCESS(ARRAY, TYPE, INDEX) \
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "fma_emu.h"
|
#include "fma_emu.h"
|
||||||
#include "mmvec/mmvec.h"
|
#include "mmvec/mmvec.h"
|
||||||
#include "mmvec/macros.h"
|
#include "mmvec/macros.h"
|
||||||
|
#include "op_helper.h"
|
||||||
|
|
||||||
#define SF_BIAS 127
|
#define SF_BIAS 127
|
||||||
#define SF_MANTBITS 23
|
#define SF_MANTBITS 23
|
||||||
|
@ -50,8 +51,8 @@ G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp)
|
||||||
do_raise_exception_err(env, excp, 0);
|
do_raise_exception_err(env, excp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_reg_write(CPUHexagonState *env, int rnum,
|
void log_reg_write(CPUHexagonState *env, int rnum,
|
||||||
target_ulong val, uint32_t slot)
|
target_ulong val, uint32_t slot)
|
||||||
{
|
{
|
||||||
HEX_DEBUG_LOG("log_reg_write[%d] = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")",
|
HEX_DEBUG_LOG("log_reg_write[%d] = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")",
|
||||||
rnum, val, val);
|
rnum, val, val);
|
||||||
|
@ -82,8 +83,8 @@ static void log_pred_write(CPUHexagonState *env, int pnum, target_ulong val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_store32(CPUHexagonState *env, target_ulong addr,
|
void log_store32(CPUHexagonState *env, target_ulong addr,
|
||||||
target_ulong val, int width, int slot)
|
target_ulong val, int width, int slot)
|
||||||
{
|
{
|
||||||
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
|
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
|
||||||
", %" PRId32 " [0x08%" PRIx32 "])\n",
|
", %" PRId32 " [0x08%" PRIx32 "])\n",
|
||||||
|
@ -93,8 +94,8 @@ static void log_store32(CPUHexagonState *env, target_ulong addr,
|
||||||
env->mem_log_stores[slot].data32 = val;
|
env->mem_log_stores[slot].data32 = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_store64(CPUHexagonState *env, target_ulong addr,
|
void log_store64(CPUHexagonState *env, target_ulong addr,
|
||||||
int64_t val, int width, int slot)
|
int64_t val, int width, int slot)
|
||||||
{
|
{
|
||||||
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
|
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
|
||||||
", %" PRId64 " [0x016%" PRIx64 "])\n",
|
", %" PRId64 " [0x016%" PRIx64 "])\n",
|
||||||
|
@ -104,21 +105,27 @@ static void log_store64(CPUHexagonState *env, target_ulong addr,
|
||||||
env->mem_log_stores[slot].data64 = val;
|
env->mem_log_stores[slot].data64 = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_new_pc(CPUHexagonState *env, target_ulong addr)
|
void write_new_pc(CPUHexagonState *env, bool pkt_has_multi_cof,
|
||||||
|
target_ulong addr)
|
||||||
{
|
{
|
||||||
HEX_DEBUG_LOG("write_new_pc(0x" TARGET_FMT_lx ")\n", addr);
|
HEX_DEBUG_LOG("write_new_pc(0x" TARGET_FMT_lx ")\n", addr);
|
||||||
|
|
||||||
/*
|
if (pkt_has_multi_cof) {
|
||||||
* If more than one branch is taken in a packet, only the first one
|
/*
|
||||||
* is actually done.
|
* If more than one branch is taken in a packet, only the first one
|
||||||
*/
|
* is actually done.
|
||||||
if (env->branch_taken) {
|
*/
|
||||||
HEX_DEBUG_LOG("INFO: multiple branches taken in same packet, "
|
if (env->branch_taken) {
|
||||||
"ignoring the second one\n");
|
HEX_DEBUG_LOG("INFO: multiple branches taken in same packet, "
|
||||||
|
"ignoring the second one\n");
|
||||||
|
} else {
|
||||||
|
fCHECK_PCALIGN(addr);
|
||||||
|
env->gpr[HEX_REG_PC] = addr;
|
||||||
|
env->branch_taken = 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fCHECK_PCALIGN(addr);
|
fCHECK_PCALIGN(addr);
|
||||||
env->branch_taken = 1;
|
env->gpr[HEX_REG_PC] = addr;
|
||||||
env->next_PC = addr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +300,7 @@ void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->next_PC);
|
HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->gpr[HEX_REG_PC]);
|
||||||
HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx
|
HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx
|
||||||
", insn = " TARGET_FMT_lx
|
", insn = " TARGET_FMT_lx
|
||||||
", hvx = " TARGET_FMT_lx "\n",
|
", hvx = " TARGET_FMT_lx "\n",
|
||||||
|
@ -535,32 +542,28 @@ static void check_noshuf(CPUHexagonState *env, uint32_t slot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mem_load1(CPUHexagonState *env, uint32_t slot,
|
uint8_t mem_load1(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||||
target_ulong vaddr)
|
|
||||||
{
|
{
|
||||||
uintptr_t ra = GETPC();
|
uintptr_t ra = GETPC();
|
||||||
check_noshuf(env, slot, vaddr, 1);
|
check_noshuf(env, slot, vaddr, 1);
|
||||||
return cpu_ldub_data_ra(env, vaddr, ra);
|
return cpu_ldub_data_ra(env, vaddr, ra);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t mem_load2(CPUHexagonState *env, uint32_t slot,
|
uint16_t mem_load2(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||||
target_ulong vaddr)
|
|
||||||
{
|
{
|
||||||
uintptr_t ra = GETPC();
|
uintptr_t ra = GETPC();
|
||||||
check_noshuf(env, slot, vaddr, 2);
|
check_noshuf(env, slot, vaddr, 2);
|
||||||
return cpu_lduw_data_ra(env, vaddr, ra);
|
return cpu_lduw_data_ra(env, vaddr, ra);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t mem_load4(CPUHexagonState *env, uint32_t slot,
|
uint32_t mem_load4(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||||
target_ulong vaddr)
|
|
||||||
{
|
{
|
||||||
uintptr_t ra = GETPC();
|
uintptr_t ra = GETPC();
|
||||||
check_noshuf(env, slot, vaddr, 4);
|
check_noshuf(env, slot, vaddr, 4);
|
||||||
return cpu_ldl_data_ra(env, vaddr, ra);
|
return cpu_ldl_data_ra(env, vaddr, ra);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t mem_load8(CPUHexagonState *env, uint32_t slot,
|
uint64_t mem_load8(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||||
target_ulong vaddr)
|
|
||||||
{
|
{
|
||||||
uintptr_t ra = GETPC();
|
uintptr_t ra = GETPC();
|
||||||
check_noshuf(env, slot, vaddr, 8);
|
check_noshuf(env, slot, vaddr, 8);
|
||||||
|
@ -1465,7 +1468,7 @@ void HELPER(vwhist128qm)(CPUHexagonState *env, int32_t uiV)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cancel_slot(CPUHexagonState *env, uint32_t slot)
|
void cancel_slot(CPUHexagonState *env, uint32_t slot)
|
||||||
{
|
{
|
||||||
HEX_DEBUG_LOG("Slot %d cancelled\n", slot);
|
HEX_DEBUG_LOG("Slot %d cancelled\n", slot);
|
||||||
env->slot_cancelled |= (1 << slot);
|
env->slot_cancelled |= (1 << slot);
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HEXAGON_OP_HELPER_H
|
||||||
|
#define HEXAGON_OP_HELPER_H
|
||||||
|
|
||||||
|
/* Misc functions */
|
||||||
|
void cancel_slot(CPUHexagonState *env, uint32_t slot);
|
||||||
|
void write_new_pc(CPUHexagonState *env, bool pkt_has_multi_cof, target_ulong addr);
|
||||||
|
|
||||||
|
uint8_t mem_load1(CPUHexagonState *env, uint32_t slot, target_ulong vaddr);
|
||||||
|
uint16_t mem_load2(CPUHexagonState *env, uint32_t slot, target_ulong vaddr);
|
||||||
|
uint32_t mem_load4(CPUHexagonState *env, uint32_t slot, target_ulong vaddr);
|
||||||
|
uint64_t mem_load8(CPUHexagonState *env, uint32_t slot, target_ulong vaddr);
|
||||||
|
|
||||||
|
void log_reg_write(CPUHexagonState *env, int rnum,
|
||||||
|
target_ulong val, uint32_t slot);
|
||||||
|
void log_store64(CPUHexagonState *env, target_ulong addr,
|
||||||
|
int64_t val, int width, int slot);
|
||||||
|
void log_store32(CPUHexagonState *env, target_ulong addr,
|
||||||
|
target_ulong val, int width, int slot);
|
||||||
|
|
||||||
|
#endif
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
|
TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
|
||||||
TCGv hex_pred[NUM_PREGS];
|
TCGv hex_pred[NUM_PREGS];
|
||||||
TCGv hex_next_PC;
|
|
||||||
TCGv hex_this_PC;
|
TCGv hex_this_PC;
|
||||||
TCGv hex_slot_cancelled;
|
TCGv hex_slot_cancelled;
|
||||||
TCGv hex_branch_taken;
|
TCGv hex_branch_taken;
|
||||||
|
@ -117,18 +116,62 @@ static void gen_exec_counters(DisasContext *ctx)
|
||||||
hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns);
|
hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool use_goto_tb(DisasContext *ctx, target_ulong dest)
|
||||||
|
{
|
||||||
|
return translator_use_goto_tb(&ctx->base, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_goto_tb(DisasContext *ctx, int idx, target_ulong dest)
|
||||||
|
{
|
||||||
|
if (use_goto_tb(ctx, dest)) {
|
||||||
|
tcg_gen_goto_tb(idx);
|
||||||
|
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], dest);
|
||||||
|
tcg_gen_exit_tb(ctx->base.tb, idx);
|
||||||
|
} else {
|
||||||
|
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], dest);
|
||||||
|
tcg_gen_lookup_and_goto_ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gen_end_tb(DisasContext *ctx)
|
static void gen_end_tb(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
|
|
||||||
gen_exec_counters(ctx);
|
gen_exec_counters(ctx);
|
||||||
tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
|
|
||||||
tcg_gen_exit_tb(NULL, 0);
|
if (ctx->branch_cond != TCG_COND_NEVER) {
|
||||||
|
if (ctx->branch_cond != TCG_COND_ALWAYS) {
|
||||||
|
TCGLabel *skip = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(ctx->branch_cond, hex_branch_taken, 0, skip);
|
||||||
|
gen_goto_tb(ctx, 0, ctx->branch_dest);
|
||||||
|
gen_set_label(skip);
|
||||||
|
gen_goto_tb(ctx, 1, ctx->next_PC);
|
||||||
|
} else {
|
||||||
|
gen_goto_tb(ctx, 0, ctx->branch_dest);
|
||||||
|
}
|
||||||
|
} else if (ctx->is_tight_loop &&
|
||||||
|
pkt->insn[pkt->num_insns - 1].opcode == J2_endloop0) {
|
||||||
|
/*
|
||||||
|
* When we're in a tight loop, we defer the endloop0 processing
|
||||||
|
* to take advantage of direct block chaining
|
||||||
|
*/
|
||||||
|
TCGLabel *skip = gen_new_label();
|
||||||
|
tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, skip);
|
||||||
|
tcg_gen_subi_tl(hex_gpr[HEX_REG_LC0], hex_gpr[HEX_REG_LC0], 1);
|
||||||
|
gen_goto_tb(ctx, 0, ctx->base.tb->pc);
|
||||||
|
gen_set_label(skip);
|
||||||
|
gen_goto_tb(ctx, 1, ctx->next_PC);
|
||||||
|
} else {
|
||||||
|
tcg_gen_lookup_and_goto_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_exception_end_tb(DisasContext *ctx, int excp)
|
static void gen_exception_end_tb(DisasContext *ctx, int excp)
|
||||||
{
|
{
|
||||||
gen_exec_counters(ctx);
|
gen_exec_counters(ctx);
|
||||||
tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
|
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC);
|
||||||
gen_exception_raw(excp);
|
gen_exception_raw(excp);
|
||||||
ctx->base.is_jmp = DISAS_NORETURN;
|
ctx->base.is_jmp = DISAS_NORETURN;
|
||||||
|
|
||||||
|
@ -194,11 +237,6 @@ static bool check_for_attrib(Packet *pkt, int attrib)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool need_pc(Packet *pkt)
|
|
||||||
{
|
|
||||||
return check_for_attrib(pkt, A_IMPLICIT_READS_PC);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool need_slot_cancelled(Packet *pkt)
|
static bool need_slot_cancelled(Packet *pkt)
|
||||||
{
|
{
|
||||||
return check_for_attrib(pkt, A_CONDEXEC);
|
return check_for_attrib(pkt, A_CONDEXEC);
|
||||||
|
@ -209,12 +247,32 @@ static bool need_pred_written(Packet *pkt)
|
||||||
return check_for_attrib(pkt, A_WRITES_PRED_REG);
|
return check_for_attrib(pkt, A_WRITES_PRED_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_start_packet(DisasContext *ctx, Packet *pkt)
|
static bool need_next_PC(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
|
|
||||||
|
/* Check for conditional control flow or HW loop end */
|
||||||
|
for (int i = 0; i < pkt->num_insns; i++) {
|
||||||
|
uint16_t opcode = pkt->insn[i].opcode;
|
||||||
|
if (GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (GET_ATTRIB(opcode, A_HWLOOP0_END) ||
|
||||||
|
GET_ATTRIB(opcode, A_HWLOOP1_END)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_start_packet(DisasContext *ctx)
|
||||||
|
{
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
target_ulong next_PC = ctx->base.pc_next + pkt->encod_pkt_size_in_bytes;
|
target_ulong next_PC = ctx->base.pc_next + pkt->encod_pkt_size_in_bytes;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Clear out the disassembly context */
|
/* Clear out the disassembly context */
|
||||||
|
ctx->next_PC = next_PC;
|
||||||
ctx->reg_log_idx = 0;
|
ctx->reg_log_idx = 0;
|
||||||
bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS);
|
bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS);
|
||||||
ctx->preg_log_idx = 0;
|
ctx->preg_log_idx = 0;
|
||||||
|
@ -240,15 +298,16 @@ static void gen_start_packet(DisasContext *ctx, Packet *pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the runtime state for packet semantics */
|
/* Initialize the runtime state for packet semantics */
|
||||||
if (need_pc(pkt)) {
|
|
||||||
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next);
|
|
||||||
}
|
|
||||||
if (need_slot_cancelled(pkt)) {
|
if (need_slot_cancelled(pkt)) {
|
||||||
tcg_gen_movi_tl(hex_slot_cancelled, 0);
|
tcg_gen_movi_tl(hex_slot_cancelled, 0);
|
||||||
}
|
}
|
||||||
if (pkt->pkt_has_cof) {
|
if (pkt->pkt_has_cof) {
|
||||||
tcg_gen_movi_tl(hex_branch_taken, 0);
|
if (pkt->pkt_has_multi_cof) {
|
||||||
tcg_gen_movi_tl(hex_next_PC, next_PC);
|
tcg_gen_movi_tl(hex_branch_taken, 0);
|
||||||
|
}
|
||||||
|
if (need_next_PC(ctx)) {
|
||||||
|
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (need_pred_written(pkt)) {
|
if (need_pred_written(pkt)) {
|
||||||
tcg_gen_movi_tl(hex_pred_written, 0);
|
tcg_gen_movi_tl(hex_pred_written, 0);
|
||||||
|
@ -260,8 +319,10 @@ static void gen_start_packet(DisasContext *ctx, Packet *pkt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_gather_store_insn(Insn *insn, Packet *pkt)
|
bool is_gather_store_insn(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
|
Insn *insn = ctx->insn;
|
||||||
if (GET_ATTRIB(insn->opcode, A_CVI_NEW) &&
|
if (GET_ATTRIB(insn->opcode, A_CVI_NEW) &&
|
||||||
insn->new_value_producer_slot == 1) {
|
insn->new_value_producer_slot == 1) {
|
||||||
/* Look for gather instruction */
|
/* Look for gather instruction */
|
||||||
|
@ -280,16 +341,25 @@ bool is_gather_store_insn(Insn *insn, Packet *pkt)
|
||||||
* However, there are some implicit writes marked as attributes
|
* However, there are some implicit writes marked as attributes
|
||||||
* of the applicable instructions.
|
* of the applicable instructions.
|
||||||
*/
|
*/
|
||||||
static void mark_implicit_reg_write(DisasContext *ctx, Insn *insn,
|
static void mark_implicit_reg_write(DisasContext *ctx, int attrib, int rnum)
|
||||||
int attrib, int rnum)
|
|
||||||
{
|
{
|
||||||
if (GET_ATTRIB(insn->opcode, attrib)) {
|
uint16_t opcode = ctx->insn->opcode;
|
||||||
|
if (GET_ATTRIB(opcode, attrib)) {
|
||||||
/*
|
/*
|
||||||
* USR is used to set overflow and FP exceptions,
|
* USR is used to set overflow and FP exceptions,
|
||||||
* so treat it as conditional
|
* so treat it as conditional
|
||||||
*/
|
*/
|
||||||
bool is_predicated = GET_ATTRIB(insn->opcode, A_CONDEXEC) ||
|
bool is_predicated = GET_ATTRIB(opcode, A_CONDEXEC) ||
|
||||||
rnum == HEX_REG_USR;
|
rnum == HEX_REG_USR;
|
||||||
|
|
||||||
|
/* LC0/LC1 is conditionally written by endloop instructions */
|
||||||
|
if ((rnum == HEX_REG_LC0 || rnum == HEX_REG_LC1) &&
|
||||||
|
(opcode == J2_endloop0 ||
|
||||||
|
opcode == J2_endloop1 ||
|
||||||
|
opcode == J2_endloop01)) {
|
||||||
|
is_predicated = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_predicated && !is_preloaded(ctx, rnum)) {
|
if (is_predicated && !is_preloaded(ctx, rnum)) {
|
||||||
tcg_gen_mov_tl(hex_new_value[rnum], hex_gpr[rnum]);
|
tcg_gen_mov_tl(hex_new_value[rnum], hex_gpr[rnum]);
|
||||||
}
|
}
|
||||||
|
@ -298,39 +368,38 @@ static void mark_implicit_reg_write(DisasContext *ctx, Insn *insn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_implicit_pred_write(DisasContext *ctx, Insn *insn,
|
static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum)
|
||||||
int attrib, int pnum)
|
|
||||||
{
|
{
|
||||||
if (GET_ATTRIB(insn->opcode, attrib)) {
|
if (GET_ATTRIB(ctx->insn->opcode, attrib)) {
|
||||||
ctx_log_pred_write(ctx, pnum);
|
ctx_log_pred_write(ctx, pnum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_implicit_reg_writes(DisasContext *ctx, Insn *insn)
|
static void mark_implicit_reg_writes(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_FP, HEX_REG_FP);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SP, HEX_REG_SP);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SP, HEX_REG_SP);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LR, HEX_REG_LR);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LR, HEX_REG_LR);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1);
|
||||||
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_USR, HEX_REG_USR);
|
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_USR, HEX_REG_USR);
|
||||||
mark_implicit_reg_write(ctx, insn, A_FPOP, HEX_REG_USR);
|
mark_implicit_reg_write(ctx, A_FPOP, HEX_REG_USR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_implicit_pred_writes(DisasContext *ctx, Insn *insn)
|
static void mark_implicit_pred_writes(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P0, 0);
|
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P0, 0);
|
||||||
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P1, 1);
|
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P1, 1);
|
||||||
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P2, 2);
|
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P2, 2);
|
||||||
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P3, 3);
|
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P3, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_store_width(DisasContext *ctx, Insn *insn)
|
static void mark_store_width(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
uint16_t opcode = insn->opcode;
|
uint16_t opcode = ctx->insn->opcode;
|
||||||
uint32_t slot = insn->slot;
|
uint32_t slot = ctx->insn->slot;
|
||||||
uint8_t width = 0;
|
uint8_t width = 0;
|
||||||
|
|
||||||
if (GET_ATTRIB(opcode, A_SCALAR_STORE)) {
|
if (GET_ATTRIB(opcode, A_SCALAR_STORE)) {
|
||||||
|
@ -351,14 +420,13 @@ static void mark_store_width(DisasContext *ctx, Insn *insn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_insn(CPUHexagonState *env, DisasContext *ctx,
|
static void gen_insn(DisasContext *ctx)
|
||||||
Insn *insn, Packet *pkt)
|
|
||||||
{
|
{
|
||||||
if (insn->generate) {
|
if (ctx->insn->generate) {
|
||||||
mark_implicit_reg_writes(ctx, insn);
|
mark_implicit_reg_writes(ctx);
|
||||||
insn->generate(env, ctx, insn, pkt);
|
ctx->insn->generate(ctx);
|
||||||
mark_implicit_pred_writes(ctx, insn);
|
mark_implicit_pred_writes(ctx);
|
||||||
mark_store_width(ctx, insn);
|
mark_store_width(ctx);
|
||||||
} else {
|
} else {
|
||||||
gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE);
|
gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE);
|
||||||
}
|
}
|
||||||
|
@ -375,10 +443,18 @@ static void gen_reg_writes(DisasContext *ctx)
|
||||||
int reg_num = ctx->reg_log[i];
|
int reg_num = ctx->reg_log[i];
|
||||||
|
|
||||||
tcg_gen_mov_tl(hex_gpr[reg_num], hex_new_value[reg_num]);
|
tcg_gen_mov_tl(hex_gpr[reg_num], hex_new_value[reg_num]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ctx->is_tight_loop is set when SA0 points to the beginning of the TB.
|
||||||
|
* If we write to SA0, we have to turn off tight loop handling.
|
||||||
|
*/
|
||||||
|
if (reg_num == HEX_REG_SA0) {
|
||||||
|
ctx->is_tight_loop = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_pred_writes(DisasContext *ctx, Packet *pkt)
|
static void gen_pred_writes(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -393,7 +469,7 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt)
|
||||||
* instructions, we can use the non-conditional
|
* instructions, we can use the non-conditional
|
||||||
* write of the predicates.
|
* write of the predicates.
|
||||||
*/
|
*/
|
||||||
if (pkt->pkt_has_endloop) {
|
if (ctx->pkt->pkt_has_endloop) {
|
||||||
TCGv zero = tcg_constant_tl(0);
|
TCGv zero = tcg_constant_tl(0);
|
||||||
TCGv pred_written = tcg_temp_new();
|
TCGv pred_written = tcg_temp_new();
|
||||||
for (i = 0; i < ctx->preg_log_idx; i++) {
|
for (i = 0; i < ctx->preg_log_idx; i++) {
|
||||||
|
@ -439,9 +515,9 @@ static bool slot_is_predicated(Packet *pkt, int slot_num)
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_store(DisasContext *ctx, Packet *pkt, int slot_num)
|
void process_store(DisasContext *ctx, int slot_num)
|
||||||
{
|
{
|
||||||
bool is_predicated = slot_is_predicated(pkt, slot_num);
|
bool is_predicated = slot_is_predicated(ctx->pkt, slot_num);
|
||||||
TCGLabel *label_end = NULL;
|
TCGLabel *label_end = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -517,27 +593,28 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_store_log(DisasContext *ctx, Packet *pkt)
|
static void process_store_log(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* When a packet has two stores, the hardware processes
|
* When a packet has two stores, the hardware processes
|
||||||
* slot 1 and then slot 0. This will be important when
|
* slot 1 and then slot 0. This will be important when
|
||||||
* the memory accesses overlap.
|
* the memory accesses overlap.
|
||||||
*/
|
*/
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
if (pkt->pkt_has_store_s1) {
|
if (pkt->pkt_has_store_s1) {
|
||||||
g_assert(!pkt->pkt_has_dczeroa);
|
g_assert(!pkt->pkt_has_dczeroa);
|
||||||
process_store(ctx, pkt, 1);
|
process_store(ctx, 1);
|
||||||
}
|
}
|
||||||
if (pkt->pkt_has_store_s0) {
|
if (pkt->pkt_has_store_s0) {
|
||||||
g_assert(!pkt->pkt_has_dczeroa);
|
g_assert(!pkt->pkt_has_dczeroa);
|
||||||
process_store(ctx, pkt, 0);
|
process_store(ctx, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zero out a 32-bit cache line */
|
/* Zero out a 32-bit cache line */
|
||||||
static void process_dczeroa(DisasContext *ctx, Packet *pkt)
|
static void process_dczeroa(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
if (pkt->pkt_has_dczeroa) {
|
if (ctx->pkt->pkt_has_dczeroa) {
|
||||||
/* Store 32 bytes of zero starting at (addr & ~0x1f) */
|
/* Store 32 bytes of zero starting at (addr & ~0x1f) */
|
||||||
TCGv addr = tcg_temp_new();
|
TCGv addr = tcg_temp_new();
|
||||||
TCGv_i64 zero = tcg_constant_i64(0);
|
TCGv_i64 zero = tcg_constant_i64(0);
|
||||||
|
@ -567,7 +644,7 @@ static bool pkt_has_hvx_store(Packet *pkt)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_commit_hvx(DisasContext *ctx, Packet *pkt)
|
static void gen_commit_hvx(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -637,13 +714,14 @@ static void gen_commit_hvx(DisasContext *ctx, Packet *pkt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkt_has_hvx_store(pkt)) {
|
if (pkt_has_hvx_store(ctx->pkt)) {
|
||||||
gen_helper_commit_hvx_stores(cpu_env);
|
gen_helper_commit_hvx_stores(cpu_env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_exec_counters(DisasContext *ctx, Packet *pkt)
|
static void update_exec_counters(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
int num_insns = pkt->num_insns;
|
int num_insns = pkt->num_insns;
|
||||||
int num_real_insns = 0;
|
int num_real_insns = 0;
|
||||||
int num_hvx_insns = 0;
|
int num_hvx_insns = 0;
|
||||||
|
@ -664,8 +742,7 @@ static void update_exec_counters(DisasContext *ctx, Packet *pkt)
|
||||||
ctx->num_hvx_insns += num_hvx_insns;
|
ctx->num_hvx_insns += num_hvx_insns;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx,
|
static void gen_commit_packet(DisasContext *ctx)
|
||||||
Packet *pkt)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If there is more than one store in a packet, make sure they are all OK
|
* If there is more than one store in a packet, make sure they are all OK
|
||||||
|
@ -684,6 +761,7 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx,
|
||||||
* store. Therefore, we call process_store_log before anything else
|
* store. Therefore, we call process_store_log before anything else
|
||||||
* involved in committing the packet.
|
* involved in committing the packet.
|
||||||
*/
|
*/
|
||||||
|
Packet *pkt = ctx->pkt;
|
||||||
bool has_store_s0 = pkt->pkt_has_store_s0;
|
bool has_store_s0 = pkt->pkt_has_store_s0;
|
||||||
bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed);
|
bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed);
|
||||||
bool has_hvx_store = pkt_has_hvx_store(pkt);
|
bool has_hvx_store = pkt_has_hvx_store(pkt);
|
||||||
|
@ -693,7 +771,7 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx,
|
||||||
* a store in slot 1 or an HVX store.
|
* a store in slot 1 or an HVX store.
|
||||||
*/
|
*/
|
||||||
g_assert(!has_store_s1 && !has_hvx_store);
|
g_assert(!has_store_s1 && !has_hvx_store);
|
||||||
process_dczeroa(ctx, pkt);
|
process_dczeroa(ctx);
|
||||||
} else if (has_hvx_store) {
|
} else if (has_hvx_store) {
|
||||||
TCGv mem_idx = tcg_constant_tl(ctx->mem_idx);
|
TCGv mem_idx = tcg_constant_tl(ctx->mem_idx);
|
||||||
|
|
||||||
|
@ -724,14 +802,14 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx,
|
||||||
gen_helper_probe_pkt_scalar_store_s0(cpu_env, mem_idx);
|
gen_helper_probe_pkt_scalar_store_s0(cpu_env, mem_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
process_store_log(ctx, pkt);
|
process_store_log(ctx);
|
||||||
|
|
||||||
gen_reg_writes(ctx);
|
gen_reg_writes(ctx);
|
||||||
gen_pred_writes(ctx, pkt);
|
gen_pred_writes(ctx);
|
||||||
if (pkt->pkt_has_hvx) {
|
if (pkt->pkt_has_hvx) {
|
||||||
gen_commit_hvx(ctx, pkt);
|
gen_commit_hvx(ctx);
|
||||||
}
|
}
|
||||||
update_exec_counters(ctx, pkt);
|
update_exec_counters(ctx);
|
||||||
if (HEX_DEBUG) {
|
if (HEX_DEBUG) {
|
||||||
TCGv has_st0 =
|
TCGv has_st0 =
|
||||||
tcg_constant_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa);
|
tcg_constant_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa);
|
||||||
|
@ -744,7 +822,8 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx,
|
||||||
|
|
||||||
if (pkt->vhist_insn != NULL) {
|
if (pkt->vhist_insn != NULL) {
|
||||||
ctx->pre_commit = false;
|
ctx->pre_commit = false;
|
||||||
pkt->vhist_insn->generate(env, ctx, pkt->vhist_insn, pkt);
|
ctx->insn = pkt->vhist_insn;
|
||||||
|
pkt->vhist_insn->generate(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkt->pkt_has_cof) {
|
if (pkt->pkt_has_cof) {
|
||||||
|
@ -766,12 +845,15 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decode_packet(nwords, words, &pkt, false) > 0) {
|
if (decode_packet(nwords, words, &pkt, false) > 0) {
|
||||||
|
pkt.pc = ctx->base.pc_next;
|
||||||
HEX_DEBUG_PRINT_PKT(&pkt);
|
HEX_DEBUG_PRINT_PKT(&pkt);
|
||||||
gen_start_packet(ctx, &pkt);
|
ctx->pkt = &pkt;
|
||||||
|
gen_start_packet(ctx);
|
||||||
for (i = 0; i < pkt.num_insns; i++) {
|
for (i = 0; i < pkt.num_insns; i++) {
|
||||||
gen_insn(env, ctx, &pkt.insn[i], &pkt);
|
ctx->insn = &pkt.insn[i];
|
||||||
|
gen_insn(ctx);
|
||||||
}
|
}
|
||||||
gen_commit_packet(env, ctx, &pkt);
|
gen_commit_packet(ctx);
|
||||||
ctx->base.pc_next += pkt.encod_pkt_size_in_bytes;
|
ctx->base.pc_next += pkt.encod_pkt_size_in_bytes;
|
||||||
} else {
|
} else {
|
||||||
gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET);
|
gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET);
|
||||||
|
@ -782,11 +864,14 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase,
|
||||||
CPUState *cs)
|
CPUState *cs)
|
||||||
{
|
{
|
||||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||||
|
uint32_t hex_flags = dcbase->tb->flags;
|
||||||
|
|
||||||
ctx->mem_idx = MMU_USER_IDX;
|
ctx->mem_idx = MMU_USER_IDX;
|
||||||
ctx->num_packets = 0;
|
ctx->num_packets = 0;
|
||||||
ctx->num_insns = 0;
|
ctx->num_insns = 0;
|
||||||
ctx->num_hvx_insns = 0;
|
ctx->num_hvx_insns = 0;
|
||||||
|
ctx->branch_cond = TCG_COND_NEVER;
|
||||||
|
ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu)
|
static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu)
|
||||||
|
@ -935,8 +1020,6 @@ void hexagon_translate_init(void)
|
||||||
}
|
}
|
||||||
hex_pred_written = tcg_global_mem_new(cpu_env,
|
hex_pred_written = tcg_global_mem_new(cpu_env,
|
||||||
offsetof(CPUHexagonState, pred_written), "pred_written");
|
offsetof(CPUHexagonState, pred_written), "pred_written");
|
||||||
hex_next_PC = tcg_global_mem_new(cpu_env,
|
|
||||||
offsetof(CPUHexagonState, next_PC), "next_PC");
|
|
||||||
hex_this_PC = tcg_global_mem_new(cpu_env,
|
hex_this_PC = tcg_global_mem_new(cpu_env,
|
||||||
offsetof(CPUHexagonState, this_PC), "this_PC");
|
offsetof(CPUHexagonState, this_PC), "this_PC");
|
||||||
hex_slot_cancelled = tcg_global_mem_new(cpu_env,
|
hex_slot_cancelled = tcg_global_mem_new(cpu_env,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
* Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -23,10 +23,14 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "exec/translator.h"
|
#include "exec/translator.h"
|
||||||
#include "tcg/tcg-op.h"
|
#include "tcg/tcg-op.h"
|
||||||
|
#include "insn.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
typedef struct DisasContext {
|
typedef struct DisasContext {
|
||||||
DisasContextBase base;
|
DisasContextBase base;
|
||||||
|
Packet *pkt;
|
||||||
|
Insn *insn;
|
||||||
|
uint32_t next_PC;
|
||||||
uint32_t mem_idx;
|
uint32_t mem_idx;
|
||||||
uint32_t num_packets;
|
uint32_t num_packets;
|
||||||
uint32_t num_insns;
|
uint32_t num_insns;
|
||||||
|
@ -53,6 +57,9 @@ typedef struct DisasContext {
|
||||||
bool qreg_is_predicated[NUM_QREGS];
|
bool qreg_is_predicated[NUM_QREGS];
|
||||||
int qreg_log_idx;
|
int qreg_log_idx;
|
||||||
bool pre_commit;
|
bool pre_commit;
|
||||||
|
TCGCond branch_cond;
|
||||||
|
target_ulong branch_dest;
|
||||||
|
bool is_tight_loop;
|
||||||
} DisasContext;
|
} DisasContext;
|
||||||
|
|
||||||
static inline void ctx_log_reg_write(DisasContext *ctx, int rnum)
|
static inline void ctx_log_reg_write(DisasContext *ctx, int rnum)
|
||||||
|
@ -83,6 +90,12 @@ static inline bool is_preloaded(DisasContext *ctx, int num)
|
||||||
return test_bit(num, ctx->regs_written);
|
return test_bit(num, ctx->regs_written);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_vreg_preloaded(DisasContext *ctx, int num)
|
||||||
|
{
|
||||||
|
return test_bit(num, ctx->vregs_updated) ||
|
||||||
|
test_bit(num, ctx->vregs_updated_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum,
|
intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum,
|
||||||
int num, bool alloc_ok);
|
int num, bool alloc_ok);
|
||||||
intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum,
|
intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum,
|
||||||
|
@ -125,7 +138,6 @@ static inline void ctx_log_qreg_write(DisasContext *ctx,
|
||||||
|
|
||||||
extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
|
extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
|
||||||
extern TCGv hex_pred[NUM_PREGS];
|
extern TCGv hex_pred[NUM_PREGS];
|
||||||
extern TCGv hex_next_PC;
|
|
||||||
extern TCGv hex_this_PC;
|
extern TCGv hex_this_PC;
|
||||||
extern TCGv hex_slot_cancelled;
|
extern TCGv hex_slot_cancelled;
|
||||||
extern TCGv hex_branch_taken;
|
extern TCGv hex_branch_taken;
|
||||||
|
@ -147,6 +159,6 @@ extern TCGv hex_vstore_addr[VSTORES_MAX];
|
||||||
extern TCGv hex_vstore_size[VSTORES_MAX];
|
extern TCGv hex_vstore_size[VSTORES_MAX];
|
||||||
extern TCGv hex_vstore_pending[VSTORES_MAX];
|
extern TCGv hex_vstore_pending[VSTORES_MAX];
|
||||||
|
|
||||||
bool is_gather_store_insn(Insn *insn, Packet *pkt);
|
bool is_gather_store_insn(DisasContext *ctx);
|
||||||
void process_store(DisasContext *ctx, Packet *pkt, int slot_num);
|
void process_store(DisasContext *ctx, int slot_num);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,7 +24,7 @@ CFLAGS += -fno-unroll-loops
|
||||||
HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon
|
HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon
|
||||||
VPATH += $(HEX_SRC)
|
VPATH += $(HEX_SRC)
|
||||||
|
|
||||||
first: $(HEX_SRC)/first.S
|
%: $(HEX_SRC)/%.S $(HEX_SRC)/crt.S
|
||||||
$(CC) -static -mv67 -nostdlib $^ -o $@
|
$(CC) -static -mv67 -nostdlib $^ -o $@
|
||||||
|
|
||||||
HEX_TESTS = first
|
HEX_TESTS = first
|
||||||
|
@ -44,6 +44,32 @@ HEX_TESTS += atomics
|
||||||
HEX_TESTS += fpstuff
|
HEX_TESTS += fpstuff
|
||||||
HEX_TESTS += overflow
|
HEX_TESTS += overflow
|
||||||
|
|
||||||
|
HEX_TESTS += test_abs
|
||||||
|
HEX_TESTS += test_bitcnt
|
||||||
|
HEX_TESTS += test_bitsplit
|
||||||
|
HEX_TESTS += test_call
|
||||||
|
HEX_TESTS += test_clobber
|
||||||
|
HEX_TESTS += test_cmp
|
||||||
|
HEX_TESTS += test_dotnew
|
||||||
|
HEX_TESTS += test_ext
|
||||||
|
HEX_TESTS += test_fibonacci
|
||||||
|
HEX_TESTS += test_hl
|
||||||
|
HEX_TESTS += test_hwloops
|
||||||
|
HEX_TESTS += test_jmp
|
||||||
|
HEX_TESTS += test_lsr
|
||||||
|
HEX_TESTS += test_mpyi
|
||||||
|
HEX_TESTS += test_packet
|
||||||
|
HEX_TESTS += test_reorder
|
||||||
|
HEX_TESTS += test_round
|
||||||
|
HEX_TESTS += test_vavgw
|
||||||
|
HEX_TESTS += test_vcmpb
|
||||||
|
HEX_TESTS += test_vcmpw
|
||||||
|
HEX_TESTS += test_vlsrw
|
||||||
|
HEX_TESTS += test_vmaxh
|
||||||
|
HEX_TESTS += test_vminh
|
||||||
|
HEX_TESTS += test_vpmpyh
|
||||||
|
HEX_TESTS += test_vspliceb
|
||||||
|
|
||||||
TESTS += $(HEX_TESTS)
|
TESTS += $(HEX_TESTS)
|
||||||
|
|
||||||
# This test has to be compiled for the -mv67t target
|
# This test has to be compiled for the -mv67t target
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#define SYS_exit_group 94
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl pass
|
||||||
|
pass:
|
||||||
|
r0 = #0
|
||||||
|
r6 = #SYS_exit_group
|
||||||
|
trap0(#1)
|
||||||
|
|
||||||
|
.globl fail
|
||||||
|
fail:
|
||||||
|
r0 = #1
|
||||||
|
r6 = #SYS_exit_group
|
||||||
|
trap0(#1)
|
|
@ -541,6 +541,75 @@ static void test_vshuff(void)
|
||||||
check_output_b(__LINE__, 1);
|
check_output_b(__LINE__, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_load_tmp_predicated(void)
|
||||||
|
{
|
||||||
|
void *p0 = buffer0;
|
||||||
|
void *p1 = buffer1;
|
||||||
|
void *pout = output;
|
||||||
|
bool pred = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < BUFSIZE; i++) {
|
||||||
|
/*
|
||||||
|
* Load into v12 as .tmp with a predicate
|
||||||
|
* When the predicate is true, we get the vector from buffer1[i]
|
||||||
|
* When the predicate is false, we get a vector of all 1's
|
||||||
|
* Regardless of the predicate, the next packet should have
|
||||||
|
* a vector of all 1's
|
||||||
|
*/
|
||||||
|
asm("v3 = vmem(%0 + #0)\n\t"
|
||||||
|
"r1 = #1\n\t"
|
||||||
|
"v12 = vsplat(r1)\n\t"
|
||||||
|
"p1 = !cmp.eq(%3, #0)\n\t"
|
||||||
|
"{\n\t"
|
||||||
|
" if (p1) v12.tmp = vmem(%1 + #0)\n\t"
|
||||||
|
" v4.w = vadd(v12.w, v3.w)\n\t"
|
||||||
|
"}\n\t"
|
||||||
|
"v4.w = vadd(v4.w, v12.w)\n\t"
|
||||||
|
"vmem(%2 + #0) = v4\n\t"
|
||||||
|
: : "r"(p0), "r"(p1), "r"(pout), "r"(pred)
|
||||||
|
: "r1", "p1", "v12", "v3", "v4", "v6", "memory");
|
||||||
|
p0 += sizeof(MMVector);
|
||||||
|
p1 += sizeof(MMVector);
|
||||||
|
pout += sizeof(MMVector);
|
||||||
|
|
||||||
|
for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) {
|
||||||
|
expect[i].w[j] =
|
||||||
|
pred ? buffer0[i].w[j] + buffer1[i].w[j] + 1
|
||||||
|
: buffer0[i].w[j] + 2;
|
||||||
|
}
|
||||||
|
pred = !pred;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_output_w(__LINE__, BUFSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_load_cur_predicated(void)
|
||||||
|
{
|
||||||
|
bool pred = true;
|
||||||
|
for (int i = 0; i < BUFSIZE; i++) {
|
||||||
|
asm volatile("p0 = !cmp.eq(%3, #0)\n\t"
|
||||||
|
"v3 = vmem(%0+#0)\n\t"
|
||||||
|
/*
|
||||||
|
* Preload v4 to make sure that the assignment from the
|
||||||
|
* packet below is not being ignored when pred is false.
|
||||||
|
*/
|
||||||
|
"r0 = #0x01237654\n\t"
|
||||||
|
"v4 = vsplat(r0)\n\t"
|
||||||
|
"{\n\t"
|
||||||
|
" if (p0) v3.cur = vmem(%1+#0)\n\t"
|
||||||
|
" v4 = v3\n\t"
|
||||||
|
"}\n\t"
|
||||||
|
"vmem(%2+#0) = v4\n\t"
|
||||||
|
:
|
||||||
|
: "r"(&buffer0[i]), "r"(&buffer1[i]),
|
||||||
|
"r"(&output[i]), "r"(pred)
|
||||||
|
: "r0", "p0", "v3", "v4", "memory");
|
||||||
|
expect[i] = pred ? buffer1[i] : buffer0[i];
|
||||||
|
pred = !pred;
|
||||||
|
}
|
||||||
|
check_output_w(__LINE__, BUFSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
init_buffers();
|
init_buffers();
|
||||||
|
@ -578,6 +647,9 @@ int main()
|
||||||
|
|
||||||
test_vshuff();
|
test_vshuff();
|
||||||
|
|
||||||
|
test_load_tmp_predicated();
|
||||||
|
test_load_cur_predicated();
|
||||||
|
|
||||||
puts(err ? "FAIL" : "PASS");
|
puts(err ? "FAIL" : "PASS");
|
||||||
return err ? 1 : 0;
|
return err ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* Purpose: test example, verify the soundness of the abs operation */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r1 = #-2
|
||||||
|
r2 = #2
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r3 = abs(r1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r3, r2); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the cl[01] operations.
|
||||||
|
*
|
||||||
|
* The number 0x000001aa has 23 leading zeroes
|
||||||
|
* they become 55 when considered as 64 bit register
|
||||||
|
* and it has 1 trailing zero.
|
||||||
|
*/
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #426
|
||||||
|
r1 = #0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = cl0(r0)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #23); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
r2 = cl0(r1:0)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #55); if (p0.new) jump:t test3
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test3:
|
||||||
|
{
|
||||||
|
r2 = ct0(r0)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #1); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* Purpose: test example, verify the soundness of the bitsplit operation */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r1 = #187
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r3:2 = bitsplit(r1, #3)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #3); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r3, #23); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test function calls and duplex instructions.
|
||||||
|
* The string "Hello there, I'm a test string!" with the first letter replaced
|
||||||
|
* with a capital L should be printed out.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SYS_write 64
|
||||||
|
#define FD_STDOUT 1
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl test
|
||||||
|
test:
|
||||||
|
{
|
||||||
|
jumpr r31
|
||||||
|
memb(r0+#0) = #76
|
||||||
|
}
|
||||||
|
.Lfunc_end0:
|
||||||
|
.Ltmp0:
|
||||||
|
.size test, .Ltmp0-test
|
||||||
|
|
||||||
|
.globl _start
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = ##dummy_buffer
|
||||||
|
allocframe(#0)
|
||||||
|
call test
|
||||||
|
}
|
||||||
|
{
|
||||||
|
call write
|
||||||
|
}
|
||||||
|
{
|
||||||
|
deallocframe
|
||||||
|
jump pass
|
||||||
|
}
|
||||||
|
.Lfunc_end1:
|
||||||
|
.Ltmp1:
|
||||||
|
.size _start, .Ltmp1-_start
|
||||||
|
|
||||||
|
write:
|
||||||
|
{
|
||||||
|
r6 = #SYS_write
|
||||||
|
r0 = #FD_STDOUT
|
||||||
|
r1 = ##dummy_buffer
|
||||||
|
r2 = #33
|
||||||
|
}
|
||||||
|
{
|
||||||
|
trap0(#1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
jumpr r31
|
||||||
|
}
|
||||||
|
|
||||||
|
.Lfunc_end2:
|
||||||
|
.Ltmp2:
|
||||||
|
.size write, .Ltmp2-write
|
||||||
|
|
||||||
|
.type dummy_buffer,@object
|
||||||
|
.data
|
||||||
|
.globl dummy_buffer
|
||||||
|
.p2align 3
|
||||||
|
dummy_buffer:
|
||||||
|
.string "Hello there, I'm a test string!\n"
|
||||||
|
.space 223
|
||||||
|
.size dummy_buffer, 256
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Purpose: demonstrate the succesful operation of the register save mechanism,
|
||||||
|
* in which the caller saves the registers that will be clobbered, and restores
|
||||||
|
* them after the call.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
allocframe(#8)
|
||||||
|
{
|
||||||
|
r16 = #47
|
||||||
|
r17 = #155
|
||||||
|
}
|
||||||
|
memd(sp+#0) = r17:16
|
||||||
|
{
|
||||||
|
r16 = #255
|
||||||
|
r17 = #42
|
||||||
|
}
|
||||||
|
{
|
||||||
|
deallocframe
|
||||||
|
r17:16 = memd(sp+#0)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r16, #47)
|
||||||
|
p0 = cmp.eq(r17, #155); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* Purpose: test a signed and unsigned comparison */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
jump signed
|
||||||
|
}
|
||||||
|
|
||||||
|
.globl signed
|
||||||
|
signed:
|
||||||
|
{
|
||||||
|
r0 = #-2
|
||||||
|
r1 = #0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.lt(r0, r1); if (p0.new) jump:t unsigned
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
.globl unsigned
|
||||||
|
unsigned:
|
||||||
|
{
|
||||||
|
r0 = #-2
|
||||||
|
r1 = #0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.gtu(r0, r1); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* Purpose: test the .new operator while performing memory stores. */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
allocframe(#16)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r0 = #1
|
||||||
|
memw(sp+#0) = r0.new
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1 = #2
|
||||||
|
memw(sp+#4) = r1.new
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #3
|
||||||
|
memw(sp+#8) = r2.new
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r0 = memw(sp+#8)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1 = memw(sp+#4)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = memw(sp+#0)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r3 = mpyi(r1, r2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
deallocframe
|
||||||
|
p0 = cmp.eq(r3, #2); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
/* Purpose: test immediate extender instructions. */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r2 = ##-559038737
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, ##-559038737); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* Purpose: computes the Fibonacci series up to a constant number. */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r2 = #100
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.gt(r2, #0); if (!p0.new) jump:nt .LBB0_3
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r3 = #0
|
||||||
|
r4 = #1
|
||||||
|
}
|
||||||
|
.LBB0_2:
|
||||||
|
{
|
||||||
|
r5 = r4
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.gt(r2, r5); if (p0.new) jump:nt .LBB0_2
|
||||||
|
r4 = add(r3, r4)
|
||||||
|
r3 = r5
|
||||||
|
}
|
||||||
|
.LBB0_3:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r3, #144); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* Purpose: test example, verify the soundness of the high/low assignment */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0.H = #42
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r0.L = #69
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #2752581); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/* Purpose: simple C Program to test hardware loops. */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
loop0(.LBB0_1, #10)
|
||||||
|
r2 = #0
|
||||||
|
}
|
||||||
|
.LBB0_1:
|
||||||
|
{
|
||||||
|
r2 = add(r2, #1)
|
||||||
|
nop
|
||||||
|
}:endloop0
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #10); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* Purpose: test example, verify the soundness of the jump operation */
|
||||||
|
|
||||||
|
#define SYS_exit_group 94
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
jump pass
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Inlined fail label in crt.S so we can fail without
|
||||||
|
* having a functioning jump
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
r0 = #1
|
||||||
|
r6 = #SYS_exit_group
|
||||||
|
}
|
||||||
|
{
|
||||||
|
trap0(#1)
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* Purpose: test the soundness of the lsr operation */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #-56984
|
||||||
|
r1 = #2147483647
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #0x19
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r0 &= lsr(r1, r2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #0x28); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
r0 = #0x0000000a
|
||||||
|
r1 = #0x00000000
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #-1
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1:0 = lsl(r1:0, r2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #0x5); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* Purpose: test a simple multiplication operation */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r1 = #4
|
||||||
|
r2 = #6
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r3 = mpyi(r1, r2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r3, #24); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test that writes of a register in a packet are performed only after
|
||||||
|
* that packet has finished its execution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
allocframe(#8)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #4
|
||||||
|
r3 = #6
|
||||||
|
}
|
||||||
|
{
|
||||||
|
memw(sp+#0) = r2
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r3 = memw(sp+#0)
|
||||||
|
r0 = add(r2, r3)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
deallocframe
|
||||||
|
p0 = cmp.eq(r3, #4)
|
||||||
|
p0 = cmp.eq(r0, #10); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Purpose: demonstrate handling of .new uses appearing before the associated
|
||||||
|
* definition.
|
||||||
|
* Here we perform a jump that skips the code resetting R2 from 0xDEADBEEF to 0,
|
||||||
|
* only if P0.new is true, but P0 is assigned to 1 (R4) in the next instruction
|
||||||
|
* in the packet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r2 = #-559038737
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r4 = #1
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if (p0.new) jump:nt skip
|
||||||
|
p0 = r4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fallthrough:
|
||||||
|
{
|
||||||
|
r2 = #0
|
||||||
|
}
|
||||||
|
|
||||||
|
skip:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #-559038737); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the cround operation
|
||||||
|
* 106 = 0b1101010 with the comma at third digit is 12.5 which is crounded to 12
|
||||||
|
* but rounded to 13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r1 = #200
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = round(r1, #4)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #13); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
r2 = cround(r1, #4)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r2, #12); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vavgw operation.
|
||||||
|
*
|
||||||
|
* 0x00030001 averaged with 0x00010003 results 0x00020002.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #3
|
||||||
|
r1 = #1
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #1
|
||||||
|
r3 = #3
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1:0 = vavgw(r1:0, r3:2):crnd
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #2); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r1, #2); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vector compare bytes
|
||||||
|
* operation.
|
||||||
|
*
|
||||||
|
* Vector byte comparison between 0x1234567887654321 and 0x1234567800000000
|
||||||
|
* should result in 0b11110000 in binary, or 0xf0 in hex.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #0x87654321
|
||||||
|
r1 = #0x12345678
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #0x00000000
|
||||||
|
r3 = #0x12345678
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p2 = vcmpb.eq(r1:0, r3:2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r4 = p2
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r4, #0xf0); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vector compare words
|
||||||
|
* operation.
|
||||||
|
*
|
||||||
|
* Vector word comparison between 0x1234567887654321 and 0x1234567800000000
|
||||||
|
* should result in 0b11110000 in binary, or 0xf0 in hex.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #0x87654321
|
||||||
|
r1 = #0x12345678
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #0x00000000
|
||||||
|
r3 = #0x12345678
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p2 = vcmpw.eq(r1:0, r3:2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r4 = p2
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r4, #0xf0); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* Purpose: test the soundness of the vlsrw operation */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #0x00000001
|
||||||
|
r1 = #0x00000001
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1:0 = vlsrw(r1:0, #1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r0 = add(r0, r1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #0); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vrmaxh operation.
|
||||||
|
*
|
||||||
|
* The maximum between 0x0002000300010005 and 0x0003000200020007 is
|
||||||
|
* 0x0003000300020007.
|
||||||
|
*
|
||||||
|
* input: r1 = 0x00010003 r0 = 0x00010005 r3 = 0x00030002 r2 = 0x00020007
|
||||||
|
* output: r1 = 0x00030003 r0 = 0x00020007
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #65541
|
||||||
|
r1 = #65539
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #131079
|
||||||
|
r3 = #196610
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1:0 = vmaxh(r1:0, r3:2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #131079); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r1, #196611); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vrmaxh operation.
|
||||||
|
*
|
||||||
|
* The minimum between 0x0002000300010005 and 0x0003000200020007 is
|
||||||
|
* 0x0003000300020007.
|
||||||
|
*
|
||||||
|
* input: r1 = 0x00010003 r0 = 0x00010005 r3 = 0x00030002 r2 = 0x00020007
|
||||||
|
* output: r1 = 0x00010002 r0 = 0x00010005
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #65541
|
||||||
|
r1 = #65539
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #131079
|
||||||
|
r3 = #196610
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1:0 = vminh(r1:0, r3:2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #65541); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r1, #65538); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vpmpyh operator.
|
||||||
|
*
|
||||||
|
* 0x01020304 vector polynomial multiplied with 0x04030201 results
|
||||||
|
* 0x000400060b060b04.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #16909060
|
||||||
|
r1 = #67305985
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r1:0 = vpmpyh(r0, r1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r0, #184945412); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r1, #262150); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Purpose: test example, verify the soundness of the vspliceb operation
|
||||||
|
* the operation is a binary splice of two 64bit operators.
|
||||||
|
*
|
||||||
|
* vspliceb(0xffffffffffffffff,0x0000000000000000,5) = 0x000000ffffffffff.
|
||||||
|
*/
|
||||||
|
.text
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
{
|
||||||
|
r0 = #-1
|
||||||
|
r1 = #-1
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r2 = #0
|
||||||
|
r3 = #0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r5:4 = vspliceb(r1:0, r3:2, #5)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r4, #-1); if (p0.new) jump:t test2
|
||||||
|
jump fail
|
||||||
|
}
|
||||||
|
|
||||||
|
test2:
|
||||||
|
{
|
||||||
|
p0 = cmp.eq(r5, #255); if (p0.new) jump:t pass
|
||||||
|
jump fail
|
||||||
|
}
|
|
@ -429,6 +429,7 @@ FUNC_P_OP_P(vabshsat, "%0 = vabsh(%2):sat")
|
||||||
FUNC_P_OP_PP(vnavgwr, "%0 = vnavgw(%2, %3):rnd:sat")
|
FUNC_P_OP_PP(vnavgwr, "%0 = vnavgw(%2, %3):rnd:sat")
|
||||||
FUNC_R_OP_RI(round_ri_sat, "%0 = round(%2, #%3):sat")
|
FUNC_R_OP_RI(round_ri_sat, "%0 = round(%2, #%3):sat")
|
||||||
FUNC_R_OP_RR(asr_r_r_sat, "%0 = asr(%2, %3):sat")
|
FUNC_R_OP_RR(asr_r_r_sat, "%0 = asr(%2, %3):sat")
|
||||||
|
FUNC_R_OP_RR(asl_r_r_sat, "%0 = asl(%2, %3):sat")
|
||||||
|
|
||||||
FUNC_XPp_OP_PP(ACS, "%0, p2 = vacsh(%3, %4)")
|
FUNC_XPp_OP_PP(ACS, "%0, p2 = vacsh(%3, %4)")
|
||||||
|
|
||||||
|
@ -907,12 +908,33 @@ int main()
|
||||||
TEST_R_OP_RI(round_ri_sat, 0x0000ffff, 2, 0x00004000, USR_CLEAR);
|
TEST_R_OP_RI(round_ri_sat, 0x0000ffff, 2, 0x00004000, USR_CLEAR);
|
||||||
TEST_R_OP_RI(round_ri_sat, 0x7fffffff, 2, 0x1fffffff, USR_OVF);
|
TEST_R_OP_RI(round_ri_sat, 0x7fffffff, 2, 0x1fffffff, USR_OVF);
|
||||||
|
|
||||||
TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x00000002, 0x00003fff,
|
TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x02, 0x00003fff, USR_CLEAR);
|
||||||
USR_CLEAR);
|
TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0x01, 0xc0000000, USR_CLEAR);
|
||||||
TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xfffffff5, 0x7fffffff,
|
TEST_R_OP_RR(asr_r_r_sat, 0xffffffff, 0x01, 0xffffffff, USR_CLEAR);
|
||||||
USR_OVF);
|
TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xf5, 0x7fffffff, USR_OVF);
|
||||||
TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xfffffff5, 0x80000000,
|
TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xf5, 0x80000000, USR_OVF);
|
||||||
USR_OVF);
|
TEST_R_OP_RR(asr_r_r_sat, 0x7fff0000, 0x42, 0x7fffffff, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, 0xff000000, 0x42, 0x80000000, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, 4096, 32, 0x00000000, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, 4096, -32, 0x7fffffff, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, -4096, 32, 0xffffffff, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, -4096, -32, 0x80000000, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, 0, -32, 0x00000000, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asr_r_r_sat, 1, -32, 0x7fffffff, USR_OVF);
|
||||||
|
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0x00000000, 0x40, 0x00000000, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0x80000000, 0xff, 0xc0000000, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0xffffffff, 0xff, 0xffffffff, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0x00ffffff, 0x0b, 0x7fffffff, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0x80000000, 0x0b, 0x80000000, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0x7fff0000, 0xbe, 0x7fffffff, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0xff000000, 0xbe, 0x80000000, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 4096, 32, 0x7fffffff, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 4096, -32, 0x00000000, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, -4096, 32, 0x80000000, USR_OVF);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, -4096, -32, 0xffffffff, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 0, 32, 0x00000000, USR_CLEAR);
|
||||||
|
TEST_R_OP_RR(asl_r_r_sat, 1, 32, 0x7fffffff, USR_OVF);
|
||||||
|
|
||||||
TEST_XPp_OP_PP(ACS, 0x0004000300020001ULL, 0x0001000200030004ULL,
|
TEST_XPp_OP_PP(ACS, 0x0004000300020001ULL, 0x0001000200030004ULL,
|
||||||
0x0000000000000000ULL, 0x0004000300030004ULL, 0xf0,
|
0x0000000000000000ULL, 0x0004000300030004ULL, 0xf0,
|
||||||
|
|
Loading…
Reference in New Issue