x/arch/arm64: implement arm64 disassembler

- Add support for all basic instructions (except for the following
  6 system instructions: AT, DC, HVC, SMC, SYS and TLBI)
- Add support for all FP basic instructions
- Add support for all SIMD instructions (plan9 syntax refer to CL 41654)
- Add cases for testing all instructions and comparing disassembly result
  with external disassembler (by gnu syntax)

Disassembler framework is mainly contributed by Zheng Xu <zheng.xu@arm.com>
Testing and bug fixing are mainly contributed by Fannie Zhang <fannie.zhang@arm.com>
Other parts (such as decoder table and argument) are mainly contributed by Wei Xiao <wei.xiao@arm.com>

Fixes #19157

Change-Id: I76b6b075f487857124c8ca774a12d764d4ee85c7
Reviewed-on: https://go-review.googlesource.com/43651
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Wei Xiao 2017-05-16 16:53:28 +08:00 коммит произвёл Cherry Zhang
Родитель b19d1a73c7
Коммит f40095975f
16 изменённых файлов: 20892 добавлений и 0 удалений

494
arm64/arm64asm/arg.go Normal file
Просмотреть файл

@ -0,0 +1,494 @@
// Generated by ARM internal tool
// DO NOT EDIT
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
// Naming for Go decoder arguments:
//
// - arg_Wd: a W register encoded in the Rd[4:0] field (31 is wzr)
//
// - arg_Xd: a X register encoded in the Rd[4:0] field (31 is xzr)
//
// - arg_Wds: a W register encoded in the Rd[4:0] field (31 is wsp)
//
// - arg_Xds: a X register encoded in the Rd[4:0] field (31 is sp)
//
// - arg_Wn: encoded in Rn[9:5]
//
// - arg_Wm: encoded in Rm[20:16]
//
// - arg_Wm_extend__UXTB_0__UXTH_1__LSL_UXTW_2__UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4:
// a W register encoded in Rm with an extend encoded in option[15:13] and an amount
// encoded in imm3[12:10] in the range [0,4].
//
// - arg_Rm_extend__UXTB_0__UXTH_1__UXTW_2__LSL_UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4:
// a W or X register encoded in Rm with an extend encoded in option[15:13] and an
// amount encoded in imm3[12:10] in the range [0,4]. If the extend is UXTX or SXTX,
// it's an X register else, it's a W register.
//
// - arg_Wm_shift__LSL_0__LSR_1__ASR_2__0_31:
// a W register encoded in Rm with a shift encoded in shift[23:22] and an amount
// encoded in imm6[15:10] in the range [0,31].
//
// - arg_IAddSub:
// An immediate for a add/sub instruction encoded in imm12[21:10] with an optional
// left shift of 12 encoded in shift[23:22].
//
// - arg_Rt_31_1__W_0__X_1:
// a W or X register encoded in Rt[4:0]. The width specifier is encoded in the field
// [31:31] (offset 31, bit count 1) and the register is W for 0 and X for 1.
//
// - arg_[s|u]label_FIELDS_POWER:
// a program label encoded as "FIELDS" times 2^POWER in the range [MIN, MAX] (determined
// by signd/unsigned, FIELDS and POWER), e.g.
// arg_slabel_imm14_2
// arg_slabel_imm19_2
// arg_slabel_imm26_2
// arg_slabel_immhi_immlo_0
// arg_slabel_immhi_immlo_12
//
// - arg_Xns_mem_post_imm7_8_signed:
// addressing mode of post-index with a base register: Xns and a signed offset encoded
// in the "imm7" field times 8
//
// - arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__3_1:
// addressing mode of extended register with a base register: Xns, an offset register
// (<Wm>|<Xm>) with an extend encoded in option[15:13] and a shift amount encoded in
// S[12:12] in the range [0,3] (S=0:0, S=1:3).
//
// - arg_Xns_mem_optional_imm12_4_unsigned:
// addressing mode of unsigned offset with a base register: Xns and an optional unsigned
// offset encoded in the "imm12" field times 4
//
// - arg_Xns_mem_wb_imm7_4_signed:
// addressing mode of pre-index with a base register: Xns and the signed offset encoded
// in the "imm7" field times 4
//
// - arg_Xns_mem_post_size_1_8_unsigned__4_0__8_1__16_2__32_3:
// a post-index immediate offset, encoded in the "size" field. It can have the following values:
// #4 when size = 00
// #8 when size = 01
// #16 when size = 10
// #32 when size = 11
//
// - arg_immediate_0_127_CRm_op2:
// an immediate encoded in "CRm:op2" in the range 0 to 127
//
// - arg_immediate_bitmask_64_N_imms_immr:
// a bitmask immediate for 64-bit variant and encoded in "N:imms:immr"
//
// - arg_immediate_SBFX_SBFM_64M_bitfield_width_64_imms:
// an immediate for the <width> bitfield of SBFX 64-bit variant
//
// - arg_immediate_shift_32_implicit_inverse_imm16_hw:
// a 32-bit immediate of the bitwise inverse of which can be encoded in "imm16:hw"
//
// - arg_cond_[Not]AllowALNV_[Invert|Normal]:
// a standard condition, encoded in the "cond" field, excluding (NotAllow) AL and NV with
// its least significant bit [Yes|No] inverted, e.g.
// arg_cond_AllowALNV_Normal
// arg_cond_NotAllowALNV_Invert
//
// - arg_immediate_OptLSL_amount_16_0_48:
// An immediate for MOV[KNZ] instruction encoded in imm16[20:5] with an optional
// left shift of 16 in the range [0, 48] encoded in hw[22, 21]
//
// - arg_immediate_0_width_m1_immh_immb__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4__UIntimmhimmb64_8:
// the left shift amount, in the range 0 to the operand width in bits minus 1,
// encoded in the "immh:immb" field. It can have the following values:
// (UInt(immh:immb)-8) when immh = 0001
// (UInt(immh:immb)-16) when immh = 001x
// (UInt(immh:immb)-32) when immh = 01xx
// (UInt(immh:immb)-64) when immh = 1xxx
//
// - arg_immediate_1_width_immh_immb__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4:
// the right shift amount, in the range 1 to the destination operand width in
// bits, encoded in the "immh:immb" field. It can have the following values:
// (16-UInt(immh:immb)) when immh = 0001
// (32-UInt(immh:immb)) when immh = 001x
// (64-UInt(immh:immb)) when immh = 01xx
//
// - arg_immediate_8x8_a_b_c_d_e_f_g_h:
// a 64-bit immediate 'aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh',
// encoded in "a:b:c:d:e:f:g:h".
//
// - arg_immediate_fbits_min_1_max_32_sub_64_scale:
// the number of bits after the binary point in the fixed-point destination,
// in the range 1 to 32, encoded as 64 minus "scale".
//
// - arg_immediate_floatzero: #0.0
//
// - arg_immediate_exp_3_pre_4_a_b_c_d_e_f_g_h:
// a signed floating-point constant with 3-bit exponent and normalized 4 bits of precision,
// encoded in "a:b:c:d:e:f:g:h"
//
// - arg_immediate_fbits_min_1_max_0_sub_0_immh_immb__64UIntimmhimmb_4__128UIntimmhimmb_8:
// the number of fractional bits, in the range 1 to the operand width, encoded
// in the "immh:immb" field. It can have the following values:
// (64-UInt(immh:immb)) when immh = 01xx
// (128-UInt(immh:immb)) when immh = 1xxx
//
// - arg_immediate_index_Q_imm4__imm4lt20gt_00__imm4_10:
// the lowest numbered byte element to be extracted, encoded in the "Q:imm4" field.
// It can have the following values:
// imm4<2:0> when Q = 0, imm4<3> = 0
// imm4 when Q = 1, imm4<3> = x
//
// - arg_sysop_AT_SYS_CR_system:
// system operation for system instruction: AT encoded in the "op1:CRm<0>:op2" field
//
// - arg_prfop_Rt:
// prefectch operation encoded in the "Rt"
//
// - arg_sysreg_o0_op1_CRn_CRm_op2:
// system register name encoded in the "o0:op1:CRn:CRm:op2"
//
// - arg_pstatefield_op1_op2__SPSel_05__DAIFSet_36__DAIFClr_37:
// PSTATE field name encoded in the "op1:op2" field
//
// - arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31:
// one register with arrangement specifier encoded in the "size:Q" field which can have the following values:
// 8B when size = 00, Q = 0
// 16B when size = 00, Q = 1
// 4H when size = 01, Q = 0
// 8H when size = 01, Q = 1
// 2S when size = 10, Q = 0
// 4S when size = 10, Q = 1
// 2D when size = 11, Q = 1
// The encoding size = 11, Q = 0 is reserved.
//
// - arg_Vt_3_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31:
// three registers with arrangement specifier encoded in the "size:Q" field which can have the following values:
// 8B when size = 00, Q = 0
// 16B when size = 00, Q = 1
// 4H when size = 01, Q = 0
// 8H when size = 01, Q = 1
// 2S when size = 10, Q = 0
// 4S when size = 10, Q = 1
// 2D when size = 11, Q = 1
// The encoding size = 11, Q = 0 is reserved.
//
// - arg_Vt_1_arrangement_H_index__Q_S_size_1:
// one register with arrangement:H and element index encoded in "Q:S:size<1>".
type instArg uint16
const (
_ instArg = iota
arg_Bt
arg_Cm
arg_Cn
arg_cond_AllowALNV_Normal
arg_conditional
arg_cond_NotAllowALNV_Invert
arg_Da
arg_Dd
arg_Dm
arg_Dn
arg_Dt
arg_Dt2
arg_Hd
arg_Hn
arg_Ht
arg_IAddSub
arg_immediate_0_127_CRm_op2
arg_immediate_0_15_CRm
arg_immediate_0_15_nzcv
arg_immediate_0_31_imm5
arg_immediate_0_31_immr
arg_immediate_0_31_imms
arg_immediate_0_63_b5_b40
arg_immediate_0_63_immh_immb__UIntimmhimmb64_8
arg_immediate_0_63_immr
arg_immediate_0_63_imms
arg_immediate_0_65535_imm16
arg_immediate_0_7_op1
arg_immediate_0_7_op2
arg_immediate_0_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4
arg_immediate_0_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4__UIntimmhimmb64_8
arg_immediate_0_width_m1_immh_immb__UIntimmhimmb8_1__UIntimmhimmb16_2__UIntimmhimmb32_4__UIntimmhimmb64_8
arg_immediate_0_width_size__8_0__16_1__32_2
arg_immediate_1_64_immh_immb__128UIntimmhimmb_8
arg_immediate_1_width_immh_immb__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4
arg_immediate_1_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4
arg_immediate_1_width_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__16UIntimmhimmb_1__32UIntimmhimmb_2__64UIntimmhimmb_4__128UIntimmhimmb_8
arg_immediate_8x8_a_b_c_d_e_f_g_h
arg_immediate_ASR_SBFM_32M_bitfield_0_31_immr
arg_immediate_ASR_SBFM_64M_bitfield_0_63_immr
arg_immediate_BFI_BFM_32M_bitfield_lsb_32_immr
arg_immediate_BFI_BFM_32M_bitfield_width_32_imms
arg_immediate_BFI_BFM_64M_bitfield_lsb_64_immr
arg_immediate_BFI_BFM_64M_bitfield_width_64_imms
arg_immediate_BFXIL_BFM_32M_bitfield_lsb_32_immr
arg_immediate_BFXIL_BFM_32M_bitfield_width_32_imms
arg_immediate_BFXIL_BFM_64M_bitfield_lsb_64_immr
arg_immediate_BFXIL_BFM_64M_bitfield_width_64_imms
arg_immediate_bitmask_32_imms_immr
arg_immediate_bitmask_64_N_imms_immr
arg_immediate_exp_3_pre_4_a_b_c_d_e_f_g_h
arg_immediate_exp_3_pre_4_imm8
arg_immediate_fbits_min_1_max_0_sub_0_immh_immb__64UIntimmhimmb_4__128UIntimmhimmb_8
arg_immediate_fbits_min_1_max_0_sub_0_immh_immb__SEEAdvancedSIMDmodifiedimmediate_0__64UIntimmhimmb_4__128UIntimmhimmb_8
arg_immediate_fbits_min_1_max_32_sub_64_scale
arg_immediate_fbits_min_1_max_64_sub_64_scale
arg_immediate_floatzero
arg_immediate_index_Q_imm4__imm4lt20gt_00__imm4_10
arg_immediate_LSL_UBFM_32M_bitfield_0_31_immr
arg_immediate_LSL_UBFM_64M_bitfield_0_63_immr
arg_immediate_LSR_UBFM_32M_bitfield_0_31_immr
arg_immediate_LSR_UBFM_64M_bitfield_0_63_immr
arg_immediate_MSL__a_b_c_d_e_f_g_h_cmode__8_0__16_1
arg_immediate_optional_0_15_CRm
arg_immediate_optional_0_65535_imm16
arg_immediate_OptLSL__a_b_c_d_e_f_g_h_cmode__0_0__8_1
arg_immediate_OptLSL__a_b_c_d_e_f_g_h_cmode__0_0__8_1__16_2__24_3
arg_immediate_OptLSL_amount_16_0_16
arg_immediate_OptLSL_amount_16_0_48
arg_immediate_OptLSLZero__a_b_c_d_e_f_g_h
arg_immediate_SBFIZ_SBFM_32M_bitfield_lsb_32_immr
arg_immediate_SBFIZ_SBFM_32M_bitfield_width_32_imms
arg_immediate_SBFIZ_SBFM_64M_bitfield_lsb_64_immr
arg_immediate_SBFIZ_SBFM_64M_bitfield_width_64_imms
arg_immediate_SBFX_SBFM_32M_bitfield_lsb_32_immr
arg_immediate_SBFX_SBFM_32M_bitfield_width_32_imms
arg_immediate_SBFX_SBFM_64M_bitfield_lsb_64_immr
arg_immediate_SBFX_SBFM_64M_bitfield_width_64_imms
arg_immediate_shift_32_implicit_imm16_hw
arg_immediate_shift_32_implicit_inverse_imm16_hw
arg_immediate_shift_64_implicit_imm16_hw
arg_immediate_shift_64_implicit_inverse_imm16_hw
arg_immediate_UBFIZ_UBFM_32M_bitfield_lsb_32_immr
arg_immediate_UBFIZ_UBFM_32M_bitfield_width_32_imms
arg_immediate_UBFIZ_UBFM_64M_bitfield_lsb_64_immr
arg_immediate_UBFIZ_UBFM_64M_bitfield_width_64_imms
arg_immediate_UBFX_UBFM_32M_bitfield_lsb_32_immr
arg_immediate_UBFX_UBFM_32M_bitfield_width_32_imms
arg_immediate_UBFX_UBFM_64M_bitfield_lsb_64_immr
arg_immediate_UBFX_UBFM_64M_bitfield_width_64_imms
arg_immediate_zero
arg_option_DMB_BO_system_CRm
arg_option_DSB_BO_system_CRm
arg_option_ISB_BI_system_CRm
arg_prfop_Rt
arg_pstatefield_op1_op2__SPSel_05__DAIFSet_36__DAIFClr_37
arg_Qd
arg_Qn
arg_Qt
arg_Qt2
arg_Rm_extend__UXTB_0__UXTH_1__UXTW_2__LSL_UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4
arg_Rn_16_5__W_1__W_2__W_4__X_8
arg_Rt_31_1__W_0__X_1
arg_Sa
arg_Sd
arg_slabel_imm14_2
arg_slabel_imm19_2
arg_slabel_imm26_2
arg_slabel_immhi_immlo_0
arg_slabel_immhi_immlo_12
arg_Sm
arg_Sn
arg_St
arg_St2
arg_sysop_AT_SYS_CR_system
arg_sysop_DC_SYS_CR_system
arg_sysop_IC_SYS_CR_system
arg_sysop_SYS_CR_system
arg_sysop_TLBI_SYS_CR_system
arg_sysreg_o0_op1_CRn_CRm_op2
arg_Vd_16_5__B_1__H_2__S_4__D_8
arg_Vd_19_4__B_1__H_2__S_4
arg_Vd_19_4__B_1__H_2__S_4__D_8
arg_Vd_19_4__D_8
arg_Vd_19_4__S_4__D_8
arg_Vd_22_1__S_0
arg_Vd_22_1__S_0__D_1
arg_Vd_22_1__S_1
arg_Vd_22_2__B_0__H_1__S_2
arg_Vd_22_2__B_0__H_1__S_2__D_3
arg_Vd_22_2__D_3
arg_Vd_22_2__H_0__S_1__D_2
arg_Vd_22_2__H_1__S_2
arg_Vd_22_2__S_1__D_2
arg_Vd_arrangement_16B
arg_Vd_arrangement_2D
arg_Vd_arrangement_4S
arg_Vd_arrangement_D_index__1
arg_Vd_arrangement_imm5___B_1__H_2__S_4__D_8_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4__imm5lt4gt_8_1
arg_Vd_arrangement_imm5_Q___8B_10__16B_11__4H_20__8H_21__2S_40__4S_41__2D_81
arg_Vd_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__2S_40__4S_41__2D_81
arg_Vd_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41
arg_Vd_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41__2D_81
arg_Vd_arrangement_immh___SEEAdvancedSIMDmodifiedimmediate_0__8H_1__4S_2__2D_4
arg_Vd_arrangement_Q___2S_0__4S_1
arg_Vd_arrangement_Q___4H_0__8H_1
arg_Vd_arrangement_Q___8B_0__16B_1
arg_Vd_arrangement_Q_sz___2S_00__4S_10__2D_11
arg_Vd_arrangement_size___4S_1__2D_2
arg_Vd_arrangement_size___8H_0__1Q_3
arg_Vd_arrangement_size___8H_0__4S_1__2D_2
arg_Vd_arrangement_size_Q___4H_00__8H_01__2S_10__4S_11__1D_20__2D_21
arg_Vd_arrangement_size_Q___4H_10__8H_11__2S_20__4S_21
arg_Vd_arrangement_size_Q___8B_00__16B_01
arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11
arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21
arg_Vd_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vd_arrangement_sz___4S_0__2D_1
arg_Vd_arrangement_sz_Q___2S_00__4S_01
arg_Vd_arrangement_sz_Q___2S_00__4S_01__2D_11
arg_Vd_arrangement_sz_Q___2S_10__4S_11
arg_Vd_arrangement_sz_Q___4H_00__8H_01__2S_10__4S_11
arg_Vm_22_1__S_0__D_1
arg_Vm_22_2__B_0__H_1__S_2__D_3
arg_Vm_22_2__D_3
arg_Vm_22_2__H_1__S_2
arg_Vm_arrangement_4S
arg_Vm_arrangement_Q___8B_0__16B_1
arg_Vm_arrangement_size___8H_0__4S_1__2D_2
arg_Vm_arrangement_size___H_1__S_2_index__size_L_H_M__HLM_1__HL_2_1
arg_Vm_arrangement_size_Q___4H_10__8H_11__2S_20__4S_21
arg_Vm_arrangement_size_Q___8B_00__16B_01
arg_Vm_arrangement_size_Q___8B_00__16B_01__1D_30__2D_31
arg_Vm_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21
arg_Vm_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vm_arrangement_sz_Q___2S_00__4S_01__2D_11
arg_Vm_arrangement_sz___S_0__D_1_index__sz_L_H__HL_00__H_10_1
arg_Vn_19_4__B_1__H_2__S_4__D_8
arg_Vn_19_4__D_8
arg_Vn_19_4__H_1__S_2__D_4
arg_Vn_19_4__S_4__D_8
arg_Vn_1_arrangement_16B
arg_Vn_22_1__D_1
arg_Vn_22_1__S_0__D_1
arg_Vn_22_2__B_0__H_1__S_2__D_3
arg_Vn_22_2__D_3
arg_Vn_22_2__H_0__S_1__D_2
arg_Vn_22_2__H_1__S_2
arg_Vn_2_arrangement_16B
arg_Vn_3_arrangement_16B
arg_Vn_4_arrangement_16B
arg_Vn_arrangement_16B
arg_Vn_arrangement_4S
arg_Vn_arrangement_D_index__1
arg_Vn_arrangement_D_index__imm5_1
arg_Vn_arrangement_imm5___B_1__H_2_index__imm5__imm5lt41gt_1__imm5lt42gt_2_1
arg_Vn_arrangement_imm5___B_1__H_2__S_4__D_8_index__imm5_imm4__imm4lt30gt_1__imm4lt31gt_2__imm4lt32gt_4__imm4lt3gt_8_1
arg_Vn_arrangement_imm5___B_1__H_2__S_4__D_8_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4__imm5lt4gt_8_1
arg_Vn_arrangement_imm5___B_1__H_2__S_4_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4_1
arg_Vn_arrangement_imm5___D_8_index__imm5_1
arg_Vn_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__2S_40__4S_41__2D_81
arg_Vn_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41
arg_Vn_arrangement_immh_Q___SEEAdvancedSIMDmodifiedimmediate_00__8B_10__16B_11__4H_20__8H_21__2S_40__4S_41__2D_81
arg_Vn_arrangement_immh___SEEAdvancedSIMDmodifiedimmediate_0__8H_1__4S_2__2D_4
arg_Vn_arrangement_Q___8B_0__16B_1
arg_Vn_arrangement_Q_sz___2S_00__4S_10__2D_11
arg_Vn_arrangement_Q_sz___4S_10
arg_Vn_arrangement_S_index__imm5__imm5lt41gt_1__imm5lt42gt_2__imm5lt43gt_4_1
arg_Vn_arrangement_size___2D_3
arg_Vn_arrangement_size___8H_0__4S_1__2D_2
arg_Vn_arrangement_size_Q___4H_10__8H_11__2S_20__4S_21
arg_Vn_arrangement_size_Q___8B_00__16B_01
arg_Vn_arrangement_size_Q___8B_00__16B_01__1D_30__2D_31
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vn_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__4S_21
arg_Vn_arrangement_sz___2D_1
arg_Vn_arrangement_sz___2S_0__2D_1
arg_Vn_arrangement_sz___4S_0__2D_1
arg_Vn_arrangement_sz_Q___2S_00__4S_01
arg_Vn_arrangement_sz_Q___2S_00__4S_01__2D_11
arg_Vn_arrangement_sz_Q___4H_00__8H_01__2S_10__4S_11
arg_Vt_1_arrangement_B_index__Q_S_size_1
arg_Vt_1_arrangement_D_index__Q_1
arg_Vt_1_arrangement_H_index__Q_S_size_1
arg_Vt_1_arrangement_S_index__Q_S_1
arg_Vt_1_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_2_arrangement_B_index__Q_S_size_1
arg_Vt_2_arrangement_D_index__Q_1
arg_Vt_2_arrangement_H_index__Q_S_size_1
arg_Vt_2_arrangement_S_index__Q_S_1
arg_Vt_2_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_2_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vt_3_arrangement_B_index__Q_S_size_1
arg_Vt_3_arrangement_D_index__Q_1
arg_Vt_3_arrangement_H_index__Q_S_size_1
arg_Vt_3_arrangement_S_index__Q_S_1
arg_Vt_3_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_3_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Vt_4_arrangement_B_index__Q_S_size_1
arg_Vt_4_arrangement_D_index__Q_1
arg_Vt_4_arrangement_H_index__Q_S_size_1
arg_Vt_4_arrangement_S_index__Q_S_1
arg_Vt_4_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__1D_30__2D_31
arg_Vt_4_arrangement_size_Q___8B_00__16B_01__4H_10__8H_11__2S_20__4S_21__2D_31
arg_Wa
arg_Wd
arg_Wds
arg_Wm
arg_Wm_extend__UXTB_0__UXTH_1__LSL_UXTW_2__UXTX_3__SXTB_4__SXTH_5__SXTW_6__SXTX_7__0_4
arg_Wm_shift__LSL_0__LSR_1__ASR_2__0_31
arg_Wm_shift__LSL_0__LSR_1__ASR_2__ROR_3__0_31
arg_Wn
arg_Wns
arg_Ws
arg_Wt
arg_Wt2
arg_Xa
arg_Xd
arg_Xds
arg_Xm
arg_Xm_shift__LSL_0__LSR_1__ASR_2__0_63
arg_Xm_shift__LSL_0__LSR_1__ASR_2__ROR_3__0_63
arg_Xn
arg_Xns
arg_Xns_mem
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__1_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__2_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__3_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__0_0__4_1
arg_Xns_mem_extend_m__UXTW_2__LSL_3__SXTW_6__SXTX_7__absent_0__0_1
arg_Xns_mem_offset
arg_Xns_mem_optional_imm12_16_unsigned
arg_Xns_mem_optional_imm12_1_unsigned
arg_Xns_mem_optional_imm12_2_unsigned
arg_Xns_mem_optional_imm12_4_unsigned
arg_Xns_mem_optional_imm12_8_unsigned
arg_Xns_mem_optional_imm7_16_signed
arg_Xns_mem_optional_imm7_4_signed
arg_Xns_mem_optional_imm7_8_signed
arg_Xns_mem_optional_imm9_1_signed
arg_Xns_mem_post_fixedimm_1
arg_Xns_mem_post_fixedimm_12
arg_Xns_mem_post_fixedimm_16
arg_Xns_mem_post_fixedimm_2
arg_Xns_mem_post_fixedimm_24
arg_Xns_mem_post_fixedimm_3
arg_Xns_mem_post_fixedimm_32
arg_Xns_mem_post_fixedimm_4
arg_Xns_mem_post_fixedimm_6
arg_Xns_mem_post_fixedimm_8
arg_Xns_mem_post_imm7_16_signed
arg_Xns_mem_post_imm7_4_signed
arg_Xns_mem_post_imm7_8_signed
arg_Xns_mem_post_imm9_1_signed
arg_Xns_mem_post_Q__16_0__32_1
arg_Xns_mem_post_Q__24_0__48_1
arg_Xns_mem_post_Q__32_0__64_1
arg_Xns_mem_post_Q__8_0__16_1
arg_Xns_mem_post_size__1_0__2_1__4_2__8_3
arg_Xns_mem_post_size__2_0__4_1__8_2__16_3
arg_Xns_mem_post_size__3_0__6_1__12_2__24_3
arg_Xns_mem_post_size__4_0__8_1__16_2__32_3
arg_Xns_mem_post_Xm
arg_Xns_mem_wb_imm7_16_signed
arg_Xns_mem_wb_imm7_4_signed
arg_Xns_mem_wb_imm7_8_signed
arg_Xns_mem_wb_imm9_1_signed
arg_Xs
arg_Xt
arg_Xt2
)

