mirror of
https://github.com/xemu-project/xemu.git
synced 2025-02-25 15:14:31 +00:00
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 -----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>
|
||||
S: Supported
|
||||
F: target/hexagon/
|
||||
X: target/hexagon/idef-parser/
|
||||
X: target/hexagon/gen_idef_parser_funcs.py
|
||||
F: linux-user/hexagon/
|
||||
F: tests/tcg/hexagon/
|
||||
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.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
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
S: Maintained
|
||||
|
@ -321,3 +321,6 @@ option('profiler', type: 'boolean', value: false,
|
||||
description: 'profiler support')
|
||||
option('slirp_smbd', type : 'feature', value : 'auto',
|
||||
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
|
||||
iclass.def Instruction class definitions used to determine
|
||||
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
|
||||
Helpers for loading the ELF file and making Linux system calls,
|
||||
signals, etc
|
||||
@ -47,6 +51,7 @@ header files in <BUILD_DIR>/target/hexagon
|
||||
gen_tcg_funcs.py -> tcg_funcs_generated.c.inc
|
||||
gen_tcg_func_table.py -> tcg_func_table_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
|
||||
DEF_HELPER declaration indicates the signature of the helper
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "mmvec/mmvec.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "hw/registerfields.h"
|
||||
|
||||
#define NUM_PREGS 4
|
||||
#define TOTAL_PER_THREAD_REGS 64
|
||||
@ -78,7 +79,6 @@ typedef struct CPUArchState {
|
||||
target_ulong gpr[TOTAL_PER_THREAD_REGS];
|
||||
target_ulong pred[NUM_PREGS];
|
||||
target_ulong branch_taken;
|
||||
target_ulong next_PC;
|
||||
|
||||
/* For comparing with LLDB on target - see adjust_stack_ptrs function */
|
||||
target_ulong last_pc_dumped;
|
||||
@ -153,16 +153,18 @@ struct ArchCPU {
|
||||
|
||||
#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,
|
||||
target_ulong *cs_base, uint32_t *flags)
|
||||
{
|
||||
uint32_t hex_flags = 0;
|
||||
*pc = env->gpr[HEX_REG_PC];
|
||||
*cs_base = 0;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
*flags = 0;
|
||||
#else
|
||||
#error System mode not supported on Hexagon yet
|
||||
#endif
|
||||
if (*pc == env->gpr[HEX_REG_SA0]) {
|
||||
hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1);
|
||||
}
|
||||
*flags = hex_flags;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
pkt->pkt_has_cof = false;
|
||||
pkt->pkt_has_multi_cof = false;
|
||||
pkt->pkt_has_endloop = 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->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
|
||||
|
||||
##
|
||||
## 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
|
||||
## 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)
|
||||
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 i > 0: f.write(", ")
|
||||
f.write("uint32_t slot")
|
||||
@ -287,11 +298,24 @@ def main():
|
||||
hex_common.read_attribs_file(sys.argv[2])
|
||||
hex_common.read_overrides_file(sys.argv[3])
|
||||
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()
|
||||
tagregs = hex_common.get_tagregs()
|
||||
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:
|
||||
## Skip the priv instructions
|
||||
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
||||
@ -308,6 +332,8 @@ def main():
|
||||
continue
|
||||
if ( hex_common.skip_qemu_helper(tag) ):
|
||||
continue
|
||||
if ( hex_common.is_idef_parser_enabled(tag) ):
|
||||
continue
|
||||
|
||||
gen_helper_function(f, tag, tagregs, tagimms)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/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
|
||||
## 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
|
||||
if (numscalarresults == 0):
|
||||
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_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))
|
||||
## The return type is void
|
||||
f.write(', void' )
|
||||
else:
|
||||
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_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))
|
||||
|
||||
## 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:
|
||||
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_part1(tag): f.write(' , i32' )
|
||||
f.write(')\n')
|
||||
@ -136,11 +146,24 @@ def main():
|
||||
hex_common.read_attribs_file(sys.argv[2])
|
||||
hex_common.read_overrides_file(sys.argv[3])
|
||||
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()
|
||||
tagregs = hex_common.get_tagregs()
|
||||
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:
|
||||
## Skip the priv instructions
|
||||
if ( "A_PRIV" in hex_common.attribdict[tag] ) :
|
||||
@ -158,6 +181,8 @@ def main():
|
||||
|
||||
if ( hex_common.skip_qemu_helper(tag) ):
|
||||
continue
|
||||
if ( hex_common.is_idef_parser_enabled(tag) ):
|
||||
continue
|
||||
|
||||
gen_helper_prototype(f, tag, tagregs, tagimms)
|
||||
|
||||
|
130
target/hexagon/gen_idef_parser_funcs.py
Normal file
130
target/hexagon/gen_idef_parser_funcs.py
Normal file
@ -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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -612,6 +612,409 @@
|
||||
tcg_temp_free(tmp); \
|
||||
} 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 */
|
||||
#define fGEN_TCG_F2_conv_sf2df(SHORTCODE) \
|
||||
gen_helper_conv_sf2df(RddV, cpu_env, RsV)
|
||||
@ -742,4 +1145,11 @@
|
||||
RsV = RsV; \
|
||||
} 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
|
||||
|
@ -173,6 +173,18 @@ def genptr_decl(f, tag, regtype, regid, regno):
|
||||
f.write(" ctx_future_vreg_off(ctx, %s%sN," % \
|
||||
(regtype, regid))
|
||||
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)):
|
||||
f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \
|
||||
(regtype, regid))
|
||||
@ -561,11 +573,7 @@ def genptr_dst_write_opn(f,regtype, regid, tag):
|
||||
## Generate the TCG code to call the helper
|
||||
## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;}
|
||||
## We produce:
|
||||
## static void generate_A2_add()
|
||||
## CPUHexagonState *env
|
||||
## DisasContext *ctx,
|
||||
## Insn *insn,
|
||||
## Packet *pkt)
|
||||
## static void generate_A2_add(DisasContext *ctx)
|
||||
## {
|
||||
## TCGv RdV = tcg_temp_local_new();
|
||||
## 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);
|
||||
##
|
||||
def gen_tcg_func(f, tag, regs, imms):
|
||||
f.write("static void generate_%s(\n" %tag)
|
||||
f.write(" CPUHexagonState *env,\n")
|
||||
f.write(" DisasContext *ctx,\n")
|
||||
f.write(" Insn *insn,\n")
|
||||
f.write(" Packet *pkt)\n")
|
||||
f.write("static void generate_%s(DisasContext *ctx)\n" %tag)
|
||||
f.write('{\n')
|
||||
|
||||
f.write(" Insn *insn __attribute__((unused)) = ctx->insn;\n")
|
||||
|
||||
if hex_common.need_ea(tag): gen_decl_ea_tcg(f, tag)
|
||||
i=0
|
||||
## 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)):
|
||||
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]))
|
||||
else:
|
||||
## Generate the call to the helper
|
||||
for immlett,bits,immshift in imms:
|
||||
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):
|
||||
f.write(" TCGv part1 = tcg_constant_tl(insn->part1);\n")
|
||||
if hex_common.need_slot(tag):
|
||||
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))
|
||||
i=0
|
||||
## 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:
|
||||
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_part1(tag): f.write(", part1" )
|
||||
f.write(");\n")
|
||||
@ -676,12 +716,27 @@ def main():
|
||||
hex_common.read_overrides_file(sys.argv[3])
|
||||
hex_common.read_overrides_file(sys.argv[4])
|
||||
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()
|
||||
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("#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:
|
||||
## 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
|
||||
* 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) \
|
||||
do { \
|
||||
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; \
|
||||
} while (0)
|
||||
|
||||
@ -736,7 +736,7 @@ static inline void assert_vhist_tmp(DisasContext *ctx)
|
||||
PRED; \
|
||||
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \
|
||||
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; \
|
||||
tcg_gen_br(end_label); \
|
||||
gen_set_label(false_label); \
|
||||
|
@ -29,8 +29,22 @@
|
||||
#undef QEMU_GENERATE
|
||||
#include "gen_tcg.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 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);
|
||||
}
|
||||
|
||||
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);
|
||||
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 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();
|
||||
|
||||
@ -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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
if (sign) {
|
||||
@ -296,7 +311,7 @@ static TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign)
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
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_movi_tl(hex_store_width[slot], width);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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_movi_tl(hex_store_width[slot], 8);
|
||||
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);
|
||||
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 ones = tcg_constant_tl(0xff);
|
||||
@ -456,6 +471,392 @@ static TCGv gen_8bitsof(TCGv result, TCGv value)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static void gen_vreg_store(DisasContext *ctx, Insn *insn, Packet *pkt,
|
||||
TCGv EA, intptr_t srcoff, int slot, bool aligned)
|
||||
static void gen_vreg_store(DisasContext *ctx, TCGv EA, intptr_t srcoff,
|
||||
int slot, bool aligned)
|
||||
{
|
||||
intptr_t dstoff = offsetof(CPUHexagonState, vstore[slot].data);
|
||||
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);
|
||||
gen_helper_gather_store(cpu_env, EA, sl);
|
||||
return;
|
||||
@ -626,12 +1027,148 @@ static void vec_to_qvec(size_t size, intptr_t dstoff, intptr_t srcoff)
|
||||
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 mem_idx = tcg_constant_tl(mi);
|
||||
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_func_table_generated.c.inc"
|
||||
|
@ -19,7 +19,43 @@
|
||||
#define HEXAGON_GENPTR_H
|
||||
|
||||
#include "insn.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "translate.h"
|
||||
|
||||
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
|
||||
|
@ -28,6 +28,7 @@ macros = {} # macro -> macro information...
|
||||
attribinfo = {} # Register information and misc
|
||||
tags = [] # list of all tags
|
||||
overrides = {} # tags with helper overrides
|
||||
idef_parser_enabled = {} # tags enabled for idef-parser
|
||||
|
||||
# We should do this as a hash for performance,
|
||||
# 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)
|
||||
|
||||
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():
|
||||
add_qemu_macro_attrib('fREAD_PC', '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:
|
||||
if regtype == "P" and is_written(regid):
|
||||
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):
|
||||
#print tag,beh,sem
|
||||
@ -194,7 +213,8 @@ def is_new_val(regtype, regid, tag):
|
||||
return regtype+regid+'N' in semdict[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_LOAD' in attribdict[tag]):
|
||||
return 1
|
||||
@ -207,6 +227,15 @@ def need_part1(tag):
|
||||
def need_ea(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):
|
||||
return tag in overrides.keys()
|
||||
|
||||
@ -217,6 +246,9 @@ def is_tmp_result(tag):
|
||||
def is_new_result(tag):
|
||||
return ('A_CVI_NEW' in attribdict[tag])
|
||||
|
||||
def is_idef_parser_enabled(tag):
|
||||
return tag in idef_parser_enabled
|
||||
|
||||
def imm_name(immlett):
|
||||
return "%siV" % immlett
|
||||
|
||||
@ -248,3 +280,9 @@ def read_overrides_file(name):
|
||||
continue
|
||||
tag = overridere.findall(line)[0]
|
||||
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)
|
||||
|
722
target/hexagon/idef-parser/README.rst
Normal file
722
target/hexagon/idef-parser/README.rst
Normal file
@ -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.
|
253
target/hexagon/idef-parser/idef-parser.h
Normal file
253
target/hexagon/idef-parser/idef-parser.h
Normal file
@ -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 */
|
471
target/hexagon/idef-parser/idef-parser.lex
Normal file
471
target/hexagon/idef-parser/idef-parser.lex
Normal file
@ -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]; }
|
||||
|
||||
%%
|
965
target/hexagon/idef-parser/idef-parser.y
Normal file
965
target/hexagon/idef-parser/idef-parser.y
Normal file
@ -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;
|
||||
}
|
140
target/hexagon/idef-parser/macros.inc
Normal file
140
target/hexagon/idef-parser/macros.inc
Normal file
@ -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)
|
2360
target/hexagon/idef-parser/parser-helpers.c
Normal file
2360
target/hexagon/idef-parser/parser-helpers.c
Normal file
File diff suppressed because it is too large
Load Diff
376
target/hexagon/idef-parser/parser-helpers.h
Normal file
376
target/hexagon/idef-parser/parser-helpers.h
Normal file
@ -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 */
|
24
target/hexagon/idef-parser/prepare
Executable file
24
target/hexagon/idef-parser/prepare
Executable file
@ -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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,10 +28,7 @@ struct Instruction;
|
||||
struct Packet;
|
||||
struct DisasContext;
|
||||
|
||||
typedef void (*SemanticInsn)(CPUHexagonState *env,
|
||||
struct DisasContext *ctx,
|
||||
struct Instruction *insn,
|
||||
struct Packet *pkt);
|
||||
typedef void (*SemanticInsn)(struct DisasContext *ctx);
|
||||
|
||||
struct Instruction {
|
||||
SemanticInsn generate; /* pointer to genptr routine */
|
||||
@ -57,9 +54,11 @@ typedef struct Instruction Insn;
|
||||
struct Packet {
|
||||
uint16_t num_insns;
|
||||
uint16_t encod_pkt_size_in_bytes;
|
||||
uint32_t pc;
|
||||
|
||||
/* Pre-decodes about COF */
|
||||
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_dczeroa;
|
||||
|
@ -94,9 +94,9 @@
|
||||
*/
|
||||
#define CHECK_NOSHUF(VA, SIZE) \
|
||||
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); \
|
||||
process_store(ctx, pkt, 1); \
|
||||
process_store(ctx, 1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -105,12 +105,12 @@
|
||||
TCGLabel *label = gen_new_label(); \
|
||||
tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, label); \
|
||||
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); \
|
||||
} \
|
||||
gen_set_label(label); \
|
||||
if (insn->slot == 0 && pkt->pkt_has_store_s1) { \
|
||||
process_store(ctx, pkt, 1); \
|
||||
if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \
|
||||
process_store(ctx, 1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -197,12 +197,21 @@
|
||||
#define MEM_STORE8(VA, DATA, SLOT) log_store64(env, VA, DATA, 8, SLOT)
|
||||
#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)
|
||||
#endif
|
||||
|
||||
#define LOAD_CANCEL(EA) do { CANCEL; } while (0)
|
||||
|
||||
#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 tmp = tcg_temp_new();
|
||||
@ -398,16 +407,16 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift)
|
||||
#else
|
||||
#define fREAD_GP() READ_REG(HEX_REG_GP)
|
||||
#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_P3() (READ_PREG(3))
|
||||
|
||||
#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 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'
|
||||
gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h'
|
||||
gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h'
|
||||
idef_parser_dir = meson.current_source_dir() / 'idef-parser'
|
||||
|
||||
#
|
||||
# Step 1
|
||||
@ -42,10 +43,7 @@ hexagon_ss.add(semantics_generated)
|
||||
# Step 2
|
||||
# We use Python scripts to generate the following files
|
||||
# shortcode_generated.h.inc
|
||||
# helper_protos_generated.h.inc
|
||||
# tcg_funcs_generated.c.inc
|
||||
# tcg_func_table_generated.c.inc
|
||||
# helper_funcs_generated.c.inc
|
||||
# printinsn_generated.h.inc
|
||||
# op_regs_generated.h.inc
|
||||
# op_attribs_generated.h.inc
|
||||
@ -60,24 +58,6 @@ shortcode_generated = custom_target(
|
||||
)
|
||||
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.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)
|
||||
|
||||
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.h.inc',
|
||||
output: 'printinsn_generated.h.inc',
|
||||
@ -179,4 +150,131 @@ hexagon_ss.add(files(
|
||||
'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}
|
||||
|
@ -288,7 +288,7 @@
|
||||
#endif
|
||||
#ifdef QEMU_GENERATE
|
||||
#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
|
||||
#ifdef QEMU_GENERATE
|
||||
#define fSTOREMMVQ(EA, SRC, MASK) \
|
||||
@ -300,7 +300,7 @@
|
||||
#endif
|
||||
#ifdef QEMU_GENERATE
|
||||
#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
|
||||
#define fVFOREACH(WIDTH, VAR) for (VAR = 0; VAR < fVELEM(WIDTH); VAR++)
|
||||
#define fVARRAY_ELEMENT_ACCESS(ARRAY, TYPE, INDEX) \
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "fma_emu.h"
|
||||
#include "mmvec/mmvec.h"
|
||||
#include "mmvec/macros.h"
|
||||
#include "op_helper.h"
|
||||
|
||||
#define SF_BIAS 127
|
||||
#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);
|
||||
}
|
||||
|
||||
static void log_reg_write(CPUHexagonState *env, int rnum,
|
||||
target_ulong val, uint32_t slot)
|
||||
void log_reg_write(CPUHexagonState *env, int rnum,
|
||||
target_ulong val, uint32_t slot)
|
||||
{
|
||||
HEX_DEBUG_LOG("log_reg_write[%d] = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")",
|
||||
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,
|
||||
target_ulong val, int width, int slot)
|
||||
void log_store32(CPUHexagonState *env, target_ulong addr,
|
||||
target_ulong val, int width, int slot)
|
||||
{
|
||||
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
|
||||
", %" PRId32 " [0x08%" PRIx32 "])\n",
|
||||
@ -93,8 +94,8 @@ static void log_store32(CPUHexagonState *env, target_ulong addr,
|
||||
env->mem_log_stores[slot].data32 = val;
|
||||
}
|
||||
|
||||
static void log_store64(CPUHexagonState *env, target_ulong addr,
|
||||
int64_t val, int width, int slot)
|
||||
void log_store64(CPUHexagonState *env, target_ulong addr,
|
||||
int64_t val, int width, int slot)
|
||||
{
|
||||
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
|
||||
", %" PRId64 " [0x016%" PRIx64 "])\n",
|
||||
@ -104,21 +105,27 @@ static void log_store64(CPUHexagonState *env, target_ulong addr,
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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, "
|
||||
"ignoring the second one\n");
|
||||
if (pkt_has_multi_cof) {
|
||||
/*
|
||||
* 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, "
|
||||
"ignoring the second one\n");
|
||||
} else {
|
||||
fCHECK_PCALIGN(addr);
|
||||
env->gpr[HEX_REG_PC] = addr;
|
||||
env->branch_taken = 1;
|
||||
}
|
||||
} else {
|
||||
fCHECK_PCALIGN(addr);
|
||||
env->branch_taken = 1;
|
||||
env->next_PC = addr;
|
||||
env->gpr[HEX_REG_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
|
||||
", insn = " TARGET_FMT_lx
|
||||
", 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,
|
||||
target_ulong vaddr)
|
||||
uint8_t mem_load1(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||
{
|
||||
uintptr_t ra = GETPC();
|
||||
check_noshuf(env, slot, vaddr, 1);
|
||||
return cpu_ldub_data_ra(env, vaddr, ra);
|
||||
}
|
||||
|
||||
static uint16_t mem_load2(CPUHexagonState *env, uint32_t slot,
|
||||
target_ulong vaddr)
|
||||
uint16_t mem_load2(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||
{
|
||||
uintptr_t ra = GETPC();
|
||||
check_noshuf(env, slot, vaddr, 2);
|
||||
return cpu_lduw_data_ra(env, vaddr, ra);
|
||||
}
|
||||
|
||||
static uint32_t mem_load4(CPUHexagonState *env, uint32_t slot,
|
||||
target_ulong vaddr)
|
||||
uint32_t mem_load4(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||
{
|
||||
uintptr_t ra = GETPC();
|
||||
check_noshuf(env, slot, vaddr, 4);
|
||||
return cpu_ldl_data_ra(env, vaddr, ra);
|
||||
}
|
||||
|
||||
static uint64_t mem_load8(CPUHexagonState *env, uint32_t slot,
|
||||
target_ulong vaddr)
|
||||
uint64_t mem_load8(CPUHexagonState *env, uint32_t slot, target_ulong vaddr)
|
||||
{
|
||||
uintptr_t ra = GETPC();
|
||||
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);
|
||||
env->slot_cancelled |= (1 << slot);
|
||||
|
37
target/hexagon/op_helper.h
Normal file
37
target/hexagon/op_helper.h
Normal file
@ -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_pred[NUM_PREGS];
|
||||
TCGv hex_next_PC;
|
||||
TCGv hex_this_PC;
|
||||
TCGv hex_slot_cancelled;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Packet *pkt = ctx->pkt;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void gen_exception_end_tb(DisasContext *ctx, int excp)
|
||||
{
|
||||
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);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
|
||||
@ -194,11 +237,6 @@ static bool check_for_attrib(Packet *pkt, int attrib)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool need_pc(Packet *pkt)
|
||||
{
|
||||
return check_for_attrib(pkt, A_IMPLICIT_READS_PC);
|
||||
}
|
||||
|
||||
static bool need_slot_cancelled(Packet *pkt)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
/* Clear out the disassembly context */
|
||||
ctx->next_PC = next_PC;
|
||||
ctx->reg_log_idx = 0;
|
||||
bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS);
|
||||
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 */
|
||||
if (need_pc(pkt)) {
|
||||
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next);
|
||||
}
|
||||
if (need_slot_cancelled(pkt)) {
|
||||
tcg_gen_movi_tl(hex_slot_cancelled, 0);
|
||||
}
|
||||
if (pkt->pkt_has_cof) {
|
||||
tcg_gen_movi_tl(hex_branch_taken, 0);
|
||||
tcg_gen_movi_tl(hex_next_PC, next_PC);
|
||||
if (pkt->pkt_has_multi_cof) {
|
||||
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)) {
|
||||
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) &&
|
||||
insn->new_value_producer_slot == 1) {
|
||||
/* 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
|
||||
* of the applicable instructions.
|
||||
*/
|
||||
static void mark_implicit_reg_write(DisasContext *ctx, Insn *insn,
|
||||
int attrib, int rnum)
|
||||
static void mark_implicit_reg_write(DisasContext *ctx, 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,
|
||||
* 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;
|
||||
|
||||
/* 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)) {
|
||||
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,
|
||||
int attrib, int pnum)
|
||||
static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum)
|
||||
{
|
||||
if (GET_ATTRIB(insn->opcode, attrib)) {
|
||||
if (GET_ATTRIB(ctx->insn->opcode, attrib)) {
|
||||
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, insn, 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, insn, 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, insn, 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, insn, A_IMPLICIT_WRITES_USR, HEX_REG_USR);
|
||||
mark_implicit_reg_write(ctx, insn, A_FPOP, HEX_REG_USR);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SP, HEX_REG_SP);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LR, HEX_REG_LR);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1);
|
||||
mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_USR, 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, insn, A_IMPLICIT_WRITES_P1, 1);
|
||||
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P2, 2);
|
||||
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P3, 3);
|
||||
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P0, 0);
|
||||
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P1, 1);
|
||||
mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P2, 2);
|
||||
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;
|
||||
uint32_t slot = insn->slot;
|
||||
uint16_t opcode = ctx->insn->opcode;
|
||||
uint32_t slot = ctx->insn->slot;
|
||||
uint8_t width = 0;
|
||||
|
||||
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,
|
||||
Insn *insn, Packet *pkt)
|
||||
static void gen_insn(DisasContext *ctx)
|
||||
{
|
||||
if (insn->generate) {
|
||||
mark_implicit_reg_writes(ctx, insn);
|
||||
insn->generate(env, ctx, insn, pkt);
|
||||
mark_implicit_pred_writes(ctx, insn);
|
||||
mark_store_width(ctx, insn);
|
||||
if (ctx->insn->generate) {
|
||||
mark_implicit_reg_writes(ctx);
|
||||
ctx->insn->generate(ctx);
|
||||
mark_implicit_pred_writes(ctx);
|
||||
mark_store_width(ctx);
|
||||
} else {
|
||||
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];
|
||||
|
||||
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;
|
||||
|
||||
@ -393,7 +469,7 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt)
|
||||
* instructions, we can use the non-conditional
|
||||
* write of the predicates.
|
||||
*/
|
||||
if (pkt->pkt_has_endloop) {
|
||||
if (ctx->pkt->pkt_has_endloop) {
|
||||
TCGv zero = tcg_constant_tl(0);
|
||||
TCGv pred_written = tcg_temp_new();
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -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
|
||||
* slot 1 and then slot 0. This will be important when
|
||||
* the memory accesses overlap.
|
||||
*/
|
||||
Packet *pkt = ctx->pkt;
|
||||
if (pkt->pkt_has_store_s1) {
|
||||
g_assert(!pkt->pkt_has_dczeroa);
|
||||
process_store(ctx, pkt, 1);
|
||||
process_store(ctx, 1);
|
||||
}
|
||||
if (pkt->pkt_has_store_s0) {
|
||||
g_assert(!pkt->pkt_has_dczeroa);
|
||||
process_store(ctx, pkt, 0);
|
||||
process_store(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) */
|
||||
TCGv addr = tcg_temp_new();
|
||||
TCGv_i64 zero = tcg_constant_i64(0);
|
||||
@ -567,7 +644,7 @@ static bool pkt_has_hvx_store(Packet *pkt)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gen_commit_hvx(DisasContext *ctx, Packet *pkt)
|
||||
static void gen_commit_hvx(DisasContext *ctx)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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_real_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;
|
||||
}
|
||||
|
||||
static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx,
|
||||
Packet *pkt)
|
||||
static void gen_commit_packet(DisasContext *ctx)
|
||||
{
|
||||
/*
|
||||
* 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
|
||||
* involved in committing the packet.
|
||||
*/
|
||||
Packet *pkt = ctx->pkt;
|
||||
bool has_store_s0 = pkt->pkt_has_store_s0;
|
||||
bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed);
|
||||
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.
|
||||
*/
|
||||
g_assert(!has_store_s1 && !has_hvx_store);
|
||||
process_dczeroa(ctx, pkt);
|
||||
process_dczeroa(ctx);
|
||||
} else if (has_hvx_store) {
|
||||
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);
|
||||
}
|
||||
|
||||
process_store_log(ctx, pkt);
|
||||
process_store_log(ctx);
|
||||
|
||||
gen_reg_writes(ctx);
|
||||
gen_pred_writes(ctx, pkt);
|
||||
gen_pred_writes(ctx);
|
||||
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) {
|
||||
TCGv has_st0 =
|
||||
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) {
|
||||
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) {
|
||||
@ -766,12 +845,15 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx)
|
||||
}
|
||||
|
||||
if (decode_packet(nwords, words, &pkt, false) > 0) {
|
||||
pkt.pc = ctx->base.pc_next;
|
||||
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++) {
|
||||
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;
|
||||
} else {
|
||||
gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET);
|
||||
@ -782,11 +864,14 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase,
|
||||
CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
uint32_t hex_flags = dcbase->tb->flags;
|
||||
|
||||
ctx->mem_idx = MMU_USER_IDX;
|
||||
ctx->num_packets = 0;
|
||||
ctx->num_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)
|
||||
@ -935,8 +1020,6 @@ void hexagon_translate_init(void)
|
||||
}
|
||||
hex_pred_written = tcg_global_mem_new(cpu_env,
|
||||
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,
|
||||
offsetof(CPUHexagonState, this_PC), "this_PC");
|
||||
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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -23,10 +23,14 @@
|
||||
#include "cpu.h"
|
||||
#include "exec/translator.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "insn.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef struct DisasContext {
|
||||
DisasContextBase base;
|
||||
Packet *pkt;
|
||||
Insn *insn;
|
||||
uint32_t next_PC;
|
||||
uint32_t mem_idx;
|
||||
uint32_t num_packets;
|
||||
uint32_t num_insns;
|
||||
@ -53,6 +57,9 @@ typedef struct DisasContext {
|
||||
bool qreg_is_predicated[NUM_QREGS];
|
||||
int qreg_log_idx;
|
||||
bool pre_commit;
|
||||
TCGCond branch_cond;
|
||||
target_ulong branch_dest;
|
||||
bool is_tight_loop;
|
||||
} DisasContext;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
int num, bool alloc_ok);
|
||||
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_pred[NUM_PREGS];
|
||||
extern TCGv hex_next_PC;
|
||||
extern TCGv hex_this_PC;
|
||||
extern TCGv hex_slot_cancelled;
|
||||
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_pending[VSTORES_MAX];
|
||||
|
||||
bool is_gather_store_insn(Insn *insn, Packet *pkt);
|
||||
void process_store(DisasContext *ctx, Packet *pkt, int slot_num);
|
||||
bool is_gather_store_insn(DisasContext *ctx);
|
||||
void process_store(DisasContext *ctx, int slot_num);
|
||||
#endif
|
||||
|
@ -24,7 +24,7 @@ CFLAGS += -fno-unroll-loops
|
||||
HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon
|
||||
VPATH += $(HEX_SRC)
|
||||
|
||||
first: $(HEX_SRC)/first.S
|
||||
%: $(HEX_SRC)/%.S $(HEX_SRC)/crt.S
|
||||
$(CC) -static -mv67 -nostdlib $^ -o $@
|
||||
|
||||
HEX_TESTS = first
|
||||
@ -44,6 +44,32 @@ HEX_TESTS += atomics
|
||||
HEX_TESTS += fpstuff
|
||||
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)
|
||||
|
||||
# This test has to be compiled for the -mv67t target
|
||||
|
14
tests/tcg/hexagon/crt.S
Normal file
14
tests/tcg/hexagon/crt.S
Normal file
@ -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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
init_buffers();
|
||||
@ -578,6 +647,9 @@ int main()
|
||||
|
||||
test_vshuff();
|
||||
|
||||
test_load_tmp_predicated();
|
||||
test_load_cur_predicated();
|
||||
|
||||
puts(err ? "FAIL" : "PASS");
|
||||
return err ? 1 : 0;
|
||||
}
|
||||
|
17
tests/tcg/hexagon/test_abs.S
Normal file
17
tests/tcg/hexagon/test_abs.S
Normal file
@ -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
|
||||
}
|
40
tests/tcg/hexagon/test_bitcnt.S
Normal file
40
tests/tcg/hexagon/test_bitcnt.S
Normal file
@ -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
|
||||
}
|
22
tests/tcg/hexagon/test_bitsplit.S
Normal file
22
tests/tcg/hexagon/test_bitsplit.S
Normal file
@ -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
|
||||
}
|
64
tests/tcg/hexagon/test_call.S
Normal file
64
tests/tcg/hexagon/test_call.S
Normal file
@ -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
|
29
tests/tcg/hexagon/test_clobber.S
Normal file
29
tests/tcg/hexagon/test_clobber.S
Normal file
@ -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
|
||||
}
|
31
tests/tcg/hexagon/test_cmp.S
Normal file
31
tests/tcg/hexagon/test_cmp.S
Normal file
@ -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
|
||||
}
|
38
tests/tcg/hexagon/test_dotnew.S
Normal file
38
tests/tcg/hexagon/test_dotnew.S
Normal file
@ -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
|
||||
}
|
13
tests/tcg/hexagon/test_ext.S
Normal file
13
tests/tcg/hexagon/test_ext.S
Normal file
@ -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
|
||||
}
|
30
tests/tcg/hexagon/test_fibonacci.S
Normal file
30
tests/tcg/hexagon/test_fibonacci.S
Normal file
@ -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
|
||||
}
|
16
tests/tcg/hexagon/test_hl.S
Normal file
16
tests/tcg/hexagon/test_hl.S
Normal file
@ -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
|
||||
}
|
19
tests/tcg/hexagon/test_hwloops.S
Normal file
19
tests/tcg/hexagon/test_hwloops.S
Normal file
@ -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
|
||||
}
|
22
tests/tcg/hexagon/test_jmp.S
Normal file
22
tests/tcg/hexagon/test_jmp.S
Normal file
@ -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)
|
||||
}
|
36
tests/tcg/hexagon/test_lsr.S
Normal file
36
tests/tcg/hexagon/test_lsr.S
Normal file
@ -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
|
||||
}
|
17
tests/tcg/hexagon/test_mpyi.S
Normal file
17
tests/tcg/hexagon/test_mpyi.S
Normal file
@ -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
|
||||
}
|
29
tests/tcg/hexagon/test_packet.S
Normal file
29
tests/tcg/hexagon/test_packet.S
Normal file
@ -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
|
||||
}
|
33
tests/tcg/hexagon/test_reorder.S
Normal file
33
tests/tcg/hexagon/test_reorder.S
Normal file
@ -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
|
||||
}
|
29
tests/tcg/hexagon/test_round.S
Normal file
29
tests/tcg/hexagon/test_round.S
Normal file
@ -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
|
||||
}
|
31
tests/tcg/hexagon/test_vavgw.S
Normal file
31
tests/tcg/hexagon/test_vavgw.S
Normal file
@ -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
|
||||
}
|
30
tests/tcg/hexagon/test_vcmpb.S
Normal file
30
tests/tcg/hexagon/test_vcmpb.S
Normal file
@ -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
|
||||
}
|
30
tests/tcg/hexagon/test_vcmpw.S
Normal file
30
tests/tcg/hexagon/test_vcmpw.S
Normal file
@ -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
|
||||
}
|
20
tests/tcg/hexagon/test_vlsrw.S
Normal file
20
tests/tcg/hexagon/test_vlsrw.S
Normal file
@ -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
|
||||
}
|
35
tests/tcg/hexagon/test_vmaxh.S
Normal file
35
tests/tcg/hexagon/test_vmaxh.S
Normal file
@ -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
|
||||
}
|
35
tests/tcg/hexagon/test_vminh.S
Normal file
35
tests/tcg/hexagon/test_vminh.S
Normal file
@ -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
|
||||
}
|
28
tests/tcg/hexagon/test_vpmpyh.S
Normal file
28
tests/tcg/hexagon/test_vpmpyh.S
Normal file
@ -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
|
||||
}
|
31
tests/tcg/hexagon/test_vspliceb.S
Normal file
31
tests/tcg/hexagon/test_vspliceb.S
Normal file
@ -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_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(asl_r_r_sat, "%0 = asl(%2, %3):sat")
|
||||
|
||||
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, 0x7fffffff, 2, 0x1fffffff, USR_OVF);
|
||||
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x00000002, 0x00003fff,
|
||||
USR_CLEAR);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xfffffff5, 0x7fffffff,
|
||||
USR_OVF);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xfffffff5, 0x80000000,
|
||||
USR_OVF);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x02, 0x00003fff, USR_CLEAR);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0x01, 0xc0000000, USR_CLEAR);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0xffffffff, 0x01, 0xffffffff, USR_CLEAR);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xf5, 0x7fffffff, USR_OVF);
|
||||
TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xf5, 0x80000000, 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,
|
||||
0x0000000000000000ULL, 0x0004000300030004ULL, 0xf0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user