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:
Peter Maydell 2022-12-18 17:02:11 +00:00
commit 4f9a4cd37e
59 changed files with 7908 additions and 210 deletions

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -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)

View File

@ -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)

View 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()

View File

@ -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

View File

@ -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

View File

@ -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); \

View File

@ -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"

View File

@ -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

View File

@ -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)

View 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.

View 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 */

View 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]; }
%%

View 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;
}

View 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)

File diff suppressed because it is too large Load Diff

View 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 */

View 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 "$@"

View File

@ -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;

View File

@ -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)

View File

@ -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}

View File

@ -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) \

View File

@ -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);

View 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

View File

@ -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,

View File

@ -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

View File

@ -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
View 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)

View File

@ -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;
}

View 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
}

View 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
}

View 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
}

View 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

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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)
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View File

@ -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,