329
arm64/arm64asm/condition.go Executable file
Просмотреть файл

@ -0,0 +1,329 @@
// Generated by ARM internal tool
// DO NOT EDIT
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
// Following functions are used as the predicator: canDecode of according instruction
// Refer to instFormat inside decode.go for more details
func at_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x7, 0x8, (instr>>5)&0x7) == Sys_AT
}
func bfi_bfm_32m_bitfield_cond(instr uint32) bool {
return (instr>>5)&0x1f != 0x1f && uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func bfi_bfm_64m_bitfield_cond(instr uint32) bool {
return (instr>>5)&0x1f != 0x1f && uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func bfxil_bfm_32m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) >= uint8((instr>>16)&0x3f)
}
func bfxil_bfm_64m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) >= uint8((instr>>16)&0x3f)
}
func cinc_csinc_32_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cinc_csinc_64_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cinv_csinv_32_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cinv_csinv_64_condsel_cond(instr uint32) bool {
return instr&0x1f0000 != 0x1f0000 && instr&0xe000 != 0xe000 && instr&0x3e0 != 0x3e0 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cneg_csneg_32_condsel_cond(instr uint32) bool {
return instr&0xe000 != 0xe000 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func cneg_csneg_64_condsel_cond(instr uint32) bool {
return instr&0xe000 != 0xe000 && (instr>>5)&0x1f == (instr>>16)&0x1f
}
func csinc_general_cond(instr uint32) bool {
return instr&0xe000 != 0xe000
}
func csinv_general_cond(instr uint32) bool {
return instr&0xe000 != 0xe000
}
func dc_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x7, (instr>>8)&0xf, (instr>>5)&0x7) == Sys_DC
}
func ic_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x7, (instr>>8)&0xf, (instr>>5)&0x7) == Sys_IC
}
func lsl_ubfm_32m_bitfield_cond(instr uint32) bool {
return instr&0xfc00 != 0x7c00 && (instr>>10)&0x3f+1 == (instr>>16)&0x3f
}
func lsl_ubfm_64m_bitfield_cond(instr uint32) bool {
return instr&0xfc00 != 0xfc00 && (instr>>10)&0x3f+1 == (instr>>16)&0x3f
}
func mov_orr_32_log_imm_cond(instr uint32) bool {
return !move_wide_preferred_4((instr>>31)&0x1, (instr>>22)&0x1, (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func mov_orr_64_log_imm_cond(instr uint32) bool {
return !move_wide_preferred_4((instr>>31)&0x1, (instr>>22)&0x1, (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func mov_movn_32_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0) && !is_ones_n16((instr>>5)&0xffff)
}
func mov_movn_64_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0)
}
func mov_add_32_addsub_imm_cond(instr uint32) bool {
return instr&0x1f == 0x1f || (instr>>5)&0x1f == 0x1f
}
func mov_add_64_addsub_imm_cond(instr uint32) bool {
return instr&0x1f == 0x1f || (instr>>5)&0x1f == 0x1f
}
func mov_movz_32_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0)
}
func mov_movz_64_movewide_cond(instr uint32) bool {
return !(is_zero((instr>>5)&0xffff) && (instr>>21)&0x3 != 0x0)
}
func ror_extr_32_extract_cond(instr uint32) bool {
return (instr>>5)&0x1f == (instr>>16)&0x1f
}
func ror_extr_64_extract_cond(instr uint32) bool {
return (instr>>5)&0x1f == (instr>>16)&0x1f
}
func sbfiz_sbfm_32m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func sbfiz_sbfm_64m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func sbfx_sbfm_32m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func sbfx_sbfm_64m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func tlbi_sys_cr_system_cond(instr uint32) bool {
return sys_op_4((instr>>16)&0x7, 0x8, (instr>>8)&0xf, (instr>>5)&0x7) == Sys_TLBI
}
func ubfiz_ubfm_32m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func ubfiz_ubfm_64m_bitfield_cond(instr uint32) bool {
return uint8((instr>>10)&0x3f) < uint8((instr>>16)&0x3f)
}
func ubfx_ubfm_32m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func ubfx_ubfm_64m_bitfield_cond(instr uint32) bool {
return bfxpreferred_4((instr>>31)&0x1, extract_bit((instr>>29)&0x3, 1), (instr>>10)&0x3f, (instr>>16)&0x3f)
}
func fcvtzs_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func fcvtzs_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func fcvtzu_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func fcvtzu_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func mov_umov_asimdins_w_w_cond(instr uint32) bool {
return ((instr>>16)&0x1f)&0x7 == 0x4
}
func mov_umov_asimdins_x_x_cond(instr uint32) bool {
return ((instr>>16)&0x1f)&0xf == 0x8
}
func mov_orr_asimdsame_only_cond(instr uint32) bool {
return (instr>>16)&0x1f == (instr>>5)&0x1f
}
func rshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func scvtf_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func scvtf_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func shl_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func shl_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func shrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sli_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sli_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrun_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqrshrun_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshl_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshl_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshlu_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshlu_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrun_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sqshrun_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sri_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sri_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srshr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srshr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srsra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func srsra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sshll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sshr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sshr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ssra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ssra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func sxtl_sshll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0 && bit_count((instr>>19)&0xf) == 1
}
func ucvtf_asisdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ucvtf_asimdshf_c_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqrshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqrshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshl_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshl_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshrn_asisdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uqshrn_asimdshf_n_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func urshr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func urshr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ursra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ursra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ushll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ushr_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func ushr_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func usra_asisdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func usra_asimdshf_r_cond(instr uint32) bool {
return instr&0x780000 != 0x0
}
func uxtl_ushll_asimdshf_l_cond(instr uint32) bool {
return instr&0x780000 != 0x0 && bit_count((instr>>19)&0xf) == 1
}

Просмотреть файл

@ -0,0 +1,81 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
func extract_bit(value, bit uint32) uint32 {
return (value >> bit) & 1
}
func bfxpreferred_4(sf, opc1, imms, immr uint32) bool {
if imms < immr {
return false
}
if (imms>>5 == sf) && (imms&0x1f == 0x1f) {
return false
}
if immr == 0 {
if sf == 0 && (imms == 7 || imms == 15) {
return false
}
if sf == 1 && opc1 == 0 && (imms == 7 ||
imms == 15 || imms == 31) {
return false
}
}
return true
}
func move_wide_preferred_4(sf, N, imms, immr uint32) bool {
if sf == 1 && N != 1 {
return false
}
if sf == 0 && !(N == 0 && ((imms>>5)&1) == 0) {
return false
}
if imms < 16 {
return (-immr)%16 <= (15 - imms)
}
width := uint32(32)
if sf == 1 {
width = uint32(64)
}
if imms >= (width - 15) {
return (immr % 16) <= (imms - (width - 15))
}
return false
}
type Sys uint8
const (
Sys_AT Sys = iota
Sys_DC
Sys_IC
Sys_TLBI
Sys_SYS
)
func sys_op_4(op1, crn, crm, op2 uint32) Sys {
// TODO: system instruction
return Sys_SYS
}
func is_zero(x uint32) bool {
return x == 0
}
func is_ones_n16(x uint32) bool {
return x == 0xffff
}
func bit_count(x uint32) uint8 {
var count uint8
for count = 0; x > 0; x >>= 1 {
if (x & 1) == 1 {
count++
}
}
return count
}

2768
arm64/arm64asm/decode.go Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"encoding/hex"
"io/ioutil"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
data, err := ioutil.ReadFile("testdata/cases.txt")
if err != nil {
t.Fatal(err)
}
all := string(data)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 3)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f[0])
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f[0])
}
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f[0], err)
continue
}
syntax, asm := f[1], f[2]
inst, decodeErr := Decode(code)
if decodeErr != nil && decodeErr != errUnknown {
// Some rarely used system instructions are not supported
// Following logicals will filter such unknown instructions
t.Errorf("parsing %x: %s", code, decodeErr)
continue
}
var out string
switch syntax {
case "gnu":
out = GNUSyntax(inst)
case "plan9":
out = GoSyntax(inst, 0, nil, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
// TODO: system instruction.
var Todo = strings.Fields(`
sys
dc
at
tlbi
ic
hvc
smc
`)
if strings.Replace(out, " ", "", -1) != strings.Replace(asm, " ", "", -1) && !hasPrefix(asm, Todo...) {
// Exclude MSR since GNU objdump result is incorrect. eg. 0xd504431f msr s0_4_c4_c3_0, xzr
if !strings.HasSuffix(asm, " nv") && !strings.HasPrefix(asm, "msr") {
t.Errorf("Decode(%s) [%s] = %s, want %s", f[0], syntax, out, asm)
}
}
}
}

601
arm64/arm64asm/ext_test.go Normal file
Просмотреть файл

@ -0,0 +1,601 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
// Copied and simplified from ../../arm/armasm/ext_test.go.
package arm64asm
import (
"bufio"
"bytes"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"testing"
"time"
)
var (
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint64
enc [4]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Arch Mode
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// InstJson describes instruction fields value got from ARMv8-A Reference Manual
type InstJson struct {
Name string
Bits string
Arch string
Syntax string
Code string
Alias string
Enc uint32
}
// A Mode is an instruction execution mode.
type Mode int
const (
_ Mode = iota
ModeARM64
)
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
arch Mode,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, inst *Inst, dec ExtInst) bool,
) {
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
Arch: arch,
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // Sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text && !strings.Contains(dec.text, "unknown") && syntax == "gnu" {
suffix := ""
if allowedMismatch(text, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
cmp := fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s\n", enc, text, len(enc), dec.text, dec.nenc, suffix)
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, cmp)
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
t.Logf("decoder coverage: %.1f%%;\n", decodeCoverage())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
// Start address of text.
const start = 0x8000
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "arm64asm")
if err != nil {
return
}
file = f.Name()
f.Seek(start, 0)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
}
w.Write(x)
w.Write(zeros[len(x):])
size += len(zeros)
})
return file, f, size, nil
}
var zeros = []byte{0, 0, 0, 0}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
if len(enc) < 4 {
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
}
return enc
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, src []byte) (inst Inst, text string) {
var err error
inst, err = Decode(src)
if err != nil {
text = "error: " + err.Error()
return
}
text = inst.String()
switch syntax {
case "gnu":
text = GNUSyntax(inst)
case "plan9": // [sic]
text = GoSyntax(inst, 0, nil, nil)
default:
text = "error: unknown syntax " + syntax
}
return
}
// decodecoverage returns a floating point number denoting the
// decoder coverage.
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return 100 * float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for cur_s := s; cur_s != ""; {
if strings.HasPrefix(cur_s, prefix) {
return true
}
i := strings.Index(cur_s, " ")
if i < 0 {
break
}
cur_s = cur_s[i+1:]
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9a-fA-F).
func isHex(b byte) bool {
return ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F')
}
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool reports whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = bytes.TrimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex(hex[j])<<4|unhex(hex[j+1]))
j += 2
}
return raw, true
}
func unhex(b byte) byte {
if '0' <= b && b <= '9' {
return b - '0'
} else if 'A' <= b && b <= 'F' {
return b - 'A' + 10
} else if 'a' <= b && b <= 'f' {
return b - 'a' + 10
}
return 0
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = bytes.TrimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// Fllowing regular expressions matches instructions using relative addressing mode.
// pcrel matches B instructions and BL instructions.
// pcrelr matches instrucions which consisted of register arguments and label arguments.
// pcrelim matches instructions which consisted of register arguments, immediate
// arguments and lable arguments.
// pcrelrzr and prcelimzr matches instructions when register arguments is zero register.
// pcrelprfm matches PRFM instructions when arguments consisted of register and lable.
// pcrelprfmim matches PRFM instructions when arguments consisted of immediate and lable.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:\.)?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|nv)?) 0x([0-9a-f]+)$`)
pcrelr = regexp.MustCompile(`^((?:.*)?(?:ldr|adrp|adr|cbnz|cbz|ldrsw) (?:x|w|s|d|q)(?:[0-9]+,)) 0x([0-9a-f]+)$`)
pcrelrzr = regexp.MustCompile(`^((?:.*)?(?:ldr|adrp|adr|cbnz|cbz|ldrsw) (?:x|w)zr,) 0x([0-9a-f]+)$`)
pcrelim = regexp.MustCompile(`^((?:.*)?(?:tbnz|tbz) (?:x|w)(?:[0-9]+,) (?:#[0-9a-f]+,)) 0x([0-9a-f]+)$`)
pcrelimzr = regexp.MustCompile(`^((?:.*)?(?:tbnz|tbz) (?:x|w)zr, (?:#[0-9a-f]+,)) 0x([0-9a-f]+)$`)
pcrelprfm = regexp.MustCompile(`^((?:.*)?(?:prfm) (?:[0-9a-z]+,)) 0x([0-9a-f]+)$`)
pcrelprfmim = regexp.MustCompile(`^((?:.*)?(?:prfm) (?:#0x[0-9a-f]+,)) 0x([0-9a-f]+)$`)
)
// Round is the multiple of the number of instructions that read from Json file.
// Round used as seed value for pseudo-random number generator provides the same sequence
// in the same round run for the external disassembler and decoder.
var Round int
// condmark is used to mark conditional instructions when need to generate and test
// conditional instructions.
var condmark bool = false
// Generate instruction binary according to Json file
// Encode variable field of instruction with random value
func doFuzzy(inst *InstJson, Ninst int) {
var testdata uint32
var NonDigRE = regexp.MustCompile(`[\D]`)
rand.Seed(int64(Round + Ninst))
off := 0
DigBit := ""
if condmark == true && !strings.Contains(inst.Bits, "cond") {
inst.Enc = 0xffffffff
} else {
for _, f := range strings.Split(inst.Bits, "|") {
if i := strings.Index(f, ":"); i >= 0 {
// consider f contains "01:2" and "Rm:5"
DigBit = f[:i]
m := NonDigRE.FindStringSubmatch(DigBit)
if m == nil {
DigBit = strings.TrimSpace(DigBit)
s := strings.Split(DigBit, "")
for i := 0; i < len(s); i++ {
switch s[i] {
case "1", "(1)":
testdata |= 1 << uint(31-off)
}
off++
}
} else {
// DigBit is "Rn" or "imm3"
n, _ := strconv.Atoi(f[i+1:])
if DigBit == "cond" && condmark == true {
r := uint8(Round)
for i := n - 1; i >= 0; i-- {
switch (r >> uint(i)) & 1 {
case 1:
testdata |= 1 << uint(31-off)
}
off++
}
} else {
for i := 0; i < n; i++ {
r := rand.Intn(2)
switch r {
case 1:
testdata |= 1 << uint(31-off)
}
off++
}
}
}
continue
}
for _, bit := range strings.Fields(f) {
switch bit {
case "0", "(0)":
off++
continue
case "1", "(1)":
testdata |= 1 << uint(31-off)
default:
r := rand.Intn(2)
switch r {
case 1:
testdata |= 1 << uint(31-off)
}
}
off++
}
}
if off != 32 {
log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off)
}
inst.Enc = testdata
}
}
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// JSONCases generates ARM64 instructions according to inst.json.
func JSONCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
data, err := ioutil.ReadFile("inst.json")
if err != nil {
log.Fatal(err)
}
var insts []InstJson
var instsN []InstJson
// Change N value to get more cases only when condmark=false.
N := 100
if condmark == true {
N = 16
}
if err := json.Unmarshal(data, &insts); err != nil {
log.Fatal(err)
}
// Append instructions to get more test cases.
for i := 0; i < N; {
for _, inst := range insts {
instsN = append(instsN, inst)
}
i++
}
Round = 0
for i := range instsN {
if i%len(insts) == 0 {
Round++
}
doFuzzy(&instsN[i], i)
}
for _, inst := range instsN {
if condmark == true && inst.Enc == 0xffffffff {
continue
}
enc := inst.Enc
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
}
}
}
// condCases generates conditional instructions.
func condCases(t *testing.T) func(func([]byte)) {
return func(try func([]byte)) {
condmark = true
JSONCases(t)(func(enc []byte) {
try(enc)
})
}
}
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/cases.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T) func(func([]byte)) {
var codes [][]byte
data, err := ioutil.ReadFile("testdata/cases.txt")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}

35
arm64/arm64asm/gnu.go Normal file
Просмотреть файл

@ -0,0 +1,35 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"strings"
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This form typically matches the syntax defined in the ARM Reference Manual.
func GNUSyntax(inst Inst) string {
switch inst.Op {
case RET:
if r, ok := inst.Args[0].(Reg); ok && r == X30 {
return "ret"
}
case B:
if _, ok := inst.Args[0].(Cond); ok {
return strings.ToLower("b." + inst.Args[0].String() + " " + inst.Args[1].String())
}
case SYSL:
result := strings.ToLower(inst.String())
return strings.Replace(result, "c", "C", -1)
case DCPS1, DCPS2, DCPS3, CLREX:
return strings.ToLower(strings.TrimSpace(inst.String()))
case ISB:
if strings.Contains(inst.String(), "SY") {
result := strings.TrimSuffix(inst.String(), " SY")
return strings.ToLower(result)
}
}
return strings.ToLower(inst.String())
}

963
arm64/arm64asm/inst.go Normal file
Просмотреть файл

@ -0,0 +1,963 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"fmt"
"strings"
)
// An Op is an ARM64 opcode.
type Op uint16
// NOTE: The actual Op values are defined in tables.go.
// They are chosen to simplify instruction decoding and
// are not a dense packing from 0 to N, although the
// density is high, probably at least 90%.
func (op Op) String() string {
if op >= Op(len(opstr)) || opstr[op] == "" {
return fmt.Sprintf("Op(%d)", int(op))
}
return opstr[op]
}
// An Inst is a single instruction.
type Inst struct {
Op Op // Opcode mnemonic
Enc uint32 // Raw encoding bits.
Args Args // Instruction arguments, in ARM manual order.
}
func (i Inst) String() string {
var args []string
for _, arg := range i.Args {
if arg == nil {
break
}
args = append(args, arg.String())
}
return i.Op.String() + " " + strings.Join(args, ", ")
}
// An Args holds the instruction arguments.
// If an instruction has fewer than 5 arguments,
// the final elements in the array are nil.
type Args [5]Arg
// An Arg is a single instruction argument, one of these types:
// Reg, RegSP, ImmShift, RegExtshiftAmount, PCRel, MemImmediate,
// MemExtend, Imm, Imm64, Imm_hint, Imm_clrex, Imm_dcps, Cond,
// Imm_c, Imm_option, Imm_prfop, Pstatefield, Systemreg, Imm_fp
// RegisterWithArrangement, RegisterWithArrangementAndIndex.
type Arg interface {
isArg()
String() string
}
// A Reg is a single register.
// The zero value denotes W0, not the absence of a register.
type Reg uint16
const (
W0 Reg = iota
W1
W2
W3
W4
W5
W6
W7
W8
W9
W10
W11
W12
W13
W14
W15
W16
W17
W18
W19
W20
W21
W22
W23
W24
W25
W26
W27
W28
W29
W30
WZR
X0
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
X11
X12
X13
X14
X15
X16
X17
X18
X19
X20
X21
X22
X23
X24
X25
X26
X27
X28
X29
X30
XZR
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
B10
B11
B12
B13
B14
B15
B16
B17
B18
B19
B20
B21
B22
B23
B24
B25
B26
B27
B28
B29
B30
B31
H0
H1
H2
H3
H4
H5
H6
H7
H8
H9
H10
H11
H12
H13
H14
H15
H16
H17
H18
H19
H20
H21
H22
H23
H24
H25
H26
H27
H28
H29
H30
H31
S0
S1
S2
S3
S4
S5
S6
S7
S8
S9
S10
S11
S12
S13
S14
S15
S16
S17
S18
S19
S20
S21
S22
S23
S24
S25
S26
S27
S28
S29
S30
S31
D0
D1
D2
D3
D4
D5
D6
D7
D8
D9
D10
D11
D12
D13
D14
D15
D16
D17
D18
D19
D20
D21
D22
D23
D24
D25
D26
D27
D28
D29
D30
D31
Q0
Q1
Q2
Q3
Q4
Q5
Q6
Q7
Q8
Q9
Q10
Q11
Q12
Q13
Q14
Q15
Q16
Q17
Q18
Q19
Q20
Q21
Q22
Q23
Q24
Q25
Q26
Q27
Q28
Q29
Q30
Q31
V0
V1
V2
V3
V4
V5
V6
V7
V8
V9
V10
V11
V12
V13
V14
V15
V16
V17
V18
V19
V20
V21
V22
V23
V24
V25
V26
V27
V28
V29
V30
V31
WSP = WZR // These are different registers with the same encoding.
SP = XZR // These are different registers with the same encoding.
)
func (Reg) isArg() {}
func (r Reg) String() string {
switch {
case r == WZR:
return "WZR"
case r == XZR:
return "XZR"
case W0 <= r && r <= W30:
return fmt.Sprintf("W%d", int(r-W0))
case X0 <= r && r <= X30:
return fmt.Sprintf("X%d", int(r-X0))
case B0 <= r && r <= B31:
return fmt.Sprintf("B%d", int(r-B0))
case H0 <= r && r <= H31:
return fmt.Sprintf("H%d", int(r-H0))
case S0 <= r && r <= S31:
return fmt.Sprintf("S%d", int(r-S0))
case D0 <= r && r <= D31:
return fmt.Sprintf("D%d", int(r-D0))
case Q0 <= r && r <= Q31:
return fmt.Sprintf("Q%d", int(r-Q0))
case V0 <= r && r <= V31:
return fmt.Sprintf("V%d", int(r-V0))
default:
return fmt.Sprintf("Reg(%d)", int(r))
}
}
// A RegSP represent a register and X31/W31 is regarded as SP/WSP.
type RegSP Reg
func (RegSP) isArg() {}
func (r RegSP) String() string {
switch Reg(r) {
case WSP:
return "WSP"
case SP:
return "SP"
default:
return Reg(r).String()
}
}
type ImmShift struct {
imm uint16
shift uint8
}
func (ImmShift) isArg() {}
func (is ImmShift) String() string {
if is.shift == 0 {
return fmt.Sprintf("#%#x", is.imm)
}
if is.shift < 128 {
return fmt.Sprintf("#%#x, LSL #%d", is.imm, is.shift)
}
return fmt.Sprintf("#%#x, MSL #%d", is.imm, is.shift-128)
}
type ExtShift uint8
const (
_ ExtShift = iota
uxtb
uxth
uxtw
uxtx
sxtb
sxth
sxtw
sxtx
lsl
lsr
asr
ror
)
func (extShift ExtShift) String() string {
switch extShift {
case uxtb:
return "UXTB"
case uxth:
return "UXTH"
case uxtw:
return "UXTW"
case uxtx:
return "UXTX"
case sxtb:
return "SXTB"
case sxth:
return "SXTH"
case sxtw:
return "SXTW"
case sxtx:
return "SXTX"
case lsl:
return "LSL"
case lsr:
return "LSR"
case asr:
return "ASR"
case ror:
return "ROR"
}
return ""
}
type RegExtshiftAmount struct {
reg Reg
extShift ExtShift
amount uint8
show_zero bool
}
func (RegExtshiftAmount) isArg() {}
func (rea RegExtshiftAmount) String() string {
buf := rea.reg.String()
if rea.extShift != ExtShift(0) {
buf += ", " + rea.extShift.String()
if rea.amount != 0 {
buf += fmt.Sprintf(" #%d", rea.amount)
} else {
if rea.show_zero == true {
buf += fmt.Sprintf(" #%d", rea.amount)
}
}
}
return buf
}
// A PCRel describes a memory address (usually a code label)
// as a distance relative to the program counter.
type PCRel int64
func (PCRel) isArg() {}
func (r PCRel) String() string {
return fmt.Sprintf(".%+#x", uint64(r))
}
// An AddrMode is an ARM addressing mode.
type AddrMode uint8
const (
_ AddrMode = iota
AddrPostIndex // [R], X - use address R, set R = R + X
AddrPreIndex // [R, X]! - use address R + X, set R = R + X
AddrOffset // [R, X] - use address R + X
AddrPostReg // [Rn], Rm - - use address Rn, set Rn = Rn + Rm
)
// A MemImmediate is a memory reference made up of a base R and immediate X.
// The effective memory address is R or R+X depending on AddrMode.
type MemImmediate struct {
Base RegSP
Mode AddrMode
imm int32
}
func (MemImmediate) isArg() {}
func (m MemImmediate) String() string {
R := m.Base.String()
X := fmt.Sprintf("#%d", m.imm)
switch m.Mode {
case AddrOffset:
if X == "#0" {
return fmt.Sprintf("[%s]", R)
}
return fmt.Sprintf("[%s,%s]", R, X)
case AddrPreIndex:
return fmt.Sprintf("[%s,%s]!", R, X)
case AddrPostIndex:
return fmt.Sprintf("[%s],%s", R, X)
case AddrPostReg:
post := Reg(X0) + Reg(m.imm)
postR := post.String()
return fmt.Sprintf("[%s], %s", R, postR)
}
return fmt.Sprintf("unimplemented!")
}
// A MemExtend is a memory reference made up of a base R and index expression X.
// The effective memory address is R or R+X depending on Index, Extend and Amount.
type MemExtend struct {
Base RegSP
Index Reg
Extend ExtShift
Amount uint8
Absent bool
}
func (MemExtend) isArg() {}
func (m MemExtend) String() string {
Rbase := m.Base.String()
RIndex := m.Index.String()
if m.Absent {
if m.Amount != 0 {
return fmt.Sprintf("[%s,%s,%s #0]", Rbase, RIndex, m.Extend.String())
} else {
if m.Extend != lsl {
return fmt.Sprintf("[%s,%s,%s]", Rbase, RIndex, m.Extend.String())
} else {
return fmt.Sprintf("[%s,%s]", Rbase, RIndex)
}
}
} else {
if m.Amount != 0 {
return fmt.Sprintf("[%s,%s,%s #%d]", Rbase, RIndex, m.Extend.String(), m.Amount)
} else {
if m.Extend != lsl {
return fmt.Sprintf("[%s,%s,%s]", Rbase, RIndex, m.Extend.String())
} else {
return fmt.Sprintf("[%s,%s]", Rbase, RIndex)
}
}
}
}
// An Imm is an integer constant.
type Imm struct {
Imm uint32
Decimal bool
}
func (Imm) isArg() {}
func (i Imm) String() string {
if !i.Decimal {
return fmt.Sprintf("#%#x", i.Imm)
} else {
return fmt.Sprintf("#%d", i.Imm)
}
}
type Imm64 struct {
Imm uint64
Decimal bool
}
func (Imm64) isArg() {}
func (i Imm64) String() string {
if !i.Decimal {
return fmt.Sprintf("#%#x", i.Imm)
} else {
return fmt.Sprintf("#%d", i.Imm)
}
}
// An Imm_hint is an integer constant for HINT instruction.
type Imm_hint uint8
func (Imm_hint) isArg() {}
func (i Imm_hint) String() string {
return fmt.Sprintf("#%#x", uint32(i))
}
// An Imm_clrex is an integer constant for CLREX instruction.
type Imm_clrex uint8
func (Imm_clrex) isArg() {}
func (i Imm_clrex) String() string {
if i == 15 {
return ""
}
return fmt.Sprintf("#%#x", uint32(i))
}
// An Imm_dcps is an integer constant for DCPS[123] instruction.
type Imm_dcps uint16
func (Imm_dcps) isArg() {}
func (i Imm_dcps) String() string {
if i == 0 {
return ""
}
return fmt.Sprintf("#%#x", uint32(i))
}
// Standard conditions.
type Cond struct {
Value uint8
Invert bool
}
func (Cond) isArg() {}
func (c Cond) String() string {
cond31 := c.Value >> 1
invert := bool((c.Value & 1) == 1)
invert = (invert != c.Invert)
switch cond31 {
case 0:
if invert {
return "NE"
} else {
return "EQ"
}
case 1:
if invert {
return "CC"
} else {
return "CS"
}
case 2:
if invert {
return "PL"
} else {
return "MI"
}
case 3:
if invert {
return "VC"
} else {
return "VS"
}
case 4:
if invert {
return "LS"
} else {
return "HI"
}
case 5:
if invert {
return "LT"
} else {
return "GE"
}
case 6:
if invert {
return "LE"
} else {
return "GT"
}
case 7:
return "AL"
}
return ""
}
// An Imm_c is an integer constant for SYS/SYSL/TLBI instruction.
type Imm_c uint8
func (Imm_c) isArg() {}
func (i Imm_c) String() string {
return fmt.Sprintf("C%d", uint8(i))
}
// An Imm_option is an integer constant for DMB/DSB/ISB instruction.
type Imm_option uint8
func (Imm_option) isArg() {}
func (i Imm_option) String() string {
switch uint8(i) {
case 15:
return "SY"
case 14:
return "ST"
case 13:
return "LD"
case 11:
return "ISH"
case 10:
return "ISHST"
case 9:
return "ISHLD"
case 7:
return "NSH"
case 6:
return "NSHST"
case 5:
return "NSHLD"
case 3:
return "OSH"
case 2:
return "OSHST"
case 1:
return "OSHLD"
}
return fmt.Sprintf("#%#02x", uint8(i))
}
// An Imm_prfop is an integer constant for PRFM instruction.
type Imm_prfop uint8
func (Imm_prfop) isArg() {}
func (i Imm_prfop) String() string {
prf_type := (i >> 3) & (1<<2 - 1)
prf_target := (i >> 1) & (1<<2 - 1)
prf_policy := i & 1
var result string
switch prf_type {
case 0:
result = "PLD"
case 1:
result = "PLI"
case 2:
result = "PST"
case 3:
return fmt.Sprintf("#%#02x", uint8(i))
}
switch prf_target {
case 0:
result += "L1"
case 1:
result += "L2"
case 2:
result += "L3"
case 3:
return fmt.Sprintf("#%#02x", uint8(i))
}
if prf_policy == 0 {
result += "KEEP"
} else {
result += "STRM"
}
return result
}
type Pstatefield uint8
const (
SPSel Pstatefield = iota
DAIFSet
DAIFClr
)
func (Pstatefield) isArg() {}
func (p Pstatefield) String() string {
switch p {
case SPSel:
return "SPSel"
case DAIFSet:
return "DAIFSet"
case DAIFClr:
return "DAIFClr"
default:
return "unimplemented"
}
}
type Systemreg struct {
op0 uint8
op1 uint8
cn uint8
cm uint8
op2 uint8
}
func (Systemreg) isArg() {}
func (s Systemreg) String() string {
return fmt.Sprintf("S%d_%d_C%d_C%d_%d",
s.op0, s.op1, s.cn, s.cm, s.op2)
}
// An Imm_fp is a signed floating-point constant.
type Imm_fp struct {
s uint8
exp int8
pre uint8
}
func (Imm_fp) isArg() {}
func (i Imm_fp) String() string {
var s, pre, numerator, denominator int16
var result float64
if i.s == 0 {
s = 1
} else {
s = -1
}
pre = s * int16(16+i.pre)
if i.exp > 0 {
numerator = (pre << uint8(i.exp))
denominator = 16
} else {
numerator = pre
denominator = (16 << uint8(-1*i.exp))
}
result = float64(numerator) / float64(denominator)
return fmt.Sprintf("#%.18e", result)
}
type Arrangement uint8
const (
_ Arrangement = iota
ArrangementB
Arrangement8B
Arrangement16B
ArrangementH
Arrangement4H
Arrangement8H
ArrangementS
Arrangement2S
Arrangement4S
ArrangementD
Arrangement1D
Arrangement2D
Arrangement1Q
)
func (a Arrangement) String() (result string) {
switch a {
case ArrangementB:
result = ".B"
case Arrangement8B:
result = ".8B"
case Arrangement16B:
result = ".16B"
case ArrangementH:
result = ".H"
case Arrangement4H:
result = ".4H"
case Arrangement8H:
result = ".8H"
case ArrangementS:
result = ".S"
case Arrangement2S:
result = ".2S"
case Arrangement4S:
result = ".4S"
case ArrangementD:
result = ".D"
case Arrangement1D:
result = ".1D"
case Arrangement2D:
result = ".2D"
case Arrangement1Q:
result = ".1Q"
}
return
}
// Register with arrangement: <Vd>.<T>, { <Vt>.8B, <Vt2>.8B},
type RegisterWithArrangement struct {
r Reg
a Arrangement
cnt uint8
}
func (RegisterWithArrangement) isArg() {}
func (r RegisterWithArrangement) String() string {
result := r.r.String()
result += r.a.String()
if r.cnt > 0 {
result = "{" + result
if r.cnt == 2 {
r1 := V0 + Reg((uint16(r.r)-uint16(V0)+1)&31)
result += ", " + r1.String() + r.a.String()
} else if r.cnt > 2 {
if (uint16(r.cnt) + ((uint16(r.r) - uint16(V0)) & 31)) > 32 {
for i := 1; i < int(r.cnt); i++ {
cur := V0 + Reg((uint16(r.r)-uint16(V0)+uint16(i))&31)
result += ", " + cur.String() + r.a.String()
}
} else {
r1 := V0 + Reg((uint16(r.r)-uint16(V0)+uint16(r.cnt)-1)&31)
result += "-" + r1.String() + r.a.String()
}
}
result += "}"
}
return result
}
// Register with arrangement and index: <Vm>.<Ts>[<index>],
// { <Vt>.B, <Vt2>.B }[<index>].
type RegisterWithArrangementAndIndex struct {
r Reg
a Arrangement
index uint8
cnt uint8
}
func (RegisterWithArrangementAndIndex) isArg() {}
func (r RegisterWithArrangementAndIndex) String() string {
result := r.r.String()
result += r.a.String()
if r.cnt > 0 {
result = "{" + result
if r.cnt == 2 {
r1 := V0 + Reg((uint16(r.r)-uint16(V0)+1)&31)
result += ", " + r1.String() + r.a.String()
} else if r.cnt > 2 {
if (uint16(r.cnt) + ((uint16(r.r) - uint16(V0)) & 31)) > 32 {
for i := 1; i < int(r.cnt); i++ {
cur := V0 + Reg((uint16(r.r)-uint16(V0)+uint16(i))&31)
result += ", " + cur.String() + r.a.String()
}
} else {
r1 := V0 + Reg((uint16(r.r)-uint16(V0)+uint16(r.cnt)-1)&31)
result += "-" + r1.String() + r.a.String()
}
}
result += "}"
}
return fmt.Sprintf("%s[%d]", result, r.index)
}

1219
arm64/arm64asm/inst.json Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,124 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"strings"
"testing"
)
func TestObjdumpARM64Testdata(t *testing.T) { testObjdumpARM64(t, testdataCases(t)) }
func TestObjdumpARM64Manual(t *testing.T) { testObjdumpARM64(t, hexCases(t, objdumpManualTests)) }
func TestObjdumpARM64Cond(t *testing.T) { testObjdumpARM64(t, condCases(t)) }
func TestObjdumpARM64(t *testing.T) { testObjdumpARM64(t, JSONCases(t)) }
// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Manual, particularly with tracing enabled.
// Note that these are byte sequences, so they must be reversed from the usual
// word presentation.
var objdumpManualTests = `
bf2003d5
9f2003d5
7f2003d5
5f2003d5
3f2003d5
1f2003d5
df4d03d5
ff4d03d5
28d91b14
da6cb530
15e5e514
ff4603d5
df4803d5
bf4100d5
9f3f03d5
9f3e03d5
9f3d03d5
9f3b03d5
9f3a03d5
9f3903d5
9f3703d5
9f3603d5
9f3503d5
9f3303d5
9f3203d5
9f3103d5
ff4603d5
df4803d5
bf4100d5
a3681b53
47dc78d3
0500a012
0500e092
0500a052
0500a0d2
cd5a206e
cd5a202e
743d050e
743d0a0e
743d0c0e
743d084e
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, inst *Inst, dec ExtInst) bool {
// Skip unsupported instructions
if hasPrefix(dec.text, todo...) {
return true
}
// GNU objdump has incorrect alias conditions for following instructions
if inst.Enc&0x000003ff == 0x000003ff && hasPrefix(dec.text, "negs") && hasPrefix(text, "cmp") {
return true
}
// GNU objdump "NV" is equal to our "AL"
if strings.HasSuffix(dec.text, " nv") && strings.HasSuffix(text, " al") {
return true
}
if strings.HasPrefix(dec.text, "b.nv") && strings.HasPrefix(text, "b.al") {
return true
}
// GNU objdump recognizes invalid binaries as following instructions
if hasPrefix(dec.text, "hint", "mrs", "msr", "bfc", "orr", "mov") {
return true
}
if strings.HasPrefix(text, "hint") {
return true
}
// GNU objdump recognizes reserved valuse as valid ones
if strings.Contains(text, "unknown instruction") && hasPrefix(dec.text, "fmla", "fmul", "fmulx", "fcvtzs", "fcvtzu", "fmls", "fmov", "scvtf", "ucvtf") {
return true
}
// GNU objdump misses spaces between operands for some instructions (e.g., "ld1 {v10.2s, v11.2s}, [x23],#16")
if strings.Replace(text, " ", "", -1) == strings.Replace(dec.text, " ", "", -1) {
return true
}
return false
}
// TODO: system instruction.
var todo = strings.Fields(`
sys
dc
at
tlbi
ic
hvc
smc
`)
// Following instructions can't be covered because they are just aliases to another instructions which are always preferred
var Ncover = strings.Fields(`
sbfm
asrv
bfm
ubfm
lslv
lsrv
rorv
ins
dup
`)

Просмотреть файл

@ -0,0 +1,299 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and simplified from ../../arm/armasm/objdumpext_test.go.
package arm64asm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"os/exec"
"strconv"
"strings"
"testing"
)
const objdumpPath = "/usr/bin/objdump"
func testObjdumpARM64(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, ModeARM64)
}
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) {
if _, err := os.Stat(objdumpPath); err != nil {
t.Skip(err)
}
// Check objdump can disassemble elf64-aarch64.
if test := objdumpinfo(); test == false {
t.Skip("Skip the test if installed objdump doesn't support aarch64 elf format")
}
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
testExtDis(t, "plan9", arch, objdump, generate, allowedMismatchObjdump)
}
func objdumpinfo() bool {
var i = []byte("aarch64")
out, err := exec.Command(objdumpPath, "-i").Output()
if err != nil {
log.Fatal(err)
}
if bytes.Contains(out, i) {
return true
}
return false
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if ext.Arch == ModeARM64 {
if err := writeELF64(ext.File, ext.Size); err != nil {
return err
}
} else {
panic("unknown arch")
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint64 = start
addr uint64
encbuf [4]byte
enc []byte
text string
)
flush := func() {
if addr == next {
// PC-relative addresses are translated to absolute addresses based on PC by GNU objdump
// Following logical rewrites the absolute addresses back to PC-relative ones for comparing
// with our disassembler output which are PC-relative
if m := pcrelprfmim.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelprfm.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelim.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelimzr.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if m := pcrelr.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
if strings.Contains(m[1], "adrp") {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr&0xfffff000)))
} else {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
}
if m := pcrelrzr.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
if strings.Contains(m[1], "adrp") {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr&0xfffff000)))
} else {
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
}
if strings.HasPrefix(text, "mov") && strings.Contains(text, "//") {
s := strings.Split(text, " //")
text = s[0]
}
text = strings.Replace(text, "#0.0", "#0", -1)
if text == "undefined" && len(enc) == 4 {
text = "error: unknown instruction"
enc = nil
}
if len(enc) == 4 {
// prints as word but we want to record bytes
enc[0], enc[3] = enc[3], enc[0]
enc[1], enc[2] = enc[2], enc[1]
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [4]byte{}
enc = nil
next += 4
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint64(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
undefined = []byte("undefined")
unpredictable = []byte("unpredictable")
)
func parseLine(line []byte, encstart []byte) (addr uint64, enc []byte, text string) {
ok := false
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(bytes.TrimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint64(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok = parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = bytes.TrimSpace(line[i:])
if bytes.Contains(line, undefined) {
text = "undefined"
return
}
if false && bytes.Contains(line, unpredictable) {
text = "unpredictable"
return
}
if i := bytes.IndexByte(line, ';'); i >= 0 {
line = bytes.TrimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF64 writes an ELF64 header to the file, describing a text
// segment that starts at start (0x8000) and extends for size bytes.
func writeELF64(f *os.File, size int) error {
f.Seek(0, 0)
var hdr elf.Header64
var prog elf.Prog64
var sect elf.Section64
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header64{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
Type: 2,
Machine: uint16(elf.EM_AARCH64),
Version: 1,
Entry: start,
Phoff: uint64(off1),
Shoff: uint64(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog64{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint64(size),
Memsz: uint64(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section64{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint64(size),
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section64{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint64(off2 + (off3-off2)*3),
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}

611
arm64/arm64asm/plan9x.go Normal file
Просмотреть файл

@ -0,0 +1,611 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64asm
import (
"fmt"
"io"
"sort"
"strings"
)
// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The pc is the program counter of the instruction, used for
// expanding PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name
// and base address of the symbol containing the target, if any;
// otherwise it returns "", 0.
// The reader text should read from the text segment using text addresses
// as offsets; it is used to display pc-relative loads as constant loads.
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var args []string
for _, a := range inst.Args {
if a == nil {
break
}
args = append(args, plan9Arg(&inst, pc, symname, a))
}
op := inst.Op.String()
switch inst.Op {
case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW:
// Check for PC-relative load.
if offset, ok := inst.Args[1].(PCRel); ok {
addr := pc + uint64(offset)
if _, ok := inst.Args[0].(Reg); !ok {
break
}
if s, base := symname(addr); s != "" && addr == base {
args[1] = fmt.Sprintf("$%s(SB)", s)
}
}
}
// Move addressing mode into opcode suffix.
suffix := ""
switch inst.Op {
case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW, STR, STRB, STRH, STUR, STURB, STURH, LD1:
switch mem := inst.Args[1].(type) {
case MemImmediate:
switch mem.Mode {
case AddrOffset:
// no suffix
case AddrPreIndex:
suffix = ".W"
case AddrPostIndex, AddrPostReg:
suffix = ".P"
}
}
case STP, LDP:
switch mem := inst.Args[2].(type) {
case MemImmediate:
switch mem.Mode {
case AddrOffset:
// no suffix
case AddrPreIndex:
suffix = ".W"
case AddrPostIndex:
suffix = ".P"
}
}
}
switch inst.Op {
case BL:
return "CALL " + args[0]
case BLR:
r := inst.Args[0].(Reg)
regno := uint16(r) & 31
return fmt.Sprintf("CALL (R%d)", regno)
case RET:
if r, ok := inst.Args[0].(Reg); ok && r == X30 {
return "RET"
}
case B:
if cond, ok := inst.Args[0].(Cond); ok {
return "B" + cond.String() + " " + args[1]
}
return "JMP" + " " + args[0]
case BR:
r := inst.Args[0].(Reg)
regno := uint16(r) & 31
return fmt.Sprintf("JMP (R%d)", regno)
case MOV:
rno := -1
switch a := inst.Args[0].(type) {
case Reg:
rno = int(a)
case RegSP:
rno = int(a)
case RegisterWithArrangementAndIndex:
op = "VMOV"
}
if rno >= 0 && rno <= int(WZR) {
op = "MOVW"
} else if rno >= int(X0) && rno <= int(XZR) {
op = "MOVD"
}
if _, ok := inst.Args[1].(RegisterWithArrangementAndIndex); ok {
op = "VMOV"
}
case LDR:
var rno uint16
if r, ok := inst.Args[0].(Reg); ok {
rno = uint16(r)
} else {
rno = uint16(inst.Args[0].(RegSP))
}
if rno <= uint16(WZR) {
op = "MOVWU" + suffix
} else {
op = "MOVD" + suffix
}
case LDRB:
op = "MOVBU" + suffix
case LDRH:
op = "MOVHU" + suffix
case LDRSW:
op = "MOVW" + suffix
case LDRSB:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op = "MOVBW" + suffix
} else {
op = "MOVB" + suffix
}
}
case LDRSH:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op = "MOVHW" + suffix
} else {
op = "MOVH" + suffix
}
}
case STR, STUR:
var rno uint16
if r, ok := inst.Args[0].(Reg); ok {
rno = uint16(r)
} else {
rno = uint16(inst.Args[0].(RegSP))
}
if rno <= uint16(WZR) {
op = "MOVW" + suffix
} else {
op = "MOVD" + suffix
}
args[0], args[1] = args[1], args[0]
case STRB, STURB:
op = "MOVB" + suffix
args[0], args[1] = args[1], args[0]
case STRH, STURH:
op = "MOVH" + suffix
args[0], args[1] = args[1], args[0]
case TBNZ, TBZ:
args[0], args[1], args[2] = args[2], args[0], args[1]
case MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[2], args[3] = args[3], args[2]
case STLR:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[0], args[1] = args[1], args[0]
case STLRB, STLRH:
args[0], args[1] = args[1], args[0]
case STLXR, STXR:
if r, ok := inst.Args[1].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[1], args[2] = args[2], args[1]
case STLXRB, STLXRH, STXRB, STXRH:
args[1], args[2] = args[2], args[1]
case BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[1], args[2], args[3] = args[3], args[1], args[2]
case STP, LDP:
args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1])
args[1] = args[2]
if op == "STP" {
op = op + suffix
return op + " " + args[0] + ", " + args[1]
} else if op == "LDP" {
op = op + suffix
return op + " " + args[1] + ", " + args[0]
}
case FCCMP, FCCMPE:
args[0], args[1] = args[1], args[0]
fallthrough
case FCMP, FCMPE:
if _, ok := inst.Args[1].(Imm); ok {
args[1] = "$(0.0)"
}
fallthrough
case FADD, FSUB, FMUL, FNMUL, FDIV, FMAX, FMIN, FMAXNM, FMINNM, FCSEL:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
}
}
case FCVT:
for i := 1; i >= 0; i-- {
if r, ok := inst.Args[i].(Reg); ok {
rno := uint16(r)
if rno >= uint16(H0) && rno <= uint16(H31) {
op = fmt.Sprintf("%sH", op)
} else if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
}
}
}
case FABS, FNEG, FSQRT, FRINTN, FRINTP, FRINTM, FRINTZ, FRINTA, FRINTX, FRINTI:
if r, ok := inst.Args[1].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
}
}
case FCVTZS, FCVTZU, SCVTF, UCVTF:
if _, ok := inst.Args[2].(Imm); !ok {
for i := 1; i >= 0; i-- {
if r, ok := inst.Args[i].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
} else if rno <= uint16(WZR) {
op += "W"
}
}
}
}
case FMOV:
for i := 0; i <= 1; i++ {
if r, ok := inst.Args[i].(Reg); ok {
rno := uint16(r)
if rno >= uint16(S0) && rno <= uint16(S31) {
op = fmt.Sprintf("%sS", op)
break
} else if rno >= uint16(D0) && rno <= uint16(D31) {
op = fmt.Sprintf("%sD", op)
break
}
}
}
case SYSL:
op1 := int(inst.Args[1].(Imm).Imm)
cn := int(inst.Args[2].(Imm_c))
cm := int(inst.Args[3].(Imm_c))
op2 := int(inst.Args[4].(Imm).Imm)
sysregno := int32(op1<<16 | cn<<12 | cm<<8 | op2<<5)
args[1] = fmt.Sprintf("$%d", sysregno)
return op + " " + args[1] + ", " + args[0]
case CBNZ, CBZ:
if r, ok := inst.Args[0].(Reg); ok {
rno := uint16(r)
if rno <= uint16(WZR) {
op += "W"
}
}
args[0], args[1] = args[1], args[0]
case ADR, ADRP:
addr := int64(inst.Args[1].(PCRel))
args[1] = fmt.Sprintf("%d(PC)", addr)
default:
index := sort.SearchStrings(noSuffixOpSet, op)
if !(index < len(noSuffixOpSet) && noSuffixOpSet[index] == op) {
rno := -1
switch a := inst.Args[0].(type) {
case Reg:
rno = int(a)
case RegSP:
rno = int(a)
case RegisterWithArrangement:
op = fmt.Sprintf("V%s", op)
}
if rno >= 0 && rno <= int(WZR) {
// Add "w" to opcode suffix.
op += "W"
}
}
op = op + suffix
}
// conditional instructions, replace args.
if _, ok := inst.Args[3].(Cond); ok {
if _, ok := inst.Args[2].(Reg); ok {
args[1], args[2] = args[2], args[1]
} else {
args[0], args[2] = args[2], args[0]
}
}
// Reverse args, placing dest last.
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
args[i], args[j] = args[j], args[i]
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return op
}
// No need add "W" to opcode suffix.
// Opcode must be inserted in ascending order.
var noSuffixOpSet = strings.Fields(`
CRC32B
CRC32CB
CRC32CH
CRC32CW
CRC32CX
CRC32H
CRC32W
CRC32X
LDARB
LDARH
LDAXRB
LDAXRH
LDXRB
LDXRH
`)
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
switch a := arg.(type) {
case Imm:
return fmt.Sprintf("$%d", uint32(a.Imm))
case Imm64:
return fmt.Sprintf("$%d", int64(a.Imm))
case ImmShift:
if a.shift == 0 {
return fmt.Sprintf("$%d", a.imm)
}
return fmt.Sprintf("$(%d<<%d)", a.imm, a.shift)
case PCRel:
addr := int64(pc) + int64(a)
if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
return fmt.Sprintf("%s(SB)", s)
}
return fmt.Sprintf("%d(PC)", a/4)
case Reg:
regenum := uint16(a)
regno := uint16(a) & 31
if regenum >= uint16(B0) && regenum <= uint16(D31) {
// FP registers are the same ones as SIMD registers
// Print Fn for scalar variant to align with assembler (e.g., FCVT)
return fmt.Sprintf("F%d", regno)
} else if regenum >= uint16(Q0) && regenum <= uint16(Q31) {
// Print Vn to align with assembler (e.g., SHA256H)
return fmt.Sprintf("V%d", regno)
}
if regno == 31 {
return "ZR"
}
return fmt.Sprintf("R%d", regno)
case RegSP:
regno := uint16(a) & 31
if regno == 31 {
return "RSP"
}
return fmt.Sprintf("R%d", regno)
case RegExtshiftAmount:
reg := ""
regno := uint16(a.reg) & 31
if regno == 31 {
reg = "ZR"
} else {
reg = fmt.Sprintf("R%d", uint16(a.reg)&31)
}
extshift := ""
amount := ""
if a.extShift != ExtShift(0) {
switch a.extShift {
default:
extshift = "." + a.extShift.String()
case lsl:
extshift = "<<"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
case lsr:
extshift = ">>"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
case asr:
extshift = "->"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
case ror:
extshift = "@>"
amount = fmt.Sprintf("%d", a.amount)
return reg + extshift + amount
}
if a.amount != 0 {
amount = fmt.Sprintf("<<%d", a.amount)
}
}
return reg + extshift + amount
case MemImmediate:
off := ""
base := ""
regno := uint16(a.Base) & 31
if regno == 31 {
base = "(RSP)"
} else {
base = fmt.Sprintf("(R%d)", regno)
}
if a.imm != 0 && a.Mode != AddrPostReg {
off = fmt.Sprintf("%d", a.imm)
} else if a.Mode == AddrPostReg {
postR := fmt.Sprintf("(R%d)", a.imm)
return base + postR
}
return off + base
case MemExtend:
base := ""
index := ""
extend := ""
indexreg := ""
regno := uint16(a.Base) & 31
if regno == 31 {
base = "(RSP)"
} else {
base = fmt.Sprintf("(R%d)", regno)
}
regno = uint16(a.Index) & 31
if regno == 31 {
indexreg = "ZR"
} else {
indexreg = fmt.Sprintf("R%d", regno)
}
if a.Extend == lsl {
if a.Amount != 0 {
extend = fmt.Sprintf("<<%d", a.Amount)
}
} else {
extend = "unimplemented!"
}
index = indexreg + extend
return index + base
case Cond:
switch arg.String() {
case "CS":
return "HS"
case "CC":
return "LO"
}
case Imm_clrex:
return fmt.Sprintf("$%d", uint32(a))
case Imm_dcps:
return fmt.Sprintf("$%d", uint32(a))
case Imm_option:
return fmt.Sprintf("$%d", uint8(a))
case Imm_hint:
return fmt.Sprintf("$%d", uint8(a))
case Imm_fp:
var s, pre, numerator, denominator int16
var result float64
if a.s == 0 {
s = 1
} else {
s = -1
}
pre = s * int16(16+a.pre)
if a.exp > 0 {
numerator = (pre << uint8(a.exp))
denominator = 16
} else {
numerator = pre
denominator = (16 << uint8(-1*a.exp))
}
result = float64(numerator) / float64(denominator)
return strings.TrimRight(fmt.Sprintf("$%f", result), "0")
case RegisterWithArrangement:
result := a.r.String()
arrange := a.a.String()
c := []rune(arrange)
switch len(c) {
case 3:
c[1], c[2] = c[2], c[1] // .8B -> .B8
case 4:
c[1], c[2], c[3] = c[3], c[1], c[2] // 16B -> B16
}
arrange = string(c)
result += arrange
if a.cnt > 0 {
result = "[" + result
for i := 1; i < int(a.cnt); i++ {
cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
result += ", " + cur.String() + arrange
}
result += "]"
}
return result
case RegisterWithArrangementAndIndex:
result := a.r.String()
arrange := a.a.String()
result += arrange
if a.cnt > 0 {
result = "[" + result
for i := 1; i < int(a.cnt); i++ {
cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
result += ", " + cur.String() + arrange
}
result += "]"
}
return fmt.Sprintf("%s[%d]", result, a.index)
case Systemreg:
return fmt.Sprintf("$%d", uint32(a.op0&1)<<14|uint32(a.op1&7)<<11|uint32(a.cn&15)<<7|uint32(a.cm&15)<<3|uint32(a.op2)&7)
}
return strings.ToUpper(arg.String())
}

3366
arm64/arm64asm/tables.go Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

7
arm64/arm64asm/testdata/Makefile поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
go test command:
cd ..; go test -run 'ObjdumpARM64Cond' -v -timeout 10h -long 2>&1 | tee log
cd ..; go test -run 'ObjdumpARM64Testdata' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'ObjdumpARM64' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'ObjdumpARM64Manual' -v -timeout 10h -long 2>&1 | tee -a log
cd ..; go test -run 'TestDecode'
cd ..; go test -run '.*'

9203
arm64/arm64asm/testdata/cases.txt поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

714
arm64/arm64spec/spec.go Normal file
Просмотреть файл

@ -0,0 +1,714 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// arm64spec reads the ``ARMv8-A Reference Manual''
// to collect instruction encoding details and writes those
// details to standard output in JSON format.
// usage: arm64spec file.pdf
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"math"
"os"
"regexp"
"sort"
"strconv"
"strings"
"rsc.io/pdf"
)
type Inst struct {
Name string
Bits string
Arch string
Syntax string
Code string
Alias string
}
const debugPage = 0
var stdout *bufio.Writer
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
log.SetFlags(0)
log.SetPrefix("arm64spec: ")
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "usage: arm64spec file.pdf\n")
os.Exit(2)
}
f, err := pdf.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
// Find instruction set reference in outline, to build instruction list.
instList := instHeadings(f.Outline())
if debugPage == 0 {
fmt.Println("the number of instructions:", len(instList))
}
if len(instList) < 200 {
log.Fatalf("only found %d instructions in table of contents", len(instList))
}
file, err := os.Create("inst.json")
check(err)
w := bufio.NewWriter(file)
_, err = w.WriteString("[")
check(err)
numTable := 0
defer w.Flush()
defer file.Close()
// Scan document looking for instructions.
// Must find exactly the ones in the outline.
n := f.NumPage()
PageLoop:
for pageNum := 435; pageNum <= n; pageNum++ {
if debugPage > 0 && pageNum != debugPage {
continue
}
if pageNum == 770 {
continue
}
if pageNum > 1495 {
break
}
p := f.Page(pageNum)
name, table := parsePage(pageNum, p, f)
if name == "" {
continue
}
if len(table) < 1 {
if false {
fmt.Fprintf(os.Stderr, "no encodings for instruction %q (page %d)\n", name, pageNum)
}
continue
}
for _, inst := range table {
if numTable > 0 {
_, err = w.WriteString(jsFix.Replace(","))
check(err)
_, err = w.WriteString("\n")
check(err)
}
numTable++
js, _ := json.Marshal(inst)
_, err = w.WriteString(jsFix.Replace(string(js)))
check(err)
}
for j, headline := range instList {
if name == headline {
instList[j] = ""
continue PageLoop
}
}
fmt.Fprintf(os.Stderr, "unexpected instruction %q (page %d)\n", name, pageNum)
}
_, err = w.WriteString("\n]\n")
check(err)
w.Flush()
if debugPage == 0 {
for _, headline := range instList {
if headline != "" {
fmt.Fprintf(os.Stderr, "missing instruction %q\n", headline)
}
}
}
}
func instHeadings(outline pdf.Outline) []string {
return appendInstHeadings(outline, nil)
}
var instRE = regexp.MustCompile(`C[\d.]+ Alphabetical list of A64 base instructions`)
var instRE_A = regexp.MustCompile(`C[\d.]+ Alphabetical list of A64 floating-point and Advanced SIMD instructions`)
var childRE = regexp.MustCompile(`C[\d.]+ (.+)`)
var sectionRE = regexp.MustCompile(`^C[\d.]+$`)
var bitRE = regexp.MustCompile(`^( |[01]|\([01]\))*$`)
var IMMRE = regexp.MustCompile(`^imm[\d]+$`)
func appendInstHeadings(outline pdf.Outline, list []string) []string {
if instRE.MatchString(outline.Title) || instRE_A.MatchString(outline.Title) {
for _, child := range outline.Child {
m := childRE.FindStringSubmatch(child.Title)
if m == nil {
fmt.Fprintf(os.Stderr, "cannot parse section title: %s\n", child.Title)
continue
}
list = append(list, m[1])
}
}
for _, child := range outline.Child {
list = appendInstHeadings(child, list)
}
return list
}
const inch = 72.0
func parsePage(num int, p pdf.Page, f *pdf.Reader) (name string, table []Inst) {
content := p.Content()
var text []pdf.Text
CrossTwoPage := true
for _, t := range content.Text {
text = append(text, t)
}
text = findWords(text)
if !(instRE.MatchString(text[1].S) || instRE_A.MatchString(text[1].S)) || len(text) == 0 || !sectionRE.MatchString(text[2].S) {
return "", nil
}
// Check whether the content crosses the page.
for _, t := range text {
if match(t, "Arial,Bold", 10, "Assembler symbols") {
CrossTwoPage = false
break
}
}
// Deal with cross page issue. To the next page content.
var Ncontent pdf.Content
Npagebox := false
CrossThreePage := false
Noffset := ""
if CrossTwoPage == true {
Np := f.Page(num + 1)
Ncontent = Np.Content()
var Ntext []pdf.Text
for _, t := range Ncontent.Text {
Ntext = append(Ntext, t)
}
Ntext = findWords(Ntext)
if len(Ntext) == 0 || sectionRE.MatchString(Ntext[2].S) {
Ntext = text[:0]
} else {
for _, t := range Ntext {
if match(t, "Arial,Bold", 10, "offset") {
Noffset = t.S
Npagebox = true
}
// This istruction cross three pages.
if match(t, "Arial,Bold", 10, "Assembler symbols") {
CrossThreePage = false
} else {
CrossThreePage = true
}
text = append(text, t)
}
}
}
if CrossThreePage == true {
NNp := f.Page(num + 2)
NNcontent := NNp.Content()
var NNtext []pdf.Text
for _, t := range NNcontent.Text {
NNtext = append(NNtext, t)
}
NNtext = findWords(NNtext)
if len(NNtext) == 0 || sectionRE.MatchString(NNtext[2].S) {
NNtext = text[:0]
} else {
for _, t := range NNtext {
text = append(text, t)
}
}
}
// Get alias and remove text we should ignore.
out := text[:0]
alias := ""
for _, t := range text {
if strings.Contains(t.S, "instruction is used by the alias") || strings.Contains(t.S, "instruction is an alias of") {
alias_t := strings.SplitAfter(t.S, ".")
alias = alias_t[0]
}
// Skip page footer
if match(t, "Arial-ItalicMT", 8, "") || match(t, "ArialMT", 8, "") {
if debugPage > 0 {
fmt.Println("==the skip page footer is:==", t)
}
continue
}
// Skip the body text
if match(t, "TimesNewRoman", 9, "") || match(t, "TimesNewRomanPS-ItalicMT", 9, "") {
if debugPage > 0 {
fmt.Println("==the skip body text is:==", t)
}
continue
}
out = append(out, t)
}
text = out
// Page header must be child title.
if len(text) == 0 || !sectionRE.MatchString(text[0].S) {
return "", nil
}
name = text[1].S
inst := Inst{
Name: name,
Alias: alias,
}
text = text[2:]
// Skip body text before bits.
OffsetMark := false
k := 0
for k = 0; k < len(text); {
if !match(text[k], "Arial", 8, "31") {
k++
} else {
break
}
}
// Check offset.
if k > 0 && match(text[k-1], "Arial,Bold", 10, "") {
OffsetMark = true
text = text[k-1:]
} else {
text = text[k:]
}
// Encodings follow.
BitMark := false
bits := ""
// Find bits.
for i := 0; i < len(text); {
inst.Bits = ""
offset := ""
abits := ""
// Read bits only one time.
if OffsetMark == true {
for i < len(text) && !match(text[i], "Arial", 8, "") {
i++
}
if i < len(text) {
offset = text[i-1].S
BitMark = false
bits = ""
} else {
break
}
}
if BitMark == false {
if Npagebox == true && Noffset == offset {
bits, i = readBitBox(name, Ncontent, text, i)
} else {
bits, i = readBitBox(name, content, text, i)
}
BitMark = true
// Every time, get "then SEE" after get bits.
enc := false
if i < len(text)-1 {
m := i
for m < len(text)-1 && !match(text[m], "Arial-BoldItalicMT", 9, "encoding") {
m++
}
if match(text[m], "Arial-BoldItalicMT", 9, "encoding") && m < len(text) {
enc = true
m = m + 1
}
if enc == true {
for m < len(text) && !match(text[m], "Arial,Bold", 10, "") && match(text[m], "LucidaSansTypewriteX", 6.48, "") {
if strings.Contains(text[m].S, "then SEE") {
inst.Code = text[m].S
break
} else {
m++
}
}
}
}
}
// Possible subarchitecture notes.
ArchLoop:
for i < len(text) {
if !match(text[i], "Arial-BoldItalicMT", 9, "variant") || match(text[i], "Arial-BoldItalicMT", 9, "encoding") {
i++
continue
}
inst.Arch = ""
inst.Arch += offset
inst.Arch += " "
inst.Arch += text[i].S
inst.Arch = strings.TrimSpace(inst.Arch)
i++
// Encoding syntaxes.
sign := ""
SynMark := false
for i < len(text) && match(text[i], "LucidaSansTypewriteX", 6.48, "") && SynMark == false {
if (strings.Contains(text[i].S, "==") || strings.Contains(text[i].S, "!=")) && SynMark == false {
sign = text[i].S
i++
continue
}
// Avoid "equivalent to" another syntax.
if SynMark == false {
SynMark = true
inst.Syntax = ""
inst.Syntax = text[i].S
i++
}
}
abits = bits
// Analyse and replace some bits value.eg, sf==1
if strings.Contains(sign, "&&") {
split := strings.Split(sign, "&&")
for k := 0; k < len(split); {
if strings.Contains(split[k], "==") && !strings.Contains(split[k], "!") {
tmp := strings.Split(split[k], "==")
prefix := strings.TrimSpace(tmp[0])
value := strings.TrimSpace(tmp[1])
if strings.Contains(bits, prefix) && !strings.Contains(value, "x") {
abits = strings.Replace(abits, prefix, value, -1)
}
}
k++
}
} else if strings.Contains(sign, "==") && !strings.Contains(sign, "!") {
split := strings.Split(sign, "==")
prefix := strings.TrimSpace(split[0])
value := strings.TrimSpace(split[1])
if strings.Contains(bits, prefix) && !strings.Contains(value, "x") {
abits = strings.Replace(abits, prefix, value, -1)
}
}
// Deal with syntax contains {2}
if strings.Contains(inst.Syntax, "{2}") {
if !strings.Contains(abits, "Q") {
fmt.Fprintf(os.Stderr, "instruction%s - syntax%s: is wrong!!\n", name, inst.Syntax)
}
syn := inst.Syntax
bits := abits
for i := 0; i < 2; {
if i == 0 {
inst.Bits = strings.Replace(bits, "Q", "0", -1)
inst.Syntax = strings.Replace(syn, "{2}", "", -1)
table = append(table, inst)
}
if i == 1 {
inst.Bits = strings.Replace(bits, "Q", "1", -1)
inst.Syntax = strings.Replace(syn, "{2}", "2", -1)
table = append(table, inst)
}
i++
}
} else {
inst.Bits = abits
table = append(table, inst)
}
if OffsetMark == true && i < len(text) && match(text[i], "Arial-BoldItalicMT", 9, "variant") && !match(text[i], "Arial-BoldItalicMT", 9, "encoding") {
continue ArchLoop
} else {
break
}
}
}
return name, table
}
func readBitBox(name string, content pdf.Content, text []pdf.Text, i int) (string, int) {
// Bits headings
y3 := 0.0
x1 := 0.0
for i < len(text) && match(text[i], "Arial", 8, "") {
if y3 == 0 {
y3 = text[i].Y
}
if x1 == 0 {
x1 = text[i].X
}
if text[i].Y != y3 {
break
}
i++
}
// Bits fields in box
x2 := 0.0
y2 := 0.0
dy1 := 0.0
for i < len(text) && match(text[i], "Arial", 8, "") {
if x2 < text[i].X+text[i].W {
x2 = text[i].X + text[i].W
}
if y2 == 0 {
y2 = text[i].Y
}
if text[i].Y != y2 {
break
}
dy1 = text[i].FontSize
i++
}
// Bits fields below box
x3 := 0.0
y1 := 0.0
for i < len(text) && match(text[i], "Arial", 8, "") {
if x3 < text[i].X+text[i].W {
x3 = text[i].X + text[i].W
}
y1 = text[i].Y
if text[i].Y != y1 {
break
}
i++
}
//no bits fields below box
below_flag := true
if y1 == 0.0 {
below_flag = false
y1 = y2
}
// Encoding box
if debugPage > 0 {
fmt.Println("encoding box", x1, y3, x2, y1)
}
// Find lines (thin rectangles) separating bit fields.
var bottom, top pdf.Rect
const (
yMargin = 0.25 * 72
xMargin = 2 * 72
)
cont := 0
if below_flag == true {
for _, r := range content.Rect {
cont = cont + 1
if x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1-yMargin < r.Min.Y && r.Min.Y < y2-dy1 {
bottom = r
}
if y2+dy1 < r.Min.Y && r.Min.Y < y3+yMargin {
top = r
}
}
}
} else {
for _, r := range content.Rect {
cont = cont + 1
if x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
if y1-yMargin-dy1 < r.Min.Y && r.Min.Y < y3-dy1 {
bottom = r
}
if y2+dy1 < r.Min.Y && r.Min.Y < y3+yMargin {
top = r
}
}
}
}
if debugPage > 0 {
fmt.Println("top", top, "bottom", bottom, "content.Rect number", cont)
}
const ε = 0.5 * 72
cont_1 := 0
var bars []pdf.Rect
for _, r := range content.Rect {
if math.Abs(r.Min.X-r.Max.X) < bottom.Max.X-bottom.Min.X-(ε/2) && math.Abs(r.Min.Y-bottom.Min.Y) < ε && math.Abs(r.Max.Y-top.Min.Y) < ε {
cont_1 = cont_1 + 1
bars = append(bars, r)
}
}
sort.Sort(RectHorizontal(bars))
if debugPage > 0 {
fmt.Println("==bars number==", cont_1)
}
// There are 16-bit and 32-bit encodings.
// In practice, they are about 2.65 and 5.3 inches wide, respectively.
// Use 4 inches as a cutoff.
nbit := 32
dx := top.Max.X - top.Min.X
if top.Max.X-top.Min.X < 4*72 {
nbit = 16
}
total := 0
var buf bytes.Buffer
for i := 0; i < len(bars); i++ {
if i > 0 {
fmt.Fprintf(&buf, "|")
}
var sub []pdf.Text
x1, x2 := bars[i].Min.X, bars[i].Max.X
for _, t := range content.Text {
tx := t.X + t.W/2
ty := t.Y
if x1 < tx && tx < x2 && y2-dy1 < ty && ty < y2+dy1 {
sub = append(sub, t)
}
}
var str []string
for _, t := range findWords(sub) {
str = append(str, t.S)
}
s := strings.Join(str, " ")
s = strings.Replace(s, ")(", ") (", -1)
// If bits contain "!" or "x", be replaced by the bits below it.
if strings.Contains(s, "!") || strings.Contains(s, "x") {
var sub1 []pdf.Text
for _, t := range content.Text {
tx := t.X + t.W/2
ty := t.Y
if x1 < tx && tx < x2 && y1-dy1 < ty && ty < y1+dy1 {
sub1 = append(sub1, t)
}
}
var str1 []string
for _, t := range findWords(sub1) {
str1 = append(str1, t.S)
}
s = strings.Join(str1, " ")
s = strings.Replace(s, ")(", ") (", -1)
}
n := len(strings.Fields(s))
var b int
if IMMRE.MatchString(s) {
bitNum := strings.TrimPrefix(s, "imm")
b, _ = strconv.Atoi(bitNum)
} else if s == "immhi" {
b = 19
} else {
b = int(float64(nbit)*(x2-x1)/dx + 0.5)
}
if n == b {
for k, f := range strings.Fields(s) {
if k > 0 {
fmt.Fprintf(&buf, "|")
}
fmt.Fprintf(&buf, "%s", f)
}
} else {
if n != 1 {
fmt.Fprintf(os.Stderr, "%s - multi-field %d-bit encoding: %s\n", name, n, s)
}
fmt.Fprintf(&buf, "%s:%d", s, b)
}
total += b
}
if total != nbit || total == 0 {
fmt.Fprintf(os.Stderr, "%s - %d-bit encoding\n", name, total)
}
return buf.String(), i
}
type RectHorizontal []pdf.Rect
func (x RectHorizontal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x RectHorizontal) Less(i, j int) bool { return x[i].Min.X < x[j].Min.X }
func (x RectHorizontal) Len() int { return len(x) }
func checkNoEncodings(num int, text []pdf.Text) {
for _, t := range text {
if match(t, "Helvetica-Bold", 9, "Encoding") {
fmt.Fprintf(os.Stderr, "page %d: unexpected encoding: %s\n", num, t.S)
}
}
}
func match(t pdf.Text, font string, size float64, substr string) bool {
return t.Font == font && math.Abs(t.FontSize-size) < 0.1 && strings.Contains(t.S, substr)
}
func findWords(chars []pdf.Text) (words []pdf.Text) {
// Sort by Y coordinate and normalize.
const nudge = 1
sort.Sort(pdf.TextVertical(chars))
old := -100000.0
for i, c := range chars {
if c.Y != old && math.Abs(old-c.Y) < nudge {
chars[i].Y = old
} else {
old = c.Y
}
}
// Sort by Y coordinate, breaking ties with X.
// This will bring letters in a single word together.
sort.Sort(pdf.TextVertical(chars))
// Loop over chars.
for i := 0; i < len(chars); {
// Find all chars on line.
j := i + 1
for j < len(chars) && chars[j].Y == chars[i].Y {
j++
}
var end float64
// Split line into words (really, phrases).
for k := i; k < j; {
ck := &chars[k]
s := ck.S
end = ck.X + ck.W
charSpace := ck.FontSize / 6
wordSpace := ck.FontSize * 2 / 3
l := k + 1
for l < j {
// Grow word.
cl := &chars[l]
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
s += cl.S
end = cl.X + cl.W
l++
continue
}
// Add space to phrase before next word.
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
s += " " + cl.S
end = cl.X + cl.W
l++
continue
}
break
}
f := ck.Font
f = strings.TrimSuffix(f, ",Italic")
f = strings.TrimSuffix(f, "-Italic")
words = append(words, pdf.Text{f, ck.FontSize, ck.X, ck.Y, end - ck.X, s})
k = l
}
i = j
}
return words
}
func sameFont(f1, f2 string) bool {
f1 = strings.TrimSuffix(f1, ",Italic")
f1 = strings.TrimSuffix(f1, "-Italic")
f2 = strings.TrimSuffix(f1, ",Italic")
f2 = strings.TrimSuffix(f1, "-Italic")
return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
}
var jsFix = strings.NewReplacer(
`\u003c`, `<`,
`\u003e`, `>`,
`\u0026`, `&`,
`\u0009`, `\t`,
)
func printTable(name string, table []Inst) {
_ = strconv.Atoi
}