Initial Commit

This commit is contained in:
Sajid
2024-09-07 18:00:09 +06:00
commit 0f9a53f75a
3352 changed files with 1563708 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
*.inc
insn_list.txt
disassemblertables_reduce2
disassemblertables2
ppc_gen_reg
arm64_gen_vreg
strinforeduce/strinforeduce
strinforeduce/strinforeduce_reduce

View File

@@ -0,0 +1,14 @@
all:
x86:
# compile disassembler2 with X86GenDisassemblerTables2.inc
$(CC) disassemblertables2.c -o disassemblertables2
# compile disassembler2 with X86GenDisassemblerTables_reduce2.inc
$(CC) -DCAPSTONE_X86_REDUCE disassemblertables2.c -o disassemblertables_reduce2
arm64:
$(CC) arm64_gen_vreg.c -o arm64_gen_vreg
clean:
$(RM) disassemblertables2 disassemblertables_reduce2 arm64_gen_vreg

View File

@@ -0,0 +1,57 @@
Sync tools to port LLVM inc files to Capstone.
For X86
=======
0. cd tablegen/, then follow its README.
1. Run genall-{full|reduce}.sh, then copy generated .inc files to arch/<ARCH>/ directory
$ ./genall-full.sh tablegen ~/projects/tmp/capstone777.git/arch/X86
$ ./genall-reduce.sh tablegen ~/projects/tmp/capstone777.git/arch/X86
2. Run disassemblertables_reduce2 & disassemblertables_reduce2 to generate optimized (index table) X86GenDisassemblerTables2.inc & X86GenDisassemblerTables_reduce2.inc
# use 2x name to avoid overwriting X86GenDisassemblerTables2.inc & X86GenDisassemblerTables_reduce2.inc
$ make
$ ./disassemblertables2 > X86GenDisassemblerTables2x.inc
$ ./disassemblertables_reduce2 > X86GenDisassemblerTables_reduce2x.inc
3. cd strinforeduce/, and follow its README.
4. Copy all generated .inc files to arch/X86/
$ cp X86GenAsmWriter_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86GenAsmWriter1_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86MappingInsnName_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86MappingInsn_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86MappingInsnOp_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86GenInstrInfo_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86GenDisassemblerTables_reduce.inc ~/projects/capstone.git/arch/X86
$ cp X86GenDisassemblerTables_reduce2x.inc ~/projects/capstone.git/arch/X86/X86GenDisassemblerTables_reduce2.inc
$ cp X86GenAsmWriter.inc ~/projects/capstone.git/arch/X86
$ cp X86GenAsmWriter1.inc ~/projects/capstone.git/arch/X86
$ cp X86MappingInsnName.inc ~/projects/capstone.git/arch/X86
$ cp X86MappingInsn.inc ~/projects/capstone.git/arch/X86
$ cp X86MappingInsnOp.inc ~/projects/capstone.git/arch/X86
$ cp X86GenInstrInfo.inc ~/projects/capstone.git/arch/X86
$ cp X86GenDisassemblerTables.inc ~/projects/capstone.git/arch/X86
$ cp X86GenDisassemblerTables2x.inc ~/projects/capstone.git/arch/X86/X86GenDisassemblerTables2.inc
5. copy insn_list.txt to include/capstone/<arch.h>
For non-X86
===========
0. cd tablegen/, then follow its README.
1. Run gen-tablegen-arch.sh
2. Run genall-arch.sh
./genall-arch.sh tablegen ~/projects/capstone.git/arch/ARM ARM
./genall-arch.sh tablegen ~/projects/capstone.git/arch/ARM AArch64
./genall-arch.sh tablegen ~/projects/capstone.git/arch/ARM PowerPC
3. Copy generated *.inc files to arch/<arch>/

View File

@@ -0,0 +1,483 @@
/*===-- X86DisassemblerDecoderCommon.h - Disassembler decoder -----*- C -*-===*
*
* The LLVM Compiler Infrastructure
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*===----------------------------------------------------------------------===*
*
* This file is part of the X86 Disassembler.
* It contains common definitions used by both the disassembler and the table
* generator.
* Documentation for the disassembler can be found in X86Disassembler.h.
*
*===----------------------------------------------------------------------===*/
/* Capstone Disassembly Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
/*
* This header file provides those definitions that need to be shared between
* the decoder and the table generator in a C-friendly manner.
*/
#ifndef CS_X86_DISASSEMBLERDECODERCOMMON_H
#define CS_X86_DISASSEMBLERDECODERCOMMON_H
#define INSTRUCTIONS_SYM x86DisassemblerInstrSpecifiers
#define CONTEXTS_SYM x86DisassemblerContexts
#define ONEBYTE_SYM x86DisassemblerOneByteOpcodes
#define TWOBYTE_SYM x86DisassemblerTwoByteOpcodes
#define THREEBYTE38_SYM x86DisassemblerThreeByte38Opcodes
#define THREEBYTE3A_SYM x86DisassemblerThreeByte3AOpcodes
#define XOP8_MAP_SYM x86DisassemblerXOP8Opcodes
#define XOP9_MAP_SYM x86DisassemblerXOP9Opcodes
#define XOPA_MAP_SYM x86DisassemblerXOPAOpcodes
#define THREEDNOW_MAP_SYM x86Disassembler3DNowOpcodes
/*
* Attributes of an instruction that must be known before the opcode can be
* processed correctly. Most of these indicate the presence of particular
* prefixes, but ATTR_64BIT is simply an attribute of the decoding context.
*/
#define ATTRIBUTE_BITS \
ENUM_ENTRY(ATTR_NONE, 0x00) \
ENUM_ENTRY(ATTR_64BIT, (0x1 << 0)) \
ENUM_ENTRY(ATTR_XS, (0x1 << 1)) \
ENUM_ENTRY(ATTR_XD, (0x1 << 2)) \
ENUM_ENTRY(ATTR_REXW, (0x1 << 3)) \
ENUM_ENTRY(ATTR_OPSIZE, (0x1 << 4)) \
ENUM_ENTRY(ATTR_ADSIZE, (0x1 << 5)) \
ENUM_ENTRY(ATTR_VEX, (0x1 << 6)) \
ENUM_ENTRY(ATTR_VEXL, (0x1 << 7)) \
ENUM_ENTRY(ATTR_EVEX, (0x1 << 8)) \
ENUM_ENTRY(ATTR_EVEXL, (0x1 << 9)) \
ENUM_ENTRY(ATTR_EVEXL2, (0x1 << 10)) \
ENUM_ENTRY(ATTR_EVEXK, (0x1 << 11)) \
ENUM_ENTRY(ATTR_EVEXKZ, (0x1 << 12)) \
ENUM_ENTRY(ATTR_EVEXB, (0x1 << 13))
#define ENUM_ENTRY(n, v) n = v,
enum attributeBits {
ATTRIBUTE_BITS
ATTR_max
};
#undef ENUM_ENTRY
/*
* Combinations of the above attributes that are relevant to instruction
* decode. Although other combinations are possible, they can be reduced to
* these without affecting the ultimately decoded instruction.
*/
// Class name Rank Rationale for rank assignment
#define INSTRUCTION_CONTEXTS \
ENUM_ENTRY(IC, 0, "says nothing about the instruction") \
ENUM_ENTRY(IC_64BIT, 1, "says the instruction applies in " \
"64-bit mode but no more") \
ENUM_ENTRY(IC_OPSIZE, 3, "requires an OPSIZE prefix, so " \
"operands change width") \
ENUM_ENTRY(IC_ADSIZE, 3, "requires an ADSIZE prefix, so " \
"operands change width") \
ENUM_ENTRY(IC_OPSIZE_ADSIZE, 4, "requires ADSIZE and OPSIZE prefixes") \
ENUM_ENTRY(IC_XD, 2, "may say something about the opcode " \
"but not the operands") \
ENUM_ENTRY(IC_XS, 2, "may say something about the opcode " \
"but not the operands") \
ENUM_ENTRY(IC_XD_OPSIZE, 3, "requires an OPSIZE prefix, so " \
"operands change width") \
ENUM_ENTRY(IC_XS_OPSIZE, 3, "requires an OPSIZE prefix, so " \
"operands change width") \
ENUM_ENTRY(IC_XD_ADSIZE, 3, "requires an ADSIZE prefix, so " \
"operands change width") \
ENUM_ENTRY(IC_XS_ADSIZE, 3, "requires an ADSIZE prefix, so " \
"operands change width") \
ENUM_ENTRY(IC_64BIT_REXW, 5, "requires a REX.W prefix, so operands "\
"change width; overrides IC_OPSIZE") \
ENUM_ENTRY(IC_64BIT_REXW_ADSIZE, 6, "requires a REX.W prefix and 0x67 " \
"prefix") \
ENUM_ENTRY(IC_64BIT_OPSIZE, 3, "Just as meaningful as IC_OPSIZE") \
ENUM_ENTRY(IC_64BIT_ADSIZE, 3, "Just as meaningful as IC_ADSIZE") \
ENUM_ENTRY(IC_64BIT_OPSIZE_ADSIZE, 4, "Just as meaningful as IC_OPSIZE/" \
"IC_ADSIZE") \
ENUM_ENTRY(IC_64BIT_XD, 6, "XD instructions are SSE; REX.W is " \
"secondary") \
ENUM_ENTRY(IC_64BIT_XS, 6, "Just as meaningful as IC_64BIT_XD") \
ENUM_ENTRY(IC_64BIT_XD_OPSIZE, 3, "Just as meaningful as IC_XD_OPSIZE") \
ENUM_ENTRY(IC_64BIT_XS_OPSIZE, 3, "Just as meaningful as IC_XS_OPSIZE") \
ENUM_ENTRY(IC_64BIT_XD_ADSIZE, 3, "Just as meaningful as IC_XD_ADSIZE") \
ENUM_ENTRY(IC_64BIT_XS_ADSIZE, 3, "Just as meaningful as IC_XS_ADSIZE") \
ENUM_ENTRY(IC_64BIT_REXW_XS, 7, "OPSIZE could mean a different " \
"opcode") \
ENUM_ENTRY(IC_64BIT_REXW_XD, 7, "Just as meaningful as " \
"IC_64BIT_REXW_XS") \
ENUM_ENTRY(IC_64BIT_REXW_OPSIZE, 8, "The Dynamic Duo! Prefer over all " \
"else because this changes most " \
"operands' meaning") \
ENUM_ENTRY(IC_VEX, 1, "requires a VEX prefix") \
ENUM_ENTRY(IC_VEX_XS, 2, "requires VEX and the XS prefix") \
ENUM_ENTRY(IC_VEX_XD, 2, "requires VEX and the XD prefix") \
ENUM_ENTRY(IC_VEX_OPSIZE, 2, "requires VEX and the OpSize prefix") \
ENUM_ENTRY(IC_VEX_W, 3, "requires VEX and the W prefix") \
ENUM_ENTRY(IC_VEX_W_XS, 4, "requires VEX, W, and XS prefix") \
ENUM_ENTRY(IC_VEX_W_XD, 4, "requires VEX, W, and XD prefix") \
ENUM_ENTRY(IC_VEX_W_OPSIZE, 4, "requires VEX, W, and OpSize") \
ENUM_ENTRY(IC_VEX_L, 3, "requires VEX and the L prefix") \
ENUM_ENTRY(IC_VEX_L_XS, 4, "requires VEX and the L and XS prefix")\
ENUM_ENTRY(IC_VEX_L_XD, 4, "requires VEX and the L and XD prefix")\
ENUM_ENTRY(IC_VEX_L_OPSIZE, 4, "requires VEX, L, and OpSize") \
ENUM_ENTRY(IC_VEX_L_W, 4, "requires VEX, L and W") \
ENUM_ENTRY(IC_VEX_L_W_XS, 5, "requires VEX, L, W and XS prefix") \
ENUM_ENTRY(IC_VEX_L_W_XD, 5, "requires VEX, L, W and XD prefix") \
ENUM_ENTRY(IC_VEX_L_W_OPSIZE, 5, "requires VEX, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX, 1, "requires an EVEX prefix") \
ENUM_ENTRY(IC_EVEX_XS, 2, "requires EVEX and the XS prefix") \
ENUM_ENTRY(IC_EVEX_XD, 2, "requires EVEX and the XD prefix") \
ENUM_ENTRY(IC_EVEX_OPSIZE, 2, "requires EVEX and the OpSize prefix") \
ENUM_ENTRY(IC_EVEX_W, 3, "requires EVEX and the W prefix") \
ENUM_ENTRY(IC_EVEX_W_XS, 4, "requires EVEX, W, and XS prefix") \
ENUM_ENTRY(IC_EVEX_W_XD, 4, "requires EVEX, W, and XD prefix") \
ENUM_ENTRY(IC_EVEX_W_OPSIZE, 4, "requires EVEX, W, and OpSize") \
ENUM_ENTRY(IC_EVEX_L, 3, "requires EVEX and the L prefix") \
ENUM_ENTRY(IC_EVEX_L_XS, 4, "requires EVEX and the L and XS prefix")\
ENUM_ENTRY(IC_EVEX_L_XD, 4, "requires EVEX and the L and XD prefix")\
ENUM_ENTRY(IC_EVEX_L_OPSIZE, 4, "requires EVEX, L, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_W, 3, "requires EVEX, L and W") \
ENUM_ENTRY(IC_EVEX_L_W_XS, 4, "requires EVEX, L, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L_W_XD, 4, "requires EVEX, L, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L_W_OPSIZE, 4, "requires EVEX, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX_L2, 3, "requires EVEX and the L2 prefix") \
ENUM_ENTRY(IC_EVEX_L2_XS, 4, "requires EVEX and the L2 and XS prefix")\
ENUM_ENTRY(IC_EVEX_L2_XD, 4, "requires EVEX and the L2 and XD prefix")\
ENUM_ENTRY(IC_EVEX_L2_OPSIZE, 4, "requires EVEX, L2, and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_W, 3, "requires EVEX, L2 and W") \
ENUM_ENTRY(IC_EVEX_L2_W_XS, 4, "requires EVEX, L2, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_XD, 4, "requires EVEX, L2, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE, 4, "requires EVEX, L2, W and OpSize") \
ENUM_ENTRY(IC_EVEX_K, 1, "requires an EVEX_K prefix") \
ENUM_ENTRY(IC_EVEX_XS_K, 2, "requires EVEX_K and the XS prefix") \
ENUM_ENTRY(IC_EVEX_XD_K, 2, "requires EVEX_K and the XD prefix") \
ENUM_ENTRY(IC_EVEX_OPSIZE_K, 2, "requires EVEX_K and the OpSize prefix") \
ENUM_ENTRY(IC_EVEX_W_K, 3, "requires EVEX_K and the W prefix") \
ENUM_ENTRY(IC_EVEX_W_XS_K, 4, "requires EVEX_K, W, and XS prefix") \
ENUM_ENTRY(IC_EVEX_W_XD_K, 4, "requires EVEX_K, W, and XD prefix") \
ENUM_ENTRY(IC_EVEX_W_OPSIZE_K, 4, "requires EVEX_K, W, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_K, 3, "requires EVEX_K and the L prefix") \
ENUM_ENTRY(IC_EVEX_L_XS_K, 4, "requires EVEX_K and the L and XS prefix")\
ENUM_ENTRY(IC_EVEX_L_XD_K, 4, "requires EVEX_K and the L and XD prefix")\
ENUM_ENTRY(IC_EVEX_L_OPSIZE_K, 4, "requires EVEX_K, L, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_W_K, 3, "requires EVEX_K, L and W") \
ENUM_ENTRY(IC_EVEX_L_W_XS_K, 4, "requires EVEX_K, L, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L_W_XD_K, 4, "requires EVEX_K, L, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_K, 4, "requires EVEX_K, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_K, 3, "requires EVEX_K and the L2 prefix") \
ENUM_ENTRY(IC_EVEX_L2_XS_K, 4, "requires EVEX_K and the L2 and XS prefix")\
ENUM_ENTRY(IC_EVEX_L2_XD_K, 4, "requires EVEX_K and the L2 and XD prefix")\
ENUM_ENTRY(IC_EVEX_L2_OPSIZE_K, 4, "requires EVEX_K, L2, and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_W_K, 3, "requires EVEX_K, L2 and W") \
ENUM_ENTRY(IC_EVEX_L2_W_XS_K, 4, "requires EVEX_K, L2, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_XD_K, 4, "requires EVEX_K, L2, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_K, 4, "requires EVEX_K, L2, W and OpSize") \
ENUM_ENTRY(IC_EVEX_B, 1, "requires an EVEX_B prefix") \
ENUM_ENTRY(IC_EVEX_XS_B, 2, "requires EVEX_B and the XS prefix") \
ENUM_ENTRY(IC_EVEX_XD_B, 2, "requires EVEX_B and the XD prefix") \
ENUM_ENTRY(IC_EVEX_OPSIZE_B, 2, "requires EVEX_B and the OpSize prefix") \
ENUM_ENTRY(IC_EVEX_W_B, 3, "requires EVEX_B and the W prefix") \
ENUM_ENTRY(IC_EVEX_W_XS_B, 4, "requires EVEX_B, W, and XS prefix") \
ENUM_ENTRY(IC_EVEX_W_XD_B, 4, "requires EVEX_B, W, and XD prefix") \
ENUM_ENTRY(IC_EVEX_W_OPSIZE_B, 4, "requires EVEX_B, W, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_B, 3, "requires EVEX_B and the L prefix") \
ENUM_ENTRY(IC_EVEX_L_XS_B, 4, "requires EVEX_B and the L and XS prefix")\
ENUM_ENTRY(IC_EVEX_L_XD_B, 4, "requires EVEX_B and the L and XD prefix")\
ENUM_ENTRY(IC_EVEX_L_OPSIZE_B, 4, "requires EVEX_B, L, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_W_B, 3, "requires EVEX_B, L and W") \
ENUM_ENTRY(IC_EVEX_L_W_XS_B, 4, "requires EVEX_B, L, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L_W_XD_B, 4, "requires EVEX_B, L, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_B, 4, "requires EVEX_B, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_B, 3, "requires EVEX_B and the L2 prefix") \
ENUM_ENTRY(IC_EVEX_L2_XS_B, 4, "requires EVEX_B and the L2 and XS prefix")\
ENUM_ENTRY(IC_EVEX_L2_XD_B, 4, "requires EVEX_B and the L2 and XD prefix")\
ENUM_ENTRY(IC_EVEX_L2_OPSIZE_B, 4, "requires EVEX_B, L2, and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_W_B, 3, "requires EVEX_B, L2 and W") \
ENUM_ENTRY(IC_EVEX_L2_W_XS_B, 4, "requires EVEX_B, L2, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_XD_B, 4, "requires EVEX_B, L2, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_B, 4, "requires EVEX_B, L2, W and OpSize") \
ENUM_ENTRY(IC_EVEX_K_B, 1, "requires EVEX_B and EVEX_K prefix") \
ENUM_ENTRY(IC_EVEX_XS_K_B, 2, "requires EVEX_B, EVEX_K and the XS prefix") \
ENUM_ENTRY(IC_EVEX_XD_K_B, 2, "requires EVEX_B, EVEX_K and the XD prefix") \
ENUM_ENTRY(IC_EVEX_OPSIZE_K_B, 2, "requires EVEX_B, EVEX_K and the OpSize prefix") \
ENUM_ENTRY(IC_EVEX_W_K_B, 3, "requires EVEX_B, EVEX_K and the W prefix") \
ENUM_ENTRY(IC_EVEX_W_XS_K_B, 4, "requires EVEX_B, EVEX_K, W, and XS prefix") \
ENUM_ENTRY(IC_EVEX_W_XD_K_B, 4, "requires EVEX_B, EVEX_K, W, and XD prefix") \
ENUM_ENTRY(IC_EVEX_W_OPSIZE_K_B, 4, "requires EVEX_B, EVEX_K, W, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_K_B, 3, "requires EVEX_B, EVEX_K and the L prefix") \
ENUM_ENTRY(IC_EVEX_L_XS_K_B, 4, "requires EVEX_B, EVEX_K and the L and XS prefix")\
ENUM_ENTRY(IC_EVEX_L_XD_K_B, 4, "requires EVEX_B, EVEX_K and the L and XD prefix")\
ENUM_ENTRY(IC_EVEX_L_OPSIZE_K_B, 4, "requires EVEX_B, EVEX_K, L, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_W_K_B, 3, "requires EVEX_B, EVEX_K, L and W") \
ENUM_ENTRY(IC_EVEX_L_W_XS_K_B, 4, "requires EVEX_B, EVEX_K, L, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L_W_XD_K_B, 4, "requires EVEX_B, EVEX_K, L, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_K_B,4, "requires EVEX_B, EVEX_K, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_K_B, 3, "requires EVEX_B, EVEX_K and the L2 prefix") \
ENUM_ENTRY(IC_EVEX_L2_XS_K_B, 4, "requires EVEX_B, EVEX_K and the L2 and XS prefix")\
ENUM_ENTRY(IC_EVEX_L2_XD_K_B, 4, "requires EVEX_B, EVEX_K and the L2 and XD prefix")\
ENUM_ENTRY(IC_EVEX_L2_OPSIZE_K_B, 4, "requires EVEX_B, EVEX_K, L2, and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_W_K_B, 3, "requires EVEX_B, EVEX_K, L2 and W") \
ENUM_ENTRY(IC_EVEX_L2_W_XS_K_B, 4, "requires EVEX_B, EVEX_K, L2, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_XD_K_B, 4, "requires EVEX_B, EVEX_K, L2, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_K_B,4, "requires EVEX_B, EVEX_K, L2, W and OpSize") \
ENUM_ENTRY(IC_EVEX_KZ_B, 1, "requires EVEX_B and EVEX_KZ prefix") \
ENUM_ENTRY(IC_EVEX_XS_KZ_B, 2, "requires EVEX_B, EVEX_KZ and the XS prefix") \
ENUM_ENTRY(IC_EVEX_XD_KZ_B, 2, "requires EVEX_B, EVEX_KZ and the XD prefix") \
ENUM_ENTRY(IC_EVEX_OPSIZE_KZ_B, 2, "requires EVEX_B, EVEX_KZ and the OpSize prefix") \
ENUM_ENTRY(IC_EVEX_W_KZ_B, 3, "requires EVEX_B, EVEX_KZ and the W prefix") \
ENUM_ENTRY(IC_EVEX_W_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ, W, and XS prefix") \
ENUM_ENTRY(IC_EVEX_W_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ, W, and XD prefix") \
ENUM_ENTRY(IC_EVEX_W_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, W, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_KZ_B, 3, "requires EVEX_B, EVEX_KZ and the L prefix") \
ENUM_ENTRY(IC_EVEX_L_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L and XS prefix")\
ENUM_ENTRY(IC_EVEX_L_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L and XD prefix")\
ENUM_ENTRY(IC_EVEX_L_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_W_KZ_B, 3, "requires EVEX_B, EVEX_KZ, L and W") \
ENUM_ENTRY(IC_EVEX_L_W_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L_W_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_KZ_B, 3, "requires EVEX_B, EVEX_KZ and the L2 prefix") \
ENUM_ENTRY(IC_EVEX_L2_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L2 and XS prefix")\
ENUM_ENTRY(IC_EVEX_L2_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L2 and XD prefix")\
ENUM_ENTRY(IC_EVEX_L2_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_W_KZ_B, 3, "requires EVEX_B, EVEX_KZ, L2 and W") \
ENUM_ENTRY(IC_EVEX_L2_W_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, W and OpSize") \
ENUM_ENTRY(IC_EVEX_KZ, 1, "requires an EVEX_KZ prefix") \
ENUM_ENTRY(IC_EVEX_XS_KZ, 2, "requires EVEX_KZ and the XS prefix") \
ENUM_ENTRY(IC_EVEX_XD_KZ, 2, "requires EVEX_KZ and the XD prefix") \
ENUM_ENTRY(IC_EVEX_OPSIZE_KZ, 2, "requires EVEX_KZ and the OpSize prefix") \
ENUM_ENTRY(IC_EVEX_W_KZ, 3, "requires EVEX_KZ and the W prefix") \
ENUM_ENTRY(IC_EVEX_W_XS_KZ, 4, "requires EVEX_KZ, W, and XS prefix") \
ENUM_ENTRY(IC_EVEX_W_XD_KZ, 4, "requires EVEX_KZ, W, and XD prefix") \
ENUM_ENTRY(IC_EVEX_W_OPSIZE_KZ, 4, "requires EVEX_KZ, W, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_KZ, 3, "requires EVEX_KZ and the L prefix") \
ENUM_ENTRY(IC_EVEX_L_XS_KZ, 4, "requires EVEX_KZ and the L and XS prefix")\
ENUM_ENTRY(IC_EVEX_L_XD_KZ, 4, "requires EVEX_KZ and the L and XD prefix")\
ENUM_ENTRY(IC_EVEX_L_OPSIZE_KZ, 4, "requires EVEX_KZ, L, and OpSize") \
ENUM_ENTRY(IC_EVEX_L_W_KZ, 3, "requires EVEX_KZ, L and W") \
ENUM_ENTRY(IC_EVEX_L_W_XS_KZ, 4, "requires EVEX_KZ, L, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L_W_XD_KZ, 4, "requires EVEX_KZ, L, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_KZ, 4, "requires EVEX_KZ, L, W and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_KZ, 3, "requires EVEX_KZ and the L2 prefix") \
ENUM_ENTRY(IC_EVEX_L2_XS_KZ, 4, "requires EVEX_KZ and the L2 and XS prefix")\
ENUM_ENTRY(IC_EVEX_L2_XD_KZ, 4, "requires EVEX_KZ and the L2 and XD prefix")\
ENUM_ENTRY(IC_EVEX_L2_OPSIZE_KZ, 4, "requires EVEX_KZ, L2, and OpSize") \
ENUM_ENTRY(IC_EVEX_L2_W_KZ, 3, "requires EVEX_KZ, L2 and W") \
ENUM_ENTRY(IC_EVEX_L2_W_XS_KZ, 4, "requires EVEX_KZ, L2, W and XS prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_XD_KZ, 4, "requires EVEX_KZ, L2, W and XD prefix") \
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_KZ, 4, "requires EVEX_KZ, L2, W and OpSize")
#define ENUM_ENTRY(n, r, d) n,
typedef enum {
INSTRUCTION_CONTEXTS
IC_max
} InstructionContext;
#undef ENUM_ENTRY
/*
* Opcode types, which determine which decode table to use, both in the Intel
* manual and also for the decoder.
*/
typedef enum {
ONEBYTE = 0,
TWOBYTE = 1,
THREEBYTE_38 = 2,
THREEBYTE_3A = 3,
XOP8_MAP = 4,
XOP9_MAP = 5,
XOPA_MAP = 6,
THREEDNOW_MAP = 7
} OpcodeType;
/*
* The following structs are used for the hierarchical decode table. After
* determining the instruction's class (i.e., which IC_* constant applies to
* it), the decoder reads the opcode. Some instructions require specific
* values of the ModR/M byte, so the ModR/M byte indexes into the final table.
*
* If a ModR/M byte is not required, "required" is left unset, and the values
* for each instructionID are identical.
*/
typedef uint16_t InstrUID;
/*
* ModRMDecisionType - describes the type of ModR/M decision, allowing the
* consumer to determine the number of entries in it.
*
* MODRM_ONEENTRY - No matter what the value of the ModR/M byte is, the decoded
* instruction is the same.
* MODRM_SPLITRM - If the ModR/M byte is between 0x00 and 0xbf, the opcode
* corresponds to one instruction; otherwise, it corresponds to
* a different instruction.
* MODRM_SPLITMISC- If the ModR/M byte is between 0x00 and 0xbf, ModR/M byte
* divided by 8 is used to select instruction; otherwise, each
* value of the ModR/M byte could correspond to a different
* instruction.
* MODRM_SPLITREG - ModR/M byte divided by 8 is used to select instruction. This
corresponds to instructions that use reg field as opcode
* MODRM_FULL - Potentially, each value of the ModR/M byte could correspond
* to a different instruction.
*/
#define MODRMTYPES \
ENUM_ENTRY(MODRM_ONEENTRY) \
ENUM_ENTRY(MODRM_SPLITRM) \
ENUM_ENTRY(MODRM_SPLITMISC) \
ENUM_ENTRY(MODRM_SPLITREG) \
ENUM_ENTRY(MODRM_FULL)
#define ENUM_ENTRY(n) n,
typedef enum {
MODRMTYPES
MODRM_max
} ModRMDecisionType;
#undef ENUM_ENTRY
#define CASE_ENCODING_RM \
case ENCODING_RM: \
case ENCODING_RM_CD2: \
case ENCODING_RM_CD4: \
case ENCODING_RM_CD8: \
case ENCODING_RM_CD16: \
case ENCODING_RM_CD32: \
case ENCODING_RM_CD64
#define CASE_ENCODING_VSIB \
case ENCODING_VSIB: \
case ENCODING_VSIB_CD2: \
case ENCODING_VSIB_CD4: \
case ENCODING_VSIB_CD8: \
case ENCODING_VSIB_CD16: \
case ENCODING_VSIB_CD32: \
case ENCODING_VSIB_CD64
// Physical encodings of instruction operands.
#define ENCODINGS \
ENUM_ENTRY(ENCODING_NONE, "") \
ENUM_ENTRY(ENCODING_REG, "Register operand in ModR/M byte.") \
ENUM_ENTRY(ENCODING_RM, "R/M operand in ModR/M byte.") \
ENUM_ENTRY(ENCODING_RM_CD2, "R/M operand with CDisp scaling of 2") \
ENUM_ENTRY(ENCODING_RM_CD4, "R/M operand with CDisp scaling of 4") \
ENUM_ENTRY(ENCODING_RM_CD8, "R/M operand with CDisp scaling of 8") \
ENUM_ENTRY(ENCODING_RM_CD16,"R/M operand with CDisp scaling of 16") \
ENUM_ENTRY(ENCODING_RM_CD32,"R/M operand with CDisp scaling of 32") \
ENUM_ENTRY(ENCODING_RM_CD64,"R/M operand with CDisp scaling of 64") \
ENUM_ENTRY(ENCODING_VSIB, "VSIB operand in ModR/M byte.") \
ENUM_ENTRY(ENCODING_VSIB_CD2, "VSIB operand with CDisp scaling of 2") \
ENUM_ENTRY(ENCODING_VSIB_CD4, "VSIB operand with CDisp scaling of 4") \
ENUM_ENTRY(ENCODING_VSIB_CD8, "VSIB operand with CDisp scaling of 8") \
ENUM_ENTRY(ENCODING_VSIB_CD16,"VSIB operand with CDisp scaling of 16") \
ENUM_ENTRY(ENCODING_VSIB_CD32,"VSIB operand with CDisp scaling of 32") \
ENUM_ENTRY(ENCODING_VSIB_CD64,"VSIB operand with CDisp scaling of 64") \
ENUM_ENTRY(ENCODING_VVVV, "Register operand in VEX.vvvv byte.") \
ENUM_ENTRY(ENCODING_WRITEMASK, "Register operand in EVEX.aaa byte.") \
ENUM_ENTRY(ENCODING_IB, "1-byte immediate") \
ENUM_ENTRY(ENCODING_IW, "2-byte") \
ENUM_ENTRY(ENCODING_ID, "4-byte") \
ENUM_ENTRY(ENCODING_IO, "8-byte") \
ENUM_ENTRY(ENCODING_RB, "(AL..DIL, R8L..R15L) Register code added to " \
"the opcode byte") \
ENUM_ENTRY(ENCODING_RW, "(AX..DI, R8W..R15W)") \
ENUM_ENTRY(ENCODING_RD, "(EAX..EDI, R8D..R15D)") \
ENUM_ENTRY(ENCODING_RO, "(RAX..RDI, R8..R15)") \
ENUM_ENTRY(ENCODING_FP, "Position on floating-point stack in ModR/M " \
"byte.") \
ENUM_ENTRY(ENCODING_Iv, "Immediate of operand size") \
ENUM_ENTRY(ENCODING_Ia, "Immediate of address size") \
ENUM_ENTRY(ENCODING_IRC, "Immediate for static rounding control") \
ENUM_ENTRY(ENCODING_Rv, "Register code of operand size added to the " \
"opcode byte") \
ENUM_ENTRY(ENCODING_DUP, "Duplicate of another operand; ID is encoded " \
"in type") \
ENUM_ENTRY(ENCODING_SI, "Source index; encoded in OpSize/Adsize prefix") \
ENUM_ENTRY(ENCODING_DI, "Destination index; encoded in prefixes")
#define ENUM_ENTRY(n, d) n,
typedef enum {
ENCODINGS
ENCODING_max
} OperandEncoding;
#undef ENUM_ENTRY
/*
* Semantic interpretations of instruction operands.
*/
#define TYPES \
ENUM_ENTRY(TYPE_NONE, "") \
ENUM_ENTRY(TYPE_REL, "immediate address") \
ENUM_ENTRY(TYPE_R8, "1-byte register operand") \
ENUM_ENTRY(TYPE_R16, "2-byte") \
ENUM_ENTRY(TYPE_R32, "4-byte") \
ENUM_ENTRY(TYPE_R64, "8-byte") \
ENUM_ENTRY(TYPE_IMM, "immediate operand") \
ENUM_ENTRY(TYPE_IMM3, "1-byte immediate operand between 0 and 7") \
ENUM_ENTRY(TYPE_IMM5, "1-byte immediate operand between 0 and 31") \
ENUM_ENTRY(TYPE_AVX512ICC, "1-byte immediate operand for AVX512 icmp") \
ENUM_ENTRY(TYPE_UIMM8, "1-byte unsigned immediate operand") \
ENUM_ENTRY(TYPE_M, "Memory operand") \
ENUM_ENTRY(TYPE_MVSIBX, "Memory operand using XMM index") \
ENUM_ENTRY(TYPE_MVSIBY, "Memory operand using YMM index") \
ENUM_ENTRY(TYPE_MVSIBZ, "Memory operand using ZMM index") \
ENUM_ENTRY(TYPE_SRCIDX, "memory at source index") \
ENUM_ENTRY(TYPE_DSTIDX, "memory at destination index") \
ENUM_ENTRY(TYPE_MOFFS, "memory offset (relative to segment base)") \
ENUM_ENTRY(TYPE_ST, "Position on the floating-point stack") \
ENUM_ENTRY(TYPE_MM64, "8-byte MMX register") \
ENUM_ENTRY(TYPE_XMM, "16-byte") \
ENUM_ENTRY(TYPE_YMM, "32-byte") \
ENUM_ENTRY(TYPE_ZMM, "64-byte") \
ENUM_ENTRY(TYPE_VK, "mask register") \
ENUM_ENTRY(TYPE_SEGMENTREG, "Segment register operand") \
ENUM_ENTRY(TYPE_DEBUGREG, "Debug register operand") \
ENUM_ENTRY(TYPE_CONTROLREG, "Control register operand") \
ENUM_ENTRY(TYPE_BNDR, "MPX bounds register") \
\
ENUM_ENTRY(TYPE_Rv, "Register operand of operand size") \
ENUM_ENTRY(TYPE_RELv, "Immediate address of operand size") \
ENUM_ENTRY(TYPE_DUP0, "Duplicate of operand 0") \
ENUM_ENTRY(TYPE_DUP1, "operand 1") \
ENUM_ENTRY(TYPE_DUP2, "operand 2") \
ENUM_ENTRY(TYPE_DUP3, "operand 3") \
ENUM_ENTRY(TYPE_DUP4, "operand 4") \
#define ENUM_ENTRY(n, d) n,
typedef enum {
TYPES
TYPE_max
} OperandType;
#undef ENUM_ENTRY
/*
* The specification for how to extract and interpret one operand.
*/
typedef struct OperandSpecifier {
uint8_t encoding;
uint8_t type;
} OperandSpecifier;
#define X86_MAX_OPERANDS 6
/*
* Decoding mode for the Intel disassembler. 16-bit, 32-bit, and 64-bit mode
* are supported, and represent real mode, IA-32e, and IA-32e in 64-bit mode,
* respectively.
*/
typedef enum {
MODE_16BIT,
MODE_32BIT,
MODE_64BIT
} DisassemblerMode;
#endif

View File

@@ -0,0 +1,886 @@
#!/usr/bin/python
# convert LLVM GenAsmWriter.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmWriter.inc> <Output-GenAsmWriter.inc> <Output-GenRegisterName.inc> <arch>" %sys.argv[0])
sys.exit(1)
arch = sys.argv[4]
f = open(sys.argv[1])
lines = f.readlines()
f.close()
f1 = open(sys.argv[2], 'w+')
f2 = open(sys.argv[3], 'w+')
f1.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f1.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f1.write("\n")
f2.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f2.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f2.write("\n")
need_endif = False
in_getRegisterName = False
in_printAliasInstr = False
fragment_no = None
skip_printing = False
skip_line = 0
skip_count = 0
def replace_getOp(line):
line2 = line
if 'MI->getOperand(0)' in line:
line2 = line.replace('MI->getOperand(0)', 'MCInst_getOperand(MI, 0)')
elif 'MI->getOperand(1)' in line:
line2 = line.replace('MI->getOperand(1)', 'MCInst_getOperand(MI, 1)')
elif 'MI->getOperand(2)' in line:
line2 = line.replace('MI->getOperand(2)', 'MCInst_getOperand(MI, 2)')
elif 'MI->getOperand(3)' in line:
line2 = line.replace('MI->getOperand(3)', 'MCInst_getOperand(MI, 3)')
elif 'MI->getOperand(4)' in line:
line2 = line.replace('MI->getOperand(4)', 'MCInst_getOperand(MI, 4)')
elif 'MI->getOperand(5)' in line:
line2 = line.replace('MI->getOperand(5)', 'MCInst_getOperand(MI, 5)')
elif 'MI->getOperand(6)' in line:
line2 = line.replace('MI->getOperand(6)', 'MCInst_getOperand(MI, 6)')
elif 'MI->getOperand(7)' in line:
line2 = line.replace('MI->getOperand(7)', 'MCInst_getOperand(MI, 7)')
elif 'MI->getOperand(8)' in line:
line2 = line.replace('MI->getOperand(8)', 'MCInst_getOperand(MI, 8)')
return line2
def replace_getReg(line):
line2 = line
if 'MI->getOperand(0).getReg()' in line:
line2 = line.replace('MI->getOperand(0).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 0))')
elif 'MI->getOperand(1).getReg()' in line:
line2 = line.replace('MI->getOperand(1).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 1))')
elif 'MI->getOperand(2).getReg()' in line:
line2 = line.replace('MI->getOperand(2).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 2))')
elif 'MI->getOperand(3).getReg()' in line:
line2 = line.replace('MI->getOperand(3).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 3))')
elif 'MI->getOperand(4).getReg()' in line:
line2 = line.replace('MI->getOperand(4).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 4))')
elif 'MI->getOperand(5).getReg()' in line:
line2 = line.replace('MI->getOperand(5).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 5))')
elif 'MI->getOperand(6).getReg()' in line:
line2 = line.replace('MI->getOperand(6).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 6))')
elif 'MI->getOperand(7).getReg()' in line:
line2 = line.replace('MI->getOperand(7).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 7))')
elif 'MI->getOperand(8).getReg()' in line:
line2 = line.replace('MI->getOperand(8).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 8))')
return line2
# extract param between text()
# MRI.getRegClass(AArch64::GPR32spRegClassID).contains(MI->getOperand(1).getReg()))
def extract_paren(line, text):
i = line.index(text)
return line[line.index('(', i)+1 : line.index(')', i)]
# extract text between <>
# printSVERegOp<'q'>
def extract_brackets(line):
if '<' in line:
return line[line.index('<')+1 : line.index('>')]
else:
return ''
# delete text between <>, including <>
# printSVERegOp<'q'>
def del_brackets(line):
if '<' in line:
return line[:line.index('<')] + line[line.index('>') + 1:]
else:
return line
def print_line(line):
line = line.replace('::', '_')
line = line.replace('nullptr', 'NULL')
if not skip_printing:
if in_getRegisterName:
f2.write(line + "\n")
else:
f1.write(line + "\n")
for line in lines:
line = line.rstrip()
#print("@", line)
# skip Alias
if arch.upper() == 'X86':
if 'PRINT_ALIAS_INSTR' in line:
# done
break
if skip_line:
skip_count += 1
if skip_count <= skip_line:
# skip this line
continue
else:
# skip enough number of lines, reset counters
skip_line = 0
skip_count = 0
if "::printInstruction" in line:
if arch.upper() in ('AARCH64', 'ARM64'):
#print_line("static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI)\n{")
print_line("static void printInstruction(MCInst *MI, SStream *O)\n{")
else:
print_line("static void printInstruction(MCInst *MI, SStream *O)\n{")
elif 'LLVM_NO_PROFILE_INSTRUMENT_FUNCTION' in line:
continue
elif 'AArch64InstPrinter::getMnemonic' in line:
print_line("static uint64_t getMnemonic(MCInst *MI, SStream *O, unsigned int opcode) {")
elif 'return {AsmStrs+(Bits' in line:
tmp = line.split(',')
prntStr = tmp[0].split('{')[1]
print_line("\tSStream_concat0(O, " + prntStr + ");")
print_line("\treturn Bits;")
elif 'MnemonicInfo = getMnemonic(' in line:
continue
elif 'O << MnemonicInfo' in line:
continue
elif 'uint64_t Bits = MnemonicInfo' in line:
print_line("\tuint64_t Bits = getMnemonic(MI, O, opcode);")
elif 'const char *AArch64InstPrinter::' in line:
continue
elif 'getRegisterName(' in line:
if 'unsigned AltIdx' in line:
print_line("static const char *getRegisterName(unsigned RegNo, unsigned AltIdx)\n{")
else:
print_line("static const char *getRegisterName(unsigned RegNo)\n{")
elif 'getRegisterName' in line:
in_getRegisterName = True
print_line(line)
elif '::printAliasInstr' in line:
if arch.upper() in ('AARCH64', 'PPC'):
print_line("static char *printAliasInstr(MCInst *MI, SStream *OS, MCRegisterInfo *MRI)\n{")
print_line(' #define GETREGCLASS_CONTAIN(_class, _reg) MCRegisterClass_contains(MCRegisterInfo_getRegClass(MRI, _class), MCOperand_getReg(MCInst_getOperand(MI, _reg)))')
else:
print_line("static bool printAliasInstr(MCInst *MI, SStream *OS)\n{")
print_line(" unsigned int I = 0, OpIdx, PrintMethodIdx;")
print_line(" char *tmpString;")
in_printAliasInstr = True
elif 'STI.getFeatureBits()[' in line:
if arch.upper() == 'ARM':
line2 = line.replace('STI.getFeatureBits()[', 'ARM_getFeatureBits(MI->csh->mode, ')
elif arch.upper() == 'AARCH64':
line2 = line.replace('STI.getFeatureBits()[', 'AArch64_getFeatureBits(')
line2 = line2.replace(']', ')')
print_line(line2)
elif 'lookupBTIByEncoding' in line:
line = line.replace('AArch64BTIHint::', '')
line = line.replace('MCOp.getImm()', 'MCOperand_getImm(MCOp)')
print_line(line)
elif 'lookupPSBByEncoding' in line:
line = line.replace('AArch64PSBHint::', '')
line = line.replace('MCOp.getImm()', 'MCOperand_getImm(MCOp)')
print_line(line)
elif ', STI, ' in line:
line2 = line.replace(', STI, ', ', ')
if 'printSVELogicalImm<' in line:
if 'int16' in line:
line2 = line2.replace('printSVELogicalImm', 'printSVELogicalImm16')
line2 = line2.replace('<int16_t>', '')
elif 'int32' in line:
line2 = line2.replace('printSVELogicalImm', 'printSVELogicalImm32')
line2 = line2.replace('<int32_t>', '')
else:
line2 = line2.replace('printSVELogicalImm', 'printSVELogicalImm64')
line2 = line2.replace('<int64_t>', '')
if 'MI->getOperand(' in line:
line2 = replace_getOp(line2)
# C++ template
if 'printPrefetchOp' in line2:
param = extract_brackets(line2)
if param == '':
param = 'false'
line2 = del_brackets(line2)
line2 = line2.replace(', O);', ', O, %s);' %param)
line2 = line2.replace(', OS);', ', OS, %s);' %param)
elif '<false>' in line2:
line2 = line2.replace('<false>', '')
line2 = line2.replace(', O);', ', O, false);')
line2 = line2.replace('STI, ', '')
elif '<true>' in line:
line2 = line2.replace('<true>', '')
line2 = line2.replace(', O);', ', O, true);')
line2 = line2.replace('STI, ', '')
elif 'printAdrLabelOperand' in line:
# C++ template
if '<0>' in line:
line2 = line2.replace('<0>', '')
line2 = line2.replace(', O);', ', O, 0);')
elif '<1>' in line:
line2 = line2.replace('<1>', '')
line2 = line2.replace(', O);', ', O, 1);')
elif '<2>' in line:
line2 = line2.replace('<2>', '')
line2 = line2.replace(', O);', ', O, 2);')
elif 'printImm8OptLsl' in line2:
param = extract_brackets(line2)
line2 = del_brackets(line2)
if '8' in param or '16' in param or '32' in param:
line2 = line2.replace('printImm8OptLsl', 'printImm8OptLsl32')
elif '64' in param:
line2 = line2.replace('printImm8OptLsl', 'printImm8OptLsl64')
elif 'printLogicalImm' in line2:
param = extract_brackets(line2)
line2 = del_brackets(line2)
if '8' in param or '16' in param or '32' in param:
line2 = line2.replace('printLogicalImm', 'printLogicalImm32')
elif '64' in param:
line2 = line2.replace('printLogicalImm', 'printLogicalImm64')
elif 'printSVERegOp' in line2 or 'printGPRSeqPairsClassOperand' in line2 or 'printTypedVectorList' in line2 or 'printPostIncOperand' in line2 or 'printImmScale' in line2 or 'printRegWithShiftExtend' in line2 or 'printUImm12Offset' in line2 or 'printExactFPImm' in line2 or 'printMemExtend' in line2 or 'printZPRasFPR' in line2 or 'printMatrixTileVector' in line2 or 'printMatrix<' in line2 or 'printSImm' in line2:
param = extract_brackets(line2)
if param == '':
param = '0'
line2 = del_brackets(line2)
line2 = line2.replace(', O);', ', O, %s);' %param)
line2 = line2.replace(', OS);', ', OS, %s);' %param)
elif 'printComplexRotationOp' in line:
# printComplexRotationOp<90, 0>(MI, 5, STI, O);
bracket_content = line2[line2.index('<') + 1 : line2.index('>')]
line2 = line2.replace('<' + bracket_content + '>', '')
line2 = line2.replace(' O);', ' O, %s);' %bracket_content)
elif 'printAlignedLabel' in line2 or 'printAdrpLabel' in line2:
line2 = line2.replace('Address, ', '')
print_line(line2)
elif "static const char AsmStrs[]" in line:
print_line("#ifndef CAPSTONE_DIET")
print_line(" static const char AsmStrs[] = {")
need_endif = True
elif "static const char AsmStrsNoRegAltName[]" in line:
print_line("#ifndef CAPSTONE_DIET")
print_line(" static const char AsmStrsNoRegAltName[] = {")
need_endif = True
elif line == ' O << "\\t";':
print_line(" unsigned int opcode = MCInst_getOpcode(MI);")
print_line(' // printf("opcode = %u\\n", opcode);');
elif 'MI->getOpcode()' in line:
if 'switch' in line:
line2 = line.replace('MI->getOpcode()', 'MCInst_getOpcode(MI)')
else:
line2 = line.replace('MI->getOpcode()', 'opcode')
print_line(line2)
elif 'O << ' in line:
if '"' in line:
line2 = line.lower()
line2 = line2.replace('o << ', 'SStream_concat0(O, ');
else:
line2 = line.replace('O << ', 'SStream_concat0(O, ');
line2 = line2.replace("'", '"')
line2 = line2.replace(';', ');')
if '" : "' in line2: # "segment : offset" in X86
line2 = line2.replace('" : "', '":"')
# ARM
print_line(line2)
if '", #0"' in line2:
print_line(' op_addImm(MI, 0);')
if '", #1"' in line2:
print_line(' op_addImm(MI, 1);')
# PowerPC
if '", 268"' in line2:
print_line(' op_addImm(MI, 268);')
elif '", 256"' in line2:
print_line(' op_addImm(MI, 256);')
elif '", 0, "' in line2 or '", 0"' in line2:
print_line(' op_addImm(MI, 0);')
elif '", -1"' in line2:
print_line(' op_addImm(MI, -1);')
if '], [' in line2 or ']!, [' in line2:
print_line(' set_mem_access(MI, false);')
print_line(' set_mem_access(MI, true);')
elif "\"[\"" in line2:
# Check for SME_Index specific string of only "["
print_line(' set_sme_index(MI, true);')
elif '[' in line2:
if not '[]' in line2:
print_line(' set_mem_access(MI, true);')
elif ']' in line2:
if not '[]' in line2:
print_line(' set_mem_access(MI, false);')
if '".f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64);')
elif '".f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32);')
elif '".f16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F16);')
elif '".s64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S64);')
elif '".s32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S32);')
elif '".s16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S16);')
elif '".s8\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S8);')
elif '".u64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U64);')
elif '".u32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U32);')
elif '".u16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U16);')
elif '".u8\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U8);')
elif '".i64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_I64);')
elif '".i32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_I32);')
elif '".i16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_I16);')
elif '".i8\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_I8);')
elif '".f16.f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F16F64);')
elif '".f64.f16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64F16);')
elif '".f16.f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F16F32);')
elif '".f32.f16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32F16);')
elif '".f64.f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64F32);')
elif '".f32.f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32F64);')
elif '".s32.f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S32F32);')
elif '".f32.s32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32S32);')
elif '".u32.f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U32F32);')
elif '".f32.u32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32U32);')
elif '".p8\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_P8);')
elif '".f64.s16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64S16);')
elif '".s16.f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S16F64);')
elif '".f32.s16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32S16);')
elif '".s16.f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S16F32);')
elif '".f64.s32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64S32);')
elif '".s32.f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_S32F64);')
elif '".f64.u16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64U16);')
elif '".u16.f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U16F64);')
elif '".f32.u16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F32U16);')
elif '".u16.f32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U16F32);')
elif '".f64.u32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F64U32);')
elif '".u32.f64\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U32F64);')
elif '".f16.u32\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F16U32);')
elif '".u32.f16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U32F16);')
elif '".f16.u16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_F16U16);')
elif '".u16.f16\\t"' in line2:
print_line(' ARM_addVectorDataType(MI, ARM_VECTORDATA_U16F16);')
elif '"\\tlr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_LR);')
elif '"\\tapsr_nzcv, fpscr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_APSR_NZCV);')
print_line(' ARM_addReg(MI, ARM_REG_FPSCR);')
elif '"\\tpc, lr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_PC);')
print_line(' ARM_addReg(MI, ARM_REG_LR);')
elif '"\\tfpscr, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPSCR);')
elif '"\\tfpexc, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPEXC);')
elif '"\\tfpinst, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPINST);')
elif '"\\tfpinst2, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPINST2);')
elif '"\\tfpsid, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPSID);')
elif '"\\tsp, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_SP);')
elif '"\\tsp!, "' in line2:
print_line(' ARM_addReg(MI, ARM_REG_SP);')
elif '", apsr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_APSR);')
elif '", spsr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_SPSR);')
elif '", fpscr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPSCR);')
elif '", fpscr"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPSCR);')
elif '", fpexc"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPEXC);')
elif '", fpinst"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPINST);')
elif '", fpinst2"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPINST2);')
elif '", fpsid"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_FPSID);')
elif '", mvfr0"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_MVFR0);')
elif '", mvfr1"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_MVFR1);')
elif '", mvfr2"' in line2:
print_line(' ARM_addReg(MI, ARM_REG_MVFR2);')
elif '.8\\t' in line2:
print_line(' ARM_addVectorDataSize(MI, 8);')
elif '.16\\t' in line2:
print_line(' ARM_addVectorDataSize(MI, 16);')
elif '.32\\t' in line2:
print_line(' ARM_addVectorDataSize(MI, 32);')
elif '.64\\t' in line2:
print_line(' ARM_addVectorDataSize(MI, 64);')
elif '" ^"' in line2:
print_line(' ARM_addUserMode(MI);')
if '.16b' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_16B);')
elif '.8b' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_8B);')
elif '.4b' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_4B);')
elif '.b' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_1B);')
elif '.8h' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_8H);')
elif '.4h' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_4H);')
elif '.2h' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_2H);')
elif '.h' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_1H);')
elif '.4s' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_4S);')
elif '.2s' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_2S);')
elif '.s' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_1S);')
elif '.2d' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_2D);')
elif '.1d' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_1D);')
elif '.1q' in line2:
print_line(' arm64_op_addVectorArrSpecifier(MI, ARM64_VAS_1Q);')
if '#0.0' in line2:
print_line(' arm64_op_addFP(MI, 0);')
elif '#0' in line2:
print_line(' arm64_op_addImm(MI, 0);')
elif '#8' in line2:
print_line(' arm64_op_addImm(MI, 8);')
elif '#16' in line2:
print_line(' arm64_op_addImm(MI, 16);')
elif '#32' in line2:
print_line(' arm64_op_addImm(MI, 32);')
# X86
if '", %rax"' in line2 or '", rax"' in line2:
print_line(' op_addReg(MI, X86_REG_RAX);')
elif '", %eax"' in line2 or '", eax"' in line2:
print_line(' op_addReg(MI, X86_REG_EAX);')
elif '", %ax"' in line2 or '", ax"' in line2:
print_line(' op_addReg(MI, X86_REG_AX);')
elif '", %al"' in line2 or '", al"' in line2:
print_line(' op_addReg(MI, X86_REG_AL);')
elif '", %dx"' in line2 or '", dx"' in line2:
print_line(' op_addReg(MI, X86_REG_DX);')
elif '", %st(0)"' in line2 or '", st(0)"' in line2:
print_line(' op_addReg(MI, X86_REG_ST0);')
elif '", 1"' in line2:
print_line(' op_addImm(MI, 1);')
elif '", cl"' in line2:
print_line(' op_addReg(MI, X86_REG_CL);')
elif '"{1to2}, "' in line2:
print_line(' op_addAvxBroadcast(MI, X86_AVX_BCAST_2);')
elif '"{1to4}, "' in line2:
print_line(' op_addAvxBroadcast(MI, X86_AVX_BCAST_4);')
elif '"{1to8}, "' in line2:
print_line(' op_addAvxBroadcast(MI, X86_AVX_BCAST_8);')
elif '"{1to16}, "' in line2:
print_line(' op_addAvxBroadcast(MI, X86_AVX_BCAST_16);')
elif '{z}{sae}' in line2:
print_line(' op_addAvxSae(MI);')
print_line(' op_addAvxZeroOpmask(MI);')
elif ('{z}' in line2):
print_line(' op_addAvxZeroOpmask(MI);')
elif '{sae}' in line2:
print_line(' op_addAvxSae(MI);')
elif 'llvm_unreachable("Invalid command number.");' in line:
line2 = line.replace('llvm_unreachable("Invalid command number.");', '// unreachable')
print_line(line2)
elif ('assert(' in line) or ('assert (' in line):
pass
elif 'Invalid alt name index' in line:
pass
elif '::' in line and 'case ' in line:
#print_line(line2)
print_line(line)
elif 'MI->getNumOperands()' in line:
line2 = line.replace('MI->getNumOperands()', 'MCInst_getNumOperands(MI)')
print_line(line2)
elif 'const MCOperand &MCOp' in line:
line2 = line.replace('const MCOperand &MCOp', 'MCOperand *MCOp')
print_line(line2)
elif 'MI->getOperand(0).isImm()' in line:
line2 = line.replace('MI->getOperand(0).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 0))')
print_line(line2)
elif 'MI->getOperand(1).isImm()' in line:
line2 = line.replace('MI->getOperand(1).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 1))')
print_line(line2)
elif 'MI->getOperand(2).isImm()' in line:
line2 = line.replace('MI->getOperand(2).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 2))')
print_line(line2)
elif 'MI->getOperand(3).isImm()' in line:
line2 = line.replace('MI->getOperand(3).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 3))')
print_line(line2)
elif 'MI->getOperand(4).isImm()' in line:
line2 = line.replace('MI->getOperand(4).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 4))')
print_line(line2)
elif 'MI->getOperand(5).isImm()' in line:
line2 = line.replace('MI->getOperand(5).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 5))')
print_line(line2)
elif 'MI->getOperand(6).isImm()' in line:
line2 = line.replace('MI->getOperand(6).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 6))')
print_line(line2)
elif 'MI->getOperand(7).isImm()' in line:
line2 = line.replace('MI->getOperand(7).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 7))')
print_line(line2)
elif 'MI->getOperand(8).isImm()' in line:
line2 = line.replace('MI->getOperand(8).isImm()', 'MCOperand_isImm(MCInst_getOperand(MI, 8))')
print_line(line2)
elif 'MI->getOperand(0).getImm()' in line:
line2 = line.replace('MI->getOperand(0).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 0))')
print_line(line2)
elif 'MI->getOperand(1).getImm()' in line:
line2 = line.replace('MI->getOperand(1).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 1))')
print_line(line2)
elif 'MI->getOperand(2).getImm()' in line:
line2 = line.replace('MI->getOperand(2).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 2))')
print_line(line2)
elif 'MI->getOperand(3).getImm()' in line:
line2 = line.replace('MI->getOperand(3).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 3))')
print_line(line2)
elif 'MI->getOperand(4).getImm()' in line:
line2 = line.replace('MI->getOperand(4).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 4))')
print_line(line2)
elif 'MI->getOperand(5).getImm()' in line:
line2 = line.replace('MI->getOperand(5).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 5))')
print_line(line2)
elif 'MI->getOperand(6).getImm()' in line:
line2 = line.replace('MI->getOperand(6).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 6))')
print_line(line2)
elif 'MI->getOperand(7).getImm()' in line:
line2 = line.replace('MI->getOperand(7).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 7))')
print_line(line2)
elif 'MI->getOperand(8).getImm()' in line:
line2 = line.replace('MI->getOperand(8).getImm()', 'MCOperand_getImm(MCInst_getOperand(MI, 8))')
print_line(line2)
elif 'MRI.getRegClass(' in line:
classid = extract_paren(line, 'getRegClass(')
operand = extract_paren(line, 'getOperand')
line2 = line.replace('MI->getNumOperands()', 'MCInst_getNumOperands(MI)')
line2 = ' GETREGCLASS_CONTAIN(%s, %s)' %(classid, operand)
if line.endswith('())) {'):
line2 += ') {'
elif line.endswith(' {'):
line2 += ' {'
elif line.endswith(' &&'):
line2 += ' &&'
print_line(line2)
elif 'MI->getOperand(' in line and 'isReg' in line:
operand = extract_paren(line, 'getOperand')
line2 = ' MCOperand_isReg(MCInst_getOperand(MI, %s))' %(operand)
# MI->getOperand(1).isReg() &&
if line.endswith(' {'):
line2 += ' {'
elif line.endswith(' &&'):
line2 += ' &&'
print_line(line2)
elif 'MI->getOperand(' in line and 'getReg' in line:
line2 = replace_getReg(line)
# one more time
line2 = replace_getReg(line2)
print_line(line2)
elif ' return false;' in line and in_printAliasInstr:
print_line(' return NULL;')
elif 'MCOp.isImm()' in line:
line2 = line.replace('MCOp.isImm()', 'MCOperand_isImm(MCOp)')
print_line(line2)
elif 'MCOp.getImm()' in line:
line2 = line.replace('MCOp.getImm()', 'MCOperand_getImm(MCOp)')
if 'int64_t Val =' in line:
line2 = line2.replace('int64_t Val =', 'Val =')
print_line(line2)
elif 'isSVEMaskOfIdenticalElements<' in line:
if 'int8' in line:
line2 = line.replace('isSVEMaskOfIdenticalElements', 'isSVEMaskOfIdenticalElements8')
line2 = line2.replace('<int8_t>', '')
elif 'int16' in line:
line2 = line.replace('isSVEMaskOfIdenticalElements', 'isSVEMaskOfIdenticalElements16')
line2 = line2.replace('<int16_t>', '')
elif 'int32' in line:
line2 = line.replace('isSVEMaskOfIdenticalElements', 'isSVEMaskOfIdenticalElements32')
line2 = line2.replace('<int32_t>', '')
else:
line2 = line.replace('isSVEMaskOfIdenticalElements', 'isSVEMaskOfIdenticalElements64')
line2 = line2.replace('<int64_t>', '')
print_line(line2)
elif 'switch (PredicateIndex) {' in line:
print_line(' int64_t Val;')
print_line(line)
elif 'uint32_t(' in line and in_printAliasInstr:
line = line.replace('uint32_t(', '')
line = line.replace(')', '')
print_line(line)
elif '#ifndef NDEBUG' in line and in_printAliasInstr:
print_line("""
char *AsmString;
const size_t OpToSize = sizeof(OpToPatterns) / sizeof(PatternsForOpcode);
const unsigned opcode = MCInst_getOpcode(MI);
// Check for alias
int OpToIndex = 0;
for(int i = 0; i < OpToSize; i++){
if(OpToPatterns[i].Opcode == opcode){
OpToIndex = i;
break;
}
}
// Check for match
if(opcode != OpToPatterns[OpToIndex].Opcode)
return NULL;
const PatternsForOpcode opToPat = OpToPatterns[OpToIndex];
// Try all patterns for this opcode
uint32_t AsmStrOffset = ~0U;
int patIdx = opToPat.PatternStart;
while(patIdx < (opToPat.PatternStart + opToPat.NumPatterns)){
// Check operand count first
if(MCInst_getNumOperands(MI) != Patterns[patIdx].NumOperands)
return NULL;
// Test all conditions for this pattern
int condIdx = Patterns[patIdx].AliasCondStart;
int opIdx = 0;
bool allPass = true;
while(condIdx < (Patterns[patIdx].AliasCondStart + Patterns[patIdx].NumConds)){
MCOperand *opnd = MCInst_getOperand(MI, opIdx);
opIdx++;
// Not concerned with any Feature related conditions as STI is disregarded
switch (Conds[condIdx].Kind)
{
case AliasPatternCond_K_Ignore :
// Operand can be anything.
break;
case AliasPatternCond_K_Reg :
// Operand must be a specific register.
allPass = allPass && (MCOperand_isReg(opnd) && MCOperand_getReg(opnd) == Conds[condIdx].Value);
break;
case AliasPatternCond_K_TiedReg :
// Operand must match the register of another operand.
allPass = allPass && (MCOperand_isReg(opnd) && MCOperand_getReg(opnd) ==
MCOperand_getReg(MCInst_getOperand(MI, Conds[condIdx].Value)));
break;
case AliasPatternCond_K_Imm :
// Operand must be a specific immediate.
allPass = allPass && (MCOperand_isImm(opnd) && MCOperand_getImm(opnd) == Conds[condIdx].Value);
break;
case AliasPatternCond_K_RegClass :
// Operand must be a register in this class. Value is a register class id.
allPass = allPass && (MCOperand_isReg(opnd) && GETREGCLASS_CONTAIN(Conds[condIdx].Value, (opIdx-1)));
break;
case AliasPatternCond_K_Custom :
// Operand must match some custom criteria.
allPass = allPass && AArch64InstPrinterValidateMCOperand(opnd, Conds[condIdx].Value);
break;
case AliasPatternCond_K_Feature :
case AliasPatternCond_K_NegFeature :
case AliasPatternCond_K_OrFeature :
case AliasPatternCond_K_OrNegFeature :
case AliasPatternCond_K_EndOrFeatures :
default :
break;
}
condIdx++;
}
if(allPass){
AsmStrOffset = Patterns[patIdx].AsmStrOffset;
break;
}
patIdx++;
}
// If no alias matched, don't print an alias.
if (AsmStrOffset == ~0U)
return NULL;
AsmString = cs_strdup(&AsmStrings[AsmStrOffset]);
tmpString = cs_strdup(AsmString);
while (AsmString[I] != ' ' && AsmString[I] != '\\t' &&
AsmString[I] != '$' && AsmString[I] != '\\0')
++I;
tmpString[I] = 0;
SStream_concat0(OS, tmpString);
if (AsmString[I] != '\\0') {
if (AsmString[I] == ' ' || AsmString[I] == '\\t') {
SStream_concat0(OS, " ");
++I;
}
bool isSME = false;
do {
if (AsmString[I] == '$') {
++I;
if (AsmString[I] == (char)0xff) {
++I;
OpIdx = AsmString[I++] - 1;
PrintMethodIdx = AsmString[I++] - 1;
printCustomAliasOperand(MI, 0, OpIdx, PrintMethodIdx, OS);
} else
printOperand(MI, (unsigned)(AsmString[I++]) - 1, OS);
} else {
if (AsmString[I] == '[') {
if (AsmString[I-1] != ' ') {
set_sme_index(MI, true);
isSME = true;
} else {
set_mem_access(MI, true);
}
} else if (AsmString[I] == ']') {
if (isSME) {
set_sme_index(MI, false);
isSME = false;
} else {
set_mem_access(MI, false);
}
}
SStream_concat1(OS, AsmString[I++]);
}
} while (AsmString[I] != '\\0');
}
cs_mem_free(AsmString);
return tmpString;
}
""")
in_printAliasInstr = False
# skip next few lines
skip_printing = True
elif '::printCustomAliasOperand' in line:
# print again
skip_printing = False
print_line('static void printCustomAliasOperand(')
elif 'const MCSubtargetInfo &STI' in line:
pass
elif 'const MCInst *MI' in line:
line2 = line.replace('const MCInst *MI', 'MCInst *MI')
print_line(line2)
elif 'llvm_unreachable("' in line:
if 'default: ' in line:
print_line(' default:')
elif 'llvm_unreachable("Unknown MCOperandPredicate kind")' in line:
print_line(' return false; // never reach')
else:
pass
elif 'raw_ostream &' in line:
line2 = line.replace('raw_ostream &', 'SStream *')
if line2.endswith(' {'):
line2 = line2.replace(' {', '\n{')
print_line(line2)
elif 'printPredicateOperand(' in line and 'STI, ' in line:
line2 = line.replace('STI, ', '')
print_line(line2)
elif '// Fragment ' in line:
# // Fragment 0 encoded into 6 bits for 51 unique commands.
tmp = line.strip().split(' ')
fragment_no = tmp[2]
print_line(line)
elif ('switch ((' in line or 'if ((' in line) and 'Bits' in line:
# switch ((Bits >> 14) & 63) {
bits = line.strip()
bits = bits.replace('switch ', '')
bits = bits.replace('if ', '')
bits = bits.replace('{', '')
bits = bits.strip()
print_line(' // printf("Fragment %s: %%"PRIu64"\\n", %s);' %(fragment_no, bits))
print_line(line)
elif not skip_printing:
print_line(line)
if line == ' };':
if need_endif and not in_getRegisterName:
# endif only for AsmStrs when we are not inside getRegisterName()
print_line("#endif")
need_endif = False
elif 'return AsmStrs+RegAsmOffset[RegNo-1];' in line:
if in_getRegisterName:
# return NULL for register name on Diet mode
print_line("#else")
print_line(" return NULL;")
print_line("#endif")
print_line("}")
need_endif = False
in_getRegisterName = False
# skip 1 line
skip_line = 1
elif line == ' }':
# ARM64
if in_getRegisterName:
# return NULL for register name on Diet mode
print_line("#else")
print_line(" return NULL;")
print_line("#endif")
print_line("}")
need_endif = False
in_getRegisterName = False
# skip 1 line
skip_line = 1
elif 'default:' in line:
# ARM64
if in_getRegisterName:
# get the size of RegAsmOffsetvreg[]
print_line(" return (const char *)(sizeof(RegAsmOffsetvreg)/sizeof(RegAsmOffsetvreg[0]));")
f1.close()
f2.close()

View File

@@ -0,0 +1,38 @@
#!/usr/bin/python
# compare instructions in 2 files of MappingInsn.inc
# find instructions in MappingInsn1, that does not exist in MappingInsn2
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <MappingInsn1.inc> <MappingInsn2.inc>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
mapping1 = f.readlines()
f.close()
f = open(sys.argv[2])
mapping2 = f.readlines()
f.close()
insn1 = []
for line in mapping1:
if 'X86_INS_' in line:
tmp = line.split(',')
insn_id = tmp[0].strip()
insn1.append(insn_id)
insn2 = []
for line in mapping2:
if 'X86_INS_' in line:
tmp = line.split(',')
insn_id = tmp[0].strip()
insn2.append(insn_id)
for insn_id in insn1:
if not insn_id in insn2:
print("instruction %s is not in list 2" %insn_id)

View File

@@ -0,0 +1,270 @@
#!/usr/bin/python
# convert LLVM GenDisassemblerTables.inc for Capstone disassembler.
# this just adds a header
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenDisassemblerTables.inc> <arch>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
print("/* Capstone Disassembly Engine, http://www.capstone-engine.org */")
print("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */")
print("/* Automatically generated file, do not edit! */\n")
print('#include "../../MCInst.h"')
print('#include "../../LEB128.h"')
print("")
print("""
// Helper function for extracting fields from encoded instructions.
//#if defined(_MSC_VER) && !defined(__clang__)
//__declspec(noinline)
//#endif
#define FieldFromInstruction(fname, InsnType) \\
static InsnType fname(InsnType insn, unsigned startBit, unsigned numBits) \\
{ \\
InsnType fieldMask; \\
if (numBits == sizeof(InsnType) * 8) \\
fieldMask = (InsnType)(-1LL); \\
else \\
fieldMask = (((InsnType)1 << numBits) - 1) << startBit; \\
return (insn & fieldMask) >> startBit; \\
}
""")
# extract text between <>
# printSVERegOp<'q'>
def extract_brackets(line):
return line[line.index('<')+1 : line.index('>')]
# delete text between <>, including <>
# printSVERegOp<'q'>
def del_brackets(line):
return line[:line.index('<')] + line[line.index('>') + 1:]
# skip printing some lines?
skip_print = True
# adding slash at the end of the line for C macro?
adding_slash = False
# skip LLVM_DEBUG
llvm_debug = False
def print_line(line):
if skip_print is True:
return
if adding_slash:
# skip blank line
if (len(line.strip()) == 0):
return
# // must be handled
if '//' in line:
line = line.replace('//', '/*')
line += ' */'
print(line + ' \\')
else:
print(line)
for line in lines:
line2 = line.rstrip()
if '#include ' in line2:
continue
# skip until the first decoder table
elif skip_print and 'static const uint8_t DecoderTable' in line2:
skip_print = False
elif 'end namespace llvm' in line2:
# done
break
elif 'llvm_unreachable' in line2:
line2 = line2.replace('llvm_unreachable', '/* llvm_unreachable')
line2 += '*/ '
if '"Invalid index!"' in line2:
pass
#line2 += '\n return true;'
elif 'Bits[' in line2:
if sys.argv[2] == 'ARM':
line2 = line2.replace('Bits[', 'ARM_getFeatureBits(MI->csh->mode, ')
line2 = line2.replace(']', ')')
elif sys.argv[2] == 'AArch64':
line2 = line2.replace('Bits[', 'AArch64_getFeatureBits(')
line2 = line2.replace(']', ')')
elif 'static bool checkDecoderPredicate(unsigned Idx, const FeatureBitset &Bits) {' in line2:
line2 = 'static bool checkDecoderPredicate(unsigned Idx, MCInst *MI)\n{'
elif 'checkDecoderPredicate(PIdx, ' in line2:
line2 = line2.replace(', Bits)', ', MI)')
elif 'template <typename InsnType>' in line2:
continue
elif 'static DecodeStatus decodeToMCInst' in line2:
line2 = '#define DecodeToMCInst(fname, fieldname, InsnType) \\\n' + \
'static DecodeStatus fname(DecodeStatus S, unsigned Idx, InsnType insn, MCInst *MI, \\\n' + \
'\t\tuint64_t Address, bool *Decoder) \\\n{'
adding_slash = True
elif 'fieldFromInstruction' in line2:
line2 = line2.replace('fieldFromInstruction', 'fieldname')
if 'InsnType FieldValue' in line2:
line2 = line2.replace('InsnType ', '')
if 'insertBits(tmp,' in line2:
line2 = line2.replace('insertBits(', '')
tmpLn = line2.split(',')
line2 = tmpLn[0] + ' |=' + tmpLn[1] + ',' + tmpLn[2] + ',' + tmpLn[3] + ' <<' + tmpLn[4] + ';'
elif 'DecodeComplete = true;' in line2:
# dead code
continue
elif 'bool &DecodeComplete) {' in line2:
continue
elif line2 == '}':
if adding_slash:
adding_slash = False
elif 'static DecodeStatus decodeInstruction' in line2:
line2 = '#define DecodeInstruction(fname, fieldname, decoder, InsnType) \\\n' + \
'static DecodeStatus fname(const uint8_t DecodeTable[], MCInst *MI, \\\n' + \
'\t\tInsnType insn, uint64_t Address) \\\n{ \\\n' + \
' unsigned Start, Len, NumToSkip, PIdx, Opc, DecodeIdx; \\\n' + \
' InsnType Val, FieldValue, PositiveMask, NegativeMask; \\\n' + \
' bool Pred, Fail, DecodeComplete = true; \\\n' + \
' uint32_t ExpectedValue;'
adding_slash = True
print_line(line2)
# skip printing few lines
skip_print = True
elif 'const MCSubtargetInfo &STI' in line2:
skip_print = False
# skip this line
continue
elif 'Bits = STI.getFeatureBits()' in line2:
# skip this line
continue
elif 'errs() << ' in line2:
continue
elif 'unsigned Start =' in line2:
line2 = line2.replace('unsigned ', '')
elif 'unsigned Len =' in line2:
line2 = line2.replace('unsigned ', '')
elif 'unsigned Len;' in line2:
continue
elif 'MCInst TmpMI;' in line2:
continue
elif 'bool Pred;' in line2:
continue
elif 'bool DecodeComplete;' in line2:
continue
elif 'unsigned NumToSkip =' in line2:
line2 = line2.replace('unsigned ', '')
elif 'unsigned PIdx =' in line2:
line2 = line2.replace('unsigned ', '')
elif 'unsigned Opc =' in line2:
line2 = line2.replace('unsigned ', '')
elif 'unsigned DecodeIdx =' in line2:
line2 = line2.replace('unsigned ', '')
elif 'InsnType Val =' in line2:
line2 = line2.replace('InsnType ', '')
elif 'bool Fail' in line2:
line2 = line2.replace('bool ', '')
elif 'InsnType PositiveMask =' in line2:
line2 = line2.replace('InsnType ', '')
elif 'InsnType NegativeMask =' in line2:
line2 = line2.replace('InsnType ', '')
elif 'InsnType ExpectedValue' in line2:
line2 = line2.replace('InsnType ', '')
elif 'ptrdiff_t Loc = ' in line2:
continue
elif 'LLVM_DEBUG(' in line2:
# just this line?
if ');' in line2:
continue
skip_print = True
llvm_debug = True
continue
elif skip_print and llvm_debug and ');' in line2:
llvm_debug = False
skip_print = False
continue
elif 'decodeToMCInst(' in line2:
line2 = line2.replace('decodeToMCInst', 'decoder')
line2 = line2.replace('DecodeComplete);', '&DecodeComplete);')
line2 = line2.replace(', DisAsm', '')
line2 = line2.replace(', TmpMI', ', MI')
elif 'TmpMI.setOpcode(Opc);' in line2:
line2 = ' MCInst_setOpcode(MI, Opc);'
elif 'MI.setOpcode(Opc);' in line2:
line2 = ' MCInst_setOpcode(MI, Opc);'
elif 'MI.clear();' in line2:
line2 = ' MCInst_clear(MI);'
elif 'assert(' in line2:
line2 = line2.replace('assert(', '/* assert(')
line2 += ' */'
elif 'Check(S, ' in line2:
line2 = line2.replace('Check(S, ', 'Check(&S, ')
if 'DecodeImm8OptLsl<' in line2:
param = extract_brackets(line2)
line2 = del_brackets(line2)
line2 = line2.replace(', Decoder)', ', Decoder, %s)' %param)
elif 'DecodeSImm<' in line2:
param = extract_brackets(line2)
line2 = del_brackets(line2)
line2 = line2.replace(', Decoder)', ', Decoder, %s)' %param)
elif 'DecodeMatrixTile<' in line2:
param = extract_brackets(line2)
line2 = del_brackets(line2)
line2 = line2.replace(', Decoder)', ', Decoder, %s)' %param)
if 'DecodeComplete = false; ' in line2:
line2 = line2.replace('DecodeComplete = false; ', '')
elif 'decodeUImmOperand<' in line2 or 'decodeSImmOperand<' in line2 :
# decodeUImmOperand<5>(MI, tmp, Address, Decoder)
param = extract_brackets(line2)
line2 = del_brackets(line2)
line2 = line2.replace(', Decoder)', ', Decoder, %s)' %param)
elif 'MI.addOperand(MCOperand::createImm(tmp));' in line2:
line2 = ' MCOperand_CreateImm0(MI, tmp);'
elif 'MI = TmpMI;' in line2:
line2 = ''
#line2 = line2.replace('TmpMI', '&TmpMI')
elif 'using TmpType = std::conditional' in line2:
continue
elif 'TmpType tmp;' in line2:
line2 = line2.replace('TmpType', 'InsnType')
line2 = line2.replace('::', '_')
print_line(line2)
if sys.argv[2] == 'ARM':
print("""
FieldFromInstruction(fieldFromInstruction_2, uint16_t)
DecodeToMCInst(decodeToMCInst_2, fieldFromInstruction_2, uint16_t)
DecodeInstruction(decodeInstruction_2, fieldFromInstruction_2, decodeToMCInst_2, uint16_t)
FieldFromInstruction(fieldFromInstruction_4, uint32_t)
DecodeToMCInst(decodeToMCInst_4, fieldFromInstruction_4, uint32_t)
DecodeInstruction(decodeInstruction_4, fieldFromInstruction_4, decodeToMCInst_4, uint32_t)
""")
if sys.argv[2] in ('AArch64', 'PPC'):
print("""
FieldFromInstruction(fieldFromInstruction_4, uint32_t)
DecodeToMCInst(decodeToMCInst_4, fieldFromInstruction_4, uint32_t)
DecodeInstruction(decodeInstruction_4, fieldFromInstruction_4, decodeToMCInst_4, uint32_t)
""")

View File

@@ -0,0 +1,45 @@
#!/usr/bin/python
# convert LLVM GenDisassemblerTables.inc for Capstone disassembler.
# for X86, this separate ContextDecision tables into another file
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenDisassemblerTables.inc> <X86GenDisassemblerTables.inc> <X86GenDisassemblerTables2.inc>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
f1 = open(sys.argv[2], 'w+')
f2 = open(sys.argv[3], 'w+')
f1.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f1.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f1.write("\n")
f2.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f2.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f2.write("\n")
# static const struct ContextDecision x86DisassemblerOneByteOpcodes = {
# static const struct ContextDecision x86DisassemblerXOP8Opcodes = {
write_to_f2 = False
for line in lines:
if 'ContextDecision x86DisassemblerOneByteOpcodes = {' in line:
# done with f1, start writing to f2
write_to_f2 = True
if write_to_f2:
f2.write(line)
else:
f1.write(line)
f1.close()
f2.close()

View File

@@ -0,0 +1,176 @@
/* Capstone Disassembly Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
// this tool is to generate arch/X86/X86GenDisassemblerTables2.inc
// NOTE: this requires updated X86GenDisassemblerTables2 & X86GenDisassemblerTables2
// generatedy by ./disassemblertables.py & disassemblertables_reduce.py
#include <stdio.h>
#include <stdint.h>
#include <string.h>
// X86DisassemblerDecoderCommon.h is copied from Capstone src
#include "../../arch/X86/X86DisassemblerDecoderCommon.h"
#define ARR_SIZE(a) (sizeof(a) / sizeof(a[0]))
/// Specifies whether a ModR/M byte is needed and (if so) which
/// instruction each possible value of the ModR/M byte corresponds to. Once
/// this information is known, we have narrowed down to a single instruction.
struct ModRMDecision {
uint8_t modrm_type;
uint16_t instructionIDs;
};
/// Specifies which set of ModR/M->instruction tables to look at
/// given a particular opcode.
struct OpcodeDecision {
struct ModRMDecision modRMDecisions[256];
};
/// Specifies which opcode->instruction tables to look at given
/// a particular context (set of attributes). Since there are many possible
/// contexts, the decoder first uses CONTEXTS_SYM to determine which context
/// applies given a specific set of attributes. Hence there are only IC_max
/// entries in this table, rather than 2^(ATTR_max).
struct ContextDecision {
struct OpcodeDecision opcodeDecisions[IC_max];
};
#ifdef CAPSTONE_X86_REDUCE
#include "X86GenDisassemblerTables_reduce2.inc"
#else
#include "X86GenDisassemblerTables2.inc"
#endif
static void index_OpcodeDecision(const struct OpcodeDecision *decisions, size_t size,
const struct OpcodeDecision *emptyDecision, unsigned int *index_table,
const char *opcodeTable,
const char *index_opcodeTable)
{
unsigned int i, count = 0;
for (i = 0; i < size; i++) {
if (memcmp((const void *)&decisions[i],
emptyDecision, sizeof(*emptyDecision)) != 0) {
// this is a non-zero entry
// index_table entry must be != 0
index_table[i] = count + 1;
count++;
} else
// empty entry
index_table[i] = 0;
}
printf("static const unsigned char %s[] = {\n", index_opcodeTable);
for (i = 0; i < size; i++) {
printf(" %u,\n", index_table[i]);
}
printf("};\n\n");
printf("static const struct OpcodeDecision %s[] = {\n", opcodeTable);
for (i = 0; i < size; i++) {
if (index_table[i]) {
unsigned int j;
const struct OpcodeDecision *decision;
// print out this non-zero entry
printf(" { {\n");
decision = &decisions[i];
for(j = 0; j < ARR_SIZE(emptyDecision->modRMDecisions); j++) {
const char *modrm;
switch(decision->modRMDecisions[j].modrm_type) {
default:
modrm = "MODRM_ONEENTRY";
break;
case 1:
modrm = "MODRM_SPLITRM";
break;
case 2:
modrm = "MODRM_SPLITMISC";
break;
case 3:
modrm = "MODRM_SPLITREG";
break;
case 4:
modrm = "MODRM_FULL";
break;
}
printf(" { %s, %u },\n",
modrm, decision->modRMDecisions[j].instructionIDs);
}
printf(" } },\n");
}
}
printf("};\n\n");
}
int main(int argc, char **argv)
{
unsigned int index_table[ARR_SIZE(x86DisassemblerOneByteOpcodes.opcodeDecisions)];
const struct OpcodeDecision emptyDecision;
memset((void *)&emptyDecision, 0, sizeof(emptyDecision));
printf("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n");
printf("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n");
printf("\n");
index_OpcodeDecision(x86DisassemblerOneByteOpcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerOneByteOpcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerOneByteOpcodes",
"index_x86DisassemblerOneByteOpcodes");
index_OpcodeDecision(x86DisassemblerTwoByteOpcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerTwoByteOpcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerTwoByteOpcodes",
"index_x86DisassemblerTwoByteOpcodes");
index_OpcodeDecision(x86DisassemblerThreeByte38Opcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerThreeByte38Opcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerThreeByte38Opcodes",
"index_x86DisassemblerThreeByte38Opcodes");
index_OpcodeDecision(x86DisassemblerThreeByte3AOpcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerThreeByte3AOpcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerThreeByte3AOpcodes",
"index_x86DisassemblerThreeByte3AOpcodes");
#ifndef CAPSTONE_X86_REDUCE
index_OpcodeDecision(x86DisassemblerXOP8Opcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerXOP8Opcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerXOP8Opcodes",
"index_x86DisassemblerXOP8Opcodes");
index_OpcodeDecision(x86DisassemblerXOP9Opcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerXOP9Opcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerXOP9Opcodes",
"index_x86DisassemblerXOP9Opcodes");
index_OpcodeDecision(x86DisassemblerXOPAOpcodes.opcodeDecisions,
ARR_SIZE(x86DisassemblerXOPAOpcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86DisassemblerXOPAOpcodes",
"index_x86DisassemblerXOPAOpcodes");
index_OpcodeDecision(x86Disassembler3DNowOpcodes.opcodeDecisions,
ARR_SIZE(x86Disassembler3DNowOpcodes.opcodeDecisions),
&emptyDecision, index_table,
"x86Disassembler3DNowOpcodes",
"index_x86Disassembler3DNowOpcodes");
#endif
return 0;
}

View File

@@ -0,0 +1,50 @@
#!/usr/bin/python
# convert LLVM GenDisassemblerTables.inc for Capstone disassembler.
# this just adds a header
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenDisassemblerTables.inc> <X86GenDisassemblerTables.inc> <X86GenDisassemblerTables2.inc>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
f1 = open(sys.argv[2], 'w+')
f2 = open(sys.argv[3], 'w+')
f1.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f1.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f1.write("\n")
f2.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f2.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f2.write("\n")
# static const struct ContextDecision x86DisassemblerOneByteOpcodes = {
# static const struct ContextDecision x86DisassemblerXOP8Opcodes = {
write_to_f2 = False
for line in lines:
# ignore all tables from XOP onwards
if 'ContextDecision x86DisassemblerXOP8Opcodes = {' in line:
# done
break
if 'ContextDecision x86DisassemblerOneByteOpcodes = {' in line:
# done with f1, start writing to f2
write_to_f2 = True
if write_to_f2:
f2.write(line)
else:
f1.write(line)
f1.close()
f2.close()

View File

@@ -0,0 +1,70 @@
#!/bin/sh
# generate all ARCH*.inc files for Capstone, by Nguyen Anh Quynh
# Syntax: genall-arch.sh <LLVM-dir-with-inc-files> <clean-old-Capstone-arch-ARCH-dir> <arch>
# ./genall-arch.sh tablegen ~/projects/capstone.git/arch/ARM ARM
# ./genall-arch.sh tablegen ~/projects/capstone.git/arch/ARM AArch64
# ./genall-arch.sh tablegen ~/projects/capstone.git/arch/ARM PowerPC
ARCH=$3
echo "Generating ${ARCH}GenAsmWriter.inc"
./asmwriter.py $1/${ARCH}GenAsmWriter.inc ${ARCH}GenAsmWriter.inc ${ARCH}GenRegisterName.inc ${ARCH}
echo "Generating ${ARCH}MappingInsnName.inc (Copy comments to include/capstone/<arch>.h for complete insn list.)"
./mapping_insn_name-arch.py $1/${ARCH}GenAsmMatcher.inc > ${ARCH}MappingInsnName.inc
#./mapping_insn_name-arch.py tablegen/ARMGenAsmMatcher.inc
echo "Generating ${ARCH}MappingInsn.inc"
./mapping_insn-arch.py $1/${ARCH}GenAsmMatcher.inc $1/${ARCH}GenInstrInfo.inc $2/${ARCH}MappingInsn.inc > ${ARCH}MappingInsn.inc
echo "Generating ${ARCH}GenInstrInfo.inc"
./instrinfo-arch.py $1/${ARCH}GenInstrInfo.inc ${ARCH} > ${ARCH}GenInstrInfo.inc
echo "Generating ${ARCH}GenDisassemblerTables.inc"
./disassemblertables-arch.py $1/${ARCH}GenDisassemblerTables.inc ${ARCH} > ${ARCH}GenDisassemblerTables.inc
echo "Generating ${ARCH}GenRegisterInfo.inc"
./registerinfo.py $1/${ARCH}GenRegisterInfo.inc ${ARCH} > ${ARCH}GenRegisterInfo.inc
echo "Generating ${ARCH}GenSubtargetInfo.inc"
./subtargetinfo.py $1/${ARCH}GenSubtargetInfo.inc ${ARCH} > ${ARCH}GenSubtargetInfo.inc
case $3 in
ARM)
# for ARM only
echo "Generating ${ARCH}GenAsmWriter-digit.inc"
./asmwriter.py $1/${ARCH}GenAsmWriter-digit.inc ${ARCH}GenAsmWriter.inc ${ARCH}GenRegisterName_digit.inc ${ARCH}
echo "Generating ${ARCH}GenSystemRegister.inc"
./systemregister.py $1/${ARCH}GenSystemRegister.inc > ${ARCH}GenSystemRegister.inc
echo "Generating instruction enum in insn_list.txt (for include/capstone/<arch>.h)"
./insn.py $1/${ARCH}GenAsmMatcher.inc $1/${ARCH}GenInstrInfo.inc $2/${ARCH}MappingInsn.inc > insn_list.txt
# then copy these instructions to include/capstone/<arch>.h
echo "Generating ${ARCH}MappingInsnOp.inc"
./mapping_insn_op-arch.py $1/${ARCH}GenAsmMatcher.inc $1/${ARCH}GenInstrInfo.inc $2/${ARCH}MappingInsnOp.inc > ${ARCH}MappingInsnOp.inc
echo "Generating ${ARCH}GenSystemRegister.inc"
./systemregister.py $1/${ARCH}GenSystemRegister.inc > ${ARCH}GenSystemRegister.inc
;;
AArch64)
make arm64
echo "Generating ${ARCH}GenSystemOperands.inc"
./systemoperand.py tablegen/AArch64GenSystemOperands.inc AArch64GenSystemOperands.inc AArch64GenSystemOperands_enum.inc
echo "Generating instruction enum in insn_list.txt (for include/capstone/<arch>.h)"
./insn.py $1/${ARCH}GenAsmMatcher.inc $1/${ARCH}GenInstrInfo.inc $2/${ARCH}MappingInsn.inc > insn_list.txt
# then copy these instructions to include/capstone/<arch>.h
./arm64_gen_vreg > AArch64GenRegisterV.inc
echo "Generating ${ARCH}MappingInsnOp.inc"
./mapping_insn_op-arch.py $1/${ARCH}GenAsmMatcher.inc $1/${ARCH}GenInstrInfo.inc $2/${ARCH}MappingInsnOp.inc > ${ARCH}MappingInsnOp.inc
;;
PowerPC)
# PowerPC
./insn3.py $1/${ARCH}GenAsmMatcher.inc > insn_list.txt
# then copy these instructions to include/capstone/arch.h
;;
*)
echo "Generating instruction enum in insn_list.txt (for include/capstone/<arch>.h)"
./insn.py $1/${ARCH}GenAsmMatcher.inc $1/${ARCH}GenInstrInfo.inc $2/${ARCH}MappingInsn.inc > insn_list.txt
;;
esac

View File

@@ -0,0 +1,33 @@
#!/bin/sh
# generate all X86*.inc files for Capstone, by Nguyen Anh Quynh
# Syntax: genall.sh <LLVM-build-lib-Target-ARCH> <clean-old-Capstone-arch-ARCH-dir>
# ./genall-full.sh tablegen ~/projects/capstone.git/arch/X86
echo "Generating GenAsmWriter.inc"
./asmwriter.py $1/X86GenAsmWriter.inc X86GenAsmWriter.inc X86GenRegisterName.inc X86
echo "Generating GenAsmWriter1.inc"
./asmwriter.py $1/X86GenAsmWriter1.inc X86GenAsmWriter1.inc X86GenRegisterName1.inc X86
echo "Generating instruction enum in insn_list.txt (for include/capstone/<arch>.h)"
./insn.py $1/X86GenAsmMatcher.inc $1/X86GenInstrInfo.inc $2/X86MappingInsn.inc > insn_list.txt
# then copy these instructions to include/capstone/x86.h
echo "Generating MappingInsnName.inc"
./mapping_insn_name.py $1/X86GenAsmMatcher.inc $1/X86GenInstrInfo.inc $2/X86MappingInsn.inc > X86MappingInsnName.inc
echo "Generating MappingInsn.inc"
./mapping_insn.py $1/X86GenAsmMatcher.inc $1/X86GenInstrInfo.inc $2/X86MappingInsn.inc > X86MappingInsn.inc
echo "Generating MappingInsnOp.inc"
./mapping_insn_op.py $1/X86GenAsmMatcher.inc $1/X86GenInstrInfo.inc $2/X86MappingInsnOp.inc > X86MappingInsnOp.inc
echo "Generating GenInstrInfo.inc"
./instrinfo.py $1/X86GenInstrInfo.inc $1/X86GenAsmMatcher.inc > X86GenInstrInfo.inc
echo "Generating GenDisassemblerTables.inc & X86GenDisassemblerTables2.inc"
./disassemblertables.py $1/X86GenDisassemblerTables.inc X86GenDisassemblerTables.inc X86GenDisassemblerTables2.inc
make x86

View File

@@ -0,0 +1,28 @@
#!/bin/sh
# generate all X86*reduce.inc files for Capstone, by Nguyen Anh Quynh
# Syntax: genall.sh <LLVM-build-lib-Target-ARCH> <clean-old-Capstone-arch-ARCH-dir>
# ./genall-reduce.sh tablegen ~/projects/capstone.git/arch/X86
echo "Generating GenAsmWriter_reduce.inc"
./asmwriter.py $1/X86GenAsmWriter_reduce.inc X86GenAsmWriter_reduce.inc X86GenRegisterName.inc X86
echo "Generating GenAsmWriter1_reduce.inc"
./asmwriter.py $1/X86GenAsmWriter1_reduce.inc X86GenAsmWriter1_reduce.inc X86GenRegisterName1.inc X86
echo "Generating MappingInsnName_reduce.inc"
./mapping_insn_name.py $1/X86GenAsmMatcher_reduce.inc $1/X86GenInstrInfo_reduce.inc $2/X86MappingInsn_reduce.inc > X86MappingInsnName_reduce.inc
echo "Generating MappingInsn_reduce.inc"
./mapping_insn.py $1/X86GenAsmMatcher_reduce.inc $1/X86GenInstrInfo_reduce.inc $2/X86MappingInsn_reduce.inc > X86MappingInsn_reduce.inc
echo "Generating MappingInsnOp_reduce.inc"
./mapping_insn_op.py $1/X86GenAsmMatcher.inc $1/X86GenInstrInfo_reduce.inc $2/X86MappingInsnOp_reduce.inc > X86MappingInsnOp_reduce.inc
echo "Generating GenInstrInfo_reduce.inc"
./instrinfo.py $1/X86GenInstrInfo_reduce.inc $1/X86GenAsmMatcher_reduce.inc > X86GenInstrInfo_reduce.inc
echo "Generating GenDisassemblerTables_reduce.inc & GenDisassemblerTables_reduce2.inc"
./disassemblertables_reduce.py $1/X86GenDisassemblerTables_reduce.inc X86GenDisassemblerTables_reduce.inc X86GenDisassemblerTables_reduce2.inc

View File

@@ -0,0 +1,324 @@
#!/usr/bin/python
# print list of instructions LLVM inc files, for Capstone disassembler.
# this will be put into capstone/<arch>.h
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc> <GenInstrInfo.inc> MappingInsn.inc" %sys.argv[0])
sys.exit(1)
# MappingInsn.inc
f = open(sys.argv[3])
mapping = f.readlines()
f.close()
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AArch64':
arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, insn_id_list, insn_lines
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
#insn_lines = []
insn_id_list = {}
arch = None
first_insn = None
pattern = None
# first we try to find Table1, or Table0
for line in lines:
if 'MatchEntry MatchTable0[] = {' in line.strip():
pattern = 'MatchEntry MatchTable0[] = {'
elif 'MatchEntry MatchTable1[] = {' in line.strip():
pattern = 'MatchEntry MatchTable1[] = {'
# last pattern, done
break
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if pattern in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
if not mnem.startswith('__'):
if not first_insn:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# print("***", arch, mnem, insn_id)
insn_id_list[insn_id] = mnem
#insn_lines.append(line)
#return arch, first_insn, insn_id_list, insn_lines
return arch, first_insn, insn_id_list
# GenAsmMatcher.inc
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])
arch = arch.upper()
#for line in insn_id_list:
# print(line)
insn_list = []
#{
# X86_AAA, X86_INS_AAA,
##ifndef CAPSTONE_DIET
# { 0 }, { 0 }, { X86_GRP_NOT64BITMODE, 0 }, 0, 0
##endif
#},
def print_entry(arch, insn_id, mnem, mapping, mnem_can_be_wrong):
print(arch, insn_id, mnem, mnem_can_be_wrong)
if not mnem_can_be_wrong:
insn = "%s_INS_%s" %(arch.upper(), mnem.upper())
if insn in insn_list:
return
print("%s," %insn)
insn_list.append(insn)
return
insn = "%s_%s" %(arch.upper(), insn_id)
# so mnem can be wrong, we need to verify with MappingInsn.inc
# first, try to find this entry in old MappingInsn.inc file
for i in range(len(mapping)):
tmp = mapping[i].split(',')
if tmp[0].strip() == insn:
insn = tmp[1].strip()
if insn in insn_list:
return
#print("==== get below from MappingInsn.inc file: %s" %insn)
print("%s," %insn)
insn_list.append(insn)
return
# extract from GenInstrInfo.inc, because the insn id is in order
enum_count = 0
meet_insn = False
# GenInstrInfo.inc
f = open(sys.argv[2])
lines = f.readlines()
f.close()
count = 0
last_mnem = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
#print(line.strip())
continue
line = line.strip()
if enum_count == 1:
if 'INSTRUCTION_LIST_END' in line:
break
else:
insn = None
if meet_insn:
# enum items
insn = line.split('=')[0].strip()
if 'CALLSTACK' in insn or 'TAILJUMP' in insn:
# pseudo instruction
insn = None
elif line.startswith(first_insn):
insn = line.split('=')[0].strip()
meet_insn = True
if insn:
count += 1
if insn == 'BSWAP16r_BAD':
last_mnem = 'BSWAP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp32':
last_mnem = 'FCMOVNP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVP_Fp3':
last_mnem = 'FCMOVP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrm_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVSX16rm16':
last_mnem = 'MOVSX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVZX16rm16':
last_mnem = 'MOVZX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'ST_Fp32m':
last_mnem = 'FST'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp64':
last_mnem = 'FCMOVNU'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrr_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSSrm_Int':
last_mnem = 'CMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSDrm_Int':
last_mnem = 'VCMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSSrm_Int':
last_mnem = 'VCMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPCMOVYrrr_REV':
last_mnem = 'VPCMOV'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESDZm':
last_mnem = 'VRNDSCALESD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESSZm':
last_mnem = 'VRNDSCALESS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPDZ128rm':
last_mnem = 'VMAXPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPSZ128rm':
last_mnem = 'VMAXPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSDZrm':
last_mnem = 'VMAXSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSSZrm':
last_mnem = 'VMAXSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPDZ128rm':
last_mnem = 'VMINPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPSZ128rm':
last_mnem = 'VMINPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSDZrm':
last_mnem = 'VMINSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSSZrm':
last_mnem = 'VMINSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMOV64toPQIZrm':
last_mnem = 'VMOVQ'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PDYrr_REV':
last_mnem = 'VPERMILPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PSYrr_REV':
last_mnem = 'VPERMILPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SI64Zrm_Int':
last_mnem = 'VCVTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SSrm_Int':
last_mnem = 'VCVTSD2SS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSS2SI64Zrm_Int':
last_mnem = 'VCVTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSD2SI64Zrm_Int':
last_mnem = 'VCVTTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSS2SI64Zrm_Int':
last_mnem = 'VCVTTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUBADD'):
if insn[len('VFMSUBADD')].isdigit():
last_mnem = insn[:len('VFMSUBADD123xy')]
else:
last_mnem = insn[:len('VFMSUBADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADDSUB'):
if insn[len('VFMADDSUB')].isdigit():
last_mnem = insn[:len('VFMADDSUB123xy')]
else:
last_mnem = insn[:len('VFMADDSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADD'):
if insn[len('VFMADD')].isdigit():
last_mnem = insn[:len('VFMADD123PD')]
else:
last_mnem = insn[:len('VFMADDPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUB'):
if insn[len('VFMSUB')].isdigit():
last_mnem = insn[:len('VFMSUB123PD')]
else:
last_mnem = insn[:len('VFMSUBPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMADD'):
if insn[len('VFNMADD')].isdigit():
last_mnem = insn[:len('VFNMADD123xy')]
else:
last_mnem = insn[:len('VFNMADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMSUB'):
if insn[len('VFNMSUB')].isdigit():
last_mnem = insn[:len('VFNMSUB123xy')]
else:
last_mnem = insn[:len('VFNMSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn in insn_id_list:
# trust old mapping table
last_mnem = insn_id_list[insn].upper()
print_entry(arch.upper(), insn, last_mnem, mapping, False)
else:
# the last option when we cannot find mnem: use the last good mnem
print_entry(arch.upper(), insn, last_mnem, mapping, True)

View File

@@ -0,0 +1,104 @@
#!/usr/bin/python
# print list of instructions LLVM inc files, for Capstone disassembler.
# this will be put into capstone/<arch>.h
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc>" %sys.argv[0])
sys.exit(1)
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AARCH64':
arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, first_insn, insn_id_list
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
mnem_list = []
insn_id_list = {}
arch = None
first_insn = None
pattern = None
# first we try to find Table1, or Table0
for line in lines:
if 'MatchEntry MatchTable0[] = {' in line.strip():
pattern = 'MatchEntry MatchTable0[] = {'
elif 'MatchEntry MatchTable1[] = {' in line.strip():
pattern = 'MatchEntry MatchTable1[] = {'
# last pattern, done
break
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if pattern in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
# skip pseudo instructions
if not mnem.startswith('__'):
# PPC
if mnem.endswith('-') or mnem.endswith('+'):
mnem = mnem[:-1]
if not first_insn:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# save this
insn_id_list[insn_id] = mnem
if not mnem in mnem_list:
print("%s_INS_%s," %(arch, mnem.upper()))
mnem_list.append(mnem)
#return arch, first_insn, insn_id_list
return arch, first_insn, insn_id_list
# GenAsmMatcher.inc
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])

View File

@@ -0,0 +1,25 @@
#!/usr/bin/python
# check MappingInsn.inc to find potential incorrect mapping - for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <MappingInsn.inc>" %sys.argv[0])
sys.exit(1)
# ARM_CMPri, ARM_INS_CMN,
f = open(sys.argv[1])
lines = f.readlines()
f.close()
for line in lines:
if '_INS_' in line:
tmp = line.strip().split(',')
if len(tmp) == 3 and tmp[2] == '':
id_private = tmp[0].strip()
id_public = tmp[1].strip()
pos = id_public.find('_INS_')
mnem = id_public[pos + len('_INS_'):]
if not mnem in id_private:
print("%s -> %s" %(id_private, id_public))

View File

@@ -0,0 +1,161 @@
#!/usr/bin/python
# convert LLVM GenInstrInfo.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenInstrInfo.inc> <arch>" %sys.argv[0])
sys.exit(1)
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# return (arch, mnem)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AArch64':
arch = 'ARM64'
return (arch, insn_mnem)
# get (arch, first insn) from MatchTable
def get_first_insn(filename):
f = open(filename)
lines = f.readlines()
f.close()
count = 0
for line in lines:
line = line.strip()
if len(line) == 0:
continue
# Intel syntax in Table1
if 'MatchEntry MatchTable1[] = {' in line:
count += 1
#print(line.strip())
continue
if count == 1:
arch, mnem = extract_insn(line)
return (arch, mnem)
return (None, None)
#arch, first_insn = get_first_insn(sys.argv[2])
#first_insn = first_insn.upper()
#print(arch, first_insn)
arch = sys.argv[2].upper()
if arch.upper() == 'AARCH64':
arch = 'AArch64'
elif arch.upper() == 'ARM64':
arch = 'AArch64'
print("""
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|* *|
|* Target Instruction Enum Values and Descriptors *|
|* *|
|* Automatically generated file, do not edit! *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifdef GET_INSTRINFO_ENUM
#undef GET_INSTRINFO_ENUM
""")
enum_count = 0
f = open(sys.argv[1])
lines = f.readlines()
f.close()
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
print(line.strip())
continue
line = line.strip()
if enum_count == 1:
if line == '};':
# done with first enum
break
else:
# skip pseudo instructions
if '__' in line or 'setjmp' in line or 'longjmp' in line or 'Pseudo' in line:
pass
else:
print("\t%s_%s" %(arch, line))
print('};\n')
print("#endif // GET_INSTRINFO_ENUM")
if arch == 'ARM64':
sys.exit(0)
print("")
print("#ifdef GET_INSTRINFO_MC_DESC")
print("#undef GET_INSTRINFO_MC_DESC")
print("")
print("#define nullptr 0")
print("")
in_insts = False
for line in lines:
if line.strip() == '':
continue
line = line.rstrip()
if 'static const MCOperandInfo ' in line:
line2 = line.replace('::', '_')
print(line2)
elif 'Insts[] = {' in line:
# extern const MCInstrDesc ARMInsts[] = {
line2 = line.replace('extern const ', 'static const ')
print("")
print(line2)
in_insts = True
elif in_insts:
if line == '};':
print(line)
break
# { 0, 1, 1, 0, 0, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::Variadic), 0x0ULL, nullptr, nullptr, OperandInfo2, -1 ,nullptr }, // Inst #0 = PHI
# take 2nd & 10th entries
tmp = line.split(',')
print(" { %s, %s }," %(tmp[1].strip(), tmp[9].strip()))
print("")
print("#endif // GET_INSTRINFO_MC_DESC")
#static const MCInstrDesc ARMInsts[] = {
#static MCOperandInfo OperandInfo2[] = { { -1, 0, MCOI_OPERAND_IMMEDIATE, 0 }, };

View File

@@ -0,0 +1,115 @@
#!/usr/bin/python
# convert LLVM GenInstrInfo.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenInstrInfo.inc> <AsmMatcher.info>" %sys.argv[0])
sys.exit(1)
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# return (arch, mnem)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AArch64':
arch = 'ARM64'
return (arch, insn_mnem)
# get (arch, first insn) from MatchTable
def get_first_insn(filename):
f = open(filename)
lines = f.readlines()
f.close()
count = 0
for line in lines:
line = line.strip()
if len(line) == 0:
continue
# Intel syntax in Table1
if 'MatchEntry MatchTable1[] = {' in line:
count += 1
#print(line.strip())
continue
if count == 1:
arch, mnem = extract_insn(line)
return (arch, mnem)
return (None, None)
arch, first_insn = get_first_insn(sys.argv[2])
first_insn = first_insn.upper()
arch = arch.upper()
#print(arch, first_insn)
print("""
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|* *|
|* Target Instruction Enum Values and Descriptors *|
|* *|
|* Automatically generated file, do not edit! *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifdef GET_INSTRINFO_ENUM
#undef GET_INSTRINFO_ENUM
""")
enum_count = 0
meet_insn = False
f = open(sys.argv[1])
lines = f.readlines()
f.close()
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
print(line.strip())
continue
line = line.strip()
if enum_count == 1:
if line == '};':
# done with first enum
break
else:
insn = None
if meet_insn:
# enum items
insn = line
elif line.startswith(first_insn):
insn = line
meet_insn = True
if insn:
print("\t%s_%s" %(arch, line))
print('};\n')
print("#endif // GET_INSTRINFO_ENUM")

View File

@@ -0,0 +1,362 @@
#!/usr/bin/python
# print MappingInsn.inc file from LLVM GenAsmMatcher.inc, for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc> <GenInstrInfo.inc> MappingInsn.inc" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[3])
mapping = f.readlines()
f.close()
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
#if arch.upper() == 'AARCH64':
# arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, first_insn, insn_id_list
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
insn_id_list = {}
arch = None
first_insn = None
pattern = None
# first we try to find Table1, or Table0
for line in lines:
if 'MatchEntry MatchTable0[] = {' in line.strip():
pattern = 'MatchEntry MatchTable0[] = {'
elif 'AArch64::' in line and pattern:
# We do not care about Apple Assembly
break
elif 'MatchEntry MatchTable1[] = {' in line.strip():
pattern = 'MatchEntry MatchTable1[] = {'
# last pattern, done
break
for line in lines:
line = line.rstrip()
# skip empty line
if len(line.strip()) == 0:
continue
if pattern in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
# skip pseudo instructions
if not mnem.startswith('__'):
# PPC
if mnem.endswith('-') or mnem.endswith('+'):
mnem = mnem[:-1]
if not first_insn:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list and mnem.upper() in insn_id:
# save this
insn_id_list[insn_id] = mnem
#return arch, first_insn, insn_id_list
return arch, first_insn, insn_id_list
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])
#arch = arch.upper()
#print("first insn = %s" %first_insn)
#for line in insn_id_list:
# print(line)
#{
# X86_AAA, X86_INS_AAA,
##ifndef CAPSTONE_DIET
# { 0 }, { 0 }, { X86_GRP_NOT64BITMODE, 0 }, 0, 0
##endif
#},
def print_entry(arch, insn_id, mnem, mapping, mnem_can_be_wrong):
#insn = "%s_%s" %(arch.upper(), insn_id)
insn = "%s_%s" %(arch, insn_id)
arch1 = arch
if arch.upper() == 'AARCH64':
arch1 = 'ARM64'
#if '64' in insn_id:
# is64bit = '1'
#else:
# is64bit = '0'
# first, try to find this entry in old MappingInsn.inc file
for i in range(len(mapping)):
tmp = mapping[i].split(',')
if tmp[0].strip() == insn:
if not mnem_can_be_wrong:
print('''
{
\t%s, %s_INS_%s,
#ifndef CAPSTONE_DIET
\t%s
#endif
},'''% (insn, arch1, mnem, mapping[i + 2].strip()))
else: # ATTENTION: mnem can be wrong
if not tmp[1].endswith(mnem):
#print("======== cannot find %s, mapping to %s (instead of %s)" %(insn, tmp[1].strip(), mnem))
pass
print('''
{
\t%s, %s,
#ifndef CAPSTONE_DIET
\t%s
#endif
},'''% (insn, tmp[1].strip(), mapping[i + 2].strip()))
return
if mnem_can_be_wrong:
#print("======== CANNOT FIND %s, mapping to %s" %(insn, mnem))
return
pass
# this insn does not exist in mapping table
print('''
{
\t%s, %s_INS_%s,
#ifndef CAPSTONE_DIET
\t{ 0 }, { 0 }, { 0 }, 0, 0
#endif
},'''% (insn, arch1, mnem))
# extract from GenInstrInfo.inc, because the insn id is in order
enum_count = 0
meet_insn = False
f = open(sys.argv[2])
lines = f.readlines()
f.close()
count = 0
last_mnem = ''
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
# skip pseudo instructions
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
#print(line.strip())
continue
line = line.strip()
if enum_count == 1:
# skip pseudo instructions
if '__' in line or 'setjmp' in line or 'longjmp' in line or 'Pseudo' in line:
continue
elif 'INSTRUCTION_LIST_END' in line:
break
else:
insn = line.split('=')[0].strip()
'''
insn = None
if meet_insn:
# enum items
insn = line.split('=')[0].strip()
if 'CALLSTACK' in insn or 'TAILJUMP' in insn:
# pseudo instruction
insn = None
elif line.startswith(first_insn):
insn = line.split('=')[0].strip()
meet_insn = True
if insn:
count += 1
if insn == 'BSWAP16r_BAD':
last_mnem = 'BSWAP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp32':
last_mnem = 'FCMOVNP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVP_Fp3':
last_mnem = 'FCMOVP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrm_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVSX16rm16':
last_mnem = 'MOVSX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVZX16rm16':
last_mnem = 'MOVZX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'ST_Fp32m':
last_mnem = 'FST'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp64':
last_mnem = 'FCMOVNU'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrr_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSSrm_Int':
last_mnem = 'CMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSDrm_Int':
last_mnem = 'VCMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSSrm_Int':
last_mnem = 'VCMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPCMOVYrrr_REV':
last_mnem = 'VPCMOV'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESDZm':
last_mnem = 'VRNDSCALESD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESSZm':
last_mnem = 'VRNDSCALESS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPDZ128rm':
last_mnem = 'VMAXPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPSZ128rm':
last_mnem = 'VMAXPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSDZrm':
last_mnem = 'VMAXSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSSZrm':
last_mnem = 'VMAXSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPDZ128rm':
last_mnem = 'VMINPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPSZ128rm':
last_mnem = 'VMINPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSDZrm':
last_mnem = 'VMINSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSSZrm':
last_mnem = 'VMINSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMOV64toPQIZrm':
last_mnem = 'VMOVQ'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PDYrr_REV':
last_mnem = 'VPERMILPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PSYrr_REV':
last_mnem = 'VPERMILPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SI64Zrm_Int':
last_mnem = 'VCVTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SSrm_Int':
last_mnem = 'VCVTSD2SS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSS2SI64Zrm_Int':
last_mnem = 'VCVTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSD2SI64Zrm_Int':
last_mnem = 'VCVTTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSS2SI64Zrm_Int':
last_mnem = 'VCVTTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUBADD'):
if insn[len('VFMSUBADD')].isdigit():
last_mnem = insn[:len('VFMSUBADD123xy')]
else:
last_mnem = insn[:len('VFMSUBADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADDSUB'):
if insn[len('VFMADDSUB')].isdigit():
last_mnem = insn[:len('VFMADDSUB123xy')]
else:
last_mnem = insn[:len('VFMADDSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADD'):
if insn[len('VFMADD')].isdigit():
last_mnem = insn[:len('VFMADD123PD')]
else:
last_mnem = insn[:len('VFMADDPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUB'):
if insn[len('VFMSUB')].isdigit():
last_mnem = insn[:len('VFMSUB123PD')]
else:
last_mnem = insn[:len('VFMSUBPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMADD'):
if insn[len('VFNMADD')].isdigit():
last_mnem = insn[:len('VFNMADD123xy')]
else:
last_mnem = insn[:len('VFNMADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMSUB'):
if insn[len('VFNMSUB')].isdigit():
last_mnem = insn[:len('VFNMSUB123xy')]
else:
last_mnem = insn[:len('VFNMSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
'''
if insn in insn_id_list:
# trust old mapping table
last_mnem = insn_id_list[insn].upper()
print_entry(arch, insn, insn_id_list[insn].upper(), mapping, False)
else:
# the last option when we cannot find mnem: use the last good mnem
print_entry(arch, insn, last_mnem, mapping, True)

View File

@@ -0,0 +1,332 @@
#!/usr/bin/python
# print MappingInsn.inc file from LLVM GenAsmMatcher.inc, for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc> <GenInstrInfo.inc> MappingInsn.inc" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[3])
mapping = f.readlines()
f.close()
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AARCH64':
arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, insn_id_list, insn_lines
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
count = 0
#insn_lines = []
insn_id_list = {}
arch = None
first_insn = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'MatchEntry MatchTable1[] = {' in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
count += 1
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
if count == 1:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# print("***", arch, mnem, insn_id)
insn_id_list[insn_id] = mnem
#insn_lines.append(line)
#return arch, first_insn, insn_id_list, insn_lines
return arch, first_insn, insn_id_list
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])
arch = arch.upper()
#for line in insn_id_list:
# print(line)
#{
# X86_AAA, X86_INS_AAA,
##ifndef CAPSTONE_DIET
# { 0 }, { 0 }, { X86_GRP_NOT64BITMODE, 0 }, 0, 0
##endif
#},
def print_entry(arch, insn_id, mnem, mapping, mnem_can_be_wrong):
insn = "%s_%s" %(arch.upper(), insn_id)
if '64' in insn_id:
is64bit = '1'
else:
is64bit = '0'
# first, try to find this entry in old MappingInsn.inc file
for i in range(len(mapping)):
tmp = mapping[i].split(',')
if tmp[0].strip() == insn:
if not mnem_can_be_wrong:
print('''
{
\t%s_%s, %s_INS_%s, %s,
#ifndef CAPSTONE_DIET
\t%s
#endif
},'''% (arch, insn_id, arch, mnem, is64bit, mapping[i + 2].strip()))
else:
if not tmp[1].endswith(mnem):
#print("======== cannot find %s, mapping to %s (instead of %s)" %(insn, tmp[1].strip(), mnem))
pass
print('''
{
\t%s_%s, %s, %s,
#ifndef CAPSTONE_DIET
\t%s
#endif
},'''% (arch, insn_id, tmp[1].strip(), is64bit, mapping[i + 2].strip()))
return
if mnem_can_be_wrong:
#print("======== CANNOT FIND %s, mapping to %s" %(insn, mnem))
pass
print('''
{
\t%s_%s, %s_INS_%s, %s,
#ifndef CAPSTONE_DIET
\t{ 0 }, { 0 }, { 0 }, 0, 0
#endif
},'''% (arch, insn_id, arch, mnem, is64bit))
# extract from GenInstrInfo.inc, because the insn id is in order
enum_count = 0
meet_insn = False
f = open(sys.argv[2])
lines = f.readlines()
f.close()
count = 0
last_mnem = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
#print(line.strip())
continue
line = line.strip()
if enum_count == 1:
if 'INSTRUCTION_LIST_END' in line:
break
else:
insn = None
if meet_insn:
# enum items
insn = line.split('=')[0].strip()
if 'CALLSTACK' in insn or 'TAILJUMP' in insn:
# pseudo instruction
insn = None
elif line.startswith(first_insn):
insn = line.split('=')[0].strip()
meet_insn = True
if insn:
count += 1
if insn == 'BSWAP16r_BAD':
last_mnem = 'BSWAP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp32':
last_mnem = 'FCMOVNP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVP_Fp3':
last_mnem = 'FCMOVP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrm_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVSX16rm16':
last_mnem = 'MOVSX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVZX16rm16':
last_mnem = 'MOVZX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'ST_Fp32m':
last_mnem = 'FST'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp64':
last_mnem = 'FCMOVNU'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrr_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSSrm_Int':
last_mnem = 'CMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSDrm_Int':
last_mnem = 'VCMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSSrm_Int':
last_mnem = 'VCMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPCMOVYrrr_REV':
last_mnem = 'VPCMOV'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESDZm':
last_mnem = 'VRNDSCALESD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESSZm':
last_mnem = 'VRNDSCALESS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPDZ128rm':
last_mnem = 'VMAXPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPSZ128rm':
last_mnem = 'VMAXPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSDZrm':
last_mnem = 'VMAXSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSSZrm':
last_mnem = 'VMAXSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPDZ128rm':
last_mnem = 'VMINPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPSZ128rm':
last_mnem = 'VMINPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSDZrm':
last_mnem = 'VMINSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSSZrm':
last_mnem = 'VMINSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMOV64toPQIZrm':
last_mnem = 'VMOVQ'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PDYrr_REV':
last_mnem = 'VPERMILPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PSYrr_REV':
last_mnem = 'VPERMILPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SI64Zrm_Int':
last_mnem = 'VCVTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SSrm_Int':
last_mnem = 'VCVTSD2SS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSS2SI64Zrm_Int':
last_mnem = 'VCVTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSD2SI64Zrm_Int':
last_mnem = 'VCVTTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSS2SI64Zrm_Int':
last_mnem = 'VCVTTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUBADD'):
if insn[len('VFMSUBADD')].isdigit():
last_mnem = insn[:len('VFMSUBADD123xy')]
else:
last_mnem = insn[:len('VFMSUBADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADDSUB'):
if insn[len('VFMADDSUB')].isdigit():
last_mnem = insn[:len('VFMADDSUB123xy')]
else:
last_mnem = insn[:len('VFMADDSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADD'):
if insn[len('VFMADD')].isdigit():
last_mnem = insn[:len('VFMADD123PD')]
else:
last_mnem = insn[:len('VFMADDPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUB'):
if insn[len('VFMSUB')].isdigit():
last_mnem = insn[:len('VFMSUB123PD')]
else:
last_mnem = insn[:len('VFMSUBPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMADD'):
if insn[len('VFNMADD')].isdigit():
last_mnem = insn[:len('VFNMADD123xy')]
else:
last_mnem = insn[:len('VFNMADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMSUB'):
if insn[len('VFNMSUB')].isdigit():
last_mnem = insn[:len('VFNMSUB123xy')]
else:
last_mnem = insn[:len('VFNMSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn in insn_id_list:
# trust old mapping table
last_mnem = insn_id_list[insn].upper()
print_entry(arch.upper(), insn, insn_id_list[insn].upper(), mapping, False)
else:
# the last option when we cannot find mnem: use the last good mnem
print_entry(arch.upper(), insn, last_mnem, mapping, True)

View File

@@ -0,0 +1,104 @@
#!/usr/bin/python
# print list of instructions LLVM inc files, for Capstone disassembler.
# this will be put into capstone/<arch>.h
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc>" %sys.argv[0])
sys.exit(1)
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AARCH64':
arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, first_insn, insn_id_list
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
mnem_list = []
insn_id_list = {}
arch = None
first_insn = None
pattern = None
# first we try to find Table1, or Table0
for line in lines:
if 'MatchEntry MatchTable0[] = {' in line.strip():
pattern = 'MatchEntry MatchTable0[] = {'
elif 'MatchEntry MatchTable1[] = {' in line.strip():
pattern = 'MatchEntry MatchTable1[] = {'
# last pattern, done
break
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if pattern in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
# skip pseudo instructions
if not mnem.startswith('__'):
# PPC
if mnem.endswith('-') or mnem.endswith('+'):
mnem = mnem[:-1]
if not first_insn:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# save this
insn_id_list[insn_id] = mnem
if not mnem in mnem_list:
print('\t"%s", // %s_INS_%s,' %(mnem.lower(), arch, mnem.upper()))
mnem_list.append(mnem)
#return arch, first_insn, insn_id_list
return arch, first_insn, insn_id_list
# GenAsmMatcher.inc
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])

View File

@@ -0,0 +1,314 @@
#!/usr/bin/python
# print list of instructions LLVM inc files, for Capstone disassembler.
# this will be put into capstone/<arch>.h
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc> <GenInstrInfo.inc> MappingInsn.inc" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[3])
mapping = f.readlines()
f.close()
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AARCH64':
arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, insn_id_list, insn_lines
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
count = 0
#insn_lines = []
insn_id_list = {}
arch = None
first_insn = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'MatchEntry MatchTable1[] = {' in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
count += 1
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
if count == 1:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# print("***", arch, mnem, insn_id)
insn_id_list[insn_id] = mnem
#insn_lines.append(line)
#return arch, first_insn, insn_id_list, insn_lines
return arch, first_insn, insn_id_list
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])
arch = arch.upper()
#for line in insn_id_list:
# print(line)
insn_list = []
#{
# X86_AAA, X86_INS_AAA,
##ifndef CAPSTONE_DIET
# { 0 }, { 0 }, { X86_GRP_NOT64BITMODE, 0 }, 0, 0
##endif
#},
def print_entry(arch, insn_id, mnem, mapping, mnem_can_be_wrong):
if not mnem_can_be_wrong:
insn = "%s_INS_%s" %(arch.upper(), mnem.upper())
if insn in insn_list:
return
print('\t"%s", // %s' %(mnem.lower(), insn))
insn_list.append(insn)
return
insn = "%s_%s" %(arch.upper(), insn_id)
# so mnem can be wrong, we need to verify with MappingInsn.inc
# first, try to find this entry in old MappingInsn.inc file
for i in range(len(mapping)):
tmp = mapping[i].split(',')
if tmp[0].strip() == insn:
insn = tmp[1].strip()
if insn in insn_list:
return
mnem = insn[len("%s_INS_" %(arch)):]
#print("==== get below from MappingInsn.inc file: %s" %insn)
print('\t"%s", // %s' %(mnem.lower(), insn))
insn_list.append(insn)
return
# extract from GenInstrInfo.inc, because the insn id is in order
enum_count = 0
meet_insn = False
f = open(sys.argv[2])
lines = f.readlines()
f.close()
count = 0
last_mnem = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
#print(line.strip())
continue
line = line.strip()
if enum_count == 1:
if 'INSTRUCTION_LIST_END' in line:
break
else:
insn = None
if meet_insn:
# enum items
insn = line.split('=')[0].strip()
if 'CALLSTACK' in insn or 'TAILJUMP' in insn:
# pseudo instruction
insn = None
elif line.startswith(first_insn):
insn = line.split('=')[0].strip()
meet_insn = True
if insn:
count += 1
if insn == 'BSWAP16r_BAD':
last_mnem = 'BSWAP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp32':
last_mnem = 'FCMOVNP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVP_Fp3':
last_mnem = 'FCMOVP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrm_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVSX16rm16':
last_mnem = 'MOVSX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVZX16rm16':
last_mnem = 'MOVZX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'ST_Fp32m':
last_mnem = 'FST'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp64':
last_mnem = 'FCMOVNU'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrr_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSSrm_Int':
last_mnem = 'CMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSDrm_Int':
last_mnem = 'VCMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSSrm_Int':
last_mnem = 'VCMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPCMOVYrrr_REV':
last_mnem = 'VPCMOV'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESDZm':
last_mnem = 'VRNDSCALESD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESSZm':
last_mnem = 'VRNDSCALESS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPDZ128rm':
last_mnem = 'VMAXPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPSZ128rm':
last_mnem = 'VMAXPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSDZrm':
last_mnem = 'VMAXSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSSZrm':
last_mnem = 'VMAXSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPDZ128rm':
last_mnem = 'VMINPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPSZ128rm':
last_mnem = 'VMINPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSDZrm':
last_mnem = 'VMINSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSSZrm':
last_mnem = 'VMINSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMOV64toPQIZrm':
last_mnem = 'VMOVQ'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PDYrr_REV':
last_mnem = 'VPERMILPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PSYrr_REV':
last_mnem = 'VPERMILPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SI64Zrm_Int':
last_mnem = 'VCVTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SSrm_Int':
last_mnem = 'VCVTSD2SS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSS2SI64Zrm_Int':
last_mnem = 'VCVTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSD2SI64Zrm_Int':
last_mnem = 'VCVTTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSS2SI64Zrm_Int':
last_mnem = 'VCVTTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUBADD'):
if insn[len('VFMSUBADD')].isdigit():
last_mnem = insn[:len('VFMSUBADD123xy')]
else:
last_mnem = insn[:len('VFMSUBADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADDSUB'):
if insn[len('VFMADDSUB')].isdigit():
last_mnem = insn[:len('VFMADDSUB123xy')]
else:
last_mnem = insn[:len('VFMADDSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADD'):
if insn[len('VFMADD')].isdigit():
last_mnem = insn[:len('VFMADD123PD')]
else:
last_mnem = insn[:len('VFMADDPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUB'):
if insn[len('VFMSUB')].isdigit():
last_mnem = insn[:len('VFMSUB123PD')]
else:
last_mnem = insn[:len('VFMSUBPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMADD'):
if insn[len('VFNMADD')].isdigit():
last_mnem = insn[:len('VFNMADD123xy')]
else:
last_mnem = insn[:len('VFNMADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMSUB'):
if insn[len('VFNMSUB')].isdigit():
last_mnem = insn[:len('VFNMSUB123xy')]
else:
last_mnem = insn[:len('VFNMSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn in insn_id_list:
# trust old mapping table
last_mnem = insn_id_list[insn].upper()
print_entry(arch.upper(), insn, last_mnem, mapping, False)
else:
# the last option when we cannot find mnem: use the last good mnem
print_entry(arch.upper(), insn, last_mnem, mapping, True)

View File

@@ -0,0 +1,379 @@
#!/usr/bin/python
# print MappingInsn.inc file from LLVM GenAsmMatcher.inc, for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc> <GenInstrInfo.inc> <MappingInsnOp.inc>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[3])
mapping = f.readlines()
f.close()
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
#if arch.upper() == 'AARCH64':
# arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, first_insn, insn_id_list
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
insn_id_list = {}
arch = None
first_insn = None
pattern = None
# first we try to find Table1, or Table0
for line in lines:
if 'MatchEntry MatchTable0[] = {' in line.strip():
pattern = 'MatchEntry MatchTable0[] = {'
elif 'AArch64::' in line and pattern:
# We do not care about Apple Assembly
break
elif 'MatchEntry MatchTable1[] = {' in line.strip():
pattern = 'MatchEntry MatchTable1[] = {'
# last pattern, done
break
for line in lines:
line = line.rstrip()
# skip empty line
if len(line.strip()) == 0:
continue
if pattern in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
# skip pseudo instructions
if not mnem.startswith('__'):
if not first_insn:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# save this
insn_id_list[insn_id] = mnem
#return arch, first_insn, insn_id_list
return arch, first_insn, insn_id_list
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])
#arch = arch.upper()
#for line in insn_id_list:
# print(line)
#{ /* X86_AAA, X86_INS_AAA: aaa */
# X86_EFLAGS_UNDEFINED_OF | X86_EFLAGS_UNDEFINED_SF | X86_EFLAGS_UNDEFINED_ZF | X86_EFLAGS_MODIFY_AF | X86_EFLAGS_UNDEFINED_PF | X86_EFLAGS_MODIFY_CF,
# { 0 }
#},
#{ /* ARM_ADCri, ARM_INS_ADC: adc${s}${p} $rd, $rn, $imm */
# { CS_AC_WRITE, CS_AC_READ, 0 }
#},
def print_entry(arch, insn_id, mnem, mapping, mnem_can_be_wrong):
insn = "%s_%s" %(arch, insn_id)
arch1 = arch
if arch.upper() == 'AARCH64':
arch1 = 'ARM64'
# first, try to find this entry in old MappingInsn.inc file
for i in range(len(mapping)):
if mapping[i].startswith('{') and '/*' in mapping[i]:
#print(mapping[i])
tmp = mapping[i].split('/*')
tmp = tmp[1].strip()
tmp = tmp.split(',')
#print("insn2 = |%s|" %tmp.strip())
if tmp[0].strip() == insn:
if not mnem_can_be_wrong:
if arch.upper() == 'ARM':
print('''
{\t/* %s, %s_INS_%s: %s */
\t%s
},'''% (insn, arch1, mnem, mnem.lower(), mapping[i + 1].strip()))
else: # ARM64
print('''
{\t/* %s, %s_INS_%s: %s */
\t%s
\t%s
},'''% (insn, arch, mnem, mnem.lower(), mapping[i + 1].strip(), mapping[i + 2].strip()))
else:
if arch.upper() == 'ARM':
print('''
{\t/* %s, %s
\t%s
},'''% (insn, ''.join(tmp[1:]), mapping[i + 1].strip()))
else: # ARM64
print('''
{\t/* %s, %s
\t%s
\t%s
},'''% (insn, ''.join(tmp[1:]), mapping[i + 1].strip(), mapping[i + 2].strip()))
return
if mnem_can_be_wrong:
#print("======== CANNOT FIND %s, mapping to %s" %(insn, mnem))
return
pass
# this insn does not exist in mapping table
if arch.upper() == 'ARM':
print('''
{\t/* %s, %s_INS_%s: %s */
\t{ 0 }
},'''% (insn, arch1, mnem, mnem.lower()))
else:
print('''
{\t/* %s, %s_INS_%s: %s */
\t0,
\t{ 0 }
},'''% (insn, arch, mnem, mnem.lower()))
# extract from GenInstrInfo.inc, because the insn id is in order
enum_count = 0
meet_insn = False
f = open(sys.argv[2])
lines = f.readlines()
f.close()
count = 0
last_mnem = None
def is_pseudo_insn(insn, lines):
return False
for line in lines:
tmp = '= %s' %insn
if tmp in line and 'MCID::Pseudo' in line:
return True
return False
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
#print(line.strip())
continue
line = line.strip()
if enum_count == 1:
# skip pseudo instructions
if '__' in line or 'setjmp' in line or 'longjmp' in line or 'Pseudo' in line:
continue
elif 'INSTRUCTION_LIST_END' in line:
break
else:
insn = line.split('=')[0].strip()
# skip more pseudo instruction
if is_pseudo_insn(insn, lines):
continue
'''
insn = None
if meet_insn:
# enum items
insn = line.split('=')[0].strip()
if 'CALLSTACK' in insn or 'TAILJUMP' in insn:
# pseudo instruction
insn = None
elif line.startswith(first_insn):
insn = line.split('=')[0].strip()
meet_insn = True
if insn:
count += 1
if insn == 'BSWAP16r_BAD':
last_mnem = 'BSWAP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp32':
last_mnem = 'FCMOVNP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVP_Fp3':
last_mnem = 'FCMOVP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrm_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVSX16rm16':
last_mnem = 'MOVSX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVZX16rm16':
last_mnem = 'MOVZX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'ST_Fp32m':
last_mnem = 'FST'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp64':
last_mnem = 'FCMOVNU'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrr_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSSrm_Int':
last_mnem = 'CMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSDrm_Int':
last_mnem = 'VCMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSSrm_Int':
last_mnem = 'VCMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPCMOVYrrr_REV':
last_mnem = 'VPCMOV'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESDZm':
last_mnem = 'VRNDSCALESD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESSZm':
last_mnem = 'VRNDSCALESS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPDZ128rm':
last_mnem = 'VMAXPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPSZ128rm':
last_mnem = 'VMAXPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSDZrm':
last_mnem = 'VMAXSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSSZrm':
last_mnem = 'VMAXSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPDZ128rm':
last_mnem = 'VMINPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPSZ128rm':
last_mnem = 'VMINPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSDZrm':
last_mnem = 'VMINSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSSZrm':
last_mnem = 'VMINSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMOV64toPQIZrm':
last_mnem = 'VMOVQ'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PDYrr_REV':
last_mnem = 'VPERMILPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PSYrr_REV':
last_mnem = 'VPERMILPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SI64Zrm_Int':
last_mnem = 'VCVTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SSrm_Int':
last_mnem = 'VCVTSD2SS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSS2SI64Zrm_Int':
last_mnem = 'VCVTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSD2SI64Zrm_Int':
last_mnem = 'VCVTTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSS2SI64Zrm_Int':
last_mnem = 'VCVTTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUBADD'):
if insn[len('VFMSUBADD')].isdigit():
last_mnem = insn[:len('VFMSUBADD123xy')]
else:
last_mnem = insn[:len('VFMSUBADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADDSUB'):
if insn[len('VFMADDSUB')].isdigit():
last_mnem = insn[:len('VFMADDSUB123xy')]
else:
last_mnem = insn[:len('VFMADDSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADD'):
if insn[len('VFMADD')].isdigit():
last_mnem = insn[:len('VFMADD123PD')]
else:
last_mnem = insn[:len('VFMADDPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUB'):
if insn[len('VFMSUB')].isdigit():
last_mnem = insn[:len('VFMSUB123PD')]
else:
last_mnem = insn[:len('VFMSUBPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMADD'):
if insn[len('VFNMADD')].isdigit():
last_mnem = insn[:len('VFNMADD123xy')]
else:
last_mnem = insn[:len('VFNMADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMSUB'):
if insn[len('VFNMSUB')].isdigit():
last_mnem = insn[:len('VFNMSUB123xy')]
else:
last_mnem = insn[:len('VFNMSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
'''
if insn in insn_id_list:
# trust old mapping table
last_mnem = insn_id_list[insn].upper()
print_entry(arch, insn, insn_id_list[insn].upper(), mapping, False)
else:
#pass
# the last option when we cannot find mnem: use the last good mnem
print_entry(arch, insn, last_mnem, mapping, True)

View File

@@ -0,0 +1,318 @@
#!/usr/bin/python
# print MappingInsn.inc file from LLVM GenAsmMatcher.inc, for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenAsmMatcher.inc> <GenInstrInfo.inc> <MappingInsnOp.inc>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[3])
mapping = f.readlines()
f.close()
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# lib/Target/X86/X86GenAsmMatcher.inc
# static const MatchEntry MatchTable1[] = {
# { 0 /* aaa */, X86::AAA, Convert_NoOperands, Feature_Not64BitMode, { }, },
# extract insn from GenAsmMatcher Table
# return (arch, mnem, insn_id)
def extract_insn(line):
tmp = line.split(',')
insn_raw = tmp[1].strip()
insn_mnem = tmp[0].split(' ')[3]
# X86 mov.s
if '.' in insn_mnem:
tmp = insn_mnem.split('.')
insn_mnem = tmp[0]
tmp = insn_raw.split('::')
arch = tmp[0]
# AArch64 -> ARM64
if arch.upper() == 'AARCH64':
arch = 'ARM64'
return (arch, insn_mnem, tmp[1])
# extract all insn lines from GenAsmMatcher
# return arch, insn_id_list, insn_lines
def extract_matcher(filename):
f = open(filename)
lines = f.readlines()
f.close()
match_count = 0
count = 0
#insn_lines = []
insn_id_list = {}
arch = None
first_insn = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'MatchEntry MatchTable1[] = {' in line.strip():
match_count += 1
#print(line.strip())
continue
line = line.strip()
if match_count == 1:
count += 1
if line == '};':
# done with first enum
break
else:
_arch, mnem, insn_id = extract_insn(line)
if count == 1:
arch, first_insn = _arch, insn_id
if not insn_id in insn_id_list:
# print("***", arch, mnem, insn_id)
insn_id_list[insn_id] = mnem
#insn_lines.append(line)
#return arch, first_insn, insn_id_list, insn_lines
return arch, first_insn, insn_id_list
#arch, first_insn, insn_id_list, match_lines = extract_matcher(sys.argv[1])
arch, first_insn, insn_id_list = extract_matcher(sys.argv[1])
arch = arch.upper()
#for line in insn_id_list:
# print(line)
#{ /* X86_AAA, X86_INS_AAA: aaa */
# X86_EFLAGS_UNDEFINED_OF | X86_EFLAGS_UNDEFINED_SF | X86_EFLAGS_UNDEFINED_ZF | X86_EFLAGS_MODIFY_AF | X86_EFLAGS_UNDEFINED_PF | X86_EFLAGS_MODIFY_CF,
# { 0 }
#},
def print_entry(arch, insn_id, mnem, mapping, mnem_can_be_wrong):
insn = "%s_%s" %(arch, insn_id)
# first, try to find this entry in old MappingInsn.inc file
for i in range(len(mapping)):
if mapping[i].startswith('{') and '/*' in mapping[i]:
#print(mapping[i])
tmp = mapping[i].split('/*')
tmp = tmp[1].strip()
tmp = tmp.split(',')
#print("insn2 = |%s|" %tmp.strip())
if tmp[0].strip() == insn:
if not mnem_can_be_wrong:
print('''
{\t/* %s, %s_INS_%s: %s */
\t%s
\t%s
},'''% (insn, arch, mnem, mnem.lower(), mapping[i + 1].strip(), mapping[i + 2].strip()))
else:
print('''
{\t/* %s, %s
\t%s
\t%s
},'''% (insn, ''.join(tmp[1:]).strip(), mapping[i + 1].strip(), mapping[i + 2].strip()))
return
print('''
{\t/* %s, %s_INS_%s: %s */
\t0,
\t{ 0 }
},'''% (insn, arch, mnem, mnem.lower()))
# extract from GenInstrInfo.inc, because the insn id is in order
enum_count = 0
meet_insn = False
f = open(sys.argv[2])
lines = f.readlines()
f.close()
count = 0
last_mnem = None
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
#print(line.strip())
continue
line = line.strip()
if enum_count == 1:
if 'INSTRUCTION_LIST_END' in line:
break
else:
insn = None
if meet_insn:
# enum items
insn = line.split('=')[0].strip()
if 'CALLSTACK' in insn or 'TAILJUMP' in insn:
# pseudo instruction
insn = None
elif line.startswith(first_insn):
insn = line.split('=')[0].strip()
meet_insn = True
if insn:
count += 1
if insn == 'BSWAP16r_BAD':
last_mnem = 'BSWAP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp32':
last_mnem = 'FCMOVNP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVP_Fp3':
last_mnem = 'FCMOVP'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrm_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVSX16rm16':
last_mnem = 'MOVSX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'MOVZX16rm16':
last_mnem = 'MOVZX'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'ST_Fp32m':
last_mnem = 'FST'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMOVNP_Fp64':
last_mnem = 'FCMOVNU'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSDrr_Int':
last_mnem = 'CMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'CMPSSrm_Int':
last_mnem = 'CMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSDrm_Int':
last_mnem = 'VCMPSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCMPSSrm_Int':
last_mnem = 'VCMPSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPCMOVYrrr_REV':
last_mnem = 'VPCMOV'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESDZm':
last_mnem = 'VRNDSCALESD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VRNDSCALESSZm':
last_mnem = 'VRNDSCALESS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPDZ128rm':
last_mnem = 'VMAXPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCPSZ128rm':
last_mnem = 'VMAXPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSDZrm':
last_mnem = 'VMAXSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMAXCSSZrm':
last_mnem = 'VMAXSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPDZ128rm':
last_mnem = 'VMINPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCPSZ128rm':
last_mnem = 'VMINPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSDZrm':
last_mnem = 'VMINSD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMINCSSZrm':
last_mnem = 'VMINSS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VMOV64toPQIZrm':
last_mnem = 'VMOVQ'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PDYrr_REV':
last_mnem = 'VPERMILPD'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VPERMIL2PSYrr_REV':
last_mnem = 'VPERMILPS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SI64Zrm_Int':
last_mnem = 'VCVTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSD2SSrm_Int':
last_mnem = 'VCVTSD2SS'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTSS2SI64Zrm_Int':
last_mnem = 'VCVTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSD2SI64Zrm_Int':
last_mnem = 'VCVTTSD2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn == 'VCVTTSS2SI64Zrm_Int':
last_mnem = 'VCVTTSS2SI'
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUBADD'):
if insn[len('VFMSUBADD')].isdigit():
last_mnem = insn[:len('VFMSUBADD123xy')]
else:
last_mnem = insn[:len('VFMSUBADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADDSUB'):
if insn[len('VFMADDSUB')].isdigit():
last_mnem = insn[:len('VFMADDSUB123xy')]
else:
last_mnem = insn[:len('VFMADDSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMADD'):
if insn[len('VFMADD')].isdigit():
last_mnem = insn[:len('VFMADD123PD')]
else:
last_mnem = insn[:len('VFMADDPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFMSUB'):
if insn[len('VFMSUB')].isdigit():
last_mnem = insn[:len('VFMSUB123PD')]
else:
last_mnem = insn[:len('VFMSUBPD')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMADD'):
if insn[len('VFNMADD')].isdigit():
last_mnem = insn[:len('VFNMADD123xy')]
else:
last_mnem = insn[:len('VFNMADDSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn.startswith('VFNMSUB'):
if insn[len('VFNMSUB')].isdigit():
last_mnem = insn[:len('VFNMSUB123xy')]
else:
last_mnem = insn[:len('VFNMSUBSS')]
print_entry(arch.upper(), insn, last_mnem, mapping, False)
elif insn in insn_id_list:
# trust old mapping table
last_mnem = insn_id_list[insn].upper()
print_entry(arch.upper(), insn, insn_id_list[insn].upper(), mapping, False)
else:
# the last option when we cannot find mnem: use the last good mnem
print_entry(arch.upper(), insn, last_mnem, mapping, True)

View File

@@ -0,0 +1,53 @@
#!/usr/bin/python
# print out all registers from LLVM GenRegisterInfo.inc for Capstone disassembler.
# NOTE: the list then must be filtered, manually.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenRegisterInfo.inc> <architecture>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
arch = sys.argv[2].upper()
enum_count = 0
print("""/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* This is auto-gen data for Capstone disassembly engine (www.capstone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
""")
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
continue
if enum_count == 1:
if line == '};':
# done with first enum
break
else:
# enum items
if 'NoRegister' in line or 'TARGET_REGS' in line:
continue
reg = line.strip().split('=')[0].strip()
if reg.startswith('H') or reg.endswith('PH') or reg.endswith('IH') or reg.endswith('WH'):
print("{ %s_%s, 0 }," %(arch, reg))
elif 'K' in reg or 'BND' in reg:
print("{ %s_%s, 0 }," %(arch, reg))
elif reg in ('DF', 'SSP', 'R8BH', 'R9BH', 'R10BH', 'R11BH', 'R12BH', 'R13BH', 'R14BH', 'R15BH'):
print("{ %s_%s, 0 }," %(arch, reg))
else:
print("{ %s_%s, %s_REG_%s }," %(arch, reg, arch, reg))

View File

@@ -0,0 +1,48 @@
#!/usr/bin/python
# print out all registers from LLVM GenRegisterInfo.inc for Capstone disassembler.
# NOTE: the list then must be filtered, manually.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenRegisterInfo.inc> <architecture>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
arch = sys.argv[2].upper()
enum_count = 0
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
continue
if enum_count == 1:
if line == '};':
# done with first enum
break
else:
# enum items
if 'NoRegister' in line or 'TARGET_REGS' in line:
continue
reg = line.strip().split('=')[0].strip()
if reg.startswith('H') or reg.endswith('PH') or or reg.endswith('IH') or or reg.endswith('WH'):
print(" %s_REG_%s = REMOVE," %(arch, reg))
elif 'K' in reg or 'BND' in reg:
print(" %s_REG_%s = REMOVE," %(arch, reg))
elif reg in ('DF', 'SSP', 'R8BH', 'R9BH', 'R10BH', 'R11BH', 'R12BH', 'R13BH', 'R14BH', 'R15BH'):
print(" %s_REG_%s = REMOVE," %(arch, reg))
else:
print(" %s_REG_%s," %(arch, reg))

View File

@@ -0,0 +1,286 @@
#!/usr/bin/python
# convert LLVM GenRegisterInfo.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenRegisterInfo.inc> <architecture>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
arch = sys.argv[2]
print("""
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\\
|* *|
|* Target Register Enum Values *|
|* *|
|* Automatically generated file, do not edit! *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifdef GET_REGINFO_ENUM
#undef GET_REGINFO_ENUM
""")
enum_count = 0
# 1st enum is register enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
print(line)
continue
if enum_count == 1:
if line.strip() == '};':
print(line)
# done with first enum
break
else:
# enum items
print(" %s_%s" %(arch, line.strip()))
# 2nd enum is register class
enum_count = 0
print("\n// Register classes")
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
if enum_count == 2:
print(line)
continue
if enum_count == 2:
if line.strip() == '};':
# done with 2nd enum
print(line.strip())
break
else:
# enum items
print(" %s_%s" %(arch, line.strip()))
if arch.upper() == 'ARM':
# 3rd enum is Subregister indices
enum_count = 0
print("\n// Subregister indices")
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
if enum_count == 3:
print(line)
continue
if enum_count == 3:
if line.strip() == '};':
# done with 2nd enum
print(line.strip())
break
else:
# enum items
print(" %s_%s" %(arch, line.strip()))
if arch.upper() == 'AARCH64':
# 3rd enum is Register alternate name indices
enum_count = 0
print("\n// Register alternate name indices")
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
enum_count += 1
if enum_count == 3:
print(line)
continue
if enum_count == 3:
if line.strip() == '};':
# done with 2nd enum
print(line.strip())
break
else:
# enum items
print(" %s_%s" %(arch, line.strip()))
# 4th enum is Subregister indices
enum_count = 0
print("\n// Subregister indices")
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {' or 'enum :' in line.strip():
enum_count += 1
if enum_count == 4:
print('enum {')
continue
if enum_count == 4:
if line.strip() == '};':
# done with 2nd enum
print(line.strip())
break
else:
# enum items
print(" %s_%s" %(arch, line.strip()))
# end of enum
print("")
print("#endif // GET_REGINFO_ENUM")
print("""
#ifdef GET_REGINFO_MC_DESC
#undef GET_REGINFO_MC_DESC
""")
# extract RegDiffLists
finding_struct = True
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if arch + 'RegDiffLists' in line:
finding_struct = False
print("static const MCPhysReg " + arch + "RegDiffLists[] = {")
continue
if finding_struct:
continue
else:
print(line)
if line == '};':
# done with this struct
print("")
break
# extract SubRegIdxLists
finding_struct = True
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if arch + 'SubRegIdxLists' in line:
finding_struct = False
print("static const uint16_t " + arch + "SubRegIdxLists[] = {")
continue
if finding_struct:
continue
else:
print(line)
if line == '};':
# done with this struct
print("")
break
# extract RegDesc
finding_struct = True
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if arch + 'RegDesc' in line:
finding_struct = False
print("static const MCRegisterDesc " + arch + "RegDesc[] = {")
continue
if finding_struct:
continue
else:
print(line)
if line == '};':
# done with this struct
print("")
break
# extract register classes
finding_struct = True
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'Register classes' in line and 'namespace' in line:
finding_struct = False
continue
if finding_struct:
continue
else:
if 'const' in line:
line2 = line.replace('const', 'static const')
print(line2)
elif '::' in line:
line2 = line.replace('::', '_')
print(line2)
elif 'end anonymous namespace' in line:
# done with this struct
break
else:
print(line)
print("\n")
# extract MCRegisterClasses
finding_struct = True
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'MCRegisterClass ' + arch + 'MCRegisterClasses[] = {' in line:
finding_struct = False
print("static const MCRegisterClass " + arch + "MCRegisterClasses[] = {")
continue
if finding_struct:
continue
else:
if line == '};':
# done with this struct
print('};\n')
break
elif '::' in line:
line = line.replace('::', '_')
# { GR8, GR8Bits, 130, 20, sizeof(GR8Bits), X86_GR8RegClassID, 1, 1, 1, 1 },
tmp = line.split(',')
print(" %s, %s, %s }," %(tmp[0].strip(), tmp[1].strip(), tmp[4].strip()))
print("#endif // GET_REGINFO_MC_DESC")

View File

@@ -0,0 +1,10 @@
all: full reduce
full:
g++ strinforeduce.cpp -o strinforeduce
reduce:
g++ -DCAPSTONE_X86_REDUCE strinforeduce.cpp -o strinforeduce_reduce
clean:
rm -rf strinforeduce strinforeduce_reduce

View File

@@ -0,0 +1,15 @@
- Run instroinfo2.py on X86GenInstrInfo.inc & X86GenInstrInfo_reduce.inc
$ ./instrinfo2.py ../tablegen/X86GenInstrInfo.inc > X86GenInstrInfo.inc
$ ./instrinfo2.py ../tablegen/X86GenInstrInfo_reduce.inc > X86GenInstrInfo_reduce.inc
- Compile
$ make
- Run
$ ./strinforeduce > X86Lookup16.inc
$ ./strinforeduce_reduce > X86Lookup16_reduce.inc
- Then copy X86Lookup16*.inc to Capstone dir arch/X86/

View File

@@ -0,0 +1,55 @@
#!/usr/bin/python
# convert LLVM GenInstrInfo.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenInstrInfo.inc>" %sys.argv[0])
sys.exit(1)
count = 0
last_line = None
f = open(sys.argv[1])
lines = f.readlines()
f.close()
# 1st enum is register enum
for line in lines:
line = line.rstrip()
# skip all MCPhysReg line
if 'static const MCPhysReg ' in line:
continue
# skip all MCOperandInfo line
if 'static const MCOperandInfo ' in line:
continue
# skip InitX86MCInstrInfo()
if 'static inline void InitX86MCInstrInfo' in line:
continue
if 'II->InitMCInstrInfo' in line:
last_line = line
continue
# skip the next line after II->InitMCInstrInfo
if last_line:
last_line = None
continue
if 'extern const MCInstrDesc ' in line:
count += 1
continue
if count == 1:
if line == '};':
# done with first enum
count += 1
continue
else:
print(line)

View File

@@ -0,0 +1,183 @@
// By Martin Tofall, Obsidium Software
#define GET_INSTRINFO_ENUM
#define GET_INSTRINFO_MC_DESC
#ifdef CAPSTONE_X86_REDUCE
#include "X86GenInstrInfo_reduce.inc"
#else
#include "X86GenInstrInfo.inc"
#endif
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string>
static const char *x86DisassemblerGetInstrName(unsigned Opcode)
{
return &llvm::X86InstrNameData[llvm::X86InstrNameIndices[Opcode]];
}
static bool is16BitEquivalent(const char* orig, const char* equiv)
{
size_t i;
for (i = 0;; i++) {
if (orig[i] == '\0' && equiv[i] == '\0')
return true;
if (orig[i] == '\0' || equiv[i] == '\0')
return false;
if (orig[i] != equiv[i]) {
if ((orig[i] == 'Q' || orig[i] == 'L') && equiv[i] == 'W')
continue;
if ((orig[i] == '6' || orig[i] == '3') && equiv[i] == '1')
continue;
if ((orig[i] == '4' || orig[i] == '2') && equiv[i] == '6')
continue;
return false;
}
}
}
// static const char *header = "#ifdef GET_INSTRINFO_MC_DESC\n#undef GET_INSTRINFO_MC_DESC\n\n"
static const char *header =
"typedef struct x86_op_id_pair {\n"\
"\tuint16_t first;\n" \
"\tuint16_t second;\n" \
"} x86_op_id_pair;\n\n" \
"static const x86_op_id_pair x86_16_bit_eq_tbl[] = {\n";
static const char *footer = "};\n\n";
static const char *header_lookup = "static const uint16_t x86_16_bit_eq_lookup[] = {\n";
//static const char *footer_lookup = "};\n\n#endif\n";
static const char *footer_lookup = "};\n";
static bool is16BitEquivalent_old(unsigned id1, unsigned id2)
{
return (is16BitEquivalent(x86DisassemblerGetInstrName(id1), x86DisassemblerGetInstrName(id2))) != false;
}
//#include "reduced.h"
#if 0
static bool is16BitEquivalent_new(unsigned orig, unsigned equiv)
{
size_t i;
uint16_t idx;
if (orig == equiv)
return true; // emulate old behaviour
if ((idx = x86_16_bit_eq_lookup[orig]) != 0) {
for (i = idx - 1; x86_16_bit_eq_tbl[i].first == orig; ++i) {
if (x86_16_bit_eq_tbl[i].second == equiv)
return true;
}
}
return false;
}
#endif
int main()
{
size_t size_names = sizeof(llvm::X86InstrNameData);
size_t size_indices = sizeof(llvm::X86InstrNameIndices);
size_t size_total = size_names + size_indices;
#if 1
printf("%s", header);
size_t eq_count = 0;
std::string str_lookup;
bool got_i = false;
for (size_t i = 0; i < llvm::X86::INSTRUCTION_LIST_END; ++i) {
const char *name1 = x86DisassemblerGetInstrName(i);
for (size_t j = 0; j < llvm::X86::INSTRUCTION_LIST_END; ++j) {
const char *name2 = x86DisassemblerGetInstrName(j);
if (i != j && is16BitEquivalent(name1, name2) != false) {
//printf("Found equivalent %d and %d\n", i, j);
printf("\t{ %zu, %zu },\n", i, j);
if (!got_i) {
char buf[16];
sprintf(buf, "\t%zu,\n", eq_count + 1);
str_lookup += buf;
got_i = true;
}
++eq_count;
}
}
if (!got_i) {
//char buf[32];
//sprintf(buf, "\t0, //%d\n", i);
//str_lookup += buf;
str_lookup += "\t0,\n";
}
// reset got_i
got_i = false;
}
printf("%s", footer);
printf("%s", header_lookup);
printf("%s", str_lookup.c_str());
printf("%s", footer_lookup);
// printf("%zu equivalents total\n", eq_count);
// size_t size_new = eq_count * 4 + llvm::X86::INSTRUCTION_LIST_END * 2;
// printf("before: %zu, after: %zu, %zu bytes saved\n", size_total, size_new, size_total - size_new);
#endif
#if 0
for (size_t i = 0; i < llvm::X86::INSTRUCTION_LIST_END; ++i) {
for (size_t j = 0; j < llvm::X86::INSTRUCTION_LIST_END; ++j) {
if (is16BitEquivalent_new(i, j) != is16BitEquivalent_old(i, j)) {
bool old_result = is16BitEquivalent_old(i, j);
bool new_result = is16BitEquivalent_new(i, j);
printf("ERROR!\n");
}
}
}
#endif
#if 0
static const size_t BENCH_LOOPS = 50;
size_t eq_count = 0;
DWORD time = GetTickCount();
for (size_t l = 0; l < BENCH_LOOPS; ++l) {
for (size_t i = 0; i < llvm::X86::INSTRUCTION_LIST_END; ++i) {
for (size_t j = 0; j < llvm::X86::INSTRUCTION_LIST_END; ++j)
if (is16BitEquivalent_new(i, j))
++eq_count;
}
}
time = GetTickCount() - time;
printf("new: %f msecs\n", static_cast<float>(time) / static_cast<float>(BENCH_LOOPS));
eq_count = 0;
time = GetTickCount();
for (size_t l = 0; l < BENCH_LOOPS; ++l) {
for (size_t i = 0; i < llvm::X86::INSTRUCTION_LIST_END; ++i) {
for (size_t j = 0; j < llvm::X86::INSTRUCTION_LIST_END; ++j)
if (is16BitEquivalent_old(i, j))
++eq_count;
}
}
time = GetTickCount() - time;
printf("old: %f msecs\n", static_cast<float>(time) / static_cast<float>(BENCH_LOOPS));
#endif
return 0;
}

View File

@@ -0,0 +1,53 @@
#!/usr/bin/python
# convert LLVM GenSubtargetInfo.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenSubtargetInfo.inc> <architecture>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
arch = sys.argv[2]
print("""
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|* *|
|* Subtarget Enumeration Source Fragment *|
|* *|
|* Automatically generated file, do not edit! *|
|* *|
\*===----------------------------------------------------------------------===*/
""")
count = 0
# 1st enum is subtarget enum
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum {':
count += 1
print(line)
continue
if count == 1:
if line.strip() == '};':
# done with first enum
break
else:
# enum items
print(" %s_%s" %(arch, line.strip()))
print('};\n')

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
#!/usr/bin/python
# convert LLVM GenSystemRegister.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019
import sys
if len(sys.argv) == 1:
print("Syntax: %s <GenSystemRegister.inc>" %sys.argv[0])
sys.exit(1)
f = open(sys.argv[1])
lines = f.readlines()
f.close()
#arch = sys.argv[2].upper()
print("""
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|* *|
|* GenSystemRegister Source Fragment *|
|* *|
|* Automatically generated file, do not edit! *|
|* *|
\*===----------------------------------------------------------------------===*/
""")
# extract BankedRegValues enum
count = 0
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if line.strip() == 'enum BankedRegValues {':
count += 1
print(line.strip())
continue
line = line.strip()
if count == 1:
if line == '};':
# done with first enum
break
else:
# skip pseudo instructions
print("\t%s" %(line))
print('};\n')
# extract MClassSysRegsList
count = 0
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'MClassSysRegsList[]' in line:
count += 1
print('static const MClassSysReg MClassSysRegsList[] = {')
continue
if count == 1:
if line.strip() == '};':
# done with first enum
break
else:
# enum items
# { "apsr_g", 0x400, 0x0, 0x400, {ARM::FeatureDSP} }, // 0
line2 = line.replace('::', '_')
sysreg = line2[line2.index('"') + 1 : line2.index('",')]
tmp = line2.split(',')
print("%s, ARM_SYSREG_%s%s" %(line2[:line2.index('",') + 1], sysreg.upper(), line2[line2.index('",') + 1 :]))
print('};\n')
# extract BankedRegsList
count = 0
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'BankedRegsList[]' in line:
count += 1
print('static const BankedReg BankedRegsList[] = {')
continue
if count == 1:
if line.strip() == '};':
# done with first enum
break
else:
# enum items
line2 = line.replace('::', '_')
sysreg = line2[line2.index('"') + 1 : line2.index('",')]
tmp = line2.split(',')
print("%s, ARM_SYSREG_%s%s" %(line2[:line2.index('",') + 1], sysreg.upper(), line2[line2.index('",') + 1 :]))
print('};\n')
# lookupMClassSysRegByM2M3Encoding8
count = 0
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'lookupMClassSysRegByM2M3Encoding8' in line and '{' in line:
count += 1
print('const MClassSysReg *lookupMClassSysRegByM2M3Encoding8(uint16_t encoding)\n{')
print(' unsigned int i;')
continue
if count == 1 and 'IndexType Index[] = {' in line:
count += 1
if count == 2:
if line.strip() == '};':
# done with array, or this function?
print(line)
break
else:
# enum items
print(line)
print("""
i = binsearch_IndexTypeEncoding(Index, ARR_SIZE(Index), encoding);
if (i == -1)
return NULL;
else
return &MClassSysRegsList[Index[i].index];
}
""")
# lookupMClassSysRegByM1Encoding12
count = 0
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'lookupMClassSysRegByM1Encoding12' in line and '{' in line:
count += 1
print('const MClassSysReg *lookupMClassSysRegByM1Encoding12(uint16_t encoding)\n{')
print(' unsigned int i;')
continue
if count == 1 and 'IndexType Index[] = {' in line:
count += 1
if count == 2:
if line.strip() == '};':
# done with array, or this function?
print(line)
break
else:
# enum items
print(line)
print("""
i = binsearch_IndexTypeEncoding(Index, ARR_SIZE(Index), encoding);
if (i == -1)
return NULL;
else
return &MClassSysRegsList[Index[i].index];
}
""")
# lookupBankedRegByEncoding
count = 0
for line in lines:
line = line.rstrip()
if len(line.strip()) == 0:
continue
if 'lookupBankedRegByEncoding' in line and '{' in line:
count += 1
print('const BankedReg *lookupBankedRegByEncoding(uint8_t encoding)\n{')
print(' unsigned int i;')
continue
if count == 1 and 'IndexType Index[] = {' in line:
count += 1
if count == 2:
if line.strip() == '};':
# done with array, or this function?
print(line)
break
else:
# enum items
print(line)
print("""
i = binsearch_IndexTypeEncoding(Index, ARR_SIZE(Index), encoding);
if (i == -1)
return NULL;
else
return &BankedRegsList[Index[i].index];
}
""")

View File

@@ -0,0 +1,45 @@
#!/bin/sh
# Generate raw .inc files for non-x86 architectures of Capstone, by Nguyen Anh Quynh
# Syntax: gen-tablegen-arch.sh <path-to-llvm-tblgen> <arch>
# Example: ./gen-tablegen-arch.sh ~/projects/llvm/7.0.1/build/bin ARM
TBLGEN_PATH=$1
DIR_TD=$2
ARCH=$2
echo "Using llvm-tblgen from ${TBLGEN_PATH}"
echo "Generating ${ARCH}GenInstrInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-instr-info -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenInstrInfo.inc
echo "Generating ${ARCH}GenRegisterInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-register-info -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenRegisterInfo.inc
echo "Generating ${ARCH}GenAsmMatcher.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-matcher -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenAsmMatcher.inc
echo "Generating ${ARCH}GenDisassemblerTables.inc"
$TBLGEN_PATH/llvm-tblgen -gen-disassembler -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenDisassemblerTables.inc
echo "Generating ${ARCH}GenAsmWriter.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenAsmWriter.inc
echo "Generating ${ARCH}GenSubtargetInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-subtarget -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenSubtargetInfo.inc
case $2 in
ARM)
# for ARM only
echo "Generating ${ARCH}GenAsmWriter-digit.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}-digit.td -o ${ARCH}GenAsmWriter-digit.inc
echo "Generating ${ARCH}GenSystemRegister.inc"
$TBLGEN_PATH/llvm-tblgen -gen-searchable-tables -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenSystemRegister.inc
;;
AArch64)
echo "Generating ${ARCH}GenSystemOperands.inc"
$TBLGEN_PATH/llvm-tblgen -gen-searchable-tables -I include -I ${DIR_TD} ${DIR_TD}/${ARCH}.td -o ${ARCH}GenSystemOperands.inc
;;
esac

View File

@@ -0,0 +1,32 @@
#!/bin/sh
# Generate raw X86*.inc files for Capstone, by Nguyen Anh Quynh
# Syntax: gen-tablegen-full.sh <path-to-llvm-tblgen> <path-to-X86-td-files>
# Example: ./gen-tablegen-full.sh ~/projects/llvm/7.0.1/build/bin X86
#TBLGEN_PATH=~/projects/llvm/7.0.1/build/bin
TBLGEN_PATH=$1
#DIR_TD="X86"
DIR_TD=$2
echo "Using llvm-tblgen from ${TBLGEN_PATH}"
echo "Generating X86GenInstrInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-instr-info -I include -I ${DIR_TD} ${DIR_TD}/X86.td -o X86GenInstrInfo.inc
echo "Generating X86GenRegisterInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-register-info -I include -I ${DIR_TD} ${DIR_TD}/X86.td -o X86GenRegisterInfo.inc
echo "Generating X86GenAsmMatcher.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-matcher -I include -I ${DIR_TD} ${DIR_TD}/X86.td -o X86GenAsmMatcher.inc
echo "Generating X86GenDisassemblerTables.inc"
$TBLGEN_PATH/llvm-tblgen -gen-disassembler -I include -I ${DIR_TD} ${DIR_TD}/X86.td -o X86GenDisassemblerTables.inc
echo "Generating X86GenAsmWriter1.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -asmwriternum=1 -I include -I ${DIR_TD} ${DIR_TD}/X86.td -o X86GenAsmWriter1.inc
echo "Generating X86GenAsmWriter.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -I include -I ${DIR_TD} ${DIR_TD}/X86.td -o X86GenAsmWriter.inc

View File

@@ -0,0 +1,28 @@
#!/bin/sh
# Generate raw X86*reduce.inc files for Capstone, by Nguyen Anh Quynh
# Syntax: gen-tablegen-reduce.sh <path-to-llvm-tblgen> X86
# Example: ./gen-tablegen-reduce.sh ~/projects/llvm/7.0.1/build/bin X86
#TBLGEN_PATH=~/projects/llvm/7.0.1/build/bin
TBLGEN_PATH=$1
#DIR_TD="X86"
DIR_TD=$2
echo "Using llvm-tblgen from ${TBLGEN_PATH}"
echo "Generating X86GenAsmMatcher_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-matcher -I include -I ${DIR_TD} ${DIR_TD}/X86_reduce.td -o X86GenAsmMatcher_reduce.inc
echo "Generating GenInstrInfo_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-instr-info -I include -I ${DIR_TD} ${DIR_TD}/X86_reduce.td -o X86GenInstrInfo_reduce.inc
echo "Generating X86GenDisassemblerTables_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-disassembler -I include -I ${DIR_TD} ${DIR_TD}/X86_reduce.td -o X86GenDisassemblerTables_reduce.inc
echo "Generating X86GenAsmWriter1_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -asmwriternum=1 -I include -I ${DIR_TD} ${DIR_TD}/X86_reduce.td -o X86GenAsmWriter1_reduce.inc
echo "Generating X86GenAsmWriter_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -I include -I ${DIR_TD} ${DIR_TD}/X86_reduce.td -o X86GenAsmWriter_reduce.inc

View File

@@ -0,0 +1,47 @@
#!/bin/sh
# Generate raw X86*.inc files for Capstone, by Nguyen Anh Quynh
# This combines both -full & -reduce scripts, so we keep it here for reference only.
# Syntax: gen-tablegen.sh <path-to-llvm-tblgen>
# Example: ./gen-tablegen.sh ~/projects/llvm/7.0.1/build/bin
#TBLGEN_PATH=~/projects/llvm/7.0.1/build/bin
TBLGEN_PATH=$1
echo "Using llvm-tblgen from ${TBLGEN_PATH}"
echo "Generating X86GenInstrInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-instr-info -I include -I X86 X86/X86.td -o X86GenInstrInfo.inc
echo "Generating X86GenRegisterInfo.inc"
$TBLGEN_PATH/llvm-tblgen -gen-register-info -I include -I X86 X86/X86.td -o X86GenRegisterInfo.inc
echo "Generating X86GenAsmMatcher.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-matcher -I include -I X86 X86/X86.td -o X86GenAsmMatcher.inc
echo "Generating X86GenDisassemblerTables.inc"
$TBLGEN_PATH/llvm-tblgen -gen-disassembler -I include -I X86 X86/X86.td -o X86GenDisassemblerTables.inc
echo "Generating X86GenAsmWriter1.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -asmwriternum=1 -I include -I X86 X86/X86.td -o X86GenAsmWriter1.inc
echo "Generating X86GenAsmWriter.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -I include -I X86 X86/X86.td -o X86GenAsmWriter.inc
echo "Generating X86GenAsmMatcher_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-matcher -I include -I X86 X86/X86_reduce.td -o X86GenAsmMatcher_reduce.inc
echo "Generating GenInstrInfo_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-instr-info -I include -I X86 X86/X86_reduce.td -o X86GenInstrInfo_reduce.inc
echo "Generating X86GenDisassemblerTables_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-disassembler -I include -I X86 X86/X86_reduce.td -o X86GenDisassemblerTables_reduce.inc
echo "Generating X86GenAsmWriter1_reduce.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -asmwriternum=1 -I include -I X86 X86/X86_reduce.td -o X86GenAsmWriter1_reduce.inc
echo "Generating X86GenAsmWriter.inc"
$TBLGEN_PATH/llvm-tblgen -gen-asm-writer -I include -I X86 X86/X86_reduce.td -o X86GenAsmWriter_reduce.inc

View File

@@ -0,0 +1,408 @@
//==- include/llvm/CodeGen/AccelTable.h - Accelerator Tables -----*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file contains support for writing accelerator tables.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_ACCELTABLE_H
#define LLVM_CODEGEN_ACCELTABLE_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/CodeGen/DwarfStringPoolEntry.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>
#include <cstdint>
#include <vector>
/// \file
/// The DWARF and Apple accelerator tables are an indirect hash table optimized
/// for null lookup rather than access to known data. The Apple accelerator
/// tables are a precursor of the newer DWARF v5 accelerator tables. Both
/// formats share common design ideas.
///
/// The Apple accelerator table are output into an on-disk format that looks
/// like this:
///
/// .------------------.
/// | HEADER |
/// |------------------|
/// | BUCKETS |
/// |------------------|
/// | HASHES |
/// |------------------|
/// | OFFSETS |
/// |------------------|
/// | DATA |
/// `------------------'
///
/// The header contains a magic number, version, type of hash function,
/// the number of buckets, total number of hashes, and room for a special struct
/// of data and the length of that struct.
///
/// The buckets contain an index (e.g. 6) into the hashes array. The hashes
/// section contains all of the 32-bit hash values in contiguous memory, and the
/// offsets contain the offset into the data area for the particular hash.
///
/// For a lookup example, we could hash a function name and take it modulo the
/// number of buckets giving us our bucket. From there we take the bucket value
/// as an index into the hashes table and look at each successive hash as long
/// as the hash value is still the same modulo result (bucket value) as earlier.
/// If we have a match we look at that same entry in the offsets table and grab
/// the offset in the data for our final match.
///
/// The DWARF v5 accelerator table consists of zero or more name indices that
/// are output into an on-disk format that looks like this:
///
/// .------------------.
/// | HEADER |
/// |------------------|
/// | CU LIST |
/// |------------------|
/// | LOCAL TU LIST |
/// |------------------|
/// | FOREIGN TU LIST |
/// |------------------|
/// | HASH TABLE |
/// |------------------|
/// | NAME TABLE |
/// |------------------|
/// | ABBREV TABLE |
/// |------------------|
/// | ENTRY POOL |
/// `------------------'
///
/// For the full documentation please refer to the DWARF 5 standard.
///
///
/// This file defines the class template AccelTable, which is represents an
/// abstract view of an Accelerator table, without any notion of an on-disk
/// layout. This class is parameterized by an entry type, which should derive
/// from AccelTableData. This is the type of individual entries in the table,
/// and it should store the data necessary to emit them. AppleAccelTableData is
/// the base class for Apple Accelerator Table entries, which have a uniform
/// structure based on a sequence of Atoms. There are different sub-classes
/// derived from AppleAccelTable, which differ in the set of Atoms and how they
/// obtain their values.
///
/// An Apple Accelerator Table can be serialized by calling emitAppleAccelTable
/// function.
namespace llvm {
class AsmPrinter;
class DwarfCompileUnit;
class DwarfDebug;
/// Interface which the different types of accelerator table data have to
/// conform. It serves as a base class for different values of the template
/// argument of the AccelTable class template.
class AccelTableData {
public:
virtual ~AccelTableData() = default;
bool operator<(const AccelTableData &Other) const {
return order() < Other.order();
}
// Subclasses should implement:
// static uint32_t hash(StringRef Name);
#ifndef NDEBUG
virtual void print(raw_ostream &OS) const = 0;
#endif
protected:
virtual uint64_t order() const = 0;
};
/// A base class holding non-template-dependant functionality of the AccelTable
/// class. Clients should not use this class directly but rather instantiate
/// AccelTable with a type derived from AccelTableData.
class AccelTableBase {
public:
using HashFn = uint32_t(StringRef);
/// Represents a group of entries with identical name (and hence, hash value).
struct HashData {
DwarfStringPoolEntryRef Name;
uint32_t HashValue;
std::vector<AccelTableData *> Values;
MCSymbol *Sym;
HashData(DwarfStringPoolEntryRef Name, HashFn *Hash)
: Name(Name), HashValue(Hash(Name.getString())) {}
#ifndef NDEBUG
void print(raw_ostream &OS) const;
void dump() const { print(dbgs()); }
#endif
};
using HashList = std::vector<HashData *>;
using BucketList = std::vector<HashList>;
protected:
/// Allocator for HashData and Values.
BumpPtrAllocator Allocator;
using StringEntries = StringMap<HashData, BumpPtrAllocator &>;
StringEntries Entries;
HashFn *Hash;
uint32_t BucketCount;
uint32_t UniqueHashCount;
HashList Hashes;
BucketList Buckets;
void computeBucketCount();
AccelTableBase(HashFn *Hash) : Entries(Allocator), Hash(Hash) {}
public:
void finalize(AsmPrinter *Asm, StringRef Prefix);
ArrayRef<HashList> getBuckets() const { return Buckets; }
uint32_t getBucketCount() const { return BucketCount; }
uint32_t getUniqueHashCount() const { return UniqueHashCount; }
uint32_t getUniqueNameCount() const { return Entries.size(); }
#ifndef NDEBUG
void print(raw_ostream &OS) const;
void dump() const { print(dbgs()); }
#endif
AccelTableBase(const AccelTableBase &) = delete;
void operator=(const AccelTableBase &) = delete;
};
/// This class holds an abstract representation of an Accelerator Table,
/// consisting of a sequence of buckets, each bucket containing a sequence of
/// HashData entries. The class is parameterized by the type of entries it
/// holds. The type template parameter also defines the hash function to use for
/// hashing names.
template <typename DataT> class AccelTable : public AccelTableBase {
public:
AccelTable() : AccelTableBase(DataT::hash) {}
template <typename... Types>
void addName(DwarfStringPoolEntryRef Name, Types &&... Args);
};
template <typename AccelTableDataT>
template <typename... Types>
void AccelTable<AccelTableDataT>::addName(DwarfStringPoolEntryRef Name,
Types &&... Args) {
assert(Buckets.empty() && "Already finalized!");
// If the string is in the list already then add this die to the list
// otherwise add a new one.
auto Iter = Entries.try_emplace(Name.getString(), Name, Hash).first;
assert(Iter->second.Name == Name);
Iter->second.Values.push_back(
new (Allocator) AccelTableDataT(std::forward<Types>(Args)...));
}
/// A base class for different implementations of Data classes for Apple
/// Accelerator Tables. The columns in the table are defined by the static Atoms
/// variable defined on the subclasses.
class AppleAccelTableData : public AccelTableData {
public:
/// An Atom defines the form of the data in an Apple accelerator table.
/// Conceptually it is a column in the accelerator consisting of a type and a
/// specification of the form of its data.
struct Atom {
/// Atom Type.
const uint16_t Type;
/// DWARF Form.
const uint16_t Form;
constexpr Atom(uint16_t Type, uint16_t Form) : Type(Type), Form(Form) {}
#ifndef NDEBUG
void print(raw_ostream &OS) const;
void dump() const { print(dbgs()); }
#endif
};
// Subclasses should define:
// static constexpr Atom Atoms[];
virtual void emit(AsmPrinter *Asm) const = 0;
static uint32_t hash(StringRef Buffer) { return djbHash(Buffer); }
};
/// The Data class implementation for DWARF v5 accelerator table. Unlike the
/// Apple Data classes, this class is just a DIE wrapper, and does not know to
/// serialize itself. The complete serialization logic is in the
/// emitDWARF5AccelTable function.
class DWARF5AccelTableData : public AccelTableData {
public:
static uint32_t hash(StringRef Name) { return caseFoldingDjbHash(Name); }
DWARF5AccelTableData(const DIE &Die) : Die(Die) {}
#ifndef NDEBUG
void print(raw_ostream &OS) const override;
#endif
const DIE &getDie() const { return Die; }
uint64_t getDieOffset() const { return Die.getOffset(); }
unsigned getDieTag() const { return Die.getTag(); }
protected:
const DIE &Die;
uint64_t order() const override { return Die.getOffset(); }
};
class DWARF5AccelTableStaticData : public AccelTableData {
public:
static uint32_t hash(StringRef Name) { return caseFoldingDjbHash(Name); }
DWARF5AccelTableStaticData(uint64_t DieOffset, unsigned DieTag,
unsigned CUIndex)
: DieOffset(DieOffset), DieTag(DieTag), CUIndex(CUIndex) {}
#ifndef NDEBUG
void print(raw_ostream &OS) const override;
#endif
uint64_t getDieOffset() const { return DieOffset; }
unsigned getDieTag() const { return DieTag; }
unsigned getCUIndex() const { return CUIndex; }
protected:
uint64_t DieOffset;
unsigned DieTag;
unsigned CUIndex;
uint64_t order() const override { return DieOffset; }
};
void emitAppleAccelTableImpl(AsmPrinter *Asm, AccelTableBase &Contents,
StringRef Prefix, const MCSymbol *SecBegin,
ArrayRef<AppleAccelTableData::Atom> Atoms);
/// Emit an Apple Accelerator Table consisting of entries in the specified
/// AccelTable. The DataT template parameter should be derived from
/// AppleAccelTableData.
template <typename DataT>
void emitAppleAccelTable(AsmPrinter *Asm, AccelTable<DataT> &Contents,
StringRef Prefix, const MCSymbol *SecBegin) {
static_assert(std::is_convertible<DataT *, AppleAccelTableData *>::value, "");
emitAppleAccelTableImpl(Asm, Contents, Prefix, SecBegin, DataT::Atoms);
}
void emitDWARF5AccelTable(AsmPrinter *Asm,
AccelTable<DWARF5AccelTableData> &Contents,
const DwarfDebug &DD,
ArrayRef<std::unique_ptr<DwarfCompileUnit>> CUs);
void emitDWARF5AccelTable(
AsmPrinter *Asm, AccelTable<DWARF5AccelTableStaticData> &Contents,
ArrayRef<MCSymbol *> CUs,
llvm::function_ref<unsigned(const DWARF5AccelTableStaticData &)>
getCUIndexForEntry);
/// Accelerator table data implementation for simple Apple accelerator tables
/// with just a DIE reference.
class AppleAccelTableOffsetData : public AppleAccelTableData {
public:
AppleAccelTableOffsetData(const DIE &D) : Die(D) {}
void emit(AsmPrinter *Asm) const override;
static constexpr Atom Atoms[] = {
Atom(dwarf::DW_ATOM_die_offset, dwarf::DW_FORM_data4)};
#ifndef NDEBUG
void print(raw_ostream &OS) const override;
#endif
protected:
uint64_t order() const override { return Die.getOffset(); }
const DIE &Die;
};
/// Accelerator table data implementation for Apple type accelerator tables.
class AppleAccelTableTypeData : public AppleAccelTableOffsetData {
public:
AppleAccelTableTypeData(const DIE &D) : AppleAccelTableOffsetData(D) {}
void emit(AsmPrinter *Asm) const override;
static constexpr Atom Atoms[] = {
Atom(dwarf::DW_ATOM_die_offset, dwarf::DW_FORM_data4),
Atom(dwarf::DW_ATOM_die_tag, dwarf::DW_FORM_data2),
Atom(dwarf::DW_ATOM_type_flags, dwarf::DW_FORM_data1)};
#ifndef NDEBUG
void print(raw_ostream &OS) const override;
#endif
};
/// Accelerator table data implementation for simple Apple accelerator tables
/// with a DIE offset but no actual DIE pointer.
class AppleAccelTableStaticOffsetData : public AppleAccelTableData {
public:
AppleAccelTableStaticOffsetData(uint32_t Offset) : Offset(Offset) {}
void emit(AsmPrinter *Asm) const override;
static constexpr Atom Atoms[] = {
Atom(dwarf::DW_ATOM_die_offset, dwarf::DW_FORM_data4)};
#ifndef NDEBUG
void print(raw_ostream &OS) const override;
#endif
protected:
uint64_t order() const override { return Offset; }
uint32_t Offset;
};
/// Accelerator table data implementation for type accelerator tables with
/// a DIE offset but no actual DIE pointer.
class AppleAccelTableStaticTypeData : public AppleAccelTableStaticOffsetData {
public:
AppleAccelTableStaticTypeData(uint32_t Offset, uint16_t Tag,
bool ObjCClassIsImplementation,
uint32_t QualifiedNameHash)
: AppleAccelTableStaticOffsetData(Offset),
QualifiedNameHash(QualifiedNameHash), Tag(Tag),
ObjCClassIsImplementation(ObjCClassIsImplementation) {}
void emit(AsmPrinter *Asm) const override;
static constexpr Atom Atoms[] = {
Atom(dwarf::DW_ATOM_die_offset, dwarf::DW_FORM_data4),
Atom(dwarf::DW_ATOM_die_tag, dwarf::DW_FORM_data2),
Atom(5, dwarf::DW_FORM_data1), Atom(6, dwarf::DW_FORM_data4)};
#ifndef NDEBUG
void print(raw_ostream &OS) const override;
#endif
protected:
uint64_t order() const override { return Offset; }
uint32_t QualifiedNameHash;
uint16_t Tag;
bool ObjCClassIsImplementation;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_ACCELTABLE_H

View File

@@ -0,0 +1,144 @@
//===- CodeGen/Analysis.h - CodeGen LLVM IR Analysis Utilities --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares several CodeGen-specific LLVM IR analysis utilities.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_ANALYSIS_H
#define LLVM_CODEGEN_ANALYSIS_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/CodeGen.h"
namespace llvm {
class GlobalValue;
class LLT;
class MachineBasicBlock;
class MachineFunction;
class TargetLoweringBase;
class TargetLowering;
class TargetMachine;
struct EVT;
/// Compute the linearized index of a member in a nested
/// aggregate/struct/array.
///
/// Given an LLVM IR aggregate type and a sequence of insertvalue or
/// extractvalue indices that identify a member, return the linearized index of
/// the start of the member, i.e the number of element in memory before the
/// sought one. This is disconnected from the number of bytes.
///
/// \param Ty is the type indexed by \p Indices.
/// \param Indices is an optional pointer in the indices list to the current
/// index.
/// \param IndicesEnd is the end of the indices list.
/// \param CurIndex is the current index in the recursion.
///
/// \returns \p CurIndex plus the linear index in \p Ty the indices list.
unsigned ComputeLinearIndex(Type *Ty,
const unsigned *Indices,
const unsigned *IndicesEnd,
unsigned CurIndex = 0);
inline unsigned ComputeLinearIndex(Type *Ty,
ArrayRef<unsigned> Indices,
unsigned CurIndex = 0) {
return ComputeLinearIndex(Ty, Indices.begin(), Indices.end(), CurIndex);
}
/// ComputeValueVTs - Given an LLVM IR type, compute a sequence of
/// EVTs that represent all the individual underlying
/// non-aggregate types that comprise it.
///
/// If Offsets is non-null, it points to a vector to be filled in
/// with the in-memory offsets of each of the individual values.
///
void ComputeValueVTs(const TargetLowering &TLI, const DataLayout &DL, Type *Ty,
SmallVectorImpl<EVT> &ValueVTs,
SmallVectorImpl<uint64_t> *Offsets = nullptr,
uint64_t StartingOffset = 0);
/// Variant of ComputeValueVTs that also produces the memory VTs.
void ComputeValueVTs(const TargetLowering &TLI, const DataLayout &DL, Type *Ty,
SmallVectorImpl<EVT> &ValueVTs,
SmallVectorImpl<EVT> *MemVTs,
SmallVectorImpl<uint64_t> *Offsets = nullptr,
uint64_t StartingOffset = 0);
/// computeValueLLTs - Given an LLVM IR type, compute a sequence of
/// LLTs that represent all the individual underlying
/// non-aggregate types that comprise it.
///
/// If Offsets is non-null, it points to a vector to be filled in
/// with the in-memory offsets of each of the individual values.
///
void computeValueLLTs(const DataLayout &DL, Type &Ty,
SmallVectorImpl<LLT> &ValueTys,
SmallVectorImpl<uint64_t> *Offsets = nullptr,
uint64_t StartingOffset = 0);
/// ExtractTypeInfo - Returns the type info, possibly bitcast, encoded in V.
GlobalValue *ExtractTypeInfo(Value *V);
/// getFCmpCondCode - Return the ISD condition code corresponding to
/// the given LLVM IR floating-point condition code. This includes
/// consideration of global floating-point math flags.
///
ISD::CondCode getFCmpCondCode(FCmpInst::Predicate Pred);
/// getFCmpCodeWithoutNaN - Given an ISD condition code comparing floats,
/// return the equivalent code if we're allowed to assume that NaNs won't occur.
ISD::CondCode getFCmpCodeWithoutNaN(ISD::CondCode CC);
/// getICmpCondCode - Return the ISD condition code corresponding to
/// the given LLVM IR integer condition code.
ISD::CondCode getICmpCondCode(ICmpInst::Predicate Pred);
/// getICmpCondCode - Return the LLVM IR integer condition code
/// corresponding to the given ISD integer condition code.
ICmpInst::Predicate getICmpCondCode(ISD::CondCode Pred);
/// Test if the given instruction is in a position to be optimized
/// with a tail-call. This roughly means that it's in a block with
/// a return and there's nothing that needs to be scheduled
/// between it and the return.
///
/// This function only tests target-independent requirements.
bool isInTailCallPosition(const CallBase &Call, const TargetMachine &TM);
/// Test if given that the input instruction is in the tail call position, if
/// there is an attribute mismatch between the caller and the callee that will
/// inhibit tail call optimizations.
/// \p AllowDifferingSizes is an output parameter which, if forming a tail call
/// is permitted, determines whether it's permitted only if the size of the
/// caller's and callee's return types match exactly.
bool attributesPermitTailCall(const Function *F, const Instruction *I,
const ReturnInst *Ret,
const TargetLoweringBase &TLI,
bool *AllowDifferingSizes = nullptr);
/// Test if given that the input instruction is in the tail call position if the
/// return type or any attributes of the function will inhibit tail call
/// optimization.
bool returnTypeIsEligibleForTailCall(const Function *F, const Instruction *I,
const ReturnInst *Ret,
const TargetLoweringBase &TLI);
DenseMap<const MachineBasicBlock *, int>
getEHScopeMembership(const MachineFunction &MF);
} // End llvm namespace
#endif

View File

@@ -0,0 +1,103 @@
//===- llvm/CodeGen/AntiDepBreaker.h - Anti-Dependence Breaking -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the AntiDepBreaker class, which implements
// anti-dependence breaking heuristics for post-register-allocation scheduling.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_ANTIDEPBREAKER_H
#define LLVM_CODEGEN_ANTIDEPBREAKER_H
#include "llvm/ADT/iterator_range.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <utility>
#include <vector>
namespace llvm {
class RegisterClassInfo;
/// This class works in conjunction with the post-RA scheduler to rename
/// registers to break register anti-dependencies (WAR hazards).
class AntiDepBreaker {
public:
using DbgValueVector =
std::vector<std::pair<MachineInstr *, MachineInstr *>>;
virtual ~AntiDepBreaker();
/// Initialize anti-dep breaking for a new basic block.
virtual void StartBlock(MachineBasicBlock *BB) = 0;
/// Identify anti-dependencies within a basic-block region and break them by
/// renaming registers. Return the number of anti-dependencies broken.
virtual unsigned BreakAntiDependencies(const std::vector<SUnit> &SUnits,
MachineBasicBlock::iterator Begin,
MachineBasicBlock::iterator End,
unsigned InsertPosIndex,
DbgValueVector &DbgValues) = 0;
/// Update liveness information to account for the current
/// instruction, which will not be scheduled.
virtual void Observe(MachineInstr &MI, unsigned Count,
unsigned InsertPosIndex) = 0;
/// Finish anti-dep breaking for a basic block.
virtual void FinishBlock() = 0;
/// Update DBG_VALUE or DBG_PHI if dependency breaker is updating
/// other machine instruction to use NewReg.
void UpdateDbgValue(MachineInstr &MI, unsigned OldReg, unsigned NewReg) {
if (MI.isDebugValue()) {
if (MI.getDebugOperand(0).isReg() &&
MI.getDebugOperand(0).getReg() == OldReg)
MI.getDebugOperand(0).setReg(NewReg);
} else if (MI.isDebugPHI()) {
if (MI.getOperand(0).isReg() &&
MI.getOperand(0).getReg() == OldReg)
MI.getOperand(0).setReg(NewReg);
} else {
llvm_unreachable("MI is not DBG_VALUE / DBG_PHI!");
}
}
/// Update all DBG_VALUE instructions that may be affected by the dependency
/// breaker's update of ParentMI to use NewReg.
void UpdateDbgValues(const DbgValueVector &DbgValues, MachineInstr *ParentMI,
unsigned OldReg, unsigned NewReg) {
// The following code is dependent on the order in which the DbgValues are
// constructed in ScheduleDAGInstrs::buildSchedGraph.
MachineInstr *PrevDbgMI = nullptr;
for (const auto &DV : make_range(DbgValues.crbegin(), DbgValues.crend())) {
MachineInstr *PrevMI = DV.second;
if ((PrevMI == ParentMI) || (PrevMI == PrevDbgMI)) {
MachineInstr *DbgMI = DV.first;
UpdateDbgValue(*DbgMI, OldReg, NewReg);
PrevDbgMI = DbgMI;
} else if (PrevDbgMI) {
break; // If no match and already found a DBG_VALUE, we're done.
}
}
}
};
AntiDepBreaker *createAggressiveAntiDepBreaker(
MachineFunction &MFi, const RegisterClassInfo &RCI,
TargetSubtargetInfo::RegClassVector &CriticalPathRCs);
AntiDepBreaker *createCriticalAntiDepBreaker(MachineFunction &MFi,
const RegisterClassInfo &RCI);
} // end namespace llvm
#endif // LLVM_CODEGEN_ANTIDEPBREAKER_H

View File

@@ -0,0 +1,816 @@
//===- llvm/CodeGen/AsmPrinter.h - AsmPrinter Framework ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains a class to be used as the base class for target specific
// asm writers. This class primarily handles common functionality used by
// all asm writers.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_ASMPRINTER_H
#define LLVM_CODEGEN_ASMPRINTER_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinterHandler.h"
#include "llvm/CodeGen/DwarfStringPoolEntry.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SourceMgr.h"
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
namespace llvm {
class BasicBlock;
class BlockAddress;
class Constant;
class ConstantArray;
class DataLayout;
class DIE;
class DIEAbbrev;
class DwarfDebug;
class GCMetadataPrinter;
class GCStrategy;
class GlobalObject;
class GlobalValue;
class GlobalVariable;
class MachineBasicBlock;
class MachineConstantPoolValue;
class MachineDominatorTree;
class MachineFunction;
class MachineInstr;
class MachineJumpTableInfo;
class MachineLoopInfo;
class MachineModuleInfo;
class MachineOptimizationRemarkEmitter;
class MCAsmInfo;
class MCCFIInstruction;
class MCContext;
class MCExpr;
class MCInst;
class MCSection;
class MCStreamer;
class MCSubtargetInfo;
class MCSymbol;
class MCTargetOptions;
class MDNode;
class Module;
class PseudoProbeHandler;
class raw_ostream;
class StackMaps;
class StringRef;
class TargetLoweringObjectFile;
class TargetMachine;
class Twine;
namespace remarks {
class RemarkStreamer;
}
/// This class is intended to be used as a driving class for all asm writers.
class AsmPrinter : public MachineFunctionPass {
public:
/// Target machine description.
TargetMachine &TM;
/// Target Asm Printer information.
const MCAsmInfo *MAI;
/// This is the context for the output file that we are streaming. This owns
/// all of the global MC-related objects for the generated translation unit.
MCContext &OutContext;
/// This is the MCStreamer object for the file we are generating. This
/// contains the transient state for the current translation unit that we are
/// generating (such as the current section etc).
std::unique_ptr<MCStreamer> OutStreamer;
/// The current machine function.
MachineFunction *MF = nullptr;
/// This is a pointer to the current MachineModuleInfo.
MachineModuleInfo *MMI = nullptr;
/// This is a pointer to the current MachineDominatorTree.
MachineDominatorTree *MDT = nullptr;
/// This is a pointer to the current MachineLoopInfo.
MachineLoopInfo *MLI = nullptr;
/// Optimization remark emitter.
MachineOptimizationRemarkEmitter *ORE;
/// The symbol for the entry in __patchable_function_entires.
MCSymbol *CurrentPatchableFunctionEntrySym = nullptr;
/// The symbol for the current function. This is recalculated at the beginning
/// of each call to runOnMachineFunction().
MCSymbol *CurrentFnSym = nullptr;
/// The symbol for the current function descriptor on AIX. This is created
/// at the beginning of each call to SetupMachineFunction().
MCSymbol *CurrentFnDescSym = nullptr;
/// The symbol used to represent the start of the current function for the
/// purpose of calculating its size (e.g. using the .size directive). By
/// default, this is equal to CurrentFnSym.
MCSymbol *CurrentFnSymForSize = nullptr;
/// Map a basic block section ID to the begin and end symbols of that section
/// which determine the section's range.
struct MBBSectionRange {
MCSymbol *BeginLabel, *EndLabel;
};
MapVector<unsigned, MBBSectionRange> MBBSectionRanges;
/// Map global GOT equivalent MCSymbols to GlobalVariables and keep track of
/// its number of uses by other globals.
using GOTEquivUsePair = std::pair<const GlobalVariable *, unsigned>;
MapVector<const MCSymbol *, GOTEquivUsePair> GlobalGOTEquivs;
/// struct HandlerInfo and Handlers permit users or target extended
/// AsmPrinter to add their own handlers.
struct HandlerInfo {
std::unique_ptr<AsmPrinterHandler> Handler;
StringRef TimerName;
StringRef TimerDescription;
StringRef TimerGroupName;
StringRef TimerGroupDescription;
HandlerInfo(std::unique_ptr<AsmPrinterHandler> Handler, StringRef TimerName,
StringRef TimerDescription, StringRef TimerGroupName,
StringRef TimerGroupDescription)
: Handler(std::move(Handler)), TimerName(TimerName),
TimerDescription(TimerDescription), TimerGroupName(TimerGroupName),
TimerGroupDescription(TimerGroupDescription) {}
};
// Flags representing which CFI section is required for a function/module.
enum class CFISection : unsigned {
None = 0, ///< Do not emit either .eh_frame or .debug_frame
EH = 1, ///< Emit .eh_frame
Debug = 2 ///< Emit .debug_frame
};
private:
MCSymbol *CurrentFnEnd = nullptr;
/// Map a basic block section ID to the exception symbol associated with that
/// section. Map entries are assigned and looked up via
/// AsmPrinter::getMBBExceptionSym.
DenseMap<unsigned, MCSymbol *> MBBSectionExceptionSyms;
// The symbol used to represent the start of the current BB section of the
// function. This is used to calculate the size of the BB section.
MCSymbol *CurrentSectionBeginSym = nullptr;
// The garbage collection metadata printer table.
void *GCMetadataPrinters = nullptr; // Really a DenseMap.
/// Emit comments in assembly output if this is true.
bool VerboseAsm;
/// Output stream for the stack usage file (i.e., .su file).
std::unique_ptr<raw_fd_ostream> StackUsageStream;
static char ID;
protected:
MCSymbol *CurrentFnBegin = nullptr;
/// A vector of all debug/EH info emitters we should use. This vector
/// maintains ownership of the emitters.
std::vector<HandlerInfo> Handlers;
size_t NumUserHandlers = 0;
private:
/// If generated on the fly this own the instance.
std::unique_ptr<MachineDominatorTree> OwnedMDT;
/// If generated on the fly this own the instance.
std::unique_ptr<MachineLoopInfo> OwnedMLI;
/// If the target supports dwarf debug info, this pointer is non-null.
DwarfDebug *DD = nullptr;
/// A handler that supports pseudo probe emission with embedded inline
/// context.
PseudoProbeHandler *PP = nullptr;
/// CFISection type the module needs i.e. either .eh_frame or .debug_frame.
CFISection ModuleCFISection = CFISection::None;
protected:
explicit AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer);
public:
~AsmPrinter() override;
DwarfDebug *getDwarfDebug() { return DD; }
DwarfDebug *getDwarfDebug() const { return DD; }
uint16_t getDwarfVersion() const;
void setDwarfVersion(uint16_t Version);
bool isDwarf64() const;
/// Returns 4 for DWARF32 and 8 for DWARF64.
unsigned int getDwarfOffsetByteSize() const;
/// Returns 4 for DWARF32 and 12 for DWARF64.
unsigned int getUnitLengthFieldByteSize() const;
/// Returns information about the byte size of DW_FORM values.
dwarf::FormParams getDwarfFormParams() const;
bool isPositionIndependent() const;
/// Return true if assembly output should contain comments.
bool isVerbose() const { return VerboseAsm; }
/// Return a unique ID for the current function.
unsigned getFunctionNumber() const;
/// Return symbol for the function pseudo stack if the stack frame is not a
/// register based.
virtual const MCSymbol *getFunctionFrameSymbol() const { return nullptr; }
MCSymbol *getFunctionBegin() const { return CurrentFnBegin; }
MCSymbol *getFunctionEnd() const { return CurrentFnEnd; }
// Return the exception symbol associated with the MBB section containing a
// given basic block.
MCSymbol *getMBBExceptionSym(const MachineBasicBlock &MBB);
/// Return information about object file lowering.
const TargetLoweringObjectFile &getObjFileLowering() const;
/// Return information about data layout.
const DataLayout &getDataLayout() const;
/// Return the pointer size from the TargetMachine
unsigned getPointerSize() const;
/// Return information about subtarget.
const MCSubtargetInfo &getSubtargetInfo() const;
void EmitToStreamer(MCStreamer &S, const MCInst &Inst);
/// Emits initial debug location directive.
void emitInitialRawDwarfLocDirective(const MachineFunction &MF);
/// Return the current section we are emitting to.
const MCSection *getCurrentSection() const;
void getNameWithPrefix(SmallVectorImpl<char> &Name,
const GlobalValue *GV) const;
MCSymbol *getSymbol(const GlobalValue *GV) const;
/// Similar to getSymbol() but preferred for references. On ELF, this uses a
/// local symbol if a reference to GV is guaranteed to be resolved to the
/// definition in the same module.
MCSymbol *getSymbolPreferLocal(const GlobalValue &GV) const;
//===------------------------------------------------------------------===//
// XRay instrumentation implementation.
//===------------------------------------------------------------------===//
public:
// This describes the kind of sled we're storing in the XRay table.
enum class SledKind : uint8_t {
FUNCTION_ENTER = 0,
FUNCTION_EXIT = 1,
TAIL_CALL = 2,
LOG_ARGS_ENTER = 3,
CUSTOM_EVENT = 4,
TYPED_EVENT = 5,
};
// The table will contain these structs that point to the sled, the function
// containing the sled, and what kind of sled (and whether they should always
// be instrumented). We also use a version identifier that the runtime can use
// to decide what to do with the sled, depending on the version of the sled.
struct XRayFunctionEntry {
const MCSymbol *Sled;
const MCSymbol *Function;
SledKind Kind;
bool AlwaysInstrument;
const class Function *Fn;
uint8_t Version;
void emit(int, MCStreamer *) const;
};
// All the sleds to be emitted.
SmallVector<XRayFunctionEntry, 4> Sleds;
// Helper function to record a given XRay sled.
void recordSled(MCSymbol *Sled, const MachineInstr &MI, SledKind Kind,
uint8_t Version = 0);
/// Emit a table with all XRay instrumentation points.
void emitXRayTable();
void emitPatchableFunctionEntries();
//===------------------------------------------------------------------===//
// MachineFunctionPass Implementation.
//===------------------------------------------------------------------===//
/// Record analysis usage.
void getAnalysisUsage(AnalysisUsage &AU) const override;
/// Set up the AsmPrinter when we are working on a new module. If your pass
/// overrides this, it must make sure to explicitly call this implementation.
bool doInitialization(Module &M) override;
/// Shut down the asmprinter. If you override this in your pass, you must make
/// sure to call it explicitly.
bool doFinalization(Module &M) override;
/// Emit the specified function out to the OutStreamer.
bool runOnMachineFunction(MachineFunction &MF) override {
SetupMachineFunction(MF);
emitFunctionBody();
return false;
}
//===------------------------------------------------------------------===//
// Coarse grained IR lowering routines.
//===------------------------------------------------------------------===//
/// This should be called when a new MachineFunction is being processed from
/// runOnMachineFunction.
virtual void SetupMachineFunction(MachineFunction &MF);
/// This method emits the body and trailer for a function.
void emitFunctionBody();
void emitCFIInstruction(const MachineInstr &MI);
void emitFrameAlloc(const MachineInstr &MI);
void emitStackSizeSection(const MachineFunction &MF);
void emitStackUsage(const MachineFunction &MF);
void emitBBAddrMapSection(const MachineFunction &MF);
void emitPseudoProbe(const MachineInstr &MI);
void emitRemarksSection(remarks::RemarkStreamer &RS);
/// Get the CFISection type for a function.
CFISection getFunctionCFISectionType(const Function &F) const;
/// Get the CFISection type for a function.
CFISection getFunctionCFISectionType(const MachineFunction &MF) const;
/// Get the CFISection type for the module.
CFISection getModuleCFISectionType() const { return ModuleCFISection; }
bool needsSEHMoves();
/// Since emitting CFI unwind information is entangled with supporting the
/// exceptions, this returns true for platforms which use CFI unwind
/// information for debugging purpose when
/// `MCAsmInfo::ExceptionsType == ExceptionHandling::None`.
bool needsCFIForDebug() const;
/// Print to the current output stream assembly representations of the
/// constants in the constant pool MCP. This is used to print out constants
/// which have been "spilled to memory" by the code generator.
virtual void emitConstantPool();
/// Print assembly representations of the jump tables used by the current
/// function to the current output stream.
virtual void emitJumpTableInfo();
/// Emit the specified global variable to the .s file.
virtual void emitGlobalVariable(const GlobalVariable *GV);
/// Check to see if the specified global is a special global used by LLVM. If
/// so, emit it and return true, otherwise do nothing and return false.
bool emitSpecialLLVMGlobal(const GlobalVariable *GV);
/// `llvm.global_ctors` and `llvm.global_dtors` are arrays of Structor
/// structs.
///
/// Priority - init priority
/// Func - global initialization or global clean-up function
/// ComdatKey - associated data
struct Structor {
int Priority = 0;
Constant *Func = nullptr;
GlobalValue *ComdatKey = nullptr;
Structor() = default;
};
/// This method gathers an array of Structors and then sorts them out by
/// Priority.
/// @param List The initializer of `llvm.global_ctors` or `llvm.global_dtors`
/// array.
/// @param[out] Structors Sorted Structor structs by Priority.
void preprocessXXStructorList(const DataLayout &DL, const Constant *List,
SmallVector<Structor, 8> &Structors);
/// This method emits `llvm.global_ctors` or `llvm.global_dtors` list.
virtual void emitXXStructorList(const DataLayout &DL, const Constant *List,
bool IsCtor);
/// Emit an alignment directive to the specified power of two boundary. If a
/// global value is specified, and if that global has an explicit alignment
/// requested, it will override the alignment request if required for
/// correctness.
void emitAlignment(Align Alignment, const GlobalObject *GV = nullptr,
unsigned MaxBytesToEmit = 0) const;
/// Lower the specified LLVM Constant to an MCExpr.
virtual const MCExpr *lowerConstant(const Constant *CV);
/// Print a general LLVM constant to the .s file.
void emitGlobalConstant(const DataLayout &DL, const Constant *CV);
/// Unnamed constant global variables solely containing a pointer to
/// another globals variable act like a global variable "proxy", or GOT
/// equivalents, i.e., it's only used to hold the address of the latter. One
/// optimization is to replace accesses to these proxies by using the GOT
/// entry for the final global instead. Hence, we select GOT equivalent
/// candidates among all the module global variables, avoid emitting them
/// unnecessarily and finally replace references to them by pc relative
/// accesses to GOT entries.
void computeGlobalGOTEquivs(Module &M);
/// Constant expressions using GOT equivalent globals may not be
/// eligible for PC relative GOT entry conversion, in such cases we need to
/// emit the proxies we previously omitted in EmitGlobalVariable.
void emitGlobalGOTEquivs();
/// Emit the stack maps.
void emitStackMaps(StackMaps &SM);
//===------------------------------------------------------------------===//
// Overridable Hooks
//===------------------------------------------------------------------===//
void addAsmPrinterHandler(HandlerInfo Handler) {
Handlers.insert(Handlers.begin(), std::move(Handler));
NumUserHandlers++;
}
// Targets can, or in the case of EmitInstruction, must implement these to
// customize output.
/// This virtual method can be overridden by targets that want to emit
/// something at the start of their file.
virtual void emitStartOfAsmFile(Module &) {}
/// This virtual method can be overridden by targets that want to emit
/// something at the end of their file.
virtual void emitEndOfAsmFile(Module &) {}
/// Targets can override this to emit stuff before the first basic block in
/// the function.
virtual void emitFunctionBodyStart() {}
/// Targets can override this to emit stuff after the last basic block in the
/// function.
virtual void emitFunctionBodyEnd() {}
/// Targets can override this to emit stuff at the start of a basic block.
/// By default, this method prints the label for the specified
/// MachineBasicBlock, an alignment (if present) and a comment describing it
/// if appropriate.
virtual void emitBasicBlockStart(const MachineBasicBlock &MBB);
/// Targets can override this to emit stuff at the end of a basic block.
virtual void emitBasicBlockEnd(const MachineBasicBlock &MBB);
/// Targets should implement this to emit instructions.
virtual void emitInstruction(const MachineInstr *) {
llvm_unreachable("EmitInstruction not implemented");
}
/// Return the symbol for the specified constant pool entry.
virtual MCSymbol *GetCPISymbol(unsigned CPID) const;
virtual void emitFunctionEntryLabel();
virtual void emitFunctionDescriptor() {
llvm_unreachable("Function descriptor is target-specific.");
}
virtual void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV);
/// Targets can override this to change how global constants that are part of
/// a C++ static/global constructor list are emitted.
virtual void emitXXStructor(const DataLayout &DL, const Constant *CV) {
emitGlobalConstant(DL, CV);
}
/// Return true if the basic block has exactly one predecessor and the control
/// transfer mechanism between the predecessor and this block is a
/// fall-through.
virtual bool
isBlockOnlyReachableByFallthrough(const MachineBasicBlock *MBB) const;
/// Targets can override this to customize the output of IMPLICIT_DEF
/// instructions in verbose mode.
virtual void emitImplicitDef(const MachineInstr *MI) const;
/// Emit N NOP instructions.
void emitNops(unsigned N);
//===------------------------------------------------------------------===//
// Symbol Lowering Routines.
//===------------------------------------------------------------------===//
MCSymbol *createTempSymbol(const Twine &Name) const;
/// Return the MCSymbol for a private symbol with global value name as its
/// base, with the specified suffix.
MCSymbol *getSymbolWithGlobalValueBase(const GlobalValue *GV,
StringRef Suffix) const;
/// Return the MCSymbol for the specified ExternalSymbol.
MCSymbol *GetExternalSymbolSymbol(StringRef Sym) const;
/// Return the symbol for the specified jump table entry.
MCSymbol *GetJTISymbol(unsigned JTID, bool isLinkerPrivate = false) const;
/// Return the symbol for the specified jump table .set
/// FIXME: privatize to AsmPrinter.
MCSymbol *GetJTSetSymbol(unsigned UID, unsigned MBBID) const;
/// Return the MCSymbol used to satisfy BlockAddress uses of the specified
/// basic block.
MCSymbol *GetBlockAddressSymbol(const BlockAddress *BA) const;
MCSymbol *GetBlockAddressSymbol(const BasicBlock *BB) const;
//===------------------------------------------------------------------===//
// Emission Helper Routines.
//===------------------------------------------------------------------===//
/// This is just convenient handler for printing offsets.
void printOffset(int64_t Offset, raw_ostream &OS) const;
/// Emit a byte directive and value.
void emitInt8(int Value) const;
/// Emit a short directive and value.
void emitInt16(int Value) const;
/// Emit a long directive and value.
void emitInt32(int Value) const;
/// Emit a long long directive and value.
void emitInt64(uint64_t Value) const;
/// Emit something like ".long Hi-Lo" where the size in bytes of the directive
/// is specified by Size and Hi/Lo specify the labels. This implicitly uses
/// .set if it is available.
void emitLabelDifference(const MCSymbol *Hi, const MCSymbol *Lo,
unsigned Size) const;
/// Emit something like ".uleb128 Hi-Lo".
void emitLabelDifferenceAsULEB128(const MCSymbol *Hi,
const MCSymbol *Lo) const;
/// Emit something like ".long Label+Offset" where the size in bytes of the
/// directive is specified by Size and Label specifies the label. This
/// implicitly uses .set if it is available.
void emitLabelPlusOffset(const MCSymbol *Label, uint64_t Offset,
unsigned Size, bool IsSectionRelative = false) const;
/// Emit something like ".long Label" where the size in bytes of the directive
/// is specified by Size and Label specifies the label.
void emitLabelReference(const MCSymbol *Label, unsigned Size,
bool IsSectionRelative = false) const {
emitLabelPlusOffset(Label, 0, Size, IsSectionRelative);
}
//===------------------------------------------------------------------===//
// Dwarf Emission Helper Routines
//===------------------------------------------------------------------===//
/// Emit the specified signed leb128 value.
void emitSLEB128(int64_t Value, const char *Desc = nullptr) const;
/// Emit the specified unsigned leb128 value.
void emitULEB128(uint64_t Value, const char *Desc = nullptr,
unsigned PadTo = 0) const;
/// Emit a .byte 42 directive that corresponds to an encoding. If verbose
/// assembly output is enabled, we output comments describing the encoding.
/// Desc is a string saying what the encoding is specifying (e.g. "LSDA").
void emitEncodingByte(unsigned Val, const char *Desc = nullptr) const;
/// Return the size of the encoding in bytes.
unsigned GetSizeOfEncodedValue(unsigned Encoding) const;
/// Emit reference to a ttype global with a specified encoding.
virtual void emitTTypeReference(const GlobalValue *GV, unsigned Encoding);
/// Emit a reference to a symbol for use in dwarf. Different object formats
/// represent this in different ways. Some use a relocation others encode
/// the label offset in its section.
void emitDwarfSymbolReference(const MCSymbol *Label,
bool ForceOffset = false) const;
/// Emit the 4- or 8-byte offset of a string from the start of its section.
///
/// When possible, emit a DwarfStringPool section offset without any
/// relocations, and without using the symbol. Otherwise, defers to \a
/// emitDwarfSymbolReference().
///
/// The length of the emitted value depends on the DWARF format.
void emitDwarfStringOffset(DwarfStringPoolEntry S) const;
/// Emit the 4-or 8-byte offset of a string from the start of its section.
void emitDwarfStringOffset(DwarfStringPoolEntryRef S) const {
emitDwarfStringOffset(S.getEntry());
}
/// Emit something like ".long Label + Offset" or ".quad Label + Offset"
/// depending on the DWARF format.
void emitDwarfOffset(const MCSymbol *Label, uint64_t Offset) const;
/// Emit 32- or 64-bit value depending on the DWARF format.
void emitDwarfLengthOrOffset(uint64_t Value) const;
/// Emit a unit length field. The actual format, DWARF32 or DWARF64, is chosen
/// according to the settings.
void emitDwarfUnitLength(uint64_t Length, const Twine &Comment) const;
/// Emit a unit length field. The actual format, DWARF32 or DWARF64, is chosen
/// according to the settings.
/// Return the end symbol generated inside, the caller needs to emit it.
MCSymbol *emitDwarfUnitLength(const Twine &Prefix,
const Twine &Comment) const;
/// Emit reference to a call site with a specified encoding
void emitCallSiteOffset(const MCSymbol *Hi, const MCSymbol *Lo,
unsigned Encoding) const;
/// Emit an integer value corresponding to the call site encoding
void emitCallSiteValue(uint64_t Value, unsigned Encoding) const;
/// Get the value for DW_AT_APPLE_isa. Zero if no isa encoding specified.
virtual unsigned getISAEncoding() { return 0; }
/// Emit the directive and value for debug thread local expression
///
/// \p Value - The value to emit.
/// \p Size - The size of the integer (in bytes) to emit.
virtual void emitDebugValue(const MCExpr *Value, unsigned Size) const;
//===------------------------------------------------------------------===//
// Dwarf Lowering Routines
//===------------------------------------------------------------------===//
/// Emit frame instruction to describe the layout of the frame.
void emitCFIInstruction(const MCCFIInstruction &Inst) const;
/// Emit Dwarf abbreviation table.
template <typename T> void emitDwarfAbbrevs(const T &Abbrevs) const {
// For each abbreviation.
for (const auto &Abbrev : Abbrevs)
emitDwarfAbbrev(*Abbrev);
// Mark end of abbreviations.
emitULEB128(0, "EOM(3)");
}
void emitDwarfAbbrev(const DIEAbbrev &Abbrev) const;
/// Recursively emit Dwarf DIE tree.
void emitDwarfDIE(const DIE &Die) const;
//===------------------------------------------------------------------===//
// Inline Asm Support
//===------------------------------------------------------------------===//
// These are hooks that targets can override to implement inline asm
// support. These should probably be moved out of AsmPrinter someday.
/// Print information related to the specified machine instr that is
/// independent of the operand, and may be independent of the instr itself.
/// This can be useful for portably encoding the comment character or other
/// bits of target-specific knowledge into the asmstrings. The syntax used is
/// ${:comment}. Targets can override this to add support for their own
/// strange codes.
virtual void PrintSpecial(const MachineInstr *MI, raw_ostream &OS,
StringRef Code) const;
/// Print the MachineOperand as a symbol. Targets with complex handling of
/// symbol references should override the base implementation.
virtual void PrintSymbolOperand(const MachineOperand &MO, raw_ostream &OS);
/// Print the specified operand of MI, an INLINEASM instruction, using the
/// specified assembler variant. Targets should override this to format as
/// appropriate. This method can return true if the operand is erroneous.
virtual bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &OS);
/// Print the specified operand of MI, an INLINEASM instruction, using the
/// specified assembler variant as an address. Targets should override this to
/// format as appropriate. This method can return true if the operand is
/// erroneous.
virtual bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &OS);
/// Let the target do anything it needs to do before emitting inlineasm.
/// \p StartInfo - the subtarget info before parsing inline asm
virtual void emitInlineAsmStart() const;
/// Let the target do anything it needs to do after emitting inlineasm.
/// This callback can be used restore the original mode in case the
/// inlineasm contains directives to switch modes.
/// \p StartInfo - the original subtarget info before inline asm
/// \p EndInfo - the final subtarget info after parsing the inline asm,
/// or NULL if the value is unknown.
virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
const MCSubtargetInfo *EndInfo) const;
/// This emits visibility information about symbol, if this is supported by
/// the target.
void emitVisibility(MCSymbol *Sym, unsigned Visibility,
bool IsDefinition = true) const;
/// This emits linkage information about \p GVSym based on \p GV, if this is
/// supported by the target.
virtual void emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const;
/// Return the alignment for the specified \p GV.
static Align getGVAlignment(const GlobalObject *GV, const DataLayout &DL,
Align InAlign = Align(1));
private:
/// Private state for PrintSpecial()
// Assign a unique ID to this machine instruction.
mutable const MachineInstr *LastMI = nullptr;
mutable unsigned LastFn = 0;
mutable unsigned Counter = ~0U;
/// This method emits the header for the current function.
virtual void emitFunctionHeader();
/// This method emits a comment next to header for the current function.
virtual void emitFunctionHeaderComment();
/// Emit a blob of inline asm to the output streamer.
void
emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
const MCTargetOptions &MCOptions,
const MDNode *LocMDNode = nullptr,
InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const;
/// This method formats and emits the specified machine instruction that is an
/// inline asm.
void emitInlineAsm(const MachineInstr *MI) const;
/// Add inline assembly info to the diagnostics machinery, so we can
/// emit file and position info. Returns SrcMgr memory buffer position.
unsigned addInlineAsmDiagBuffer(StringRef AsmStr,
const MDNode *LocMDNode) const;
//===------------------------------------------------------------------===//
// Internal Implementation Details
//===------------------------------------------------------------------===//
void emitJumpTableEntry(const MachineJumpTableInfo *MJTI,
const MachineBasicBlock *MBB, unsigned uid) const;
void emitLLVMUsedList(const ConstantArray *InitList);
/// Emit llvm.ident metadata in an '.ident' directive.
void emitModuleIdents(Module &M);
/// Emit bytes for llvm.commandline metadata.
void emitModuleCommandLines(Module &M);
GCMetadataPrinter *GetOrCreateGCPrinter(GCStrategy &S);
void emitGlobalAlias(Module &M, const GlobalAlias &GA);
void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
/// This method decides whether the specified basic block requires a label.
bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const;
protected:
virtual bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const {
return false;
}
};
} // end namespace llvm
#endif // LLVM_CODEGEN_ASMPRINTER_H

View File

@@ -0,0 +1,84 @@
//===-- llvm/CodeGen/AsmPrinterHandler.h -----------------------*- C++ -*--===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains a generic interface for AsmPrinter handlers,
// like debug and EH info emitters.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_ASMPRINTERHANDLER_H
#define LLVM_CODEGEN_ASMPRINTERHANDLER_H
#include "llvm/Support/DataTypes.h"
namespace llvm {
class AsmPrinter;
class MachineBasicBlock;
class MachineFunction;
class MachineInstr;
class MCSymbol;
class Module;
typedef MCSymbol *ExceptionSymbolProvider(AsmPrinter *Asm,
const MachineBasicBlock *MBB);
/// Collects and handles AsmPrinter objects required to build debug
/// or EH information.
class AsmPrinterHandler {
public:
virtual ~AsmPrinterHandler();
/// For symbols that have a size designated (e.g. common symbols),
/// this tracks that size.
virtual void setSymbolSize(const MCSymbol *Sym, uint64_t Size) = 0;
virtual void beginModule(Module *M) {}
/// Emit all sections that should come after the content.
virtual void endModule() = 0;
/// Gather pre-function debug information.
/// Every beginFunction(MF) call should be followed by an endFunction(MF)
/// call.
virtual void beginFunction(const MachineFunction *MF) = 0;
// Emit any of function marker (like .cfi_endproc). This is called
// before endFunction and cannot switch sections.
virtual void markFunctionEnd();
/// Gather post-function debug information.
/// Please note that some AsmPrinter implementations may not call
/// beginFunction at all.
virtual void endFunction(const MachineFunction *MF) = 0;
virtual void beginFragment(const MachineBasicBlock *MBB,
ExceptionSymbolProvider ESP) {}
virtual void endFragment() {}
/// Emit target-specific EH funclet machinery.
virtual void beginFunclet(const MachineBasicBlock &MBB,
MCSymbol *Sym = nullptr) {}
virtual void endFunclet() {}
/// Process beginning of an instruction.
virtual void beginInstruction(const MachineInstr *MI) = 0;
/// Process end of an instruction.
virtual void endInstruction() = 0;
/// Process beginning of a basic block during basic block sections.
virtual void beginBasicBlock(const MachineBasicBlock &MBB) {}
/// Process end of a basic block during basic block sections.
virtual void endBasicBlock(const MachineBasicBlock &MBB) {}
};
} // End of namespace llvm
#endif

View File

@@ -0,0 +1,64 @@
//===- AtomicExpandUtils.h - Utilities for expanding atomic instructions --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_ATOMICEXPANDUTILS_H
#define LLVM_CODEGEN_ATOMICEXPANDUTILS_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Support/AtomicOrdering.h"
namespace llvm {
class AtomicRMWInst;
class Value;
/// Parameters (see the expansion example below):
/// (the builder, %addr, %loaded, %new_val, ordering,
/// /* OUT */ %success, /* OUT */ %new_loaded)
using CreateCmpXchgInstFun =
function_ref<void(IRBuilder<> &, Value *, Value *, Value *, Align,
AtomicOrdering, SyncScope::ID, Value *&, Value *&)>;
/// Expand an atomic RMW instruction into a loop utilizing
/// cmpxchg. You'll want to make sure your target machine likes cmpxchg
/// instructions in the first place and that there isn't another, better,
/// transformation available (for example AArch32/AArch64 have linked loads).
///
/// This is useful in passes which can't rewrite the more exotic RMW
/// instructions directly into a platform specific intrinsics (because, say,
/// those intrinsics don't exist). If such a pass is able to expand cmpxchg
/// instructions directly however, then, with this function, it could avoid two
/// extra module passes (avoiding passes by `-atomic-expand` and itself). A
/// specific example would be PNaCl's `RewriteAtomics` pass.
///
/// Given: atomicrmw some_op iN* %addr, iN %incr ordering
///
/// The standard expansion we produce is:
/// [...]
/// %init_loaded = load atomic iN* %addr
/// br label %loop
/// loop:
/// %loaded = phi iN [ %init_loaded, %entry ], [ %new_loaded, %loop ]
/// %new = some_op iN %loaded, %incr
/// ; This is what -atomic-expand will produce using this function on i686
/// targets:
/// %pair = cmpxchg iN* %addr, iN %loaded, iN %new_val
/// %new_loaded = extractvalue { iN, i1 } %pair, 0
/// %success = extractvalue { iN, i1 } %pair, 1
/// ; End callback produced IR
/// br i1 %success, label %atomicrmw.end, label %loop
/// atomicrmw.end:
/// [...]
///
/// Returns true if the containing function was modified.
bool expandAtomicRMWToCmpXchg(AtomicRMWInst *AI, CreateCmpXchgInstFun CreateCmpXchg);
} // end namespace llvm
#endif // LLVM_CODEGEN_ATOMICEXPANDUTILS_H

View File

@@ -0,0 +1,30 @@
//===- BasicBlockSectionUtils.h - Utilities for basic block sections --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_BASICBLOCKSECTIONUTILS_H
#define LLVM_CODEGEN_BASICBLOCKSECTIONUTILS_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
namespace llvm {
extern cl::opt<std::string> BBSectionsColdTextPrefix;
class MachineFunction;
class MachineBasicBlock;
using MachineBasicBlockComparator =
function_ref<bool(const MachineBasicBlock &, const MachineBasicBlock &)>;
void sortBasicBlocksAndUpdateBranches(MachineFunction &MF,
MachineBasicBlockComparator MBBCmp);
} // end namespace llvm
#endif // LLVM_CODEGEN_BASICBLOCKSECTIONUTILS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
//===- CSEConfigBase.h - A CSEConfig interface ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_CSECONFIGBASE_H
#define LLVM_CODEGEN_CSECONFIGBASE_H
namespace llvm {
// Class representing some configuration that can be done during GlobalISel's
// CSEInfo analysis. We define it here because TargetPassConfig can't depend on
// the GlobalISel library, and so we use this in the interface between them
// so that the derived classes in GISel can reference generic opcodes.
class CSEConfigBase {
public:
virtual ~CSEConfigBase() = default;
// Hook for defining which Generic instructions should be CSEd.
// GISelCSEInfo currently only calls this hook when dealing with generic
// opcodes.
virtual bool shouldCSEOpc(unsigned Opc) { return false; }
};
} // namespace llvm
#endif // LLVM_CODEGEN_CSECONFIGBASE_H

View File

@@ -0,0 +1,119 @@
//===- lib/CodeGen/CalcSpillWeights.h ---------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_CALCSPILLWEIGHTS_H
#define LLVM_CODEGEN_CALCSPILLWEIGHTS_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/CodeGen/SlotIndexes.h"
namespace llvm {
class LiveInterval;
class LiveIntervals;
class MachineBlockFrequencyInfo;
class MachineFunction;
class MachineLoopInfo;
class VirtRegMap;
/// Normalize the spill weight of a live interval
///
/// The spill weight of a live interval is computed as:
///
/// (sum(use freq) + sum(def freq)) / (K + size)
///
/// @param UseDefFreq Expected number of executed use and def instructions
/// per function call. Derived from block frequencies.
/// @param Size Size of live interval as returnexd by getSize()
/// @param NumInstr Number of instructions using this live interval
static inline float normalizeSpillWeight(float UseDefFreq, unsigned Size,
unsigned NumInstr) {
// The constant 25 instructions is added to avoid depending too much on
// accidental SlotIndex gaps for small intervals. The effect is that small
// intervals have a spill weight that is mostly proportional to the number
// of uses, while large intervals get a spill weight that is closer to a use
// density.
return UseDefFreq / (Size + 25*SlotIndex::InstrDist);
}
/// Calculate auxiliary information for a virtual register such as its
/// spill weight and allocation hint.
class VirtRegAuxInfo {
MachineFunction &MF;
LiveIntervals &LIS;
const VirtRegMap &VRM;
const MachineLoopInfo &Loops;
const MachineBlockFrequencyInfo &MBFI;
/// Returns true if Reg of live interval LI is used in instruction with many
/// operands like STATEPOINT.
bool isLiveAtStatepointVarArg(LiveInterval &LI);
public:
VirtRegAuxInfo(MachineFunction &MF, LiveIntervals &LIS,
const VirtRegMap &VRM, const MachineLoopInfo &Loops,
const MachineBlockFrequencyInfo &MBFI)
: MF(MF), LIS(LIS), VRM(VRM), Loops(Loops), MBFI(MBFI) {}
virtual ~VirtRegAuxInfo() = default;
/// (re)compute li's spill weight and allocation hint.
void calculateSpillWeightAndHint(LiveInterval &LI);
/// Compute future expected spill weight of a split artifact of LI
/// that will span between start and end slot indexes.
/// \param LI The live interval to be split.
/// \param Start The expected beginning of the split artifact. Instructions
/// before start will not affect the weight.
/// \param End The expected end of the split artifact. Instructions
/// after end will not affect the weight.
/// \return The expected spill weight of the split artifact. Returns
/// negative weight for unspillable LI.
float futureWeight(LiveInterval &LI, SlotIndex Start, SlotIndex End);
/// Compute spill weights and allocation hints for all virtual register
/// live intervals.
void calculateSpillWeightsAndHints();
/// Return the preferred allocation register for reg, given a COPY
/// instruction.
static Register copyHint(const MachineInstr *MI, unsigned Reg,
const TargetRegisterInfo &TRI,
const MachineRegisterInfo &MRI);
/// Determine if all values in LI are rematerializable.
static bool isRematerializable(const LiveInterval &LI,
const LiveIntervals &LIS,
const VirtRegMap &VRM,
const TargetInstrInfo &TII);
protected:
/// Helper function for weight calculations.
/// (Re)compute LI's spill weight and allocation hint, or, for non null
/// start and end - compute future expected spill weight of a split
/// artifact of LI that will span between start and end slot indexes.
/// \param LI The live interval for which to compute the weight.
/// \param Start The expected beginning of the split artifact. Instructions
/// before start will not affect the weight. Relevant for
/// weight calculation of future split artifact.
/// \param End The expected end of the split artifact. Instructions
/// after end will not affect the weight. Relevant for
/// weight calculation of future split artifact.
/// \return The spill weight. Returns negative weight for unspillable LI.
float weightCalcHelper(LiveInterval &LI, SlotIndex *Start = nullptr,
SlotIndex *End = nullptr);
/// Weight normalization function.
virtual float normalize(float UseDefFreq, unsigned Size,
unsigned NumInstr) {
return normalizeSpillWeight(UseDefFreq, Size, NumInstr);
}
};
} // end namespace llvm
#endif // LLVM_CODEGEN_CALCSPILLWEIGHTS_H

View File

@@ -0,0 +1,568 @@
//===- llvm/CallingConvLower.h - Calling Conventions ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares the CCState and CCValAssign classes, used for lowering
// and implementing calling conventions.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_CALLINGCONVLOWER_H
#define LLVM_CODEGEN_CALLINGCONVLOWER_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/CodeGen/TargetCallingConv.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/Alignment.h"
namespace llvm {
class CCState;
class MachineFunction;
class MVT;
class TargetRegisterInfo;
/// CCValAssign - Represent assignment of one arg/retval to a location.
class CCValAssign {
public:
enum LocInfo {
Full, // The value fills the full location.
SExt, // The value is sign extended in the location.
ZExt, // The value is zero extended in the location.
AExt, // The value is extended with undefined upper bits.
SExtUpper, // The value is in the upper bits of the location and should be
// sign extended when retrieved.
ZExtUpper, // The value is in the upper bits of the location and should be
// zero extended when retrieved.
AExtUpper, // The value is in the upper bits of the location and should be
// extended with undefined upper bits when retrieved.
BCvt, // The value is bit-converted in the location.
Trunc, // The value is truncated in the location.
VExt, // The value is vector-widened in the location.
// FIXME: Not implemented yet. Code that uses AExt to mean
// vector-widen should be fixed to use VExt instead.
FPExt, // The floating-point value is fp-extended in the location.
Indirect // The location contains pointer to the value.
// TODO: a subset of the value is in the location.
};
private:
/// ValNo - This is the value number being assigned (e.g. an argument number).
unsigned ValNo;
/// Loc is either a stack offset or a register number.
unsigned Loc;
/// isMem - True if this is a memory loc, false if it is a register loc.
unsigned isMem : 1;
/// isCustom - True if this arg/retval requires special handling.
unsigned isCustom : 1;
/// Information about how the value is assigned.
LocInfo HTP : 6;
/// ValVT - The type of the value being assigned.
MVT ValVT;
/// LocVT - The type of the location being assigned to.
MVT LocVT;
public:
static CCValAssign getReg(unsigned ValNo, MVT ValVT,
unsigned RegNo, MVT LocVT,
LocInfo HTP) {
CCValAssign Ret;
Ret.ValNo = ValNo;
Ret.Loc = RegNo;
Ret.isMem = false;
Ret.isCustom = false;
Ret.HTP = HTP;
Ret.ValVT = ValVT;
Ret.LocVT = LocVT;
return Ret;
}
static CCValAssign getCustomReg(unsigned ValNo, MVT ValVT,
unsigned RegNo, MVT LocVT,
LocInfo HTP) {
CCValAssign Ret;
Ret = getReg(ValNo, ValVT, RegNo, LocVT, HTP);
Ret.isCustom = true;
return Ret;
}
static CCValAssign getMem(unsigned ValNo, MVT ValVT,
unsigned Offset, MVT LocVT,
LocInfo HTP) {
CCValAssign Ret;
Ret.ValNo = ValNo;
Ret.Loc = Offset;
Ret.isMem = true;
Ret.isCustom = false;
Ret.HTP = HTP;
Ret.ValVT = ValVT;
Ret.LocVT = LocVT;
return Ret;
}
static CCValAssign getCustomMem(unsigned ValNo, MVT ValVT,
unsigned Offset, MVT LocVT,
LocInfo HTP) {
CCValAssign Ret;
Ret = getMem(ValNo, ValVT, Offset, LocVT, HTP);
Ret.isCustom = true;
return Ret;
}
// There is no need to differentiate between a pending CCValAssign and other
// kinds, as they are stored in a different list.
static CCValAssign getPending(unsigned ValNo, MVT ValVT, MVT LocVT,
LocInfo HTP, unsigned ExtraInfo = 0) {
return getReg(ValNo, ValVT, ExtraInfo, LocVT, HTP);
}
void convertToReg(unsigned RegNo) {
Loc = RegNo;
isMem = false;
}
void convertToMem(unsigned Offset) {
Loc = Offset;
isMem = true;
}
unsigned getValNo() const { return ValNo; }
MVT getValVT() const { return ValVT; }
bool isRegLoc() const { return !isMem; }
bool isMemLoc() const { return isMem; }
bool needsCustom() const { return isCustom; }
Register getLocReg() const { assert(isRegLoc()); return Loc; }
unsigned getLocMemOffset() const { assert(isMemLoc()); return Loc; }
unsigned getExtraInfo() const { return Loc; }
MVT getLocVT() const { return LocVT; }
LocInfo getLocInfo() const { return HTP; }
bool isExtInLoc() const {
return (HTP == AExt || HTP == SExt || HTP == ZExt);
}
bool isUpperBitsInLoc() const {
return HTP == AExtUpper || HTP == SExtUpper || HTP == ZExtUpper;
}
};
/// Describes a register that needs to be forwarded from the prologue to a
/// musttail call.
struct ForwardedRegister {
ForwardedRegister(Register VReg, MCPhysReg PReg, MVT VT)
: VReg(VReg), PReg(PReg), VT(VT) {}
Register VReg;
MCPhysReg PReg;
MVT VT;
};
/// CCAssignFn - This function assigns a location for Val, updating State to
/// reflect the change. It returns 'true' if it failed to handle Val.
typedef bool CCAssignFn(unsigned ValNo, MVT ValVT,
MVT LocVT, CCValAssign::LocInfo LocInfo,
ISD::ArgFlagsTy ArgFlags, CCState &State);
/// CCCustomFn - This function assigns a location for Val, possibly updating
/// all args to reflect changes and indicates if it handled it. It must set
/// isCustom if it handles the arg and returns true.
typedef bool CCCustomFn(unsigned &ValNo, MVT &ValVT,
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State);
/// CCState - This class holds information needed while lowering arguments and
/// return values. It captures which registers are already assigned and which
/// stack slots are used. It provides accessors to allocate these values.
class CCState {
private:
CallingConv::ID CallingConv;
bool IsVarArg;
bool AnalyzingMustTailForwardedRegs = false;
MachineFunction &MF;
const TargetRegisterInfo &TRI;
SmallVectorImpl<CCValAssign> &Locs;
LLVMContext &Context;
unsigned StackOffset;
Align MaxStackArgAlign;
SmallVector<uint32_t, 16> UsedRegs;
SmallVector<CCValAssign, 4> PendingLocs;
SmallVector<ISD::ArgFlagsTy, 4> PendingArgFlags;
// ByValInfo and SmallVector<ByValInfo, 4> ByValRegs:
//
// Vector of ByValInfo instances (ByValRegs) is introduced for byval registers
// tracking.
// Or, in another words it tracks byval parameters that are stored in
// general purpose registers.
//
// For 4 byte stack alignment,
// instance index means byval parameter number in formal
// arguments set. Assume, we have some "struct_type" with size = 4 bytes,
// then, for function "foo":
//
// i32 foo(i32 %p, %struct_type* %r, i32 %s, %struct_type* %t)
//
// ByValRegs[0] describes how "%r" is stored (Begin == r1, End == r2)
// ByValRegs[1] describes how "%t" is stored (Begin == r3, End == r4).
//
// In case of 8 bytes stack alignment,
// In function shown above, r3 would be wasted according to AAPCS rules.
// ByValRegs vector size still would be 2,
// while "%t" goes to the stack: it wouldn't be described in ByValRegs.
//
// Supposed use-case for this collection:
// 1. Initially ByValRegs is empty, InRegsParamsProcessed is 0.
// 2. HandleByVal fills up ByValRegs.
// 3. Argument analysis (LowerFormatArguments, for example). After
// some byval argument was analyzed, InRegsParamsProcessed is increased.
struct ByValInfo {
ByValInfo(unsigned B, unsigned E) : Begin(B), End(E) {}
// First register allocated for current parameter.
unsigned Begin;
// First after last register allocated for current parameter.
unsigned End;
};
SmallVector<ByValInfo, 4 > ByValRegs;
// InRegsParamsProcessed - shows how many instances of ByValRegs was proceed
// during argument analysis.
unsigned InRegsParamsProcessed;
public:
CCState(CallingConv::ID CC, bool isVarArg, MachineFunction &MF,
SmallVectorImpl<CCValAssign> &locs, LLVMContext &C);
void addLoc(const CCValAssign &V) {
Locs.push_back(V);
}
LLVMContext &getContext() const { return Context; }
MachineFunction &getMachineFunction() const { return MF; }
CallingConv::ID getCallingConv() const { return CallingConv; }
bool isVarArg() const { return IsVarArg; }
/// getNextStackOffset - Return the next stack offset such that all stack
/// slots satisfy their alignment requirements.
unsigned getNextStackOffset() const {
return StackOffset;
}
/// getAlignedCallFrameSize - Return the size of the call frame needed to
/// be able to store all arguments and such that the alignment requirement
/// of each of the arguments is satisfied.
unsigned getAlignedCallFrameSize() const {
return alignTo(StackOffset, MaxStackArgAlign);
}
/// isAllocated - Return true if the specified register (or an alias) is
/// allocated.
bool isAllocated(MCRegister Reg) const {
return UsedRegs[Reg / 32] & (1 << (Reg & 31));
}
/// AnalyzeFormalArguments - Analyze an array of argument values,
/// incorporating info about the formals into this state.
void AnalyzeFormalArguments(const SmallVectorImpl<ISD::InputArg> &Ins,
CCAssignFn Fn);
/// The function will invoke AnalyzeFormalArguments.
void AnalyzeArguments(const SmallVectorImpl<ISD::InputArg> &Ins,
CCAssignFn Fn) {
AnalyzeFormalArguments(Ins, Fn);
}
/// AnalyzeReturn - Analyze the returned values of a return,
/// incorporating info about the result values into this state.
void AnalyzeReturn(const SmallVectorImpl<ISD::OutputArg> &Outs,
CCAssignFn Fn);
/// CheckReturn - Analyze the return values of a function, returning
/// true if the return can be performed without sret-demotion, and
/// false otherwise.
bool CheckReturn(const SmallVectorImpl<ISD::OutputArg> &Outs,
CCAssignFn Fn);
/// AnalyzeCallOperands - Analyze the outgoing arguments to a call,
/// incorporating info about the passed values into this state.
void AnalyzeCallOperands(const SmallVectorImpl<ISD::OutputArg> &Outs,
CCAssignFn Fn);
/// AnalyzeCallOperands - Same as above except it takes vectors of types
/// and argument flags.
void AnalyzeCallOperands(SmallVectorImpl<MVT> &ArgVTs,
SmallVectorImpl<ISD::ArgFlagsTy> &Flags,
CCAssignFn Fn);
/// The function will invoke AnalyzeCallOperands.
void AnalyzeArguments(const SmallVectorImpl<ISD::OutputArg> &Outs,
CCAssignFn Fn) {
AnalyzeCallOperands(Outs, Fn);
}
/// AnalyzeCallResult - Analyze the return values of a call,
/// incorporating info about the passed values into this state.
void AnalyzeCallResult(const SmallVectorImpl<ISD::InputArg> &Ins,
CCAssignFn Fn);
/// A shadow allocated register is a register that was allocated
/// but wasn't added to the location list (Locs).
/// \returns true if the register was allocated as shadow or false otherwise.
bool IsShadowAllocatedReg(MCRegister Reg) const;
/// AnalyzeCallResult - Same as above except it's specialized for calls which
/// produce a single value.
void AnalyzeCallResult(MVT VT, CCAssignFn Fn);
/// getFirstUnallocated - Return the index of the first unallocated register
/// in the set, or Regs.size() if they are all allocated.
unsigned getFirstUnallocated(ArrayRef<MCPhysReg> Regs) const {
for (unsigned i = 0; i < Regs.size(); ++i)
if (!isAllocated(Regs[i]))
return i;
return Regs.size();
}
void DeallocateReg(MCPhysReg Reg) {
assert(isAllocated(Reg) && "Trying to deallocate an unallocated register");
MarkUnallocated(Reg);
}
/// AllocateReg - Attempt to allocate one register. If it is not available,
/// return zero. Otherwise, return the register, marking it and any aliases
/// as allocated.
MCRegister AllocateReg(MCPhysReg Reg) {
if (isAllocated(Reg))
return MCRegister();
MarkAllocated(Reg);
return Reg;
}
/// Version of AllocateReg with extra register to be shadowed.
MCRegister AllocateReg(MCPhysReg Reg, MCPhysReg ShadowReg) {
if (isAllocated(Reg))
return MCRegister();
MarkAllocated(Reg);
MarkAllocated(ShadowReg);
return Reg;
}
/// AllocateReg - Attempt to allocate one of the specified registers. If none
/// are available, return zero. Otherwise, return the first one available,
/// marking it and any aliases as allocated.
MCPhysReg AllocateReg(ArrayRef<MCPhysReg> Regs) {
unsigned FirstUnalloc = getFirstUnallocated(Regs);
if (FirstUnalloc == Regs.size())
return MCRegister(); // Didn't find the reg.
// Mark the register and any aliases as allocated.
MCPhysReg Reg = Regs[FirstUnalloc];
MarkAllocated(Reg);
return Reg;
}
/// AllocateRegBlock - Attempt to allocate a block of RegsRequired consecutive
/// registers. If this is not possible, return zero. Otherwise, return the first
/// register of the block that were allocated, marking the entire block as allocated.
MCPhysReg AllocateRegBlock(ArrayRef<MCPhysReg> Regs, unsigned RegsRequired) {
if (RegsRequired > Regs.size())
return 0;
for (unsigned StartIdx = 0; StartIdx <= Regs.size() - RegsRequired;
++StartIdx) {
bool BlockAvailable = true;
// Check for already-allocated regs in this block
for (unsigned BlockIdx = 0; BlockIdx < RegsRequired; ++BlockIdx) {
if (isAllocated(Regs[StartIdx + BlockIdx])) {
BlockAvailable = false;
break;
}
}
if (BlockAvailable) {
// Mark the entire block as allocated
for (unsigned BlockIdx = 0; BlockIdx < RegsRequired; ++BlockIdx) {
MarkAllocated(Regs[StartIdx + BlockIdx]);
}
return Regs[StartIdx];
}
}
// No block was available
return 0;
}
/// Version of AllocateReg with list of registers to be shadowed.
MCRegister AllocateReg(ArrayRef<MCPhysReg> Regs, const MCPhysReg *ShadowRegs) {
unsigned FirstUnalloc = getFirstUnallocated(Regs);
if (FirstUnalloc == Regs.size())
return MCRegister(); // Didn't find the reg.
// Mark the register and any aliases as allocated.
MCRegister Reg = Regs[FirstUnalloc], ShadowReg = ShadowRegs[FirstUnalloc];
MarkAllocated(Reg);
MarkAllocated(ShadowReg);
return Reg;
}
/// AllocateStack - Allocate a chunk of stack space with the specified size
/// and alignment.
unsigned AllocateStack(unsigned Size, Align Alignment) {
StackOffset = alignTo(StackOffset, Alignment);
unsigned Result = StackOffset;
StackOffset += Size;
MaxStackArgAlign = std::max(Alignment, MaxStackArgAlign);
ensureMaxAlignment(Alignment);
return Result;
}
void ensureMaxAlignment(Align Alignment);
/// Version of AllocateStack with list of extra registers to be shadowed.
/// Note that, unlike AllocateReg, this shadows ALL of the shadow registers.
unsigned AllocateStack(unsigned Size, Align Alignment,
ArrayRef<MCPhysReg> ShadowRegs) {
for (unsigned i = 0; i < ShadowRegs.size(); ++i)
MarkAllocated(ShadowRegs[i]);
return AllocateStack(Size, Alignment);
}
// HandleByVal - Allocate a stack slot large enough to pass an argument by
// value. The size and alignment information of the argument is encoded in its
// parameter attribute.
void HandleByVal(unsigned ValNo, MVT ValVT, MVT LocVT,
CCValAssign::LocInfo LocInfo, int MinSize, Align MinAlign,
ISD::ArgFlagsTy ArgFlags);
// Returns count of byval arguments that are to be stored (even partly)
// in registers.
unsigned getInRegsParamsCount() const { return ByValRegs.size(); }
// Returns count of byval in-regs arguments processed.
unsigned getInRegsParamsProcessed() const { return InRegsParamsProcessed; }
// Get information about N-th byval parameter that is stored in registers.
// Here "ByValParamIndex" is N.
void getInRegsParamInfo(unsigned InRegsParamRecordIndex,
unsigned& BeginReg, unsigned& EndReg) const {
assert(InRegsParamRecordIndex < ByValRegs.size() &&
"Wrong ByVal parameter index");
const ByValInfo& info = ByValRegs[InRegsParamRecordIndex];
BeginReg = info.Begin;
EndReg = info.End;
}
// Add information about parameter that is kept in registers.
void addInRegsParamInfo(unsigned RegBegin, unsigned RegEnd) {
ByValRegs.push_back(ByValInfo(RegBegin, RegEnd));
}
// Goes either to next byval parameter (excluding "waste" record), or
// to the end of collection.
// Returns false, if end is reached.
bool nextInRegsParam() {
unsigned e = ByValRegs.size();
if (InRegsParamsProcessed < e)
++InRegsParamsProcessed;
return InRegsParamsProcessed < e;
}
// Clear byval registers tracking info.
void clearByValRegsInfo() {
InRegsParamsProcessed = 0;
ByValRegs.clear();
}
// Rewind byval registers tracking info.
void rewindByValRegsInfo() {
InRegsParamsProcessed = 0;
}
// Get list of pending assignments
SmallVectorImpl<CCValAssign> &getPendingLocs() {
return PendingLocs;
}
// Get a list of argflags for pending assignments.
SmallVectorImpl<ISD::ArgFlagsTy> &getPendingArgFlags() {
return PendingArgFlags;
}
/// Compute the remaining unused register parameters that would be used for
/// the given value type. This is useful when varargs are passed in the
/// registers that normal prototyped parameters would be passed in, or for
/// implementing perfect forwarding.
void getRemainingRegParmsForType(SmallVectorImpl<MCPhysReg> &Regs, MVT VT,
CCAssignFn Fn);
/// Compute the set of registers that need to be preserved and forwarded to
/// any musttail calls.
void analyzeMustTailForwardedRegisters(
SmallVectorImpl<ForwardedRegister> &Forwards, ArrayRef<MVT> RegParmTypes,
CCAssignFn Fn);
/// Returns true if the results of the two calling conventions are compatible.
/// This is usually part of the check for tailcall eligibility.
static bool resultsCompatible(CallingConv::ID CalleeCC,
CallingConv::ID CallerCC, MachineFunction &MF,
LLVMContext &C,
const SmallVectorImpl<ISD::InputArg> &Ins,
CCAssignFn CalleeFn, CCAssignFn CallerFn);
/// The function runs an additional analysis pass over function arguments.
/// It will mark each argument with the attribute flag SecArgPass.
/// After running, it will sort the locs list.
template <class T>
void AnalyzeArgumentsSecondPass(const SmallVectorImpl<T> &Args,
CCAssignFn Fn) {
unsigned NumFirstPassLocs = Locs.size();
/// Creates similar argument list to \p Args in which each argument is
/// marked using SecArgPass flag.
SmallVector<T, 16> SecPassArg;
// SmallVector<ISD::InputArg, 16> SecPassArg;
for (auto Arg : Args) {
Arg.Flags.setSecArgPass();
SecPassArg.push_back(Arg);
}
// Run the second argument pass
AnalyzeArguments(SecPassArg, Fn);
// Sort the locations of the arguments according to their original position.
SmallVector<CCValAssign, 16> TmpArgLocs;
TmpArgLocs.swap(Locs);
auto B = TmpArgLocs.begin(), E = TmpArgLocs.end();
std::merge(B, B + NumFirstPassLocs, B + NumFirstPassLocs, E,
std::back_inserter(Locs),
[](const CCValAssign &A, const CCValAssign &B) -> bool {
return A.getValNo() < B.getValNo();
});
}
private:
/// MarkAllocated - Mark a register and all of its aliases as allocated.
void MarkAllocated(MCPhysReg Reg);
void MarkUnallocated(MCPhysReg Reg);
};
} // end namespace llvm
#endif // LLVM_CODEGEN_CALLINGCONVLOWER_H

View File

@@ -0,0 +1,219 @@
//===- CodeGenCommonISel.h - Common code between ISels ---------*- C++ -*--===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares common utilities that are shared between SelectionDAG and
// GlobalISel frameworks.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_CODEGENCOMMONISEL_H
#define LLVM_CODEGEN_CODEGENCOMMONISEL_H
#include "llvm/CodeGen/MachineBasicBlock.h"
#include <cassert>
namespace llvm {
class BasicBlock;
class MachineBasicBlock;
/// Encapsulates all of the information needed to generate a stack protector
/// check, and signals to isel when initialized that one needs to be generated.
///
/// *NOTE* The following is a high level documentation of SelectionDAG Stack
/// Protector Generation. This is now also ported be shared with GlobalISel,
/// but without any significant changes.
///
/// High Level Overview of ISel Stack Protector Generation:
///
/// Previously, the "stack protector" IR pass handled stack protector
/// generation. This necessitated splitting basic blocks at the IR level to
/// create the success/failure basic blocks in the tail of the basic block in
/// question. As a result of this, calls that would have qualified for the
/// sibling call optimization were no longer eligible for optimization since
/// said calls were no longer right in the "tail position" (i.e. the immediate
/// predecessor of a ReturnInst instruction).
///
/// Since the sibling call optimization causes the callee to reuse the caller's
/// stack, if we could delay the generation of the stack protector check until
/// later in CodeGen after the sibling call decision was made, we get both the
/// tail call optimization and the stack protector check!
///
/// A few goals in solving this problem were:
///
/// 1. Preserve the architecture independence of stack protector generation.
///
/// 2. Preserve the normal IR level stack protector check for platforms like
/// OpenBSD for which we support platform-specific stack protector
/// generation.
///
/// The main problem that guided the present solution is that one can not
/// solve this problem in an architecture independent manner at the IR level
/// only. This is because:
///
/// 1. The decision on whether or not to perform a sibling call on certain
/// platforms (for instance i386) requires lower level information
/// related to available registers that can not be known at the IR level.
///
/// 2. Even if the previous point were not true, the decision on whether to
/// perform a tail call is done in LowerCallTo in SelectionDAG (or
/// CallLowering in GlobalISel) which occurs after the Stack Protector
/// Pass. As a result, one would need to put the relevant callinst into the
/// stack protector check success basic block (where the return inst is
/// placed) and then move it back later at ISel/MI time before the
/// stack protector check if the tail call optimization failed. The MI
/// level option was nixed immediately since it would require
/// platform-specific pattern matching. The ISel level option was
/// nixed because SelectionDAG only processes one IR level basic block at a
/// time implying one could not create a DAG Combine to move the callinst.
///
/// To get around this problem:
///
/// 1. SelectionDAG can only process one block at a time, we can generate
/// multiple machine basic blocks for one IR level basic block.
/// This is how we handle bit tests and switches.
///
/// 2. At the MI level, tail calls are represented via a special return
/// MIInst called "tcreturn". Thus if we know the basic block in which we
/// wish to insert the stack protector check, we get the correct behavior
/// by always inserting the stack protector check right before the return
/// statement. This is a "magical transformation" since no matter where
/// the stack protector check intrinsic is, we always insert the stack
/// protector check code at the end of the BB.
///
/// Given the aforementioned constraints, the following solution was devised:
///
/// 1. On platforms that do not support ISel stack protector check
/// generation, allow for the normal IR level stack protector check
/// generation to continue.
///
/// 2. On platforms that do support ISel stack protector check
/// generation:
///
/// a. Use the IR level stack protector pass to decide if a stack
/// protector is required/which BB we insert the stack protector check
/// in by reusing the logic already therein.
///
/// b. After we finish selecting the basic block, we produce the validation
/// code with one of these techniques:
/// 1) with a call to a guard check function
/// 2) with inlined instrumentation
///
/// 1) We insert a call to the check function before the terminator.
///
/// 2) We first find a splice point in the parent basic block
/// before the terminator and then splice the terminator of said basic
/// block into the success basic block. Then we code-gen a new tail for
/// the parent basic block consisting of the two loads, the comparison,
/// and finally two branches to the success/failure basic blocks. We
/// conclude by code-gening the failure basic block if we have not
/// code-gened it already (all stack protector checks we generate in
/// the same function, use the same failure basic block).
class StackProtectorDescriptor {
public:
StackProtectorDescriptor() = default;
/// Returns true if all fields of the stack protector descriptor are
/// initialized implying that we should/are ready to emit a stack protector.
bool shouldEmitStackProtector() const {
return ParentMBB && SuccessMBB && FailureMBB;
}
bool shouldEmitFunctionBasedCheckStackProtector() const {
return ParentMBB && !SuccessMBB && !FailureMBB;
}
/// Initialize the stack protector descriptor structure for a new basic
/// block.
void initialize(const BasicBlock *BB, MachineBasicBlock *MBB,
bool FunctionBasedInstrumentation) {
// Make sure we are not initialized yet.
assert(!shouldEmitStackProtector() && "Stack Protector Descriptor is "
"already initialized!");
ParentMBB = MBB;
if (!FunctionBasedInstrumentation) {
SuccessMBB = addSuccessorMBB(BB, MBB, /* IsLikely */ true);
FailureMBB = addSuccessorMBB(BB, MBB, /* IsLikely */ false, FailureMBB);
}
}
/// Reset state that changes when we handle different basic blocks.
///
/// This currently includes:
///
/// 1. The specific basic block we are generating a
/// stack protector for (ParentMBB).
///
/// 2. The successor machine basic block that will contain the tail of
/// parent mbb after we create the stack protector check (SuccessMBB). This
/// BB is visited only on stack protector check success.
void resetPerBBState() {
ParentMBB = nullptr;
SuccessMBB = nullptr;
}
/// Reset state that only changes when we switch functions.
///
/// This currently includes:
///
/// 1. FailureMBB since we reuse the failure code path for all stack
/// protector checks created in an individual function.
///
/// 2.The guard variable since the guard variable we are checking against is
/// always the same.
void resetPerFunctionState() { FailureMBB = nullptr; }
MachineBasicBlock *getParentMBB() { return ParentMBB; }
MachineBasicBlock *getSuccessMBB() { return SuccessMBB; }
MachineBasicBlock *getFailureMBB() { return FailureMBB; }
private:
/// The basic block for which we are generating the stack protector.
///
/// As a result of stack protector generation, we will splice the
/// terminators of this basic block into the successor mbb SuccessMBB and
/// replace it with a compare/branch to the successor mbbs
/// SuccessMBB/FailureMBB depending on whether or not the stack protector
/// was violated.
MachineBasicBlock *ParentMBB = nullptr;
/// A basic block visited on stack protector check success that contains the
/// terminators of ParentMBB.
MachineBasicBlock *SuccessMBB = nullptr;
/// This basic block visited on stack protector check failure that will
/// contain a call to __stack_chk_fail().
MachineBasicBlock *FailureMBB = nullptr;
/// Add a successor machine basic block to ParentMBB. If the successor mbb
/// has not been created yet (i.e. if SuccMBB = 0), then the machine basic
/// block will be created. Assign a large weight if IsLikely is true.
MachineBasicBlock *addSuccessorMBB(const BasicBlock *BB,
MachineBasicBlock *ParentMBB,
bool IsLikely,
MachineBasicBlock *SuccMBB = nullptr);
};
/// Find the split point at which to splice the end of BB into its success stack
/// protector check machine basic block.
///
/// On many platforms, due to ABI constraints, terminators, even before register
/// allocation, use physical registers. This creates an issue for us since
/// physical registers at this point can not travel across basic
/// blocks. Luckily, selectiondag always moves physical registers into vregs
/// when they enter functions and moves them through a sequence of copies back
/// into the physical registers right before the terminator creating a
/// ``Terminator Sequence''. This function is searching for the beginning of the
/// terminator sequence so that we can ensure that we splice off not just the
/// terminator, but additionally the copies that move the vregs into the
/// physical registers.
MachineBasicBlock::iterator
findSplitPointForStackProtector(MachineBasicBlock *BB,
const TargetInstrInfo &TII);
} // namespace llvm
#endif // LLVM_CODEGEN_CODEGENCOMMONISEL_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
//===-- CommandFlags.h - Command Line Flags Interface -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains codegen-specific flags that are shared between different
// command line tools. The tools "llc" and "opt" both use this file to prevent
// flag duplication.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_COMMANDFLAGS_H
#define LLVM_CODEGEN_COMMANDFLAGS_H
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetOptions.h"
#include <string>
#include <vector>
namespace llvm {
class Module;
namespace codegen {
std::string getMArch();
std::string getMCPU();
std::vector<std::string> getMAttrs();
Reloc::Model getRelocModel();
Optional<Reloc::Model> getExplicitRelocModel();
ThreadModel::Model getThreadModel();
CodeModel::Model getCodeModel();
Optional<CodeModel::Model> getExplicitCodeModel();
llvm::ExceptionHandling getExceptionModel();
Optional<CodeGenFileType> getExplicitFileType();
CodeGenFileType getFileType();
FramePointerKind getFramePointerUsage();
bool getEnableUnsafeFPMath();
bool getEnableNoInfsFPMath();
bool getEnableNoNaNsFPMath();
bool getEnableNoSignedZerosFPMath();
bool getEnableNoTrappingFPMath();
DenormalMode::DenormalModeKind getDenormalFPMath();
DenormalMode::DenormalModeKind getDenormalFP32Math();
bool getEnableHonorSignDependentRoundingFPMath();
llvm::FloatABI::ABIType getFloatABIForCalls();
llvm::FPOpFusion::FPOpFusionMode getFuseFPOps();
SwiftAsyncFramePointerMode getSwiftAsyncFramePointer();
bool getDontPlaceZerosInBSS();
bool getEnableGuaranteedTailCallOpt();
bool getEnableAIXExtendedAltivecABI();
bool getDisableTailCalls();
bool getStackSymbolOrdering();
unsigned getOverrideStackAlignment();
bool getStackRealign();
std::string getTrapFuncName();
bool getUseCtors();
bool getRelaxELFRelocations();
bool getDataSections();
Optional<bool> getExplicitDataSections();
bool getFunctionSections();
Optional<bool> getExplicitFunctionSections();
bool getIgnoreXCOFFVisibility();
bool getXCOFFTracebackTable();
std::string getBBSections();
unsigned getTLSSize();
bool getEmulatedTLS();
bool getUniqueSectionNames();
bool getUniqueBasicBlockSectionNames();
llvm::EABI getEABIVersion();
llvm::DebuggerKind getDebuggerTuningOpt();
bool getEnableStackSizeSection();
bool getEnableAddrsig();
bool getEmitCallSiteInfo();
bool getEnableMachineFunctionSplitter();
bool getEnableDebugEntryValues();
bool getValueTrackingVariableLocations();
Optional<bool> getExplicitValueTrackingVariableLocations();
bool getForceDwarfFrameSection();
bool getXRayOmitFunctionIndex();
bool getDebugStrictDwarf();
unsigned getAlignLoops();
/// Create this object with static storage to register codegen-related command
/// line options.
struct RegisterCodeGenFlags {
RegisterCodeGenFlags();
};
llvm::BasicBlockSection getBBSectionsMode(llvm::TargetOptions &Options);
/// Common utility function tightly tied to the options listed here. Initializes
/// a TargetOptions object with CodeGen flags and returns it.
/// \p TheTriple is used to determine the default value for options if
/// options are not explicitly specified. If those triple dependant options
/// value do not have effect for your component, a default Triple() could be
/// passed in.
TargetOptions InitTargetOptionsFromCodeGenFlags(const llvm::Triple &TheTriple);
std::string getCPUStr();
std::string getFeaturesStr();
std::vector<std::string> getFeatureList();
void renderBoolStringAttr(AttrBuilder &B, StringRef Name, bool Val);
/// Set function attributes of function \p F based on CPU, Features, and command
/// line flags.
void setFunctionAttributes(StringRef CPU, StringRef Features, Function &F);
/// Set function attributes of functions in Module M based on CPU,
/// Features, and command line flags.
void setFunctionAttributes(StringRef CPU, StringRef Features, Module &M);
/// Should value-tracking variable locations / instruction referencing be
/// enabled by default for this triple?
bool getDefaultValueTrackingVariableLocations(const llvm::Triple &T);
} // namespace codegen
} // namespace llvm
#endif // LLVM_CODEGEN_COMMANDFLAGS_H

View File

@@ -0,0 +1,89 @@
//===-- CostTable.h - Instruction Cost Table handling -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Cost tables and simple lookup functions
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_COSTTABLE_H_
#define LLVM_CODEGEN_COSTTABLE_H_
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/MachineValueType.h"
namespace llvm {
/// Cost Table Entry
template <typename CostType>
struct CostTblEntryT {
int ISD;
MVT::SimpleValueType Type;
CostType Cost;
};
using CostTblEntry = CostTblEntryT<unsigned>;
/// Find in cost table.
template <class CostType>
inline const CostTblEntryT<CostType> *
CostTableLookup(ArrayRef<CostTblEntryT<CostType>> Tbl, int ISD, MVT Ty) {
auto I = find_if(Tbl, [=](const CostTblEntryT<CostType> &Entry) {
return ISD == Entry.ISD && Ty == Entry.Type;
});
if (I != Tbl.end())
return I;
// Could not find an entry.
return nullptr;
}
template <size_t N, class CostType>
inline const CostTblEntryT<CostType> *
CostTableLookup(const CostTblEntryT<CostType> (&Table)[N], int ISD, MVT Ty) {
// Wrapper to fix template argument deduction failures.
return CostTableLookup<CostType>(makeArrayRef(Table), ISD, Ty);
}
/// Type Conversion Cost Table
template <typename CostType>
struct TypeConversionCostTblEntryT {
int ISD;
MVT::SimpleValueType Dst;
MVT::SimpleValueType Src;
CostType Cost;
};
using TypeConversionCostTblEntry = TypeConversionCostTblEntryT<unsigned>;
/// Find in type conversion cost table.
template <class CostType>
inline const TypeConversionCostTblEntryT<CostType> *
ConvertCostTableLookup(ArrayRef<TypeConversionCostTblEntryT<CostType>> Tbl,
int ISD, MVT Dst, MVT Src) {
auto I =
find_if(Tbl, [=](const TypeConversionCostTblEntryT<CostType> &Entry) {
return ISD == Entry.ISD && Src == Entry.Src && Dst == Entry.Dst;
});
if (I != Tbl.end())
return I;
// Could not find an entry.
return nullptr;
}
template <size_t N, class CostType>
inline const TypeConversionCostTblEntryT<CostType> *
ConvertCostTableLookup(const TypeConversionCostTblEntryT<CostType> (&Table)[N],
int ISD, MVT Dst, MVT Src) {
// Wrapper to fix template argument deduction failures.
return ConvertCostTableLookup<CostType>(makeArrayRef(Table), ISD, Dst, Src);
}
} // namespace llvm
#endif /* LLVM_CODEGEN_COSTTABLE_H_ */

View File

@@ -0,0 +1,24 @@
//===-- llvm/CodeGen/DAGCombine.h ------- SelectionDAG Nodes ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#ifndef LLVM_CODEGEN_DAGCOMBINE_H
#define LLVM_CODEGEN_DAGCOMBINE_H
namespace llvm {
enum CombineLevel {
BeforeLegalizeTypes,
AfterLegalizeTypes,
AfterLegalizeVectorOps,
AfterLegalizeDAG
};
} // end llvm namespace
#endif

View File

@@ -0,0 +1,200 @@
//===- llvm/CodeGen/DFAPacketizer.h - DFA Packetizer for VLIW ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This class implements a deterministic finite automaton (DFA) based
// packetizing mechanism for VLIW architectures. It provides APIs to
// determine whether there exists a legal mapping of instructions to
// functional unit assignments in a packet. The DFA is auto-generated from
// the target's Schedule.td file.
//
// A DFA consists of 3 major elements: states, inputs, and transitions. For
// the packetizing mechanism, the input is the set of instruction classes for
// a target. The state models all possible combinations of functional unit
// consumption for a given set of instructions in a packet. A transition
// models the addition of an instruction to a packet. In the DFA constructed
// by this class, if an instruction can be added to a packet, then a valid
// transition exists from the corresponding state. Invalid transitions
// indicate that the instruction cannot be added to the current packet.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_DFAPACKETIZER_H
#define LLVM_CODEGEN_DFAPACKETIZER_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/ScheduleDAGMutation.h"
#include "llvm/Support/Automaton.h"
#include <cstdint>
#include <map>
#include <memory>
#include <utility>
#include <vector>
namespace llvm {
class DefaultVLIWScheduler;
class InstrItineraryData;
class MachineFunction;
class MachineInstr;
class MachineLoopInfo;
class MCInstrDesc;
class SUnit;
class TargetInstrInfo;
class DFAPacketizer {
private:
const InstrItineraryData *InstrItins;
Automaton<uint64_t> A;
/// For every itinerary, an "action" to apply to the automaton. This removes
/// the redundancy in actions between itinerary classes.
ArrayRef<unsigned> ItinActions;
public:
DFAPacketizer(const InstrItineraryData *InstrItins, Automaton<uint64_t> a,
ArrayRef<unsigned> ItinActions)
: InstrItins(InstrItins), A(std::move(a)), ItinActions(ItinActions) {
// Start off with resource tracking disabled.
A.enableTranscription(false);
}
// Reset the current state to make all resources available.
void clearResources() {
A.reset();
}
// Set whether this packetizer should track not just whether instructions
// can be packetized, but also which functional units each instruction ends up
// using after packetization.
void setTrackResources(bool Track) {
A.enableTranscription(Track);
}
// Check if the resources occupied by a MCInstrDesc are available in
// the current state.
bool canReserveResources(const MCInstrDesc *MID);
// Reserve the resources occupied by a MCInstrDesc and change the current
// state to reflect that change.
void reserveResources(const MCInstrDesc *MID);
// Check if the resources occupied by a machine instruction are available
// in the current state.
bool canReserveResources(MachineInstr &MI);
// Reserve the resources occupied by a machine instruction and change the
// current state to reflect that change.
void reserveResources(MachineInstr &MI);
// Return the resources used by the InstIdx'th instruction added to this
// packet. The resources are returned as a bitvector of functional units.
//
// Note that a bundle may be packed in multiple valid ways. This function
// returns one arbitrary valid packing.
//
// Requires setTrackResources(true) to have been called.
unsigned getUsedResources(unsigned InstIdx);
const InstrItineraryData *getInstrItins() const { return InstrItins; }
};
// VLIWPacketizerList implements a simple VLIW packetizer using DFA. The
// packetizer works on machine basic blocks. For each instruction I in BB,
// the packetizer consults the DFA to see if machine resources are available
// to execute I. If so, the packetizer checks if I depends on any instruction
// in the current packet. If no dependency is found, I is added to current
// packet and the machine resource is marked as taken. If any dependency is
// found, a target API call is made to prune the dependence.
class VLIWPacketizerList {
protected:
MachineFunction &MF;
const TargetInstrInfo *TII;
AAResults *AA;
// The VLIW Scheduler.
DefaultVLIWScheduler *VLIWScheduler;
// Vector of instructions assigned to the current packet.
std::vector<MachineInstr*> CurrentPacketMIs;
// DFA resource tracker.
DFAPacketizer *ResourceTracker;
// Map: MI -> SU.
std::map<MachineInstr*, SUnit*> MIToSUnit;
public:
// The AAResults parameter can be nullptr.
VLIWPacketizerList(MachineFunction &MF, MachineLoopInfo &MLI,
AAResults *AA);
virtual ~VLIWPacketizerList();
// Implement this API in the backend to bundle instructions.
void PacketizeMIs(MachineBasicBlock *MBB,
MachineBasicBlock::iterator BeginItr,
MachineBasicBlock::iterator EndItr);
// Return the ResourceTracker.
DFAPacketizer *getResourceTracker() {return ResourceTracker;}
// addToPacket - Add MI to the current packet.
virtual MachineBasicBlock::iterator addToPacket(MachineInstr &MI) {
CurrentPacketMIs.push_back(&MI);
ResourceTracker->reserveResources(MI);
return MI;
}
// End the current packet and reset the state of the packetizer.
// Overriding this function allows the target-specific packetizer
// to perform custom finalization.
virtual void endPacket(MachineBasicBlock *MBB,
MachineBasicBlock::iterator MI);
// Perform initialization before packetizing an instruction. This
// function is supposed to be overrided by the target dependent packetizer.
virtual void initPacketizerState() {}
// Check if the given instruction I should be ignored by the packetizer.
virtual bool ignorePseudoInstruction(const MachineInstr &I,
const MachineBasicBlock *MBB) {
return false;
}
// Return true if instruction MI can not be packetized with any other
// instruction, which means that MI itself is a packet.
virtual bool isSoloInstruction(const MachineInstr &MI) { return true; }
// Check if the packetizer should try to add the given instruction to
// the current packet. One reasons for which it may not be desirable
// to include an instruction in the current packet could be that it
// would cause a stall.
// If this function returns "false", the current packet will be ended,
// and the instruction will be added to the next packet.
virtual bool shouldAddToPacket(const MachineInstr &MI) { return true; }
// Check if it is legal to packetize SUI and SUJ together.
virtual bool isLegalToPacketizeTogether(SUnit *SUI, SUnit *SUJ) {
return false;
}
// Check if it is legal to prune dependence between SUI and SUJ.
virtual bool isLegalToPruneDependencies(SUnit *SUI, SUnit *SUJ) {
return false;
}
// Add a DAG mutation to be done before the packetization begins.
void addMutation(std::unique_ptr<ScheduleDAGMutation> Mutation);
bool alias(const MachineInstr &MI1, const MachineInstr &MI2,
bool UseTBAA = true) const;
private:
bool alias(const MachineMemOperand &Op1, const MachineMemOperand &Op2,
bool UseTBAA = true) const;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_DFAPACKETIZER_H

View File

@@ -0,0 +1,998 @@
//===- lib/CodeGen/DIE.h - DWARF Info Entries -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Data structures for DWARF info entries.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_DIE_H
#define LLVM_CODEGEN_DIE_H
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/DwarfStringPoolEntry.h"
#include "llvm/Support/AlignOf.h"
#include "llvm/Support/Allocator.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <new>
#include <type_traits>
#include <utility>
#include <vector>
namespace llvm {
class AsmPrinter;
class DIE;
class DIEUnit;
class DwarfCompileUnit;
class MCExpr;
class MCSection;
class MCSymbol;
class raw_ostream;
//===--------------------------------------------------------------------===//
/// Dwarf abbreviation data, describes one attribute of a Dwarf abbreviation.
class DIEAbbrevData {
/// Dwarf attribute code.
dwarf::Attribute Attribute;
/// Dwarf form code.
dwarf::Form Form;
/// Dwarf attribute value for DW_FORM_implicit_const
int64_t Value = 0;
public:
DIEAbbrevData(dwarf::Attribute A, dwarf::Form F)
: Attribute(A), Form(F) {}
DIEAbbrevData(dwarf::Attribute A, int64_t V)
: Attribute(A), Form(dwarf::DW_FORM_implicit_const), Value(V) {}
/// Accessors.
/// @{
dwarf::Attribute getAttribute() const { return Attribute; }
dwarf::Form getForm() const { return Form; }
int64_t getValue() const { return Value; }
/// @}
/// Used to gather unique data for the abbreviation folding set.
void Profile(FoldingSetNodeID &ID) const;
};
//===--------------------------------------------------------------------===//
/// Dwarf abbreviation, describes the organization of a debug information
/// object.
class DIEAbbrev : public FoldingSetNode {
/// Unique number for node.
unsigned Number = 0;
/// Dwarf tag code.
dwarf::Tag Tag;
/// Whether or not this node has children.
///
/// This cheats a bit in all of the uses since the values in the standard
/// are 0 and 1 for no children and children respectively.
bool Children;
/// Raw data bytes for abbreviation.
SmallVector<DIEAbbrevData, 12> Data;
public:
DIEAbbrev(dwarf::Tag T, bool C) : Tag(T), Children(C) {}
/// Accessors.
/// @{
dwarf::Tag getTag() const { return Tag; }
unsigned getNumber() const { return Number; }
bool hasChildren() const { return Children; }
const SmallVectorImpl<DIEAbbrevData> &getData() const { return Data; }
void setChildrenFlag(bool hasChild) { Children = hasChild; }
void setNumber(unsigned N) { Number = N; }
/// @}
/// Adds another set of attribute information to the abbreviation.
void AddAttribute(dwarf::Attribute Attribute, dwarf::Form Form) {
Data.push_back(DIEAbbrevData(Attribute, Form));
}
/// Adds attribute with DW_FORM_implicit_const value
void AddImplicitConstAttribute(dwarf::Attribute Attribute, int64_t Value) {
Data.push_back(DIEAbbrevData(Attribute, Value));
}
/// Used to gather unique data for the abbreviation folding set.
void Profile(FoldingSetNodeID &ID) const;
/// Print the abbreviation using the specified asm printer.
void Emit(const AsmPrinter *AP) const;
void print(raw_ostream &O) const;
void dump() const;
};
//===--------------------------------------------------------------------===//
/// Helps unique DIEAbbrev objects and assigns abbreviation numbers.
///
/// This class will unique the DIE abbreviations for a llvm::DIE object and
/// assign a unique abbreviation number to each unique DIEAbbrev object it
/// finds. The resulting collection of DIEAbbrev objects can then be emitted
/// into the .debug_abbrev section.
class DIEAbbrevSet {
/// The bump allocator to use when creating DIEAbbrev objects in the uniqued
/// storage container.
BumpPtrAllocator &Alloc;
/// FoldingSet that uniques the abbreviations.
FoldingSet<DIEAbbrev> AbbreviationsSet;
/// A list of all the unique abbreviations in use.
std::vector<DIEAbbrev *> Abbreviations;
public:
DIEAbbrevSet(BumpPtrAllocator &A) : Alloc(A) {}
~DIEAbbrevSet();
/// Generate the abbreviation declaration for a DIE and return a pointer to
/// the generated abbreviation.
///
/// \param Die the debug info entry to generate the abbreviation for.
/// \returns A reference to the uniqued abbreviation declaration that is
/// owned by this class.
DIEAbbrev &uniqueAbbreviation(DIE &Die);
/// Print all abbreviations using the specified asm printer.
void Emit(const AsmPrinter *AP, MCSection *Section) const;
};
//===--------------------------------------------------------------------===//
/// An integer value DIE.
///
class DIEInteger {
uint64_t Integer;
public:
explicit DIEInteger(uint64_t I) : Integer(I) {}
/// Choose the best form for integer.
static dwarf::Form BestForm(bool IsSigned, uint64_t Int) {
if (IsSigned) {
const int64_t SignedInt = Int;
if ((char)Int == SignedInt)
return dwarf::DW_FORM_data1;
if ((short)Int == SignedInt)
return dwarf::DW_FORM_data2;
if ((int)Int == SignedInt)
return dwarf::DW_FORM_data4;
} else {
if ((unsigned char)Int == Int)
return dwarf::DW_FORM_data1;
if ((unsigned short)Int == Int)
return dwarf::DW_FORM_data2;
if ((unsigned int)Int == Int)
return dwarf::DW_FORM_data4;
}
return dwarf::DW_FORM_data8;
}
uint64_t getValue() const { return Integer; }
void setValue(uint64_t Val) { Integer = Val; }
void emitValue(const AsmPrinter *Asm, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// An expression DIE.
class DIEExpr {
const MCExpr *Expr;
public:
explicit DIEExpr(const MCExpr *E) : Expr(E) {}
/// Get MCExpr.
const MCExpr *getValue() const { return Expr; }
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A label DIE.
class DIELabel {
const MCSymbol *Label;
public:
explicit DIELabel(const MCSymbol *L) : Label(L) {}
/// Get MCSymbol.
const MCSymbol *getValue() const { return Label; }
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A BaseTypeRef DIE.
class DIEBaseTypeRef {
const DwarfCompileUnit *CU;
const uint64_t Index;
static constexpr unsigned ULEB128PadSize = 4;
public:
explicit DIEBaseTypeRef(const DwarfCompileUnit *TheCU, uint64_t Idx)
: CU(TheCU), Index(Idx) {}
/// EmitValue - Emit base type reference.
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
/// sizeOf - Determine size of the base type reference in bytes.
unsigned sizeOf(const dwarf::FormParams &, dwarf::Form) const;
void print(raw_ostream &O) const;
uint64_t getIndex() const { return Index; }
};
//===--------------------------------------------------------------------===//
/// A simple label difference DIE.
///
class DIEDelta {
const MCSymbol *LabelHi;
const MCSymbol *LabelLo;
public:
DIEDelta(const MCSymbol *Hi, const MCSymbol *Lo) : LabelHi(Hi), LabelLo(Lo) {}
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A container for string pool string values.
///
/// This class is used with the DW_FORM_strp and DW_FORM_GNU_str_index forms.
class DIEString {
DwarfStringPoolEntryRef S;
public:
DIEString(DwarfStringPoolEntryRef S) : S(S) {}
/// Grab the string out of the object.
StringRef getString() const { return S.getString(); }
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A container for inline string values.
///
/// This class is used with the DW_FORM_string form.
class DIEInlineString {
StringRef S;
public:
template <typename Allocator>
explicit DIEInlineString(StringRef Str, Allocator &A) : S(Str.copy(A)) {}
~DIEInlineString() = default;
/// Grab the string out of the object.
StringRef getString() const { return S; }
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &, dwarf::Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A pointer to another debug information entry. An instance of this class can
/// also be used as a proxy for a debug information entry not yet defined
/// (ie. types.)
class DIEEntry {
DIE *Entry;
public:
DIEEntry() = delete;
explicit DIEEntry(DIE &E) : Entry(&E) {}
DIE &getEntry() const { return *Entry; }
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// Represents a pointer to a location list in the debug_loc
/// section.
class DIELocList {
/// Index into the .debug_loc vector.
size_t Index;
public:
DIELocList(size_t I) : Index(I) {}
/// Grab the current index out.
size_t getValue() const { return Index; }
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A BaseTypeRef DIE.
class DIEAddrOffset {
DIEInteger Addr;
DIEDelta Offset;
public:
explicit DIEAddrOffset(uint64_t Idx, const MCSymbol *Hi, const MCSymbol *Lo)
: Addr(Idx), Offset(Hi, Lo) {}
void emitValue(const AsmPrinter *AP, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &FormParams, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// A debug information entry value. Some of these roughly correlate
/// to DWARF attribute classes.
class DIEBlock;
class DIELoc;
class DIEValue {
public:
enum Type {
isNone,
#define HANDLE_DIEVALUE(T) is##T,
#include "llvm/CodeGen/DIEValue.def"
};
private:
/// Type of data stored in the value.
Type Ty = isNone;
dwarf::Attribute Attribute = (dwarf::Attribute)0;
dwarf::Form Form = (dwarf::Form)0;
/// Storage for the value.
///
/// All values that aren't standard layout (or are larger than 8 bytes)
/// should be stored by reference instead of by value.
using ValTy =
AlignedCharArrayUnion<DIEInteger, DIEString, DIEExpr, DIELabel,
DIEDelta *, DIEEntry, DIEBlock *, DIELoc *,
DIELocList, DIEBaseTypeRef *, DIEAddrOffset *>;
static_assert(sizeof(ValTy) <= sizeof(uint64_t) ||
sizeof(ValTy) <= sizeof(void *),
"Expected all large types to be stored via pointer");
/// Underlying stored value.
ValTy Val;
template <class T> void construct(T V) {
static_assert(std::is_standard_layout<T>::value ||
std::is_pointer<T>::value,
"Expected standard layout or pointer");
new (reinterpret_cast<void *>(&Val)) T(V);
}
template <class T> T *get() { return reinterpret_cast<T *>(&Val); }
template <class T> const T *get() const {
return reinterpret_cast<const T *>(&Val);
}
template <class T> void destruct() { get<T>()->~T(); }
/// Destroy the underlying value.
///
/// This should get optimized down to a no-op. We could skip it if we could
/// add a static assert on \a std::is_trivially_copyable(), but we currently
/// support versions of GCC that don't understand that.
void destroyVal() {
switch (Ty) {
case isNone:
return;
#define HANDLE_DIEVALUE_SMALL(T) \
case is##T: \
destruct<DIE##T>(); \
return;
#define HANDLE_DIEVALUE_LARGE(T) \
case is##T: \
destruct<const DIE##T *>(); \
return;
#include "llvm/CodeGen/DIEValue.def"
}
}
/// Copy the underlying value.
///
/// This should get optimized down to a simple copy. We need to actually
/// construct the value, rather than calling memcpy, to satisfy strict
/// aliasing rules.
void copyVal(const DIEValue &X) {
switch (Ty) {
case isNone:
return;
#define HANDLE_DIEVALUE_SMALL(T) \
case is##T: \
construct<DIE##T>(*X.get<DIE##T>()); \
return;
#define HANDLE_DIEVALUE_LARGE(T) \
case is##T: \
construct<const DIE##T *>(*X.get<const DIE##T *>()); \
return;
#include "llvm/CodeGen/DIEValue.def"
}
}
public:
DIEValue() = default;
DIEValue(const DIEValue &X) : Ty(X.Ty), Attribute(X.Attribute), Form(X.Form) {
copyVal(X);
}
DIEValue &operator=(const DIEValue &X) {
destroyVal();
Ty = X.Ty;
Attribute = X.Attribute;
Form = X.Form;
copyVal(X);
return *this;
}
~DIEValue() { destroyVal(); }
#define HANDLE_DIEVALUE_SMALL(T) \
DIEValue(dwarf::Attribute Attribute, dwarf::Form Form, const DIE##T &V) \
: Ty(is##T), Attribute(Attribute), Form(Form) { \
construct<DIE##T>(V); \
}
#define HANDLE_DIEVALUE_LARGE(T) \
DIEValue(dwarf::Attribute Attribute, dwarf::Form Form, const DIE##T *V) \
: Ty(is##T), Attribute(Attribute), Form(Form) { \
assert(V && "Expected valid value"); \
construct<const DIE##T *>(V); \
}
#include "llvm/CodeGen/DIEValue.def"
/// Accessors.
/// @{
Type getType() const { return Ty; }
dwarf::Attribute getAttribute() const { return Attribute; }
dwarf::Form getForm() const { return Form; }
explicit operator bool() const { return Ty; }
/// @}
#define HANDLE_DIEVALUE_SMALL(T) \
const DIE##T &getDIE##T() const { \
assert(getType() == is##T && "Expected " #T); \
return *get<DIE##T>(); \
}
#define HANDLE_DIEVALUE_LARGE(T) \
const DIE##T &getDIE##T() const { \
assert(getType() == is##T && "Expected " #T); \
return **get<const DIE##T *>(); \
}
#include "llvm/CodeGen/DIEValue.def"
/// Emit value via the Dwarf writer.
void emitValue(const AsmPrinter *AP) const;
/// Return the size of a value in bytes.
unsigned sizeOf(const dwarf::FormParams &FormParams) const;
void print(raw_ostream &O) const;
void dump() const;
};
struct IntrusiveBackListNode {
PointerIntPair<IntrusiveBackListNode *, 1> Next;
IntrusiveBackListNode() : Next(this, true) {}
IntrusiveBackListNode *getNext() const {
return Next.getInt() ? nullptr : Next.getPointer();
}
};
struct IntrusiveBackListBase {
using Node = IntrusiveBackListNode;
Node *Last = nullptr;
bool empty() const { return !Last; }
void push_back(Node &N) {
assert(N.Next.getPointer() == &N && "Expected unlinked node");
assert(N.Next.getInt() == true && "Expected unlinked node");
if (Last) {
N.Next = Last->Next;
Last->Next.setPointerAndInt(&N, false);
}
Last = &N;
}
void push_front(Node &N) {
assert(N.Next.getPointer() == &N && "Expected unlinked node");
assert(N.Next.getInt() == true && "Expected unlinked node");
if (Last) {
N.Next.setPointerAndInt(Last->Next.getPointer(), false);
Last->Next.setPointerAndInt(&N, true);
} else {
Last = &N;
}
}
};
template <class T> class IntrusiveBackList : IntrusiveBackListBase {
public:
using IntrusiveBackListBase::empty;
void push_back(T &N) { IntrusiveBackListBase::push_back(N); }
void push_front(T &N) { IntrusiveBackListBase::push_front(N); }
T &back() { return *static_cast<T *>(Last); }
const T &back() const { return *static_cast<T *>(Last); }
T &front() {
return *static_cast<T *>(Last ? Last->Next.getPointer() : nullptr);
}
const T &front() const {
return *static_cast<T *>(Last ? Last->Next.getPointer() : nullptr);
}
void takeNodes(IntrusiveBackList<T> &Other) {
if (Other.empty())
return;
T *FirstNode = static_cast<T *>(Other.Last->Next.getPointer());
T *IterNode = FirstNode;
do {
// Keep a pointer to the node and increment the iterator.
T *TmpNode = IterNode;
IterNode = static_cast<T *>(IterNode->Next.getPointer());
// Unlink the node and push it back to this list.
TmpNode->Next.setPointerAndInt(TmpNode, true);
push_back(*TmpNode);
} while (IterNode != FirstNode);
Other.Last = nullptr;
}
class const_iterator;
class iterator
: public iterator_facade_base<iterator, std::forward_iterator_tag, T> {
friend class const_iterator;
Node *N = nullptr;
public:
iterator() = default;
explicit iterator(T *N) : N(N) {}
iterator &operator++() {
N = N->getNext();
return *this;
}
explicit operator bool() const { return N; }
T &operator*() const { return *static_cast<T *>(N); }
bool operator==(const iterator &X) const { return N == X.N; }
};
class const_iterator
: public iterator_facade_base<const_iterator, std::forward_iterator_tag,
const T> {
const Node *N = nullptr;
public:
const_iterator() = default;
// Placate MSVC by explicitly scoping 'iterator'.
const_iterator(typename IntrusiveBackList<T>::iterator X) : N(X.N) {}
explicit const_iterator(const T *N) : N(N) {}
const_iterator &operator++() {
N = N->getNext();
return *this;
}
explicit operator bool() const { return N; }
const T &operator*() const { return *static_cast<const T *>(N); }
bool operator==(const const_iterator &X) const { return N == X.N; }
};
iterator begin() {
return Last ? iterator(static_cast<T *>(Last->Next.getPointer())) : end();
}
const_iterator begin() const {
return const_cast<IntrusiveBackList *>(this)->begin();
}
iterator end() { return iterator(); }
const_iterator end() const { return const_iterator(); }
static iterator toIterator(T &N) { return iterator(&N); }
static const_iterator toIterator(const T &N) { return const_iterator(&N); }
};
/// A list of DIE values.
///
/// This is a singly-linked list, but instead of reversing the order of
/// insertion, we keep a pointer to the back of the list so we can push in
/// order.
///
/// There are two main reasons to choose a linked list over a customized
/// vector-like data structure.
///
/// 1. For teardown efficiency, we want DIEs to be BumpPtrAllocated. Using a
/// linked list here makes this way easier to accomplish.
/// 2. Carrying an extra pointer per \a DIEValue isn't expensive. 45% of DIEs
/// have 2 or fewer values, and 90% have 5 or fewer. A vector would be
/// over-allocated by 50% on average anyway, the same cost as the
/// linked-list node.
class DIEValueList {
struct Node : IntrusiveBackListNode {
DIEValue V;
explicit Node(DIEValue V) : V(V) {}
};
using ListTy = IntrusiveBackList<Node>;
ListTy List;
public:
class const_value_iterator;
class value_iterator
: public iterator_adaptor_base<value_iterator, ListTy::iterator,
std::forward_iterator_tag, DIEValue> {
friend class const_value_iterator;
using iterator_adaptor =
iterator_adaptor_base<value_iterator, ListTy::iterator,
std::forward_iterator_tag, DIEValue>;
public:
value_iterator() = default;
explicit value_iterator(ListTy::iterator X) : iterator_adaptor(X) {}
explicit operator bool() const { return bool(wrapped()); }
DIEValue &operator*() const { return wrapped()->V; }
};
class const_value_iterator : public iterator_adaptor_base<
const_value_iterator, ListTy::const_iterator,
std::forward_iterator_tag, const DIEValue> {
using iterator_adaptor =
iterator_adaptor_base<const_value_iterator, ListTy::const_iterator,
std::forward_iterator_tag, const DIEValue>;
public:
const_value_iterator() = default;
const_value_iterator(DIEValueList::value_iterator X)
: iterator_adaptor(X.wrapped()) {}
explicit const_value_iterator(ListTy::const_iterator X)
: iterator_adaptor(X) {}
explicit operator bool() const { return bool(wrapped()); }
const DIEValue &operator*() const { return wrapped()->V; }
};
using value_range = iterator_range<value_iterator>;
using const_value_range = iterator_range<const_value_iterator>;
value_iterator addValue(BumpPtrAllocator &Alloc, const DIEValue &V) {
List.push_back(*new (Alloc) Node(V));
return value_iterator(ListTy::toIterator(List.back()));
}
template <class T>
value_iterator addValue(BumpPtrAllocator &Alloc, dwarf::Attribute Attribute,
dwarf::Form Form, T &&Value) {
return addValue(Alloc, DIEValue(Attribute, Form, std::forward<T>(Value)));
}
/// Take ownership of the nodes in \p Other, and append them to the back of
/// the list.
void takeValues(DIEValueList &Other) { List.takeNodes(Other.List); }
value_range values() {
return make_range(value_iterator(List.begin()), value_iterator(List.end()));
}
const_value_range values() const {
return make_range(const_value_iterator(List.begin()),
const_value_iterator(List.end()));
}
};
//===--------------------------------------------------------------------===//
/// A structured debug information entry. Has an abbreviation which
/// describes its organization.
class DIE : IntrusiveBackListNode, public DIEValueList {
friend class IntrusiveBackList<DIE>;
friend class DIEUnit;
/// Dwarf unit relative offset.
unsigned Offset = 0;
/// Size of instance + children.
unsigned Size = 0;
unsigned AbbrevNumber = ~0u;
/// Dwarf tag code.
dwarf::Tag Tag = (dwarf::Tag)0;
/// Set to true to force a DIE to emit an abbreviation that says it has
/// children even when it doesn't. This is used for unit testing purposes.
bool ForceChildren = false;
/// Children DIEs.
IntrusiveBackList<DIE> Children;
/// The owner is either the parent DIE for children of other DIEs, or a
/// DIEUnit which contains this DIE as its unit DIE.
PointerUnion<DIE *, DIEUnit *> Owner;
explicit DIE(dwarf::Tag Tag) : Tag(Tag) {}
public:
DIE() = delete;
DIE(const DIE &RHS) = delete;
DIE(DIE &&RHS) = delete;
DIE &operator=(const DIE &RHS) = delete;
DIE &operator=(const DIE &&RHS) = delete;
static DIE *get(BumpPtrAllocator &Alloc, dwarf::Tag Tag) {
return new (Alloc) DIE(Tag);
}
// Accessors.
unsigned getAbbrevNumber() const { return AbbrevNumber; }
dwarf::Tag getTag() const { return Tag; }
/// Get the compile/type unit relative offset of this DIE.
unsigned getOffset() const {
// A real Offset can't be zero because the unit headers are at offset zero.
assert(Offset && "Offset being queried before it's been computed.");
return Offset;
}
unsigned getSize() const {
// A real Size can't be zero because it includes the non-empty abbrev code.
assert(Size && "Size being queried before it's been ocmputed.");
return Size;
}
bool hasChildren() const { return ForceChildren || !Children.empty(); }
void setForceChildren(bool B) { ForceChildren = B; }
using child_iterator = IntrusiveBackList<DIE>::iterator;
using const_child_iterator = IntrusiveBackList<DIE>::const_iterator;
using child_range = iterator_range<child_iterator>;
using const_child_range = iterator_range<const_child_iterator>;
child_range children() {
return make_range(Children.begin(), Children.end());
}
const_child_range children() const {
return make_range(Children.begin(), Children.end());
}
DIE *getParent() const;
/// Generate the abbreviation for this DIE.
///
/// Calculate the abbreviation for this, which should be uniqued and
/// eventually used to call \a setAbbrevNumber().
DIEAbbrev generateAbbrev() const;
/// Set the abbreviation number for this DIE.
void setAbbrevNumber(unsigned I) { AbbrevNumber = I; }
/// Get the absolute offset within the .debug_info or .debug_types section
/// for this DIE.
uint64_t getDebugSectionOffset() const;
/// Compute the offset of this DIE and all its children.
///
/// This function gets called just before we are going to generate the debug
/// information and gives each DIE a chance to figure out its CU relative DIE
/// offset, unique its abbreviation and fill in the abbreviation code, and
/// return the unit offset that points to where the next DIE will be emitted
/// within the debug unit section. After this function has been called for all
/// DIE objects, the DWARF can be generated since all DIEs will be able to
/// properly refer to other DIE objects since all DIEs have calculated their
/// offsets.
///
/// \param FormParams Used when calculating sizes.
/// \param AbbrevSet the abbreviation used to unique DIE abbreviations.
/// \param CUOffset the compile/type unit relative offset in bytes.
/// \returns the offset for the DIE that follows this DIE within the
/// current compile/type unit.
unsigned computeOffsetsAndAbbrevs(const dwarf::FormParams &FormParams,
DIEAbbrevSet &AbbrevSet, unsigned CUOffset);
/// Climb up the parent chain to get the compile unit or type unit DIE that
/// this DIE belongs to.
///
/// \returns the compile or type unit DIE that owns this DIE, or NULL if
/// this DIE hasn't been added to a unit DIE.
const DIE *getUnitDie() const;
/// Climb up the parent chain to get the compile unit or type unit that this
/// DIE belongs to.
///
/// \returns the DIEUnit that represents the compile or type unit that owns
/// this DIE, or NULL if this DIE hasn't been added to a unit DIE.
DIEUnit *getUnit() const;
void setOffset(unsigned O) { Offset = O; }
void setSize(unsigned S) { Size = S; }
/// Add a child to the DIE.
DIE &addChild(DIE *Child) {
assert(!Child->getParent() && "Child should be orphaned");
Child->Owner = this;
Children.push_back(*Child);
return Children.back();
}
DIE &addChildFront(DIE *Child) {
assert(!Child->getParent() && "Child should be orphaned");
Child->Owner = this;
Children.push_front(*Child);
return Children.front();
}
/// Find a value in the DIE with the attribute given.
///
/// Returns a default-constructed DIEValue (where \a DIEValue::getType()
/// gives \a DIEValue::isNone) if no such attribute exists.
DIEValue findAttribute(dwarf::Attribute Attribute) const;
void print(raw_ostream &O, unsigned IndentCount = 0) const;
void dump() const;
};
//===--------------------------------------------------------------------===//
/// Represents a compile or type unit.
class DIEUnit {
/// The compile unit or type unit DIE. This variable must be an instance of
/// DIE so that we can calculate the DIEUnit from any DIE by traversing the
/// parent backchain and getting the Unit DIE, and then casting itself to a
/// DIEUnit. This allows us to be able to find the DIEUnit for any DIE without
/// having to store a pointer to the DIEUnit in each DIE instance.
DIE Die;
/// The section this unit will be emitted in. This may or may not be set to
/// a valid section depending on the client that is emitting DWARF.
MCSection *Section = nullptr;
uint64_t Offset = 0; /// .debug_info or .debug_types absolute section offset.
protected:
virtual ~DIEUnit() = default;
public:
explicit DIEUnit(dwarf::Tag UnitTag);
DIEUnit(const DIEUnit &RHS) = delete;
DIEUnit(DIEUnit &&RHS) = delete;
void operator=(const DIEUnit &RHS) = delete;
void operator=(const DIEUnit &&RHS) = delete;
/// Set the section that this DIEUnit will be emitted into.
///
/// This function is used by some clients to set the section. Not all clients
/// that emit DWARF use this section variable.
void setSection(MCSection *Section) {
assert(!this->Section);
this->Section = Section;
}
virtual const MCSymbol *getCrossSectionRelativeBaseAddress() const {
return nullptr;
}
/// Return the section that this DIEUnit will be emitted into.
///
/// \returns Section pointer which can be NULL.
MCSection *getSection() const { return Section; }
void setDebugSectionOffset(uint64_t O) { Offset = O; }
uint64_t getDebugSectionOffset() const { return Offset; }
DIE &getUnitDie() { return Die; }
const DIE &getUnitDie() const { return Die; }
};
struct BasicDIEUnit final : DIEUnit {
explicit BasicDIEUnit(dwarf::Tag UnitTag) : DIEUnit(UnitTag) {}
};
//===--------------------------------------------------------------------===//
/// DIELoc - Represents an expression location.
//
class DIELoc : public DIEValueList {
mutable unsigned Size = 0; // Size in bytes excluding size header.
public:
DIELoc() = default;
/// Calculate the size of the location expression.
unsigned computeSize(const dwarf::FormParams &FormParams) const;
// TODO: move setSize() and Size to DIEValueList.
void setSize(unsigned size) { Size = size; }
/// BestForm - Choose the best form for data.
///
dwarf::Form BestForm(unsigned DwarfVersion) const {
if (DwarfVersion > 3)
return dwarf::DW_FORM_exprloc;
// Pre-DWARF4 location expressions were blocks and not exprloc.
if ((unsigned char)Size == Size)
return dwarf::DW_FORM_block1;
if ((unsigned short)Size == Size)
return dwarf::DW_FORM_block2;
if ((unsigned int)Size == Size)
return dwarf::DW_FORM_block4;
return dwarf::DW_FORM_block;
}
void emitValue(const AsmPrinter *Asm, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
//===--------------------------------------------------------------------===//
/// DIEBlock - Represents a block of values.
//
class DIEBlock : public DIEValueList {
mutable unsigned Size = 0; // Size in bytes excluding size header.
public:
DIEBlock() = default;
/// Calculate the size of the location expression.
unsigned computeSize(const dwarf::FormParams &FormParams) const;
// TODO: move setSize() and Size to DIEValueList.
void setSize(unsigned size) { Size = size; }
/// BestForm - Choose the best form for data.
///
dwarf::Form BestForm() const {
if ((unsigned char)Size == Size)
return dwarf::DW_FORM_block1;
if ((unsigned short)Size == Size)
return dwarf::DW_FORM_block2;
if ((unsigned int)Size == Size)
return dwarf::DW_FORM_block4;
return dwarf::DW_FORM_block;
}
void emitValue(const AsmPrinter *Asm, dwarf::Form Form) const;
unsigned sizeOf(const dwarf::FormParams &, dwarf::Form Form) const;
void print(raw_ostream &O) const;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_DIE_H

View File

@@ -0,0 +1,48 @@
//===- llvm/CodeGen/DIEValue.def - DIEValue types ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Macros for running through all types of DIEValue.
//
//===----------------------------------------------------------------------===//
#if !(defined HANDLE_DIEVALUE || defined HANDLE_DIEVALUE_SMALL || \
defined HANDLE_DIEVALUE_LARGE)
#error "Missing macro definition of HANDLE_DIEVALUE"
#endif
// Handler for all values.
#ifndef HANDLE_DIEVALUE
#define HANDLE_DIEVALUE(T)
#endif
// Handler for small values.
#ifndef HANDLE_DIEVALUE_SMALL
#define HANDLE_DIEVALUE_SMALL(T) HANDLE_DIEVALUE(T)
#endif
// Handler for large values.
#ifndef HANDLE_DIEVALUE_LARGE
#define HANDLE_DIEVALUE_LARGE(T) HANDLE_DIEVALUE(T)
#endif
HANDLE_DIEVALUE_SMALL(Integer)
HANDLE_DIEVALUE_SMALL(String)
HANDLE_DIEVALUE_SMALL(Expr)
HANDLE_DIEVALUE_SMALL(Label)
HANDLE_DIEVALUE_LARGE(BaseTypeRef)
HANDLE_DIEVALUE_LARGE(Delta)
HANDLE_DIEVALUE_SMALL(Entry)
HANDLE_DIEVALUE_LARGE(Block)
HANDLE_DIEVALUE_LARGE(Loc)
HANDLE_DIEVALUE_SMALL(LocList)
HANDLE_DIEVALUE_LARGE(InlineString)
HANDLE_DIEVALUE_LARGE(AddrOffset)
#undef HANDLE_DIEVALUE
#undef HANDLE_DIEVALUE_SMALL
#undef HANDLE_DIEVALUE_LARGE

View File

@@ -0,0 +1,156 @@
//===- llvm/CodeGen/DbgEntityHistoryCalculator.h ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_DBGENTITYHISTORYCALCULATOR_H
#define LLVM_CODEGEN_DBGENTITYHISTORYCALCULATOR_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/LexicalScopes.h"
#include <utility>
namespace llvm {
class DILocation;
class DINode;
class MachineFunction;
class MachineInstr;
class TargetRegisterInfo;
/// Record instruction ordering so we can query their relative positions within
/// a function. Meta instructions are given the same ordinal as the preceding
/// non-meta instruction. Class state is invalid if MF is modified after
/// calling initialize.
class InstructionOrdering {
public:
void initialize(const MachineFunction &MF);
void clear() { InstNumberMap.clear(); }
/// Check if instruction \p A comes before \p B, where \p A and \p B both
/// belong to the MachineFunction passed to initialize().
bool isBefore(const MachineInstr *A, const MachineInstr *B) const;
private:
/// Each instruction is assigned an order number.
DenseMap<const MachineInstr *, unsigned> InstNumberMap;
};
/// For each user variable, keep a list of instruction ranges where this
/// variable is accessible. The variables are listed in order of appearance.
class DbgValueHistoryMap {
public:
/// Index in the entry vector.
typedef size_t EntryIndex;
/// Special value to indicate that an entry is valid until the end of the
/// function.
static const EntryIndex NoEntry = std::numeric_limits<EntryIndex>::max();
/// Specifies a change in a variable's debug value history.
///
/// There exist two types of entries:
///
/// * Debug value entry:
///
/// A new debug value becomes live. If the entry's \p EndIndex is \p NoEntry,
/// the value is valid until the end of the function. For other values, the
/// index points to the entry in the entry vector that ends this debug
/// value. The ending entry can either be an overlapping debug value, or
/// an instruction that clobbers the value.
///
/// * Clobbering entry:
///
/// This entry's instruction clobbers one or more preceding
/// register-described debug values that have their end index
/// set to this entry's position in the entry vector.
class Entry {
friend DbgValueHistoryMap;
public:
enum EntryKind { DbgValue, Clobber };
Entry(const MachineInstr *Instr, EntryKind Kind)
: Instr(Instr, Kind), EndIndex(NoEntry) {}
const MachineInstr *getInstr() const { return Instr.getPointer(); }
EntryIndex getEndIndex() const { return EndIndex; }
EntryKind getEntryKind() const { return Instr.getInt(); }
bool isClobber() const { return getEntryKind() == Clobber; }
bool isDbgValue() const { return getEntryKind() == DbgValue; }
bool isClosed() const { return EndIndex != NoEntry; }
void endEntry(EntryIndex EndIndex);
private:
PointerIntPair<const MachineInstr *, 1, EntryKind> Instr;
EntryIndex EndIndex;
};
using Entries = SmallVector<Entry, 4>;
using InlinedEntity = std::pair<const DINode *, const DILocation *>;
using EntriesMap = MapVector<InlinedEntity, Entries>;
private:
EntriesMap VarEntries;
public:
bool startDbgValue(InlinedEntity Var, const MachineInstr &MI,
EntryIndex &NewIndex);
EntryIndex startClobber(InlinedEntity Var, const MachineInstr &MI);
Entry &getEntry(InlinedEntity Var, EntryIndex Index) {
auto &Entries = VarEntries[Var];
return Entries[Index];
}
/// Test whether a vector of entries features any non-empty locations. It
/// could have no entries, or only DBG_VALUE $noreg entries.
bool hasNonEmptyLocation(const Entries &Entries) const;
/// Drop location ranges which exist entirely outside each variable's scope.
void trimLocationRanges(const MachineFunction &MF, LexicalScopes &LScopes,
const InstructionOrdering &Ordering);
bool empty() const { return VarEntries.empty(); }
void clear() { VarEntries.clear(); }
EntriesMap::const_iterator begin() const { return VarEntries.begin(); }
EntriesMap::const_iterator end() const { return VarEntries.end(); }
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void dump() const;
#endif
};
/// For each inlined instance of a source-level label, keep the corresponding
/// DBG_LABEL instruction. The DBG_LABEL instruction could be used to generate
/// a temporary (assembler) label before it.
class DbgLabelInstrMap {
public:
using InlinedEntity = std::pair<const DINode *, const DILocation *>;
using InstrMap = MapVector<InlinedEntity, const MachineInstr *>;
private:
InstrMap LabelInstr;
public:
void addInstr(InlinedEntity Label, const MachineInstr &MI);
bool empty() const { return LabelInstr.empty(); }
void clear() { LabelInstr.clear(); }
InstrMap::const_iterator begin() const { return LabelInstr.begin(); }
InstrMap::const_iterator end() const { return LabelInstr.end(); }
};
void calculateDbgEntityHistory(const MachineFunction *MF,
const TargetRegisterInfo *TRI,
DbgValueHistoryMap &DbgValues,
DbgLabelInstrMap &DbgLabels);
} // end namespace llvm
#endif // LLVM_CODEGEN_DBGENTITYHISTORYCALCULATOR_H

View File

@@ -0,0 +1,146 @@
//===-- llvm/CodeGen/DebugHandlerBase.h -----------------------*- C++ -*--===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Common functionality for different debug information format backends.
// LLVM currently supports DWARF and CodeView.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_DEBUGHANDLERBASE_H
#define LLVM_CODEGEN_DEBUGHANDLERBASE_H
#include "llvm/ADT/Optional.h"
#include "llvm/CodeGen/AsmPrinterHandler.h"
#include "llvm/CodeGen/DbgEntityHistoryCalculator.h"
#include "llvm/CodeGen/LexicalScopes.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
namespace llvm {
class AsmPrinter;
class MachineInstr;
class MachineModuleInfo;
/// Represents the location at which a variable is stored.
struct DbgVariableLocation {
/// Base register.
unsigned Register;
/// Chain of offsetted loads necessary to load the value if it lives in
/// memory. Every load except for the last is pointer-sized.
SmallVector<int64_t, 1> LoadChain;
/// Present if the location is part of a larger variable.
llvm::Optional<llvm::DIExpression::FragmentInfo> FragmentInfo;
/// Extract a VariableLocation from a MachineInstr.
/// This will only work if Instruction is a debug value instruction
/// and the associated DIExpression is in one of the supported forms.
/// If these requirements are not met, the returned Optional will not
/// have a value.
static Optional<DbgVariableLocation>
extractFromMachineInstruction(const MachineInstr &Instruction);
};
/// Base class for debug information backends. Common functionality related to
/// tracking which variables and scopes are alive at a given PC live here.
class DebugHandlerBase : public AsmPrinterHandler {
protected:
DebugHandlerBase(AsmPrinter *A);
/// Target of debug info emission.
AsmPrinter *Asm;
/// Collected machine module information.
MachineModuleInfo *MMI;
/// Previous instruction's location information. This is used to
/// determine label location to indicate scope boundaries in debug info.
/// We track the previous instruction's source location (if not line 0),
/// whether it was a label, and its parent BB.
DebugLoc PrevInstLoc;
MCSymbol *PrevLabel = nullptr;
const MachineBasicBlock *PrevInstBB = nullptr;
/// This location indicates end of function prologue and beginning of
/// function body.
DebugLoc PrologEndLoc;
/// If nonnull, stores the current machine instruction we're processing.
const MachineInstr *CurMI = nullptr;
LexicalScopes LScopes;
/// History of DBG_VALUE and clobber instructions for each user
/// variable. Variables are listed in order of appearance.
DbgValueHistoryMap DbgValues;
/// Mapping of inlined labels and DBG_LABEL machine instruction.
DbgLabelInstrMap DbgLabels;
/// Maps instruction with label emitted before instruction.
/// FIXME: Make this private from DwarfDebug, we have the necessary accessors
/// for it.
DenseMap<const MachineInstr *, MCSymbol *> LabelsBeforeInsn;
/// Maps instruction with label emitted after instruction.
DenseMap<const MachineInstr *, MCSymbol *> LabelsAfterInsn;
/// Identify instructions that are marking the beginning of or
/// ending of a scope.
void identifyScopeMarkers();
/// Ensure that a label will be emitted before MI.
void requestLabelBeforeInsn(const MachineInstr *MI) {
LabelsBeforeInsn.insert(std::make_pair(MI, nullptr));
}
/// Ensure that a label will be emitted after MI.
void requestLabelAfterInsn(const MachineInstr *MI) {
LabelsAfterInsn.insert(std::make_pair(MI, nullptr));
}
virtual void beginFunctionImpl(const MachineFunction *MF) = 0;
virtual void endFunctionImpl(const MachineFunction *MF) = 0;
virtual void skippedNonDebugFunction() {}
private:
InstructionOrdering InstOrdering;
// AsmPrinterHandler overrides.
public:
void beginModule(Module *M) override;
void beginInstruction(const MachineInstr *MI) override;
void endInstruction() override;
void beginFunction(const MachineFunction *MF) override;
void endFunction(const MachineFunction *MF) override;
void beginBasicBlock(const MachineBasicBlock &MBB) override;
void endBasicBlock(const MachineBasicBlock &MBB) override;
/// Return Label preceding the instruction.
MCSymbol *getLabelBeforeInsn(const MachineInstr *MI);
/// Return Label immediately following the instruction.
MCSymbol *getLabelAfterInsn(const MachineInstr *MI);
/// If this type is derived from a base type then return base type size.
static uint64_t getBaseTypeSize(const DIType *Ty);
/// Return true if type encoding is unsigned.
static bool isUnsignedDIType(const DIType *Ty);
const InstructionOrdering &getInstOrdering() const { return InstOrdering; }
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,71 @@
//===- llvm/CodeGen/DwarfStringPoolEntry.h - String pool entry --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_DWARFSTRINGPOOLENTRY_H
#define LLVM_CODEGEN_DWARFSTRINGPOOLENTRY_H
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/StringMap.h"
namespace llvm {
class MCSymbol;
/// Data for a string pool entry.
struct DwarfStringPoolEntry {
static constexpr unsigned NotIndexed = -1;
MCSymbol *Symbol;
uint64_t Offset;
unsigned Index;
bool isIndexed() const { return Index != NotIndexed; }
};
/// String pool entry reference.
class DwarfStringPoolEntryRef {
PointerIntPair<const StringMapEntry<DwarfStringPoolEntry> *, 1, bool>
MapEntryAndIndexed;
const StringMapEntry<DwarfStringPoolEntry> *getMapEntry() const {
return MapEntryAndIndexed.getPointer();
}
public:
DwarfStringPoolEntryRef() = default;
DwarfStringPoolEntryRef(const StringMapEntry<DwarfStringPoolEntry> &Entry,
bool Indexed)
: MapEntryAndIndexed(&Entry, Indexed) {}
explicit operator bool() const { return getMapEntry(); }
MCSymbol *getSymbol() const {
assert(getMapEntry()->second.Symbol && "No symbol available!");
return getMapEntry()->second.Symbol;
}
uint64_t getOffset() const { return getMapEntry()->second.Offset; }
bool isIndexed() const { return MapEntryAndIndexed.getInt(); }
unsigned getIndex() const {
assert(isIndexed());
assert(getMapEntry()->getValue().isIndexed());
return getMapEntry()->second.Index;
}
StringRef getString() const { return getMapEntry()->first(); }
/// Return the entire string pool entry for convenience.
DwarfStringPoolEntry getEntry() const { return getMapEntry()->getValue(); }
bool operator==(const DwarfStringPoolEntryRef &X) const {
return getMapEntry() == X.getMapEntry();
}
bool operator!=(const DwarfStringPoolEntryRef &X) const {
return getMapEntry() != X.getMapEntry();
}
};
} // end namespace llvm
#endif

View File

@@ -0,0 +1,62 @@
//===-------- EdgeBundles.h - Bundles of CFG edges --------------*- c++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// The EdgeBundles analysis forms equivalence classes of CFG edges such that all
// edges leaving a machine basic block are in the same bundle, and all edges
// entering a machine basic block are in the same bundle.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_EDGEBUNDLES_H
#define LLVM_CODEGEN_EDGEBUNDLES_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/IntEqClasses.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
namespace llvm {
class EdgeBundles : public MachineFunctionPass {
const MachineFunction *MF;
/// EC - Each edge bundle is an equivalence class. The keys are:
/// 2*BB->getNumber() -> Ingoing bundle.
/// 2*BB->getNumber()+1 -> Outgoing bundle.
IntEqClasses EC;
/// Blocks - Map each bundle to a list of basic block numbers.
SmallVector<SmallVector<unsigned, 8>, 4> Blocks;
public:
static char ID;
EdgeBundles() : MachineFunctionPass(ID) {}
/// getBundle - Return the ingoing (Out = false) or outgoing (Out = true)
/// bundle number for basic block #N
unsigned getBundle(unsigned N, bool Out) const { return EC[2 * N + Out]; }
/// getNumBundles - Return the total number of bundles in the CFG.
unsigned getNumBundles() const { return EC.getNumClasses(); }
/// getBlocks - Return an array of blocks that are connected to Bundle.
ArrayRef<unsigned> getBlocks(unsigned Bundle) const { return Blocks[Bundle]; }
/// getMachineFunction - Return the last machine function computed.
const MachineFunction *getMachineFunction() const { return MF; }
/// view - Visualize the annotated bipartite CFG with Graphviz.
void view() const;
private:
bool runOnMachineFunction(MachineFunction&) override;
void getAnalysisUsage(AnalysisUsage&) const override;
};
} // end namespace llvm
#endif

View File

@@ -0,0 +1,221 @@
//==-- llvm/CodeGen/ExecutionDomainFix.h - Execution Domain Fix -*- C++ -*--==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file Execution Domain Fix pass.
///
/// Some X86 SSE instructions like mov, and, or, xor are available in different
/// variants for different operand types. These variant instructions are
/// equivalent, but on Nehalem and newer cpus there is extra latency
/// transferring data between integer and floating point domains. ARM cores
/// have similar issues when they are configured with both VFP and NEON
/// pipelines.
///
/// This pass changes the variant instructions to minimize domain crossings.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_EXECUTIONDOMAINFIX_H
#define LLVM_CODEGEN_EXECUTIONDOMAINFIX_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/LoopTraversal.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/ReachingDefAnalysis.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
namespace llvm {
class MachineInstr;
class TargetInstrInfo;
/// A DomainValue is a bit like LiveIntervals' ValNo, but it also keeps track
/// of execution domains.
///
/// An open DomainValue represents a set of instructions that can still switch
/// execution domain. Multiple registers may refer to the same open
/// DomainValue - they will eventually be collapsed to the same execution
/// domain.
///
/// A collapsed DomainValue represents a single register that has been forced
/// into one of more execution domains. There is a separate collapsed
/// DomainValue for each register, but it may contain multiple execution
/// domains. A register value is initially created in a single execution
/// domain, but if we were forced to pay the penalty of a domain crossing, we
/// keep track of the fact that the register is now available in multiple
/// domains.
struct DomainValue {
/// Basic reference counting.
unsigned Refs = 0;
/// Bitmask of available domains. For an open DomainValue, it is the still
/// possible domains for collapsing. For a collapsed DomainValue it is the
/// domains where the register is available for free.
unsigned AvailableDomains;
/// Pointer to the next DomainValue in a chain. When two DomainValues are
/// merged, Victim.Next is set to point to Victor, so old DomainValue
/// references can be updated by following the chain.
DomainValue *Next;
/// Twiddleable instructions using or defining these registers.
SmallVector<MachineInstr *, 8> Instrs;
DomainValue() { clear(); }
/// A collapsed DomainValue has no instructions to twiddle - it simply keeps
/// track of the domains where the registers are already available.
bool isCollapsed() const { return Instrs.empty(); }
/// Is domain available?
bool hasDomain(unsigned domain) const {
assert(domain <
static_cast<unsigned>(std::numeric_limits<unsigned>::digits) &&
"undefined behavior");
return AvailableDomains & (1u << domain);
}
/// Mark domain as available.
void addDomain(unsigned domain) {
assert(domain <
static_cast<unsigned>(std::numeric_limits<unsigned>::digits) &&
"undefined behavior");
AvailableDomains |= 1u << domain;
}
// Restrict to a single domain available.
void setSingleDomain(unsigned domain) {
assert(domain <
static_cast<unsigned>(std::numeric_limits<unsigned>::digits) &&
"undefined behavior");
AvailableDomains = 1u << domain;
}
/// Return bitmask of domains that are available and in mask.
unsigned getCommonDomains(unsigned mask) const {
return AvailableDomains & mask;
}
/// First domain available.
unsigned getFirstDomain() const {
return countTrailingZeros(AvailableDomains);
}
/// Clear this DomainValue and point to next which has all its data.
void clear() {
AvailableDomains = 0;
Next = nullptr;
Instrs.clear();
}
};
class ExecutionDomainFix : public MachineFunctionPass {
SpecificBumpPtrAllocator<DomainValue> Allocator;
SmallVector<DomainValue *, 16> Avail;
const TargetRegisterClass *const RC;
MachineFunction *MF;
const TargetInstrInfo *TII;
const TargetRegisterInfo *TRI;
std::vector<SmallVector<int, 1>> AliasMap;
const unsigned NumRegs;
/// Value currently in each register, or NULL when no value is being tracked.
/// This counts as a DomainValue reference.
using LiveRegsDVInfo = std::vector<DomainValue *>;
LiveRegsDVInfo LiveRegs;
/// Keeps domain information for all registers. Note that this
/// is different from the usual definition notion of liveness. The CPU
/// doesn't care whether or not we consider a register killed.
using OutRegsInfoMap = SmallVector<LiveRegsDVInfo, 4>;
OutRegsInfoMap MBBOutRegsInfos;
ReachingDefAnalysis *RDA;
public:
ExecutionDomainFix(char &PassID, const TargetRegisterClass &RC)
: MachineFunctionPass(PassID), RC(&RC), NumRegs(RC.getNumRegs()) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
AU.addRequired<ReachingDefAnalysis>();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool runOnMachineFunction(MachineFunction &MF) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
private:
/// Translate TRI register number to a list of indices into our smaller tables
/// of interesting registers.
iterator_range<SmallVectorImpl<int>::const_iterator>
regIndices(unsigned Reg) const;
/// DomainValue allocation.
DomainValue *alloc(int domain = -1);
/// Add reference to DV.
DomainValue *retain(DomainValue *DV) {
if (DV)
++DV->Refs;
return DV;
}
/// Release a reference to DV. When the last reference is released,
/// collapse if needed.
void release(DomainValue *);
/// Follow the chain of dead DomainValues until a live DomainValue is reached.
/// Update the referenced pointer when necessary.
DomainValue *resolve(DomainValue *&);
/// Set LiveRegs[rx] = dv, updating reference counts.
void setLiveReg(int rx, DomainValue *DV);
/// Kill register rx, recycle or collapse any DomainValue.
void kill(int rx);
/// Force register rx into domain.
void force(int rx, unsigned domain);
/// Collapse open DomainValue into given domain. If there are multiple
/// registers using dv, they each get a unique collapsed DomainValue.
void collapse(DomainValue *dv, unsigned domain);
/// All instructions and registers in B are moved to A, and B is released.
bool merge(DomainValue *A, DomainValue *B);
/// Set up LiveRegs by merging predecessor live-out values.
void enterBasicBlock(const LoopTraversal::TraversedMBBInfo &TraversedMBB);
/// Update live-out values.
void leaveBasicBlock(const LoopTraversal::TraversedMBBInfo &TraversedMBB);
/// Process he given basic block.
void processBasicBlock(const LoopTraversal::TraversedMBBInfo &TraversedMBB);
/// Visit given insturcion.
bool visitInstr(MachineInstr *);
/// Update def-ages for registers defined by MI.
/// If Kill is set, also kill off DomainValues clobbered by the defs.
void processDefs(MachineInstr *, bool Kill);
/// A soft instruction can be changed to work in other domains given by mask.
void visitSoftInstr(MachineInstr *, unsigned mask);
/// A hard instruction only works in one domain. All input registers will be
/// forced into that domain.
void visitHardInstr(MachineInstr *, unsigned domain);
};
} // namespace llvm
#endif // LLVM_CODEGEN_EXECUTIONDOMAINFIX_H

View File

@@ -0,0 +1,23 @@
//===----- ExpandReductions.h - Expand experimental reduction intrinsics --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_EXPANDREDUCTIONS_H
#define LLVM_CODEGEN_EXPANDREDUCTIONS_H
#include "llvm/IR/PassManager.h"
namespace llvm {
class ExpandReductionsPass
: public PassInfoMixin<ExpandReductionsPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};
} // end namespace llvm
#endif // LLVM_CODEGEN_EXPANDREDUCTIONS_H

View File

@@ -0,0 +1,23 @@
//===-- ExpandVectorPredication.h - Expand vector predication ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_EXPANDVECTORPREDICATION_H
#define LLVM_CODEGEN_EXPANDVECTORPREDICATION_H
#include "llvm/IR/PassManager.h"
namespace llvm {
class ExpandVectorPredicationPass
: public PassInfoMixin<ExpandVectorPredicationPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};
} // end namespace llvm
#endif // LLVM_CODEGEN_EXPANDVECTORPREDICATION_H

View File

@@ -0,0 +1,568 @@
//===- FastISel.h - Definition of the FastISel class ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the FastISel class.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_FASTISEL_H
#define LLVM_CODEGEN_FASTISEL_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/MachineValueType.h"
#include <algorithm>
#include <cstdint>
#include <utility>
namespace llvm {
class AllocaInst;
class BasicBlock;
class CallInst;
class Constant;
class ConstantFP;
class DataLayout;
class FunctionLoweringInfo;
class LoadInst;
class MachineConstantPool;
class MachineFrameInfo;
class MachineFunction;
class MachineInstr;
class MachineMemOperand;
class MachineOperand;
class MachineRegisterInfo;
class MCContext;
class MCInstrDesc;
class MCSymbol;
class TargetInstrInfo;
class TargetLibraryInfo;
class TargetMachine;
class TargetRegisterClass;
class TargetRegisterInfo;
class Type;
class User;
class Value;
/// This is a fast-path instruction selection class that generates poor
/// code and doesn't support illegal types or non-trivial lowering, but runs
/// quickly.
class FastISel {
public:
using ArgListEntry = TargetLoweringBase::ArgListEntry;
using ArgListTy = TargetLoweringBase::ArgListTy;
struct CallLoweringInfo {
Type *RetTy = nullptr;
bool RetSExt : 1;
bool RetZExt : 1;
bool IsVarArg : 1;
bool IsInReg : 1;
bool DoesNotReturn : 1;
bool IsReturnValueUsed : 1;
bool IsPatchPoint : 1;
// IsTailCall Should be modified by implementations of FastLowerCall
// that perform tail call conversions.
bool IsTailCall = false;
unsigned NumFixedArgs = -1;
CallingConv::ID CallConv = CallingConv::C;
const Value *Callee = nullptr;
MCSymbol *Symbol = nullptr;
ArgListTy Args;
const CallBase *CB = nullptr;
MachineInstr *Call = nullptr;
Register ResultReg;
unsigned NumResultRegs = 0;
SmallVector<Value *, 16> OutVals;
SmallVector<ISD::ArgFlagsTy, 16> OutFlags;
SmallVector<Register, 16> OutRegs;
SmallVector<ISD::InputArg, 4> Ins;
SmallVector<Register, 4> InRegs;
CallLoweringInfo()
: RetSExt(false), RetZExt(false), IsVarArg(false), IsInReg(false),
DoesNotReturn(false), IsReturnValueUsed(true), IsPatchPoint(false) {}
CallLoweringInfo &setCallee(Type *ResultTy, FunctionType *FuncTy,
const Value *Target, ArgListTy &&ArgsList,
const CallBase &Call) {
RetTy = ResultTy;
Callee = Target;
IsInReg = Call.hasRetAttr(Attribute::InReg);
DoesNotReturn = Call.doesNotReturn();
IsVarArg = FuncTy->isVarArg();
IsReturnValueUsed = !Call.use_empty();
RetSExt = Call.hasRetAttr(Attribute::SExt);
RetZExt = Call.hasRetAttr(Attribute::ZExt);
CallConv = Call.getCallingConv();
Args = std::move(ArgsList);
NumFixedArgs = FuncTy->getNumParams();
CB = &Call;
return *this;
}
CallLoweringInfo &setCallee(Type *ResultTy, FunctionType *FuncTy,
MCSymbol *Target, ArgListTy &&ArgsList,
const CallBase &Call,
unsigned FixedArgs = ~0U) {
RetTy = ResultTy;
Callee = Call.getCalledOperand();
Symbol = Target;
IsInReg = Call.hasRetAttr(Attribute::InReg);
DoesNotReturn = Call.doesNotReturn();
IsVarArg = FuncTy->isVarArg();
IsReturnValueUsed = !Call.use_empty();
RetSExt = Call.hasRetAttr(Attribute::SExt);
RetZExt = Call.hasRetAttr(Attribute::ZExt);
CallConv = Call.getCallingConv();
Args = std::move(ArgsList);
NumFixedArgs = (FixedArgs == ~0U) ? FuncTy->getNumParams() : FixedArgs;
CB = &Call;
return *this;
}
CallLoweringInfo &setCallee(CallingConv::ID CC, Type *ResultTy,
const Value *Target, ArgListTy &&ArgsList,
unsigned FixedArgs = ~0U) {
RetTy = ResultTy;
Callee = Target;
CallConv = CC;
Args = std::move(ArgsList);
NumFixedArgs = (FixedArgs == ~0U) ? Args.size() : FixedArgs;
return *this;
}
CallLoweringInfo &setCallee(const DataLayout &DL, MCContext &Ctx,
CallingConv::ID CC, Type *ResultTy,
StringRef Target, ArgListTy &&ArgsList,
unsigned FixedArgs = ~0U);
CallLoweringInfo &setCallee(CallingConv::ID CC, Type *ResultTy,
MCSymbol *Target, ArgListTy &&ArgsList,
unsigned FixedArgs = ~0U) {
RetTy = ResultTy;
Symbol = Target;
CallConv = CC;
Args = std::move(ArgsList);
NumFixedArgs = (FixedArgs == ~0U) ? Args.size() : FixedArgs;
return *this;
}
CallLoweringInfo &setTailCall(bool Value = true) {
IsTailCall = Value;
return *this;
}
CallLoweringInfo &setIsPatchPoint(bool Value = true) {
IsPatchPoint = Value;
return *this;
}
ArgListTy &getArgs() { return Args; }
void clearOuts() {
OutVals.clear();
OutFlags.clear();
OutRegs.clear();
}
void clearIns() {
Ins.clear();
InRegs.clear();
}
};
protected:
DenseMap<const Value *, Register> LocalValueMap;
FunctionLoweringInfo &FuncInfo;
MachineFunction *MF;
MachineRegisterInfo &MRI;
MachineFrameInfo &MFI;
MachineConstantPool &MCP;
DebugLoc DbgLoc;
const TargetMachine &TM;
const DataLayout &DL;
const TargetInstrInfo &TII;
const TargetLowering &TLI;
const TargetRegisterInfo &TRI;
const TargetLibraryInfo *LibInfo;
bool SkipTargetIndependentISel;
bool UseInstrRefDebugInfo = false;
/// The position of the last instruction for materializing constants
/// for use in the current block. It resets to EmitStartPt when it makes sense
/// (for example, it's usually profitable to avoid function calls between the
/// definition and the use)
MachineInstr *LastLocalValue = nullptr;
/// The top most instruction in the current block that is allowed for
/// emitting local variables. LastLocalValue resets to EmitStartPt when it
/// makes sense (for example, on function calls)
MachineInstr *EmitStartPt = nullptr;
public:
virtual ~FastISel();
/// Return the position of the last instruction emitted for
/// materializing constants for use in the current block.
MachineInstr *getLastLocalValue() { return LastLocalValue; }
/// Update the position of the last instruction emitted for
/// materializing constants for use in the current block.
void setLastLocalValue(MachineInstr *I) {
EmitStartPt = I;
LastLocalValue = I;
}
/// Set the current block to which generated machine instructions will
/// be appended.
void startNewBlock();
/// Flush the local value map.
void finishBasicBlock();
/// Return current debug location information.
DebugLoc getCurDebugLoc() const { return DbgLoc; }
/// Do "fast" instruction selection for function arguments and append
/// the machine instructions to the current block. Returns true when
/// successful.
bool lowerArguments();
/// Do "fast" instruction selection for the given LLVM IR instruction
/// and append the generated machine instructions to the current block.
/// Returns true if selection was successful.
bool selectInstruction(const Instruction *I);
/// Do "fast" instruction selection for the given LLVM IR operator
/// (Instruction or ConstantExpr), and append generated machine instructions
/// to the current block. Return true if selection was successful.
bool selectOperator(const User *I, unsigned Opcode);
/// Create a virtual register and arrange for it to be assigned the
/// value for the given LLVM value.
Register getRegForValue(const Value *V);
/// Look up the value to see if its value is already cached in a
/// register. It may be defined by instructions across blocks or defined
/// locally.
Register lookUpRegForValue(const Value *V);
/// This is a wrapper around getRegForValue that also takes care of
/// truncating or sign-extending the given getelementptr index value.
Register getRegForGEPIndex(const Value *Idx);
/// We're checking to see if we can fold \p LI into \p FoldInst. Note
/// that we could have a sequence where multiple LLVM IR instructions are
/// folded into the same machineinstr. For example we could have:
///
/// A: x = load i32 *P
/// B: y = icmp A, 42
/// C: br y, ...
///
/// In this scenario, \p LI is "A", and \p FoldInst is "C". We know about "B"
/// (and any other folded instructions) because it is between A and C.
///
/// If we succeed folding, return true.
bool tryToFoldLoad(const LoadInst *LI, const Instruction *FoldInst);
/// The specified machine instr operand is a vreg, and that vreg is
/// being provided by the specified load instruction. If possible, try to
/// fold the load as an operand to the instruction, returning true if
/// possible.
///
/// This method should be implemented by targets.
virtual bool tryToFoldLoadIntoMI(MachineInstr * /*MI*/, unsigned /*OpNo*/,
const LoadInst * /*LI*/) {
return false;
}
/// Reset InsertPt to prepare for inserting instructions into the
/// current block.
void recomputeInsertPt();
/// Remove all dead instructions between the I and E.
void removeDeadCode(MachineBasicBlock::iterator I,
MachineBasicBlock::iterator E);
using SavePoint = MachineBasicBlock::iterator;
/// Prepare InsertPt to begin inserting instructions into the local
/// value area and return the old insert position.
SavePoint enterLocalValueArea();
/// Reset InsertPt to the given old insert position.
void leaveLocalValueArea(SavePoint Old);
/// Signal whether instruction referencing variable locations are desired for
/// this function's debug-info.
void useInstrRefDebugInfo(bool Flag) {
UseInstrRefDebugInfo = Flag;
}
protected:
explicit FastISel(FunctionLoweringInfo &FuncInfo,
const TargetLibraryInfo *LibInfo,
bool SkipTargetIndependentISel = false);
/// This method is called by target-independent code when the normal
/// FastISel process fails to select an instruction. This gives targets a
/// chance to emit code for anything that doesn't fit into FastISel's
/// framework. It returns true if it was successful.
virtual bool fastSelectInstruction(const Instruction *I) = 0;
/// This method is called by target-independent code to do target-
/// specific argument lowering. It returns true if it was successful.
virtual bool fastLowerArguments();
/// This method is called by target-independent code to do target-
/// specific call lowering. It returns true if it was successful.
virtual bool fastLowerCall(CallLoweringInfo &CLI);
/// This method is called by target-independent code to do target-
/// specific intrinsic lowering. It returns true if it was successful.
virtual bool fastLowerIntrinsicCall(const IntrinsicInst *II);
/// This method is called by target-independent code to request that an
/// instruction with the given type and opcode be emitted.
virtual unsigned fastEmit_(MVT VT, MVT RetVT, unsigned Opcode);
/// This method is called by target-independent code to request that an
/// instruction with the given type, opcode, and register operand be emitted.
virtual unsigned fastEmit_r(MVT VT, MVT RetVT, unsigned Opcode, unsigned Op0);
/// This method is called by target-independent code to request that an
/// instruction with the given type, opcode, and register operands be emitted.
virtual unsigned fastEmit_rr(MVT VT, MVT RetVT, unsigned Opcode, unsigned Op0,
unsigned Op1);
/// This method is called by target-independent code to request that an
/// instruction with the given type, opcode, and register and immediate
/// operands be emitted.
virtual unsigned fastEmit_ri(MVT VT, MVT RetVT, unsigned Opcode, unsigned Op0,
uint64_t Imm);
/// This method is a wrapper of fastEmit_ri.
///
/// It first tries to emit an instruction with an immediate operand using
/// fastEmit_ri. If that fails, it materializes the immediate into a register
/// and try fastEmit_rr instead.
Register fastEmit_ri_(MVT VT, unsigned Opcode, unsigned Op0, uint64_t Imm,
MVT ImmType);
/// This method is called by target-independent code to request that an
/// instruction with the given type, opcode, and immediate operand be emitted.
virtual unsigned fastEmit_i(MVT VT, MVT RetVT, unsigned Opcode, uint64_t Imm);
/// This method is called by target-independent code to request that an
/// instruction with the given type, opcode, and floating-point immediate
/// operand be emitted.
virtual unsigned fastEmit_f(MVT VT, MVT RetVT, unsigned Opcode,
const ConstantFP *FPImm);
/// Emit a MachineInstr with no operands and a result register in the
/// given register class.
Register fastEmitInst_(unsigned MachineInstOpcode,
const TargetRegisterClass *RC);
/// Emit a MachineInstr with one register operand and a result register
/// in the given register class.
Register fastEmitInst_r(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, unsigned Op0);
/// Emit a MachineInstr with two register operands and a result
/// register in the given register class.
Register fastEmitInst_rr(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, unsigned Op0,
unsigned Op1);
/// Emit a MachineInstr with three register operands and a result
/// register in the given register class.
Register fastEmitInst_rrr(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, unsigned Op0,
unsigned Op1, unsigned Op2);
/// Emit a MachineInstr with a register operand, an immediate, and a
/// result register in the given register class.
Register fastEmitInst_ri(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, unsigned Op0,
uint64_t Imm);
/// Emit a MachineInstr with one register operand and two immediate
/// operands.
Register fastEmitInst_rii(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, unsigned Op0,
uint64_t Imm1, uint64_t Imm2);
/// Emit a MachineInstr with a floating point immediate, and a result
/// register in the given register class.
Register fastEmitInst_f(unsigned MachineInstOpcode,
const TargetRegisterClass *RC,
const ConstantFP *FPImm);
/// Emit a MachineInstr with two register operands, an immediate, and a
/// result register in the given register class.
Register fastEmitInst_rri(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, unsigned Op0,
unsigned Op1, uint64_t Imm);
/// Emit a MachineInstr with a single immediate operand, and a result
/// register in the given register class.
Register fastEmitInst_i(unsigned MachineInstOpcode,
const TargetRegisterClass *RC, uint64_t Imm);
/// Emit a MachineInstr for an extract_subreg from a specified index of
/// a superregister to a specified type.
Register fastEmitInst_extractsubreg(MVT RetVT, unsigned Op0, uint32_t Idx);
/// Emit MachineInstrs to compute the value of Op with all but the
/// least significant bit set to zero.
Register fastEmitZExtFromI1(MVT VT, unsigned Op0);
/// Emit an unconditional branch to the given block, unless it is the
/// immediate (fall-through) successor, and update the CFG.
void fastEmitBranch(MachineBasicBlock *MSucc, const DebugLoc &DbgLoc);
/// Emit an unconditional branch to \p FalseMBB, obtains the branch weight
/// and adds TrueMBB and FalseMBB to the successor list.
void finishCondBranch(const BasicBlock *BranchBB, MachineBasicBlock *TrueMBB,
MachineBasicBlock *FalseMBB);
/// Update the value map to include the new mapping for this
/// instruction, or insert an extra copy to get the result in a previous
/// determined register.
///
/// NOTE: This is only necessary because we might select a block that uses a
/// value before we select the block that defines the value. It might be
/// possible to fix this by selecting blocks in reverse postorder.
void updateValueMap(const Value *I, Register Reg, unsigned NumRegs = 1);
Register createResultReg(const TargetRegisterClass *RC);
/// Try to constrain Op so that it is usable by argument OpNum of the
/// provided MCInstrDesc. If this fails, create a new virtual register in the
/// correct class and COPY the value there.
Register constrainOperandRegClass(const MCInstrDesc &II, Register Op,
unsigned OpNum);
/// Emit a constant in a register using target-specific logic, such as
/// constant pool loads.
virtual unsigned fastMaterializeConstant(const Constant *C) { return 0; }
/// Emit an alloca address in a register using target-specific logic.
virtual unsigned fastMaterializeAlloca(const AllocaInst *C) { return 0; }
/// Emit the floating-point constant +0.0 in a register using target-
/// specific logic.
virtual unsigned fastMaterializeFloatZero(const ConstantFP *CF) {
return 0;
}
/// Check if \c Add is an add that can be safely folded into \c GEP.
///
/// \c Add can be folded into \c GEP if:
/// - \c Add is an add,
/// - \c Add's size matches \c GEP's,
/// - \c Add is in the same basic block as \c GEP, and
/// - \c Add has a constant operand.
bool canFoldAddIntoGEP(const User *GEP, const Value *Add);
/// Create a machine mem operand from the given instruction.
MachineMemOperand *createMachineMemOperandFor(const Instruction *I) const;
CmpInst::Predicate optimizeCmpPredicate(const CmpInst *CI) const;
bool lowerCallTo(const CallInst *CI, MCSymbol *Symbol, unsigned NumArgs);
bool lowerCallTo(const CallInst *CI, const char *SymName,
unsigned NumArgs);
bool lowerCallTo(CallLoweringInfo &CLI);
bool lowerCall(const CallInst *I);
/// Select and emit code for a binary operator instruction, which has
/// an opcode which directly corresponds to the given ISD opcode.
bool selectBinaryOp(const User *I, unsigned ISDOpcode);
bool selectFNeg(const User *I, const Value *In);
bool selectGetElementPtr(const User *I);
bool selectStackmap(const CallInst *I);
bool selectPatchpoint(const CallInst *I);
bool selectCall(const User *I);
bool selectIntrinsicCall(const IntrinsicInst *II);
bool selectBitCast(const User *I);
bool selectFreeze(const User *I);
bool selectCast(const User *I, unsigned Opcode);
bool selectExtractValue(const User *U);
bool selectXRayCustomEvent(const CallInst *II);
bool selectXRayTypedEvent(const CallInst *II);
bool shouldOptForSize(const MachineFunction *MF) const {
// TODO: Implement PGSO.
return MF->getFunction().hasOptSize();
}
private:
/// Handle PHI nodes in successor blocks.
///
/// Emit code to ensure constants are copied into registers when needed.
/// Remember the virtual registers that need to be added to the Machine PHI
/// nodes as input. We cannot just directly add them, because expansion might
/// result in multiple MBB's for one BB. As such, the start of the BB might
/// correspond to a different MBB than the end.
bool handlePHINodesInSuccessorBlocks(const BasicBlock *LLVMBB);
/// Helper for materializeRegForValue to materialize a constant in a
/// target-independent way.
Register materializeConstant(const Value *V, MVT VT);
/// Helper for getRegForVale. This function is called when the value
/// isn't already available in a register and must be materialized with new
/// instructions.
Register materializeRegForValue(const Value *V, MVT VT);
/// Clears LocalValueMap and moves the area for the new local variables
/// to the beginning of the block. It helps to avoid spilling cached variables
/// across heavy instructions like calls.
void flushLocalValueMap();
/// Removes dead local value instructions after SavedLastLocalvalue.
void removeDeadLocalValueCode(MachineInstr *SavedLastLocalValue);
/// Insertion point before trying to select the current instruction.
MachineBasicBlock::iterator SavedInsertPt;
/// Add a stackmap or patchpoint intrinsic call's live variable
/// operands to a stackmap or patchpoint machine instruction.
bool addStackMapLiveVars(SmallVectorImpl<MachineOperand> &Ops,
const CallInst *CI, unsigned StartIdx);
bool lowerCallOperands(const CallInst *CI, unsigned ArgIdx, unsigned NumArgs,
const Value *Callee, bool ForceRetVoidTy,
CallLoweringInfo &CLI);
};
} // end namespace llvm
#endif // LLVM_CODEGEN_FASTISEL_H

View File

@@ -0,0 +1,77 @@
//===- FaultMaps.h - The "FaultMaps" section --------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_FAULTMAPS_H
#define LLVM_CODEGEN_FAULTMAPS_H
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/Endian.h"
#include <map>
#include <vector>
namespace llvm {
class AsmPrinter;
class MCExpr;
class FaultMaps {
public:
enum FaultKind {
FaultingLoad = 1,
FaultingLoadStore,
FaultingStore,
FaultKindMax
};
explicit FaultMaps(AsmPrinter &AP);
static const char *faultTypeToString(FaultKind);
void recordFaultingOp(FaultKind FaultTy, const MCSymbol *FaultingLabel,
const MCSymbol *HandlerLabel);
void serializeToFaultMapSection();
void reset() {
FunctionInfos.clear();
}
private:
static const char *WFMP;
struct FaultInfo {
FaultKind Kind = FaultKindMax;
const MCExpr *FaultingOffsetExpr = nullptr;
const MCExpr *HandlerOffsetExpr = nullptr;
FaultInfo() = default;
explicit FaultInfo(FaultMaps::FaultKind Kind, const MCExpr *FaultingOffset,
const MCExpr *HandlerOffset)
: Kind(Kind), FaultingOffsetExpr(FaultingOffset),
HandlerOffsetExpr(HandlerOffset) {}
};
using FunctionFaultInfos = std::vector<FaultInfo>;
// We'd like to keep a stable iteration order for FunctionInfos to help
// FileCheck based testing.
struct MCSymbolComparator {
bool operator()(const MCSymbol *LHS, const MCSymbol *RHS) const {
return LHS->getName() < RHS->getName();
}
};
std::map<const MCSymbol *, FunctionFaultInfos, MCSymbolComparator>
FunctionInfos;
AsmPrinter &AP;
void emitFunctionInfo(const MCSymbol *FnLabel, const FunctionFaultInfos &FFI);
};
} // end namespace llvm
#endif // LLVM_CODEGEN_FAULTMAPS_H

View File

@@ -0,0 +1,285 @@
//===- FunctionLoweringInfo.h - Lower functions from LLVM IR ---*- C++ -*--===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This implements routines for translating functions from LLVM IR into
// Machine IR.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_FUNCTIONLOWERINGINFO_H
#define LLVM_CODEGEN_FUNCTIONLOWERINGINFO_H
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/KnownBits.h"
#include <cassert>
#include <utility>
#include <vector>
namespace llvm {
class Argument;
class BasicBlock;
class BranchProbabilityInfo;
class LegacyDivergenceAnalysis;
class Function;
class Instruction;
class MachineFunction;
class MachineInstr;
class MachineRegisterInfo;
class MVT;
class SelectionDAG;
class TargetLowering;
//===--------------------------------------------------------------------===//
/// FunctionLoweringInfo - This contains information that is global to a
/// function that is used when lowering a region of the function.
///
class FunctionLoweringInfo {
public:
const Function *Fn;
MachineFunction *MF;
const TargetLowering *TLI;
MachineRegisterInfo *RegInfo;
BranchProbabilityInfo *BPI;
const LegacyDivergenceAnalysis *DA;
/// CanLowerReturn - true iff the function's return value can be lowered to
/// registers.
bool CanLowerReturn;
/// True if part of the CSRs will be handled via explicit copies.
bool SplitCSR;
/// DemoteRegister - if CanLowerReturn is false, DemoteRegister is a vreg
/// allocated to hold a pointer to the hidden sret parameter.
Register DemoteRegister;
/// MBBMap - A mapping from LLVM basic blocks to their machine code entry.
DenseMap<const BasicBlock*, MachineBasicBlock *> MBBMap;
/// ValueMap - Since we emit code for the function a basic block at a time,
/// we must remember which virtual registers hold the values for
/// cross-basic-block values.
DenseMap<const Value *, Register> ValueMap;
/// VirtReg2Value map is needed by the Divergence Analysis driven
/// instruction selection. It is reverted ValueMap. It is computed
/// in lazy style - on demand. It is used to get the Value corresponding
/// to the live in virtual register and is called from the
/// TargetLowerinInfo::isSDNodeSourceOfDivergence.
DenseMap<Register, const Value*> VirtReg2Value;
/// This method is called from TargetLowerinInfo::isSDNodeSourceOfDivergence
/// to get the Value corresponding to the live-in virtual register.
const Value *getValueFromVirtualReg(Register Vreg);
/// Track virtual registers created for exception pointers.
DenseMap<const Value *, Register> CatchPadExceptionPointers;
/// Helper object to track which of three possible relocation mechanisms are
/// used for a particular value being relocated over a statepoint.
struct StatepointRelocationRecord {
enum RelocType {
// Value did not need to be relocated and can be used directly.
NoRelocate,
// Value was spilled to stack and needs filled at the gc.relocate.
Spill,
// Value was lowered to tied def and gc.relocate should be replaced with
// copy from vreg.
VReg,
} type = NoRelocate;
// Payload contains either frame index of the stack slot in which the value
// was spilled, or virtual register which contains the re-definition.
union payload_t {
payload_t() : FI(-1) {}
int FI;
Register Reg;
} payload;
};
/// Keep track of each value which was relocated and the strategy used to
/// relocate that value. This information is required when visiting
/// gc.relocates which may appear in following blocks.
using StatepointSpillMapTy =
DenseMap<const Value *, StatepointRelocationRecord>;
DenseMap<const Instruction *, StatepointSpillMapTy> StatepointRelocationMaps;
/// StaticAllocaMap - Keep track of frame indices for fixed sized allocas in
/// the entry block. This allows the allocas to be efficiently referenced
/// anywhere in the function.
DenseMap<const AllocaInst*, int> StaticAllocaMap;
/// ByValArgFrameIndexMap - Keep track of frame indices for byval arguments.
DenseMap<const Argument*, int> ByValArgFrameIndexMap;
/// ArgDbgValues - A list of DBG_VALUE instructions created during isel for
/// function arguments that are inserted after scheduling is completed.
SmallVector<MachineInstr*, 8> ArgDbgValues;
/// Bitvector with a bit set if corresponding argument is described in
/// ArgDbgValues. Using arg numbers according to Argument numbering.
BitVector DescribedArgs;
/// RegFixups - Registers which need to be replaced after isel is done.
DenseMap<Register, Register> RegFixups;
DenseSet<Register> RegsWithFixups;
/// StatepointStackSlots - A list of temporary stack slots (frame indices)
/// used to spill values at a statepoint. We store them here to enable
/// reuse of the same stack slots across different statepoints in different
/// basic blocks.
SmallVector<unsigned, 50> StatepointStackSlots;
/// MBB - The current block.
MachineBasicBlock *MBB;
/// MBB - The current insert position inside the current block.
MachineBasicBlock::iterator InsertPt;
struct LiveOutInfo {
unsigned NumSignBits : 31;
unsigned IsValid : 1;
KnownBits Known = 1;
LiveOutInfo() : NumSignBits(0), IsValid(true) {}
};
/// Record the preferred extend type (ISD::SIGN_EXTEND or ISD::ZERO_EXTEND)
/// for a value.
DenseMap<const Value *, ISD::NodeType> PreferredExtendType;
/// VisitedBBs - The set of basic blocks visited thus far by instruction
/// selection.
SmallPtrSet<const BasicBlock*, 4> VisitedBBs;
/// PHINodesToUpdate - A list of phi instructions whose operand list will
/// be updated after processing the current basic block.
/// TODO: This isn't per-function state, it's per-basic-block state. But
/// there's no other convenient place for it to live right now.
std::vector<std::pair<MachineInstr*, unsigned> > PHINodesToUpdate;
unsigned OrigNumPHINodesToUpdate;
/// If the current MBB is a landing pad, the exception pointer and exception
/// selector registers are copied into these virtual registers by
/// SelectionDAGISel::PrepareEHLandingPad().
unsigned ExceptionPointerVirtReg, ExceptionSelectorVirtReg;
/// set - Initialize this FunctionLoweringInfo with the given Function
/// and its associated MachineFunction.
///
void set(const Function &Fn, MachineFunction &MF, SelectionDAG *DAG);
/// clear - Clear out all the function-specific state. This returns this
/// FunctionLoweringInfo to an empty state, ready to be used for a
/// different function.
void clear();
/// isExportedInst - Return true if the specified value is an instruction
/// exported from its block.
bool isExportedInst(const Value *V) const {
return ValueMap.count(V);
}
Register CreateReg(MVT VT, bool isDivergent = false);
Register CreateRegs(const Value *V);
Register CreateRegs(Type *Ty, bool isDivergent = false);
Register InitializeRegForValue(const Value *V) {
// Tokens never live in vregs.
if (V->getType()->isTokenTy())
return 0;
Register &R = ValueMap[V];
assert(R == 0 && "Already initialized this value register!");
assert(VirtReg2Value.empty());
return R = CreateRegs(V);
}
/// GetLiveOutRegInfo - Gets LiveOutInfo for a register, returning NULL if the
/// register is a PHI destination and the PHI's LiveOutInfo is not valid.
const LiveOutInfo *GetLiveOutRegInfo(Register Reg) {
if (!LiveOutRegInfo.inBounds(Reg))
return nullptr;
const LiveOutInfo *LOI = &LiveOutRegInfo[Reg];
if (!LOI->IsValid)
return nullptr;
return LOI;
}
/// GetLiveOutRegInfo - Gets LiveOutInfo for a register, returning NULL if the
/// register is a PHI destination and the PHI's LiveOutInfo is not valid. If
/// the register's LiveOutInfo is for a smaller bit width, it is extended to
/// the larger bit width by zero extension. The bit width must be no smaller
/// than the LiveOutInfo's existing bit width.
const LiveOutInfo *GetLiveOutRegInfo(Register Reg, unsigned BitWidth);
/// AddLiveOutRegInfo - Adds LiveOutInfo for a register.
void AddLiveOutRegInfo(Register Reg, unsigned NumSignBits,
const KnownBits &Known) {
// Only install this information if it tells us something.
if (NumSignBits == 1 && Known.isUnknown())
return;
LiveOutRegInfo.grow(Reg);
LiveOutInfo &LOI = LiveOutRegInfo[Reg];
LOI.NumSignBits = NumSignBits;
LOI.Known.One = Known.One;
LOI.Known.Zero = Known.Zero;
}
/// ComputePHILiveOutRegInfo - Compute LiveOutInfo for a PHI's destination
/// register based on the LiveOutInfo of its operands.
void ComputePHILiveOutRegInfo(const PHINode*);
/// InvalidatePHILiveOutRegInfo - Invalidates a PHI's LiveOutInfo, to be
/// called when a block is visited before all of its predecessors.
void InvalidatePHILiveOutRegInfo(const PHINode *PN) {
// PHIs with no uses have no ValueMap entry.
DenseMap<const Value*, Register>::const_iterator It = ValueMap.find(PN);
if (It == ValueMap.end())
return;
Register Reg = It->second;
if (Reg == 0)
return;
LiveOutRegInfo.grow(Reg);
LiveOutRegInfo[Reg].IsValid = false;
}
/// setArgumentFrameIndex - Record frame index for the byval
/// argument.
void setArgumentFrameIndex(const Argument *A, int FI);
/// getArgumentFrameIndex - Get frame index for the byval argument.
int getArgumentFrameIndex(const Argument *A);
Register getCatchPadExceptionPointerVReg(const Value *CPI,
const TargetRegisterClass *RC);
private:
/// LiveOutRegInfo - Information about live out vregs.
IndexedMap<LiveOutInfo, VirtReg2IndexFunctor> LiveOutRegInfo;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_FUNCTIONLOWERINGINFO_H

View File

@@ -0,0 +1,205 @@
//===- GCMetadata.h - Garbage collector metadata ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares the GCFunctionInfo and GCModuleInfo classes, which are
// used as a communication channel from the target code generator to the target
// garbage collectors. This interface allows code generators and garbage
// collectors to be developed independently.
//
// The GCFunctionInfo class logs the data necessary to build a type accurate
// stack map. The code generator outputs:
//
// - Safe points as specified by the GCStrategy's NeededSafePoints.
// - Stack offsets for GC roots, as specified by calls to llvm.gcroot
//
// As a refinement, liveness analysis calculates the set of live roots at each
// safe point. Liveness analysis is not presently performed by the code
// generator, so all roots are assumed live.
//
// GCModuleInfo simply collects GCFunctionInfo instances for each Function as
// they are compiled. This accretion is necessary for collectors which must emit
// a stack map for the compilation unit as a whole. Therefore, GCFunctionInfo
// outlives the MachineFunction from which it is derived and must not refer to
// any code generator data structures.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GCMETADATA_H
#define LLVM_CODEGEN_GCMETADATA_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/GCStrategy.h"
#include "llvm/Pass.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
namespace llvm {
class Constant;
class Function;
class MCSymbol;
/// GCPoint - Metadata for a collector-safe point in machine code.
///
struct GCPoint {
MCSymbol *Label; ///< A label.
DebugLoc Loc;
GCPoint(MCSymbol *L, DebugLoc DL)
: Label(L), Loc(std::move(DL)) {}
};
/// GCRoot - Metadata for a pointer to an object managed by the garbage
/// collector.
struct GCRoot {
int Num; ///< Usually a frame index.
int StackOffset = -1; ///< Offset from the stack pointer.
const Constant *Metadata; ///< Metadata straight from the call
///< to llvm.gcroot.
GCRoot(int N, const Constant *MD) : Num(N), Metadata(MD) {}
};
/// Garbage collection metadata for a single function. Currently, this
/// information only applies to GCStrategies which use GCRoot.
class GCFunctionInfo {
public:
using iterator = std::vector<GCPoint>::iterator;
using roots_iterator = std::vector<GCRoot>::iterator;
using live_iterator = std::vector<GCRoot>::const_iterator;
private:
const Function &F;
GCStrategy &S;
uint64_t FrameSize;
std::vector<GCRoot> Roots;
std::vector<GCPoint> SafePoints;
// FIXME: Liveness. A 2D BitVector, perhaps?
//
// BitVector Liveness;
//
// bool islive(int point, int root) =
// Liveness[point * SafePoints.size() + root]
//
// The bit vector is the more compact representation where >3.2% of roots
// are live per safe point (1.5% on 64-bit hosts).
public:
GCFunctionInfo(const Function &F, GCStrategy &S);
~GCFunctionInfo();
/// getFunction - Return the function to which this metadata applies.
const Function &getFunction() const { return F; }
/// getStrategy - Return the GC strategy for the function.
GCStrategy &getStrategy() { return S; }
/// addStackRoot - Registers a root that lives on the stack. Num is the
/// stack object ID for the alloca (if the code generator is
// using MachineFrameInfo).
void addStackRoot(int Num, const Constant *Metadata) {
Roots.push_back(GCRoot(Num, Metadata));
}
/// removeStackRoot - Removes a root.
roots_iterator removeStackRoot(roots_iterator position) {
return Roots.erase(position);
}
/// addSafePoint - Notes the existence of a safe point. Num is the ID of the
/// label just prior to the safe point (if the code generator is using
/// MachineModuleInfo).
void addSafePoint(MCSymbol *Label, const DebugLoc &DL) {
SafePoints.emplace_back(Label, DL);
}
/// getFrameSize/setFrameSize - Records the function's frame size.
uint64_t getFrameSize() const { return FrameSize; }
void setFrameSize(uint64_t S) { FrameSize = S; }
/// begin/end - Iterators for safe points.
iterator begin() { return SafePoints.begin(); }
iterator end() { return SafePoints.end(); }
size_t size() const { return SafePoints.size(); }
/// roots_begin/roots_end - Iterators for all roots in the function.
roots_iterator roots_begin() { return Roots.begin(); }
roots_iterator roots_end() { return Roots.end(); }
size_t roots_size() const { return Roots.size(); }
/// live_begin/live_end - Iterators for live roots at a given safe point.
live_iterator live_begin(const iterator &p) { return roots_begin(); }
live_iterator live_end(const iterator &p) { return roots_end(); }
size_t live_size(const iterator &p) const { return roots_size(); }
};
/// An analysis pass which caches information about the entire Module.
/// Records both the function level information used by GCRoots and a
/// cache of the 'active' gc strategy objects for the current Module.
class GCModuleInfo : public ImmutablePass {
/// An owning list of all GCStrategies which have been created
SmallVector<std::unique_ptr<GCStrategy>, 1> GCStrategyList;
/// A helper map to speedup lookups into the above list
StringMap<GCStrategy*> GCStrategyMap;
public:
/// Lookup the GCStrategy object associated with the given gc name.
/// Objects are owned internally; No caller should attempt to delete the
/// returned objects.
GCStrategy *getGCStrategy(const StringRef Name);
/// List of per function info objects. In theory, Each of these
/// may be associated with a different GC.
using FuncInfoVec = std::vector<std::unique_ptr<GCFunctionInfo>>;
FuncInfoVec::iterator funcinfo_begin() { return Functions.begin(); }
FuncInfoVec::iterator funcinfo_end() { return Functions.end(); }
private:
/// Owning list of all GCFunctionInfos associated with this Module
FuncInfoVec Functions;
/// Non-owning map to bypass linear search when finding the GCFunctionInfo
/// associated with a particular Function.
using finfo_map_type = DenseMap<const Function *, GCFunctionInfo *>;
finfo_map_type FInfoMap;
public:
using iterator = SmallVector<std::unique_ptr<GCStrategy>, 1>::const_iterator;
static char ID;
GCModuleInfo();
/// clear - Resets the pass. Any pass, which uses GCModuleInfo, should
/// call it in doFinalization().
///
void clear();
/// begin/end - Iterators for used strategies.
///
iterator begin() const { return GCStrategyList.begin(); }
iterator end() const { return GCStrategyList.end(); }
/// get - Look up function metadata. This is currently assumed
/// have the side effect of initializing the associated GCStrategy. That
/// will soon change.
GCFunctionInfo &getFunctionInfo(const Function &F);
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GCMETADATA_H

View File

@@ -0,0 +1,72 @@
//===- llvm/CodeGen/GCMetadataPrinter.h - Prints asm GC tables --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// The abstract base class GCMetadataPrinter supports writing GC metadata tables
// as assembly code. This is a separate class from GCStrategy in order to allow
// users of the LLVM JIT to avoid linking with the AsmWriter.
//
// Subclasses of GCMetadataPrinter must be registered using the
// GCMetadataPrinterRegistry. This is separate from the GCStrategy itself
// because these subclasses are logically plugins for the AsmWriter.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GCMETADATAPRINTER_H
#define LLVM_CODEGEN_GCMETADATAPRINTER_H
#include "llvm/Support/Registry.h"
namespace llvm {
class AsmPrinter;
class GCMetadataPrinter;
class GCModuleInfo;
class GCStrategy;
class Module;
class StackMaps;
/// GCMetadataPrinterRegistry - The GC assembly printer registry uses all the
/// defaults from Registry.
using GCMetadataPrinterRegistry = Registry<GCMetadataPrinter>;
/// GCMetadataPrinter - Emits GC metadata as assembly code. Instances are
/// created, managed, and owned by the AsmPrinter.
class GCMetadataPrinter {
private:
friend class AsmPrinter;
GCStrategy *S;
protected:
// May only be subclassed.
GCMetadataPrinter();
public:
GCMetadataPrinter(const GCMetadataPrinter &) = delete;
GCMetadataPrinter &operator=(const GCMetadataPrinter &) = delete;
virtual ~GCMetadataPrinter();
GCStrategy &getStrategy() { return *S; }
/// Called before the assembly for the module is generated by
/// the AsmPrinter (but after target specific hooks.)
virtual void beginAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) {}
/// Called after the assembly for the module is generated by
/// the AsmPrinter (but before target specific hooks)
virtual void finishAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) {}
/// Called when the stack maps are generated. Return true if
/// stack maps with a custom format are generated. Otherwise
/// returns false and the default format will be used.
virtual bool emitStackMaps(StackMaps &SM, AsmPrinter &AP) { return false; }
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GCMETADATAPRINTER_H

View File

@@ -0,0 +1,242 @@
//===- llvm/CodeGen/GlobalISel/CSEInfo.h ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Provides analysis for continuously CSEing during GISel passes.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_CSEINFO_H
#define LLVM_CODEGEN_GLOBALISEL_CSEINFO_H
#include "llvm/ADT/FoldingSet.h"
#include "llvm/CodeGen/CSEConfigBase.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/CodeGen.h"
namespace llvm {
class MachineBasicBlock;
/// A class that wraps MachineInstrs and derives from FoldingSetNode in order to
/// be uniqued in a CSEMap. The tradeoff here is extra memory allocations for
/// UniqueMachineInstr vs making MachineInstr bigger.
class UniqueMachineInstr : public FoldingSetNode {
friend class GISelCSEInfo;
const MachineInstr *MI;
explicit UniqueMachineInstr(const MachineInstr *MI) : MI(MI) {}
public:
void Profile(FoldingSetNodeID &ID);
};
// A CSE config for fully optimized builds.
class CSEConfigFull : public CSEConfigBase {
public:
virtual ~CSEConfigFull() = default;
virtual bool shouldCSEOpc(unsigned Opc) override;
};
// Commonly used for O0 config.
class CSEConfigConstantOnly : public CSEConfigBase {
public:
virtual ~CSEConfigConstantOnly() = default;
virtual bool shouldCSEOpc(unsigned Opc) override;
};
// Returns the standard expected CSEConfig for the given optimization level.
// We have this logic here so targets can make use of it from their derived
// TargetPassConfig, but can't put this logic into TargetPassConfig directly
// because the CodeGen library can't depend on GlobalISel.
std::unique_ptr<CSEConfigBase>
getStandardCSEConfigForOpt(CodeGenOpt::Level Level);
/// The CSE Analysis object.
/// This installs itself as a delegate to the MachineFunction to track
/// new instructions as well as deletions. It however will not be able to
/// track instruction mutations. In such cases, recordNewInstruction should be
/// called (for eg inside MachineIRBuilder::recordInsertion).
/// Also because of how just the instruction can be inserted without adding any
/// operands to the instruction, instructions are uniqued and inserted lazily.
/// CSEInfo should assert when trying to enter an incomplete instruction into
/// the CSEMap. There is Opcode level granularity on which instructions can be
/// CSE'd and for now, only Generic instructions are CSEable.
class GISelCSEInfo : public GISelChangeObserver {
// Make it accessible only to CSEMIRBuilder.
friend class CSEMIRBuilder;
BumpPtrAllocator UniqueInstrAllocator;
FoldingSet<UniqueMachineInstr> CSEMap;
MachineRegisterInfo *MRI = nullptr;
MachineFunction *MF = nullptr;
std::unique_ptr<CSEConfigBase> CSEOpt;
/// Keep a cache of UniqueInstrs for each MachineInstr. In GISel,
/// often instructions are mutated (while their ID has completely changed).
/// Whenever mutation happens, invalidate the UniqueMachineInstr for the
/// MachineInstr
DenseMap<const MachineInstr *, UniqueMachineInstr *> InstrMapping;
/// Store instructions that are not fully formed in TemporaryInsts.
/// Also because CSE insertion happens lazily, we can remove insts from this
/// list and avoid inserting and then removing from the CSEMap.
GISelWorkList<8> TemporaryInsts;
// Only used in asserts.
DenseMap<unsigned, unsigned> OpcodeHitTable;
bool isUniqueMachineInstValid(const UniqueMachineInstr &UMI) const;
void invalidateUniqueMachineInstr(UniqueMachineInstr *UMI);
UniqueMachineInstr *getNodeIfExists(FoldingSetNodeID &ID,
MachineBasicBlock *MBB, void *&InsertPos);
/// Allocate and construct a new UniqueMachineInstr for MI and return.
UniqueMachineInstr *getUniqueInstrForMI(const MachineInstr *MI);
void insertNode(UniqueMachineInstr *UMI, void *InsertPos = nullptr);
/// Get the MachineInstr(Unique) if it exists already in the CSEMap and the
/// same MachineBasicBlock.
MachineInstr *getMachineInstrIfExists(FoldingSetNodeID &ID,
MachineBasicBlock *MBB,
void *&InsertPos);
/// Use this method to allocate a new UniqueMachineInstr for MI and insert it
/// into the CSEMap. MI should return true for shouldCSE(MI->getOpcode())
void insertInstr(MachineInstr *MI, void *InsertPos = nullptr);
public:
GISelCSEInfo() = default;
virtual ~GISelCSEInfo();
void setMF(MachineFunction &MF);
Error verify();
/// Records a newly created inst in a list and lazily insert it to the CSEMap.
/// Sometimes, this method might be called with a partially constructed
/// MachineInstr,
// (right after BuildMI without adding any operands) - and in such cases,
// defer the hashing of the instruction to a later stage.
void recordNewInstruction(MachineInstr *MI);
/// Use this callback to inform CSE about a newly fully created instruction.
void handleRecordedInst(MachineInstr *MI);
/// Use this callback to insert all the recorded instructions. At this point,
/// all of these insts need to be fully constructed and should not be missing
/// any operands.
void handleRecordedInsts();
/// Remove this inst from the CSE map. If this inst has not been inserted yet,
/// it will be removed from the Tempinsts list if it exists.
void handleRemoveInst(MachineInstr *MI);
void releaseMemory();
void setCSEConfig(std::unique_ptr<CSEConfigBase> Opt) {
CSEOpt = std::move(Opt);
}
bool shouldCSE(unsigned Opc) const;
void analyze(MachineFunction &MF);
void countOpcodeHit(unsigned Opc);
void print();
// Observer API
void erasingInstr(MachineInstr &MI) override;
void createdInstr(MachineInstr &MI) override;
void changingInstr(MachineInstr &MI) override;
void changedInstr(MachineInstr &MI) override;
};
class TargetRegisterClass;
class RegisterBank;
// Simple builder class to easily profile properties about MIs.
class GISelInstProfileBuilder {
FoldingSetNodeID &ID;
const MachineRegisterInfo &MRI;
public:
GISelInstProfileBuilder(FoldingSetNodeID &ID, const MachineRegisterInfo &MRI)
: ID(ID), MRI(MRI) {}
// Profiling methods.
const GISelInstProfileBuilder &addNodeIDOpcode(unsigned Opc) const;
const GISelInstProfileBuilder &addNodeIDRegType(const LLT Ty) const;
const GISelInstProfileBuilder &addNodeIDRegType(const Register) const;
const GISelInstProfileBuilder &
addNodeIDRegType(const TargetRegisterClass *RC) const;
const GISelInstProfileBuilder &addNodeIDRegType(const RegisterBank *RB) const;
const GISelInstProfileBuilder &addNodeIDRegNum(Register Reg) const;
const GISelInstProfileBuilder &addNodeIDReg(Register Reg) const;
const GISelInstProfileBuilder &addNodeIDImmediate(int64_t Imm) const;
const GISelInstProfileBuilder &
addNodeIDMBB(const MachineBasicBlock *MBB) const;
const GISelInstProfileBuilder &
addNodeIDMachineOperand(const MachineOperand &MO) const;
const GISelInstProfileBuilder &addNodeIDFlag(unsigned Flag) const;
const GISelInstProfileBuilder &addNodeID(const MachineInstr *MI) const;
};
/// Simple wrapper that does the following.
/// 1) Lazily evaluate the MachineFunction to compute CSEable instructions.
/// 2) Allows configuration of which instructions are CSEd through CSEConfig
/// object. Provides a method called get which takes a CSEConfig object.
class GISelCSEAnalysisWrapper {
GISelCSEInfo Info;
MachineFunction *MF = nullptr;
bool AlreadyComputed = false;
public:
/// Takes a CSEConfigBase object that defines what opcodes get CSEd.
/// If CSEConfig is already set, and the CSE Analysis has been preserved,
/// it will not use the new CSEOpt(use Recompute to force using the new
/// CSEOpt).
GISelCSEInfo &get(std::unique_ptr<CSEConfigBase> CSEOpt,
bool ReCompute = false);
void setMF(MachineFunction &MFunc) { MF = &MFunc; }
void setComputed(bool Computed) { AlreadyComputed = Computed; }
void releaseMemory() { Info.releaseMemory(); }
};
/// The actual analysis pass wrapper.
class GISelCSEAnalysisWrapperPass : public MachineFunctionPass {
GISelCSEAnalysisWrapper Wrapper;
public:
static char ID;
GISelCSEAnalysisWrapperPass();
void getAnalysisUsage(AnalysisUsage &AU) const override;
const GISelCSEAnalysisWrapper &getCSEWrapper() const { return Wrapper; }
GISelCSEAnalysisWrapper &getCSEWrapper() { return Wrapper; }
bool runOnMachineFunction(MachineFunction &MF) override;
void releaseMemory() override {
Wrapper.releaseMemory();
Wrapper.setComputed(false);
}
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,109 @@
//===-- llvm/CodeGen/GlobalISel/CSEMIRBuilder.h --*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements a version of MachineIRBuilder which CSEs insts within
/// a MachineBasicBlock.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H
#define LLVM_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
namespace llvm {
/// Defines a builder that does CSE of MachineInstructions using GISelCSEInfo.
/// Eg usage.
///
///
/// GISelCSEInfo *Info =
/// &getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEInfo(); CSEMIRBuilder
/// CB(Builder.getState()); CB.setCSEInfo(Info); auto A = CB.buildConstant(s32,
/// 42); auto B = CB.buildConstant(s32, 42); assert(A == B); unsigned CReg =
/// MRI.createGenericVirtualRegister(s32); auto C = CB.buildConstant(CReg, 42);
/// assert(C->getOpcode() == TargetOpcode::COPY);
/// Explicitly passing in a register would materialize a copy if possible.
/// CSEMIRBuilder also does trivial constant folding for binary ops.
class CSEMIRBuilder : public MachineIRBuilder {
/// Returns true if A dominates B (within the same basic block).
/// Both iterators must be in the same basic block.
//
// TODO: Another approach for checking dominance is having two iterators and
// making them go towards each other until they meet or reach begin/end. Which
// approach is better? Should this even change dynamically? For G_CONSTANTS
// most of which will be at the top of the BB, the top down approach would be
// a better choice. Does IRTranslator placing constants at the beginning still
// make sense? Should this change based on Opcode?
bool dominates(MachineBasicBlock::const_iterator A,
MachineBasicBlock::const_iterator B) const;
/// For given ID, find a machineinstr in the CSE Map. If found, check if it
/// dominates the current insertion point and if not, move it just before the
/// current insertion point and return it. If not found, return Null
/// MachineInstrBuilder.
MachineInstrBuilder getDominatingInstrForID(FoldingSetNodeID &ID,
void *&NodeInsertPos);
/// Simple check if we can CSE (we have the CSEInfo) or if this Opcode is
/// safe to CSE.
bool canPerformCSEForOpc(unsigned Opc) const;
void profileDstOp(const DstOp &Op, GISelInstProfileBuilder &B) const;
void profileDstOps(ArrayRef<DstOp> Ops, GISelInstProfileBuilder &B) const {
for (const DstOp &Op : Ops)
profileDstOp(Op, B);
}
void profileSrcOp(const SrcOp &Op, GISelInstProfileBuilder &B) const;
void profileSrcOps(ArrayRef<SrcOp> Ops, GISelInstProfileBuilder &B) const {
for (const SrcOp &Op : Ops)
profileSrcOp(Op, B);
}
void profileMBBOpcode(GISelInstProfileBuilder &B, unsigned Opc) const;
void profileEverything(unsigned Opc, ArrayRef<DstOp> DstOps,
ArrayRef<SrcOp> SrcOps, Optional<unsigned> Flags,
GISelInstProfileBuilder &B) const;
// Takes a MachineInstrBuilder and inserts it into the CSEMap using the
// NodeInsertPos.
MachineInstrBuilder memoizeMI(MachineInstrBuilder MIB, void *NodeInsertPos);
// If we have can CSE an instruction, but still need to materialize to a VReg,
// we emit a copy from the CSE'd inst to the VReg.
MachineInstrBuilder generateCopiesIfRequired(ArrayRef<DstOp> DstOps,
MachineInstrBuilder &MIB);
// If we have can CSE an instruction, but still need to materialize to a VReg,
// check if we can generate copies. It's not possible to return a single MIB,
// while emitting copies to multiple vregs.
bool checkCopyToDefsPossible(ArrayRef<DstOp> DstOps);
public:
// Pull in base class constructors.
using MachineIRBuilder::MachineIRBuilder;
// Unhide buildInstr
MachineInstrBuilder buildInstr(unsigned Opc, ArrayRef<DstOp> DstOps,
ArrayRef<SrcOp> SrcOps,
Optional<unsigned> Flag = None) override;
// Bring in the other overload from the base class.
using MachineIRBuilder::buildConstant;
MachineInstrBuilder buildConstant(const DstOp &Res,
const ConstantInt &Val) override;
// Bring in the other overload from the base class.
using MachineIRBuilder::buildFConstant;
MachineInstrBuilder buildFConstant(const DstOp &Res,
const ConstantFP &Val) override;
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,591 @@
//===- llvm/CodeGen/GlobalISel/CallLowering.h - Call lowering ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file describes how to lower LLVM calls to machine code calls.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_CALLLOWERING_H
#define LLVM_CODEGEN_GLOBALISEL_CALLLOWERING_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/TargetCallingConv.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MachineValueType.h"
#include <cstdint>
#include <functional>
namespace llvm {
class CallBase;
class DataLayout;
class Function;
class FunctionLoweringInfo;
class MachineIRBuilder;
struct MachinePointerInfo;
class MachineRegisterInfo;
class TargetLowering;
class CallLowering {
const TargetLowering *TLI;
virtual void anchor();
public:
struct BaseArgInfo {
Type *Ty;
SmallVector<ISD::ArgFlagsTy, 4> Flags;
bool IsFixed;
BaseArgInfo(Type *Ty,
ArrayRef<ISD::ArgFlagsTy> Flags = ArrayRef<ISD::ArgFlagsTy>(),
bool IsFixed = true)
: Ty(Ty), Flags(Flags.begin(), Flags.end()), IsFixed(IsFixed) {}
BaseArgInfo() : Ty(nullptr), IsFixed(false) {}
};
struct ArgInfo : public BaseArgInfo {
SmallVector<Register, 4> Regs;
// If the argument had to be split into multiple parts according to the
// target calling convention, then this contains the original vregs
// if the argument was an incoming arg.
SmallVector<Register, 2> OrigRegs;
/// Optionally track the original IR value for the argument. This may not be
/// meaningful in all contexts. This should only be used on for forwarding
/// through to use for aliasing information in MachinePointerInfo for memory
/// arguments.
const Value *OrigValue = nullptr;
/// Index original Function's argument.
unsigned OrigArgIndex;
/// Sentinel value for implicit machine-level input arguments.
static const unsigned NoArgIndex = UINT_MAX;
ArgInfo(ArrayRef<Register> Regs, Type *Ty, unsigned OrigIndex,
ArrayRef<ISD::ArgFlagsTy> Flags = ArrayRef<ISD::ArgFlagsTy>(),
bool IsFixed = true, const Value *OrigValue = nullptr)
: BaseArgInfo(Ty, Flags, IsFixed), Regs(Regs.begin(), Regs.end()),
OrigValue(OrigValue), OrigArgIndex(OrigIndex) {
if (!Regs.empty() && Flags.empty())
this->Flags.push_back(ISD::ArgFlagsTy());
// FIXME: We should have just one way of saying "no register".
assert(((Ty->isVoidTy() || Ty->isEmptyTy()) ==
(Regs.empty() || Regs[0] == 0)) &&
"only void types should have no register");
}
ArgInfo(ArrayRef<Register> Regs, const Value &OrigValue, unsigned OrigIndex,
ArrayRef<ISD::ArgFlagsTy> Flags = ArrayRef<ISD::ArgFlagsTy>(),
bool IsFixed = true)
: ArgInfo(Regs, OrigValue.getType(), OrigIndex, Flags, IsFixed, &OrigValue) {}
ArgInfo() = default;
};
struct CallLoweringInfo {
/// Calling convention to be used for the call.
CallingConv::ID CallConv = CallingConv::C;
/// Destination of the call. It should be either a register, globaladdress,
/// or externalsymbol.
MachineOperand Callee = MachineOperand::CreateImm(0);
/// Descriptor for the return type of the function.
ArgInfo OrigRet;
/// List of descriptors of the arguments passed to the function.
SmallVector<ArgInfo, 32> OrigArgs;
/// Valid if the call has a swifterror inout parameter, and contains the
/// vreg that the swifterror should be copied into after the call.
Register SwiftErrorVReg;
/// Original IR callsite corresponding to this call, if available.
const CallBase *CB = nullptr;
MDNode *KnownCallees = nullptr;
/// True if the call must be tail call optimized.
bool IsMustTailCall = false;
/// True if the call passes all target-independent checks for tail call
/// optimization.
bool IsTailCall = false;
/// True if the call was lowered as a tail call. This is consumed by the
/// legalizer. This allows the legalizer to lower libcalls as tail calls.
bool LoweredTailCall = false;
/// True if the call is to a vararg function.
bool IsVarArg = false;
/// True if the function's return value can be lowered to registers.
bool CanLowerReturn = true;
/// VReg to hold the hidden sret parameter.
Register DemoteRegister;
/// The stack index for sret demotion.
int DemoteStackIndex;
};
/// Argument handling is mostly uniform between the four places that
/// make these decisions: function formal arguments, call
/// instruction args, call instruction returns and function
/// returns. However, once a decision has been made on where an
/// argument should go, exactly what happens can vary slightly. This
/// class abstracts the differences.
///
/// ValueAssigner should not depend on any specific function state, and
/// only determine the types and locations for arguments.
struct ValueAssigner {
ValueAssigner(bool IsIncoming, CCAssignFn *AssignFn_,
CCAssignFn *AssignFnVarArg_ = nullptr)
: AssignFn(AssignFn_), AssignFnVarArg(AssignFnVarArg_),
IsIncomingArgumentHandler(IsIncoming) {
// Some targets change the handler depending on whether the call is
// varargs or not. If
if (!AssignFnVarArg)
AssignFnVarArg = AssignFn;
}
virtual ~ValueAssigner() = default;
/// Returns true if the handler is dealing with incoming arguments,
/// i.e. those that move values from some physical location to vregs.
bool isIncomingArgumentHandler() const {
return IsIncomingArgumentHandler;
}
/// Wrap call to (typically tablegenerated CCAssignFn). This may be
/// overridden to track additional state information as arguments are
/// assigned or apply target specific hacks around the legacy
/// infrastructure.
virtual bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT,
CCValAssign::LocInfo LocInfo, const ArgInfo &Info,
ISD::ArgFlagsTy Flags, CCState &State) {
if (getAssignFn(State.isVarArg())(ValNo, ValVT, LocVT, LocInfo, Flags,
State))
return true;
StackOffset = State.getNextStackOffset();
return false;
}
/// Assignment function to use for a general call.
CCAssignFn *AssignFn;
/// Assignment function to use for a variadic call. This is usually the same
/// as AssignFn on most targets.
CCAssignFn *AssignFnVarArg;
/// Stack offset for next argument. At the end of argument evaluation, this
/// is typically the total stack size.
uint64_t StackOffset = 0;
/// Select the appropriate assignment function depending on whether this is
/// a variadic call.
CCAssignFn *getAssignFn(bool IsVarArg) const {
return IsVarArg ? AssignFnVarArg : AssignFn;
}
private:
const bool IsIncomingArgumentHandler;
virtual void anchor();
};
struct IncomingValueAssigner : public ValueAssigner {
IncomingValueAssigner(CCAssignFn *AssignFn_,
CCAssignFn *AssignFnVarArg_ = nullptr)
: ValueAssigner(true, AssignFn_, AssignFnVarArg_) {}
};
struct OutgoingValueAssigner : public ValueAssigner {
OutgoingValueAssigner(CCAssignFn *AssignFn_,
CCAssignFn *AssignFnVarArg_ = nullptr)
: ValueAssigner(false, AssignFn_, AssignFnVarArg_) {}
};
struct ValueHandler {
MachineIRBuilder &MIRBuilder;
MachineRegisterInfo &MRI;
const bool IsIncomingArgumentHandler;
ValueHandler(bool IsIncoming, MachineIRBuilder &MIRBuilder,
MachineRegisterInfo &MRI)
: MIRBuilder(MIRBuilder), MRI(MRI),
IsIncomingArgumentHandler(IsIncoming) {}
virtual ~ValueHandler() = default;
/// Returns true if the handler is dealing with incoming arguments,
/// i.e. those that move values from some physical location to vregs.
bool isIncomingArgumentHandler() const {
return IsIncomingArgumentHandler;
}
/// Materialize a VReg containing the address of the specified
/// stack-based object. This is either based on a FrameIndex or
/// direct SP manipulation, depending on the context. \p MPO
/// should be initialized to an appropriate description of the
/// address created.
virtual Register getStackAddress(uint64_t MemSize, int64_t Offset,
MachinePointerInfo &MPO,
ISD::ArgFlagsTy Flags) = 0;
/// Return the in-memory size to write for the argument at \p VA. This may
/// be smaller than the allocated stack slot size.
///
/// This is overridable primarily for targets to maintain compatibility with
/// hacks around the existing DAG call lowering infrastructure.
virtual LLT getStackValueStoreType(const DataLayout &DL,
const CCValAssign &VA,
ISD::ArgFlagsTy Flags) const;
/// The specified value has been assigned to a physical register,
/// handle the appropriate COPY (either to or from) and mark any
/// relevant uses/defines as needed.
virtual void assignValueToReg(Register ValVReg, Register PhysReg,
CCValAssign VA) = 0;
/// The specified value has been assigned to a stack
/// location. Load or store it there, with appropriate extension
/// if necessary.
virtual void assignValueToAddress(Register ValVReg, Register Addr,
LLT MemTy, MachinePointerInfo &MPO,
CCValAssign &VA) = 0;
/// An overload which takes an ArgInfo if additional information about the
/// arg is needed. \p ValRegIndex is the index in \p Arg.Regs for the value
/// to store.
virtual void assignValueToAddress(const ArgInfo &Arg, unsigned ValRegIndex,
Register Addr, LLT MemTy,
MachinePointerInfo &MPO,
CCValAssign &VA) {
assignValueToAddress(Arg.Regs[ValRegIndex], Addr, MemTy, MPO, VA);
}
/// Handle custom values, which may be passed into one or more of \p VAs.
/// \p If the handler wants the assignments to be delayed until after
/// mem loc assignments, then it sets \p Thunk to the thunk to do the
/// assignment.
/// \return The number of \p VAs that have been assigned after the first
/// one, and which should therefore be skipped from further
/// processing.
virtual unsigned assignCustomValue(ArgInfo &Arg, ArrayRef<CCValAssign> VAs,
std::function<void()> *Thunk = nullptr) {
// This is not a pure virtual method because not all targets need to worry
// about custom values.
llvm_unreachable("Custom values not supported");
}
/// Do a memory copy of \p MemSize bytes from \p SrcPtr to \p DstPtr. This
/// is necessary for outgoing stack-passed byval arguments.
void
copyArgumentMemory(const ArgInfo &Arg, Register DstPtr, Register SrcPtr,
const MachinePointerInfo &DstPtrInfo, Align DstAlign,
const MachinePointerInfo &SrcPtrInfo, Align SrcAlign,
uint64_t MemSize, CCValAssign &VA) const;
/// Extend a register to the location type given in VA, capped at extending
/// to at most MaxSize bits. If MaxSizeBits is 0 then no maximum is set.
Register extendRegister(Register ValReg, CCValAssign &VA,
unsigned MaxSizeBits = 0);
};
/// Base class for ValueHandlers used for arguments coming into the current
/// function, or for return values received from a call.
struct IncomingValueHandler : public ValueHandler {
IncomingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
: ValueHandler(/*IsIncoming*/ true, MIRBuilder, MRI) {}
/// Insert G_ASSERT_ZEXT/G_ASSERT_SEXT or other hint instruction based on \p
/// VA, returning the new register if a hint was inserted.
Register buildExtensionHint(CCValAssign &VA, Register SrcReg, LLT NarrowTy);
/// Provides a default implementation for argument handling.
void assignValueToReg(Register ValVReg, Register PhysReg,
CCValAssign VA) override;
};
/// Base class for ValueHandlers used for arguments passed to a function call,
/// or for return values.
struct OutgoingValueHandler : public ValueHandler {
OutgoingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
: ValueHandler(/*IsIncoming*/ false, MIRBuilder, MRI) {}
};
protected:
/// Getter for generic TargetLowering class.
const TargetLowering *getTLI() const {
return TLI;
}
/// Getter for target specific TargetLowering class.
template <class XXXTargetLowering>
const XXXTargetLowering *getTLI() const {
return static_cast<const XXXTargetLowering *>(TLI);
}
/// \returns Flags corresponding to the attributes on the \p ArgIdx-th
/// parameter of \p Call.
ISD::ArgFlagsTy getAttributesForArgIdx(const CallBase &Call,
unsigned ArgIdx) const;
/// Adds flags to \p Flags based off of the attributes in \p Attrs.
/// \p OpIdx is the index in \p Attrs to add flags from.
void addArgFlagsFromAttributes(ISD::ArgFlagsTy &Flags,
const AttributeList &Attrs,
unsigned OpIdx) const;
template <typename FuncInfoTy>
void setArgFlags(ArgInfo &Arg, unsigned OpIdx, const DataLayout &DL,
const FuncInfoTy &FuncInfo) const;
/// Break \p OrigArgInfo into one or more pieces the calling convention can
/// process, returned in \p SplitArgs. For example, this should break structs
/// down into individual fields.
///
/// If \p Offsets is non-null, it points to a vector to be filled in
/// with the in-memory offsets of each of the individual values.
void splitToValueTypes(const ArgInfo &OrigArgInfo,
SmallVectorImpl<ArgInfo> &SplitArgs,
const DataLayout &DL, CallingConv::ID CallConv,
SmallVectorImpl<uint64_t> *Offsets = nullptr) const;
/// Analyze the argument list in \p Args, using \p Assigner to populate \p
/// CCInfo. This will determine the types and locations to use for passed or
/// returned values. This may resize fields in \p Args if the value is split
/// across multiple registers or stack slots.
///
/// This is independent of the function state and can be used
/// to determine how a call would pass arguments without needing to change the
/// function. This can be used to check if arguments are suitable for tail
/// call lowering.
///
/// \return True if everything has succeeded, false otherwise.
bool determineAssignments(ValueAssigner &Assigner,
SmallVectorImpl<ArgInfo> &Args,
CCState &CCInfo) const;
/// Invoke ValueAssigner::assignArg on each of the given \p Args and then use
/// \p Handler to move them to the assigned locations.
///
/// \return True if everything has succeeded, false otherwise.
bool
determineAndHandleAssignments(ValueHandler &Handler, ValueAssigner &Assigner,
SmallVectorImpl<ArgInfo> &Args,
MachineIRBuilder &MIRBuilder,
CallingConv::ID CallConv, bool IsVarArg,
ArrayRef<Register> ThisReturnRegs = None) const;
/// Use \p Handler to insert code to handle the argument/return values
/// represented by \p Args. It's expected determineAssignments previously
/// processed these arguments to populate \p CCState and \p ArgLocs.
bool handleAssignments(ValueHandler &Handler, SmallVectorImpl<ArgInfo> &Args,
CCState &CCState,
SmallVectorImpl<CCValAssign> &ArgLocs,
MachineIRBuilder &MIRBuilder,
ArrayRef<Register> ThisReturnRegs = None) const;
/// Check whether parameters to a call that are passed in callee saved
/// registers are the same as from the calling function. This needs to be
/// checked for tail call eligibility.
bool parametersInCSRMatch(const MachineRegisterInfo &MRI,
const uint32_t *CallerPreservedMask,
const SmallVectorImpl<CCValAssign> &ArgLocs,
const SmallVectorImpl<ArgInfo> &OutVals) const;
/// \returns True if the calling convention for a callee and its caller pass
/// results in the same way. Typically used for tail call eligibility checks.
///
/// \p Info is the CallLoweringInfo for the call.
/// \p MF is the MachineFunction for the caller.
/// \p InArgs contains the results of the call.
/// \p CalleeAssigner specifies the target's handling of the argument types
/// for the callee.
/// \p CallerAssigner specifies the target's handling of the
/// argument types for the caller.
bool resultsCompatible(CallLoweringInfo &Info, MachineFunction &MF,
SmallVectorImpl<ArgInfo> &InArgs,
ValueAssigner &CalleeAssigner,
ValueAssigner &CallerAssigner) const;
public:
CallLowering(const TargetLowering *TLI) : TLI(TLI) {}
virtual ~CallLowering() = default;
/// \return true if the target is capable of handling swifterror values that
/// have been promoted to a specified register. The extended versions of
/// lowerReturn and lowerCall should be implemented.
virtual bool supportSwiftError() const {
return false;
}
/// Load the returned value from the stack into virtual registers in \p VRegs.
/// It uses the frame index \p FI and the start offset from \p DemoteReg.
/// The loaded data size will be determined from \p RetTy.
void insertSRetLoads(MachineIRBuilder &MIRBuilder, Type *RetTy,
ArrayRef<Register> VRegs, Register DemoteReg,
int FI) const;
/// Store the return value given by \p VRegs into stack starting at the offset
/// specified in \p DemoteReg.
void insertSRetStores(MachineIRBuilder &MIRBuilder, Type *RetTy,
ArrayRef<Register> VRegs, Register DemoteReg) const;
/// Insert the hidden sret ArgInfo to the beginning of \p SplitArgs.
/// This function should be called from the target specific
/// lowerFormalArguments when \p F requires the sret demotion.
void insertSRetIncomingArgument(const Function &F,
SmallVectorImpl<ArgInfo> &SplitArgs,
Register &DemoteReg, MachineRegisterInfo &MRI,
const DataLayout &DL) const;
/// For the call-base described by \p CB, insert the hidden sret ArgInfo to
/// the OrigArgs field of \p Info.
void insertSRetOutgoingArgument(MachineIRBuilder &MIRBuilder,
const CallBase &CB,
CallLoweringInfo &Info) const;
/// \return True if the return type described by \p Outs can be returned
/// without performing sret demotion.
bool checkReturn(CCState &CCInfo, SmallVectorImpl<BaseArgInfo> &Outs,
CCAssignFn *Fn) const;
/// Get the type and the ArgFlags for the split components of \p RetTy as
/// returned by \c ComputeValueVTs.
void getReturnInfo(CallingConv::ID CallConv, Type *RetTy, AttributeList Attrs,
SmallVectorImpl<BaseArgInfo> &Outs,
const DataLayout &DL) const;
/// Toplevel function to check the return type based on the target calling
/// convention. \return True if the return value of \p MF can be returned
/// without performing sret demotion.
bool checkReturnTypeForCallConv(MachineFunction &MF) const;
/// This hook must be implemented to check whether the return values
/// described by \p Outs can fit into the return registers. If false
/// is returned, an sret-demotion is performed.
virtual bool canLowerReturn(MachineFunction &MF, CallingConv::ID CallConv,
SmallVectorImpl<BaseArgInfo> &Outs,
bool IsVarArg) const {
return true;
}
/// This hook must be implemented to lower outgoing return values, described
/// by \p Val, into the specified virtual registers \p VRegs.
/// This hook is used by GlobalISel.
///
/// \p FLI is required for sret demotion.
///
/// \p SwiftErrorVReg is non-zero if the function has a swifterror parameter
/// that needs to be implicitly returned.
///
/// \return True if the lowering succeeds, false otherwise.
virtual bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
ArrayRef<Register> VRegs, FunctionLoweringInfo &FLI,
Register SwiftErrorVReg) const {
if (!supportSwiftError()) {
assert(SwiftErrorVReg == 0 && "attempt to use unsupported swifterror");
return lowerReturn(MIRBuilder, Val, VRegs, FLI);
}
return false;
}
/// This hook behaves as the extended lowerReturn function, but for targets
/// that do not support swifterror value promotion.
virtual bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
ArrayRef<Register> VRegs,
FunctionLoweringInfo &FLI) const {
return false;
}
virtual bool fallBackToDAGISel(const MachineFunction &MF) const {
return false;
}
/// This hook must be implemented to lower the incoming (formal)
/// arguments, described by \p VRegs, for GlobalISel. Each argument
/// must end up in the related virtual registers described by \p VRegs.
/// In other words, the first argument should end up in \c VRegs[0],
/// the second in \c VRegs[1], and so on. For each argument, there will be one
/// register for each non-aggregate type, as returned by \c computeValueLLTs.
/// \p MIRBuilder is set to the proper insertion for the argument
/// lowering. \p FLI is required for sret demotion.
///
/// \return True if the lowering succeeded, false otherwise.
virtual bool lowerFormalArguments(MachineIRBuilder &MIRBuilder,
const Function &F,
ArrayRef<ArrayRef<Register>> VRegs,
FunctionLoweringInfo &FLI) const {
return false;
}
/// This hook must be implemented to lower the given call instruction,
/// including argument and return value marshalling.
///
///
/// \return true if the lowering succeeded, false otherwise.
virtual bool lowerCall(MachineIRBuilder &MIRBuilder,
CallLoweringInfo &Info) const {
return false;
}
/// Lower the given call instruction, including argument and return value
/// marshalling.
///
/// \p CI is the call/invoke instruction.
///
/// \p ResRegs are the registers where the call's return value should be
/// stored (or 0 if there is no return value). There will be one register for
/// each non-aggregate type, as returned by \c computeValueLLTs.
///
/// \p ArgRegs is a list of lists of virtual registers containing each
/// argument that needs to be passed (argument \c i should be placed in \c
/// ArgRegs[i]). For each argument, there will be one register for each
/// non-aggregate type, as returned by \c computeValueLLTs.
///
/// \p SwiftErrorVReg is non-zero if the call has a swifterror inout
/// parameter, and contains the vreg that the swifterror should be copied into
/// after the call.
///
/// \p GetCalleeReg is a callback to materialize a register for the callee if
/// the target determines it cannot jump to the destination based purely on \p
/// CI. This might be because \p CI is indirect, or because of the limited
/// range of an immediate jump.
///
/// \return true if the lowering succeeded, false otherwise.
bool lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &Call,
ArrayRef<Register> ResRegs,
ArrayRef<ArrayRef<Register>> ArgRegs, Register SwiftErrorVReg,
std::function<unsigned()> GetCalleeReg) const;
/// For targets which want to use big-endian can enable it with
/// enableBigEndian() hook
virtual bool enableBigEndian() const { return false; }
/// For targets which support the "returned" parameter attribute, returns
/// true if the given type is a valid one to use with "returned".
virtual bool isTypeIsValidForThisReturn(EVT Ty) const { return false; }
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_CALLLOWERING_H

View File

@@ -0,0 +1,46 @@
//== ----- llvm/CodeGen/GlobalISel/Combiner.h -------------------*- C++ -*-== //
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This contains common code to drive combines. Combiner Passes will need to
/// setup a CombinerInfo and call combineMachineFunction.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_COMBINER_H
#define LLVM_CODEGEN_GLOBALISEL_COMBINER_H
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
namespace llvm {
class MachineRegisterInfo;
class CombinerInfo;
class GISelCSEInfo;
class TargetPassConfig;
class MachineFunction;
class Combiner {
public:
Combiner(CombinerInfo &CombinerInfo, const TargetPassConfig *TPC);
/// If CSEInfo is not null, then the Combiner will setup observer for
/// CSEInfo and instantiate a CSEMIRBuilder. Pass nullptr if CSE is not
/// needed.
bool combineMachineInstrs(MachineFunction &MF, GISelCSEInfo *CSEInfo);
protected:
CombinerInfo &CInfo;
MachineRegisterInfo *MRI = nullptr;
const TargetPassConfig *TPC;
std::unique_ptr<MachineIRBuilder> Builder;
};
} // End namespace llvm.
#endif // LLVM_CODEGEN_GLOBALISEL_COMBINER_H

View File

@@ -0,0 +1,753 @@
//===-- llvm/CodeGen/GlobalISel/CombinerHelper.h --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===--------------------------------------------------------------------===//
/// \file
/// This contains common combine transformations that may be used in a combine
/// pass,or by the target elsewhere.
/// Targets can pick individual opcode transformations from the helper or use
/// tryCombine which invokes all transformations. All of the transformations
/// return true if the MachineInstruction changed and false otherwise.
///
//===--------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_COMBINERHELPER_H
#define LLVM_CODEGEN_GLOBALISEL_COMBINERHELPER_H
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/LowLevelType.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/Support/Alignment.h"
namespace llvm {
class GISelChangeObserver;
class MachineIRBuilder;
class MachineInstrBuilder;
class MachineRegisterInfo;
class MachineInstr;
class MachineOperand;
class GISelKnownBits;
class MachineDominatorTree;
class LegalizerInfo;
struct LegalityQuery;
class RegisterBank;
class RegisterBankInfo;
class TargetLowering;
class TargetRegisterInfo;
struct PreferredTuple {
LLT Ty; // The result type of the extend.
unsigned ExtendOpcode; // G_ANYEXT/G_SEXT/G_ZEXT
MachineInstr *MI;
};
struct IndexedLoadStoreMatchInfo {
Register Addr;
Register Base;
Register Offset;
bool IsPre;
};
struct PtrAddChain {
int64_t Imm;
Register Base;
const RegisterBank *Bank;
};
struct RegisterImmPair {
Register Reg;
int64_t Imm;
};
struct ShiftOfShiftedLogic {
MachineInstr *Logic;
MachineInstr *Shift2;
Register LogicNonShiftReg;
uint64_t ValSum;
};
using BuildFnTy = std::function<void(MachineIRBuilder &)>;
struct MergeTruncStoresInfo {
SmallVector<GStore *> FoundStores;
GStore *LowestIdxStore = nullptr;
Register WideSrcVal;
bool NeedBSwap = false;
bool NeedRotate = false;
};
using OperandBuildSteps =
SmallVector<std::function<void(MachineInstrBuilder &)>, 4>;
struct InstructionBuildSteps {
unsigned Opcode = 0; /// The opcode for the produced instruction.
OperandBuildSteps OperandFns; /// Operands to be added to the instruction.
InstructionBuildSteps() = default;
InstructionBuildSteps(unsigned Opcode, const OperandBuildSteps &OperandFns)
: Opcode(Opcode), OperandFns(OperandFns) {}
};
struct InstructionStepsMatchInfo {
/// Describes instructions to be built during a combine.
SmallVector<InstructionBuildSteps, 2> InstrsToBuild;
InstructionStepsMatchInfo() = default;
InstructionStepsMatchInfo(
std::initializer_list<InstructionBuildSteps> InstrsToBuild)
: InstrsToBuild(InstrsToBuild) {}
};
class CombinerHelper {
protected:
MachineIRBuilder &Builder;
MachineRegisterInfo &MRI;
GISelChangeObserver &Observer;
GISelKnownBits *KB;
MachineDominatorTree *MDT;
const LegalizerInfo *LI;
const RegisterBankInfo *RBI;
const TargetRegisterInfo *TRI;
public:
CombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B,
GISelKnownBits *KB = nullptr,
MachineDominatorTree *MDT = nullptr,
const LegalizerInfo *LI = nullptr);
GISelKnownBits *getKnownBits() const {
return KB;
}
const TargetLowering &getTargetLowering() const;
/// \return true if the combine is running prior to legalization, or if \p
/// Query is legal on the target.
bool isLegalOrBeforeLegalizer(const LegalityQuery &Query) const;
/// MachineRegisterInfo::replaceRegWith() and inform the observer of the changes
void replaceRegWith(MachineRegisterInfo &MRI, Register FromReg, Register ToReg) const;
/// Replace a single register operand with a new register and inform the
/// observer of the changes.
void replaceRegOpWith(MachineRegisterInfo &MRI, MachineOperand &FromRegOp,
Register ToReg) const;
/// Replace the opcode in instruction with a new opcode and inform the
/// observer of the changes.
void replaceOpcodeWith(MachineInstr &FromMI, unsigned ToOpcode) const;
/// Get the register bank of \p Reg.
/// If Reg has not been assigned a register, a register class,
/// or a register bank, then this returns nullptr.
///
/// \pre Reg.isValid()
const RegisterBank *getRegBank(Register Reg) const;
/// Set the register bank of \p Reg.
/// Does nothing if the RegBank is null.
/// This is the counterpart to getRegBank.
void setRegBank(Register Reg, const RegisterBank *RegBank);
/// If \p MI is COPY, try to combine it.
/// Returns true if MI changed.
bool tryCombineCopy(MachineInstr &MI);
bool matchCombineCopy(MachineInstr &MI);
void applyCombineCopy(MachineInstr &MI);
/// Returns true if \p DefMI precedes \p UseMI or they are the same
/// instruction. Both must be in the same basic block.
bool isPredecessor(const MachineInstr &DefMI, const MachineInstr &UseMI);
/// Returns true if \p DefMI dominates \p UseMI. By definition an
/// instruction dominates itself.
///
/// If we haven't been provided with a MachineDominatorTree during
/// construction, this function returns a conservative result that tracks just
/// a single basic block.
bool dominates(const MachineInstr &DefMI, const MachineInstr &UseMI);
/// If \p MI is extend that consumes the result of a load, try to combine it.
/// Returns true if MI changed.
bool tryCombineExtendingLoads(MachineInstr &MI);
bool matchCombineExtendingLoads(MachineInstr &MI, PreferredTuple &MatchInfo);
void applyCombineExtendingLoads(MachineInstr &MI, PreferredTuple &MatchInfo);
/// Match (and (load x), mask) -> zextload x
bool matchCombineLoadWithAndMask(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Combine \p MI into a pre-indexed or post-indexed load/store operation if
/// legal and the surrounding code makes it useful.
bool tryCombineIndexedLoadStore(MachineInstr &MI);
bool matchCombineIndexedLoadStore(MachineInstr &MI, IndexedLoadStoreMatchInfo &MatchInfo);
void applyCombineIndexedLoadStore(MachineInstr &MI, IndexedLoadStoreMatchInfo &MatchInfo);
bool matchSextTruncSextLoad(MachineInstr &MI);
void applySextTruncSextLoad(MachineInstr &MI);
/// Match sext_inreg(load p), imm -> sextload p
bool matchSextInRegOfLoad(MachineInstr &MI, std::tuple<Register, unsigned> &MatchInfo);
void applySextInRegOfLoad(MachineInstr &MI, std::tuple<Register, unsigned> &MatchInfo);
/// Try to combine G_[SU]DIV and G_[SU]REM into a single G_[SU]DIVREM
/// when their source operands are identical.
bool matchCombineDivRem(MachineInstr &MI, MachineInstr *&OtherMI);
void applyCombineDivRem(MachineInstr &MI, MachineInstr *&OtherMI);
/// If a brcond's true block is not the fallthrough, make it so by inverting
/// the condition and swapping operands.
bool matchOptBrCondByInvertingCond(MachineInstr &MI, MachineInstr *&BrCond);
void applyOptBrCondByInvertingCond(MachineInstr &MI, MachineInstr *&BrCond);
/// If \p MI is G_CONCAT_VECTORS, try to combine it.
/// Returns true if MI changed.
/// Right now, we support:
/// - concat_vector(undef, undef) => undef
/// - concat_vector(build_vector(A, B), build_vector(C, D)) =>
/// build_vector(A, B, C, D)
///
/// \pre MI.getOpcode() == G_CONCAT_VECTORS.
bool tryCombineConcatVectors(MachineInstr &MI);
/// Check if the G_CONCAT_VECTORS \p MI is undef or if it
/// can be flattened into a build_vector.
/// In the first case \p IsUndef will be true.
/// In the second case \p Ops will contain the operands needed
/// to produce the flattened build_vector.
///
/// \pre MI.getOpcode() == G_CONCAT_VECTORS.
bool matchCombineConcatVectors(MachineInstr &MI, bool &IsUndef,
SmallVectorImpl<Register> &Ops);
/// Replace \p MI with a flattened build_vector with \p Ops or an
/// implicit_def if IsUndef is true.
void applyCombineConcatVectors(MachineInstr &MI, bool IsUndef,
const ArrayRef<Register> Ops);
/// Try to combine G_SHUFFLE_VECTOR into G_CONCAT_VECTORS.
/// Returns true if MI changed.
///
/// \pre MI.getOpcode() == G_SHUFFLE_VECTOR.
bool tryCombineShuffleVector(MachineInstr &MI);
/// Check if the G_SHUFFLE_VECTOR \p MI can be replaced by a
/// concat_vectors.
/// \p Ops will contain the operands needed to produce the flattened
/// concat_vectors.
///
/// \pre MI.getOpcode() == G_SHUFFLE_VECTOR.
bool matchCombineShuffleVector(MachineInstr &MI,
SmallVectorImpl<Register> &Ops);
/// Replace \p MI with a concat_vectors with \p Ops.
void applyCombineShuffleVector(MachineInstr &MI,
const ArrayRef<Register> Ops);
/// Optimize memcpy intrinsics et al, e.g. constant len calls.
/// /p MaxLen if non-zero specifies the max length of a mem libcall to inline.
///
/// For example (pre-indexed):
///
/// $addr = G_PTR_ADD $base, $offset
/// [...]
/// $val = G_LOAD $addr
/// [...]
/// $whatever = COPY $addr
///
/// -->
///
/// $val, $addr = G_INDEXED_LOAD $base, $offset, 1 (IsPre)
/// [...]
/// $whatever = COPY $addr
///
/// or (post-indexed):
///
/// G_STORE $val, $base
/// [...]
/// $addr = G_PTR_ADD $base, $offset
/// [...]
/// $whatever = COPY $addr
///
/// -->
///
/// $addr = G_INDEXED_STORE $val, $base, $offset
/// [...]
/// $whatever = COPY $addr
bool tryCombineMemCpyFamily(MachineInstr &MI, unsigned MaxLen = 0);
bool matchPtrAddImmedChain(MachineInstr &MI, PtrAddChain &MatchInfo);
void applyPtrAddImmedChain(MachineInstr &MI, PtrAddChain &MatchInfo);
/// Fold (shift (shift base, x), y) -> (shift base (x+y))
bool matchShiftImmedChain(MachineInstr &MI, RegisterImmPair &MatchInfo);
void applyShiftImmedChain(MachineInstr &MI, RegisterImmPair &MatchInfo);
/// If we have a shift-by-constant of a bitwise logic op that itself has a
/// shift-by-constant operand with identical opcode, we may be able to convert
/// that into 2 independent shifts followed by the logic op.
bool matchShiftOfShiftedLogic(MachineInstr &MI,
ShiftOfShiftedLogic &MatchInfo);
void applyShiftOfShiftedLogic(MachineInstr &MI,
ShiftOfShiftedLogic &MatchInfo);
/// Transform a multiply by a power-of-2 value to a left shift.
bool matchCombineMulToShl(MachineInstr &MI, unsigned &ShiftVal);
void applyCombineMulToShl(MachineInstr &MI, unsigned &ShiftVal);
// Transform a G_SHL with an extended source into a narrower shift if
// possible.
bool matchCombineShlOfExtend(MachineInstr &MI, RegisterImmPair &MatchData);
void applyCombineShlOfExtend(MachineInstr &MI,
const RegisterImmPair &MatchData);
/// Fold away a merge of an unmerge of the corresponding values.
bool matchCombineMergeUnmerge(MachineInstr &MI, Register &MatchInfo);
/// Reduce a shift by a constant to an unmerge and a shift on a half sized
/// type. This will not produce a shift smaller than \p TargetShiftSize.
bool matchCombineShiftToUnmerge(MachineInstr &MI, unsigned TargetShiftSize,
unsigned &ShiftVal);
void applyCombineShiftToUnmerge(MachineInstr &MI, const unsigned &ShiftVal);
bool tryCombineShiftToUnmerge(MachineInstr &MI, unsigned TargetShiftAmount);
/// Transform <ty,...> G_UNMERGE(G_MERGE ty X, Y, Z) -> ty X, Y, Z.
bool
matchCombineUnmergeMergeToPlainValues(MachineInstr &MI,
SmallVectorImpl<Register> &Operands);
void
applyCombineUnmergeMergeToPlainValues(MachineInstr &MI,
SmallVectorImpl<Register> &Operands);
/// Transform G_UNMERGE Constant -> Constant1, Constant2, ...
bool matchCombineUnmergeConstant(MachineInstr &MI,
SmallVectorImpl<APInt> &Csts);
void applyCombineUnmergeConstant(MachineInstr &MI,
SmallVectorImpl<APInt> &Csts);
/// Transform G_UNMERGE G_IMPLICIT_DEF -> G_IMPLICIT_DEF, G_IMPLICIT_DEF, ...
bool
matchCombineUnmergeUndef(MachineInstr &MI,
std::function<void(MachineIRBuilder &)> &MatchInfo);
/// Transform X, Y<dead> = G_UNMERGE Z -> X = G_TRUNC Z.
bool matchCombineUnmergeWithDeadLanesToTrunc(MachineInstr &MI);
void applyCombineUnmergeWithDeadLanesToTrunc(MachineInstr &MI);
/// Transform X, Y = G_UNMERGE(G_ZEXT(Z)) -> X = G_ZEXT(Z); Y = G_CONSTANT 0
bool matchCombineUnmergeZExtToZExt(MachineInstr &MI);
void applyCombineUnmergeZExtToZExt(MachineInstr &MI);
/// Transform fp_instr(cst) to constant result of the fp operation.
bool matchCombineConstantFoldFpUnary(MachineInstr &MI,
Optional<APFloat> &Cst);
void applyCombineConstantFoldFpUnary(MachineInstr &MI,
Optional<APFloat> &Cst);
/// Transform IntToPtr(PtrToInt(x)) to x if cast is in the same address space.
bool matchCombineI2PToP2I(MachineInstr &MI, Register &Reg);
void applyCombineI2PToP2I(MachineInstr &MI, Register &Reg);
/// Transform PtrToInt(IntToPtr(x)) to x.
bool matchCombineP2IToI2P(MachineInstr &MI, Register &Reg);
void applyCombineP2IToI2P(MachineInstr &MI, Register &Reg);
/// Transform G_ADD (G_PTRTOINT x), y -> G_PTRTOINT (G_PTR_ADD x, y)
/// Transform G_ADD y, (G_PTRTOINT x) -> G_PTRTOINT (G_PTR_ADD x, y)
bool matchCombineAddP2IToPtrAdd(MachineInstr &MI,
std::pair<Register, bool> &PtrRegAndCommute);
void applyCombineAddP2IToPtrAdd(MachineInstr &MI,
std::pair<Register, bool> &PtrRegAndCommute);
// Transform G_PTR_ADD (G_PTRTOINT C1), C2 -> C1 + C2
bool matchCombineConstPtrAddToI2P(MachineInstr &MI, APInt &NewCst);
void applyCombineConstPtrAddToI2P(MachineInstr &MI, APInt &NewCst);
/// Transform anyext(trunc(x)) to x.
bool matchCombineAnyExtTrunc(MachineInstr &MI, Register &Reg);
void applyCombineAnyExtTrunc(MachineInstr &MI, Register &Reg);
/// Transform zext(trunc(x)) to x.
bool matchCombineZextTrunc(MachineInstr &MI, Register &Reg);
/// Transform [asz]ext([asz]ext(x)) to [asz]ext x.
bool matchCombineExtOfExt(MachineInstr &MI,
std::tuple<Register, unsigned> &MatchInfo);
void applyCombineExtOfExt(MachineInstr &MI,
std::tuple<Register, unsigned> &MatchInfo);
/// Transform fneg(fneg(x)) to x.
bool matchCombineFNegOfFNeg(MachineInstr &MI, Register &Reg);
/// Match fabs(fabs(x)) to fabs(x).
bool matchCombineFAbsOfFAbs(MachineInstr &MI, Register &Src);
void applyCombineFAbsOfFAbs(MachineInstr &MI, Register &Src);
/// Transform fabs(fneg(x)) to fabs(x).
bool matchCombineFAbsOfFNeg(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Transform trunc ([asz]ext x) to x or ([asz]ext x) or (trunc x).
bool matchCombineTruncOfExt(MachineInstr &MI,
std::pair<Register, unsigned> &MatchInfo);
void applyCombineTruncOfExt(MachineInstr &MI,
std::pair<Register, unsigned> &MatchInfo);
/// Transform trunc (shl x, K) to shl (trunc x),
/// K => K < VT.getScalarSizeInBits().
bool matchCombineTruncOfShl(MachineInstr &MI,
std::pair<Register, Register> &MatchInfo);
void applyCombineTruncOfShl(MachineInstr &MI,
std::pair<Register, Register> &MatchInfo);
/// Transform G_MUL(x, -1) to G_SUB(0, x)
void applyCombineMulByNegativeOne(MachineInstr &MI);
/// Return true if any explicit use operand on \p MI is defined by a
/// G_IMPLICIT_DEF.
bool matchAnyExplicitUseIsUndef(MachineInstr &MI);
/// Return true if all register explicit use operands on \p MI are defined by
/// a G_IMPLICIT_DEF.
bool matchAllExplicitUsesAreUndef(MachineInstr &MI);
/// Return true if a G_SHUFFLE_VECTOR instruction \p MI has an undef mask.
bool matchUndefShuffleVectorMask(MachineInstr &MI);
/// Return true if a G_STORE instruction \p MI is storing an undef value.
bool matchUndefStore(MachineInstr &MI);
/// Return true if a G_SELECT instruction \p MI has an undef comparison.
bool matchUndefSelectCmp(MachineInstr &MI);
/// Return true if a G_SELECT instruction \p MI has a constant comparison. If
/// true, \p OpIdx will store the operand index of the known selected value.
bool matchConstantSelectCmp(MachineInstr &MI, unsigned &OpIdx);
/// Replace an instruction with a G_FCONSTANT with value \p C.
bool replaceInstWithFConstant(MachineInstr &MI, double C);
/// Replace an instruction with a G_CONSTANT with value \p C.
bool replaceInstWithConstant(MachineInstr &MI, int64_t C);
/// Replace an instruction with a G_CONSTANT with value \p C.
bool replaceInstWithConstant(MachineInstr &MI, APInt C);
/// Replace an instruction with a G_IMPLICIT_DEF.
bool replaceInstWithUndef(MachineInstr &MI);
/// Delete \p MI and replace all of its uses with its \p OpIdx-th operand.
bool replaceSingleDefInstWithOperand(MachineInstr &MI, unsigned OpIdx);
/// Delete \p MI and replace all of its uses with \p Replacement.
bool replaceSingleDefInstWithReg(MachineInstr &MI, Register Replacement);
/// Return true if \p MOP1 and \p MOP2 are register operands are defined by
/// equivalent instructions.
bool matchEqualDefs(const MachineOperand &MOP1, const MachineOperand &MOP2);
/// Return true if \p MOP is defined by a G_CONSTANT with a value equal to
/// \p C.
bool matchConstantOp(const MachineOperand &MOP, int64_t C);
/// Optimize (cond ? x : x) -> x
bool matchSelectSameVal(MachineInstr &MI);
/// Optimize (x op x) -> x
bool matchBinOpSameVal(MachineInstr &MI);
/// Check if operand \p OpIdx is zero.
bool matchOperandIsZero(MachineInstr &MI, unsigned OpIdx);
/// Check if operand \p OpIdx is undef.
bool matchOperandIsUndef(MachineInstr &MI, unsigned OpIdx);
/// Check if operand \p OpIdx is known to be a power of 2.
bool matchOperandIsKnownToBeAPowerOfTwo(MachineInstr &MI, unsigned OpIdx);
/// Erase \p MI
bool eraseInst(MachineInstr &MI);
/// Return true if MI is a G_ADD which can be simplified to a G_SUB.
bool matchSimplifyAddToSub(MachineInstr &MI,
std::tuple<Register, Register> &MatchInfo);
void applySimplifyAddToSub(MachineInstr &MI,
std::tuple<Register, Register> &MatchInfo);
/// Match (logic_op (op x...), (op y...)) -> (op (logic_op x, y))
bool
matchHoistLogicOpWithSameOpcodeHands(MachineInstr &MI,
InstructionStepsMatchInfo &MatchInfo);
/// Replace \p MI with a series of instructions described in \p MatchInfo.
void applyBuildInstructionSteps(MachineInstr &MI,
InstructionStepsMatchInfo &MatchInfo);
/// Match ashr (shl x, C), C -> sext_inreg (C)
bool matchAshrShlToSextInreg(MachineInstr &MI,
std::tuple<Register, int64_t> &MatchInfo);
void applyAshShlToSextInreg(MachineInstr &MI,
std::tuple<Register, int64_t> &MatchInfo);
/// Fold and(and(x, C1), C2) -> C1&C2 ? and(x, C1&C2) : 0
bool matchOverlappingAnd(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// \return true if \p MI is a G_AND instruction whose operands are x and y
/// where x & y == x or x & y == y. (E.g., one of operands is all-ones value.)
///
/// \param [in] MI - The G_AND instruction.
/// \param [out] Replacement - A register the G_AND should be replaced with on
/// success.
bool matchRedundantAnd(MachineInstr &MI, Register &Replacement);
/// \return true if \p MI is a G_OR instruction whose operands are x and y
/// where x | y == x or x | y == y. (E.g., one of operands is all-zeros
/// value.)
///
/// \param [in] MI - The G_OR instruction.
/// \param [out] Replacement - A register the G_OR should be replaced with on
/// success.
bool matchRedundantOr(MachineInstr &MI, Register &Replacement);
/// \return true if \p MI is a G_SEXT_INREG that can be erased.
bool matchRedundantSExtInReg(MachineInstr &MI);
/// Combine inverting a result of a compare into the opposite cond code.
bool matchNotCmp(MachineInstr &MI, SmallVectorImpl<Register> &RegsToNegate);
void applyNotCmp(MachineInstr &MI, SmallVectorImpl<Register> &RegsToNegate);
/// Fold (xor (and x, y), y) -> (and (not x), y)
///{
bool matchXorOfAndWithSameReg(MachineInstr &MI,
std::pair<Register, Register> &MatchInfo);
void applyXorOfAndWithSameReg(MachineInstr &MI,
std::pair<Register, Register> &MatchInfo);
///}
/// Combine G_PTR_ADD with nullptr to G_INTTOPTR
bool matchPtrAddZero(MachineInstr &MI);
void applyPtrAddZero(MachineInstr &MI);
/// Combine G_UREM x, (known power of 2) to an add and bitmasking.
void applySimplifyURemByPow2(MachineInstr &MI);
bool matchCombineInsertVecElts(MachineInstr &MI,
SmallVectorImpl<Register> &MatchInfo);
void applyCombineInsertVecElts(MachineInstr &MI,
SmallVectorImpl<Register> &MatchInfo);
/// Match expression trees of the form
///
/// \code
/// sN *a = ...
/// sM val = a[0] | (a[1] << N) | (a[2] << 2N) | (a[3] << 3N) ...
/// \endcode
///
/// And check if the tree can be replaced with a M-bit load + possibly a
/// bswap.
bool matchLoadOrCombine(MachineInstr &MI, BuildFnTy &MatchInfo);
bool matchTruncStoreMerge(MachineInstr &MI, MergeTruncStoresInfo &MatchInfo);
void applyTruncStoreMerge(MachineInstr &MI, MergeTruncStoresInfo &MatchInfo);
bool matchExtendThroughPhis(MachineInstr &MI, MachineInstr *&ExtMI);
void applyExtendThroughPhis(MachineInstr &MI, MachineInstr *&ExtMI);
bool matchExtractVecEltBuildVec(MachineInstr &MI, Register &Reg);
void applyExtractVecEltBuildVec(MachineInstr &MI, Register &Reg);
bool matchExtractAllEltsFromBuildVector(
MachineInstr &MI,
SmallVectorImpl<std::pair<Register, MachineInstr *>> &MatchInfo);
void applyExtractAllEltsFromBuildVector(
MachineInstr &MI,
SmallVectorImpl<std::pair<Register, MachineInstr *>> &MatchInfo);
/// Use a function which takes in a MachineIRBuilder to perform a combine.
/// By default, it erases the instruction \p MI from the function.
void applyBuildFn(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Use a function which takes in a MachineIRBuilder to perform a combine.
/// This variant does not erase \p MI after calling the build function.
void applyBuildFnNoErase(MachineInstr &MI, BuildFnTy &MatchInfo);
bool matchOrShiftToFunnelShift(MachineInstr &MI, BuildFnTy &MatchInfo);
bool matchFunnelShiftToRotate(MachineInstr &MI);
void applyFunnelShiftToRotate(MachineInstr &MI);
bool matchRotateOutOfRange(MachineInstr &MI);
void applyRotateOutOfRange(MachineInstr &MI);
/// \returns true if a G_ICMP instruction \p MI can be replaced with a true
/// or false constant based off of KnownBits information.
bool matchICmpToTrueFalseKnownBits(MachineInstr &MI, int64_t &MatchInfo);
/// \returns true if a G_ICMP \p MI can be replaced with its LHS based off of
/// KnownBits information.
bool
matchICmpToLHSKnownBits(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// \returns true if (and (or x, c1), c2) can be replaced with (and x, c2)
bool matchAndOrDisjointMask(MachineInstr &MI, BuildFnTy &MatchInfo);
bool matchBitfieldExtractFromSExtInReg(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// Match: and (lshr x, cst), mask -> ubfx x, cst, width
bool matchBitfieldExtractFromAnd(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Match: shr (shl x, n), k -> sbfx/ubfx x, pos, width
bool matchBitfieldExtractFromShr(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Match: shr (and x, n), k -> ubfx x, pos, width
bool matchBitfieldExtractFromShrAnd(MachineInstr &MI, BuildFnTy &MatchInfo);
// Helpers for reassociation:
bool matchReassocConstantInnerRHS(GPtrAdd &MI, MachineInstr *RHS,
BuildFnTy &MatchInfo);
bool matchReassocFoldConstantsInSubTree(GPtrAdd &MI, MachineInstr *LHS,
MachineInstr *RHS,
BuildFnTy &MatchInfo);
bool matchReassocConstantInnerLHS(GPtrAdd &MI, MachineInstr *LHS,
MachineInstr *RHS, BuildFnTy &MatchInfo);
/// Reassociate pointer calculations with G_ADD involved, to allow better
/// addressing mode usage.
bool matchReassocPtrAdd(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Do constant folding when opportunities are exposed after MIR building.
bool matchConstantFold(MachineInstr &MI, APInt &MatchInfo);
/// \returns true if it is possible to narrow the width of a scalar binop
/// feeding a G_AND instruction \p MI.
bool matchNarrowBinopFeedingAnd(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Given an G_UDIV \p MI expressing a divide by constant, return an
/// expression that implements it by multiplying by a magic number.
/// Ref: "Hacker's Delight" or "The PowerPC Compiler Writer's Guide".
MachineInstr *buildUDivUsingMul(MachineInstr &MI);
/// Combine G_UDIV by constant into a multiply by magic constant.
bool matchUDivByConst(MachineInstr &MI);
void applyUDivByConst(MachineInstr &MI);
// G_UMULH x, (1 << c)) -> x >> (bitwidth - c)
bool matchUMulHToLShr(MachineInstr &MI);
void applyUMulHToLShr(MachineInstr &MI);
/// Try to transform \p MI by using all of the above
/// combine functions. Returns true if changed.
bool tryCombine(MachineInstr &MI);
/// Emit loads and stores that perform the given memcpy.
/// Assumes \p MI is a G_MEMCPY_INLINE
/// TODO: implement dynamically sized inline memcpy,
/// and rename: s/bool tryEmit/void emit/
bool tryEmitMemcpyInline(MachineInstr &MI);
/// Match:
/// (G_UMULO x, 2) -> (G_UADDO x, x)
/// (G_SMULO x, 2) -> (G_SADDO x, x)
bool matchMulOBy2(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Transform (fadd x, fneg(y)) -> (fsub x, y)
/// (fadd fneg(x), y) -> (fsub y, x)
/// (fsub x, fneg(y)) -> (fadd x, y)
/// (fmul fneg(x), fneg(y)) -> (fmul x, y)
/// (fdiv fneg(x), fneg(y)) -> (fdiv x, y)
/// (fmad fneg(x), fneg(y), z) -> (fmad x, y, z)
/// (fma fneg(x), fneg(y), z) -> (fma x, y, z)
bool matchRedundantNegOperands(MachineInstr &MI, BuildFnTy &MatchInfo);
bool canCombineFMadOrFMA(MachineInstr &MI, bool &AllowFusionGlobally,
bool &HasFMAD, bool &Aggressive,
bool CanReassociate = false);
/// Transform (fadd (fmul x, y), z) -> (fma x, y, z)
/// (fadd (fmul x, y), z) -> (fmad x, y, z)
bool matchCombineFAddFMulToFMadOrFMA(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Transform (fadd (fpext (fmul x, y)), z) -> (fma (fpext x), (fpext y), z)
/// (fadd (fpext (fmul x, y)), z) -> (fmad (fpext x), (fpext y), z)
bool matchCombineFAddFpExtFMulToFMadOrFMA(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// Transform (fadd (fma x, y, (fmul u, v)), z) -> (fma x, y, (fma u, v, z))
/// (fadd (fmad x, y, (fmul u, v)), z) -> (fmad x, y, (fmad u, v, z))
bool matchCombineFAddFMAFMulToFMadOrFMA(MachineInstr &MI,
BuildFnTy &MatchInfo);
// Transform (fadd (fma x, y, (fpext (fmul u, v))), z)
// -> (fma x, y, (fma (fpext u), (fpext v), z))
// (fadd (fmad x, y, (fpext (fmul u, v))), z)
// -> (fmad x, y, (fmad (fpext u), (fpext v), z))
bool matchCombineFAddFpExtFMulToFMadOrFMAAggressive(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// Transform (fsub (fmul x, y), z) -> (fma x, y, -z)
/// (fsub (fmul x, y), z) -> (fmad x, y, -z)
bool matchCombineFSubFMulToFMadOrFMA(MachineInstr &MI, BuildFnTy &MatchInfo);
/// Transform (fsub (fneg (fmul, x, y)), z) -> (fma (fneg x), y, (fneg z))
/// (fsub (fneg (fmul, x, y)), z) -> (fmad (fneg x), y, (fneg z))
bool matchCombineFSubFNegFMulToFMadOrFMA(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// Transform (fsub (fpext (fmul x, y)), z)
/// -> (fma (fpext x), (fpext y), (fneg z))
/// (fsub (fpext (fmul x, y)), z)
/// -> (fmad (fpext x), (fpext y), (fneg z))
bool matchCombineFSubFpExtFMulToFMadOrFMA(MachineInstr &MI,
BuildFnTy &MatchInfo);
/// Transform (fsub (fpext (fneg (fmul x, y))), z)
/// -> (fneg (fma (fpext x), (fpext y), z))
/// (fsub (fpext (fneg (fmul x, y))), z)
/// -> (fneg (fmad (fpext x), (fpext y), z))
bool matchCombineFSubFpExtFNegFMulToFMadOrFMA(MachineInstr &MI,
BuildFnTy &MatchInfo);
private:
/// Given a non-indexed load or store instruction \p MI, find an offset that
/// can be usefully and legally folded into it as a post-indexing operation.
///
/// \returns true if a candidate is found.
bool findPostIndexCandidate(MachineInstr &MI, Register &Addr, Register &Base,
Register &Offset);
/// Given a non-indexed load or store instruction \p MI, find an offset that
/// can be usefully and legally folded into it as a pre-indexing operation.
///
/// \returns true if a candidate is found.
bool findPreIndexCandidate(MachineInstr &MI, Register &Addr, Register &Base,
Register &Offset);
/// Helper function for matchLoadOrCombine. Searches for Registers
/// which may have been produced by a load instruction + some arithmetic.
///
/// \param [in] Root - The search root.
///
/// \returns The Registers found during the search.
Optional<SmallVector<Register, 8>>
findCandidatesForLoadOrCombine(const MachineInstr *Root) const;
/// Helper function for matchLoadOrCombine.
///
/// Checks if every register in \p RegsToVisit is defined by a load
/// instruction + some arithmetic.
///
/// \param [out] MemOffset2Idx - Maps the byte positions each load ends up
/// at to the index of the load.
/// \param [in] MemSizeInBits - The number of bits each load should produce.
///
/// \returns On success, a 3-tuple containing lowest-index load found, the
/// lowest index, and the last load in the sequence.
Optional<std::tuple<GZExtLoad *, int64_t, GZExtLoad *>>
findLoadOffsetsForLoadOrCombine(
SmallDenseMap<int64_t, int64_t, 8> &MemOffset2Idx,
const SmallVector<Register, 8> &RegsToVisit,
const unsigned MemSizeInBits);
/// Examines the G_PTR_ADD instruction \p PtrAdd and determines if performing
/// a re-association of its operands would break an existing legal addressing
/// mode that the address computation currently represents.
bool reassociationCanBreakAddressingModePattern(MachineInstr &PtrAdd);
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,71 @@
//===- llvm/CodeGen/GlobalISel/CombinerInfo.h ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Interface for Targets to specify which operations are combined how and when.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_COMBINERINFO_H
#define LLVM_CODEGEN_GLOBALISEL_COMBINERINFO_H
#include <cassert>
namespace llvm {
class GISelChangeObserver;
class LegalizerInfo;
class MachineInstr;
class MachineIRBuilder;
// Contains information relevant to enabling/disabling various combines for a
// pass.
class CombinerInfo {
public:
CombinerInfo(bool AllowIllegalOps, bool ShouldLegalizeIllegal,
const LegalizerInfo *LInfo, bool OptEnabled, bool OptSize,
bool MinSize)
: IllegalOpsAllowed(AllowIllegalOps),
LegalizeIllegalOps(ShouldLegalizeIllegal), LInfo(LInfo),
EnableOpt(OptEnabled), EnableOptSize(OptSize), EnableMinSize(MinSize) {
assert(((AllowIllegalOps || !LegalizeIllegalOps) || LInfo) &&
"Expecting legalizerInfo when illegalops not allowed");
}
virtual ~CombinerInfo() = default;
/// If \p IllegalOpsAllowed is false, the CombinerHelper will make use of
/// the legalizerInfo to check for legality before each transformation.
bool IllegalOpsAllowed; // TODO: Make use of this.
/// If \p LegalizeIllegalOps is true, the Combiner will also legalize the
/// illegal ops that are created.
bool LegalizeIllegalOps; // TODO: Make use of this.
const LegalizerInfo *LInfo;
/// Whether optimizations should be enabled. This is to distinguish between
/// uses of the combiner unconditionally and only when optimizations are
/// specifically enabled/
bool EnableOpt;
/// Whether we're optimizing for size.
bool EnableOptSize;
/// Whether we're optimizing for minsize (-Oz).
bool EnableMinSize;
/// Attempt to combine instructions using MI as the root.
///
/// Use Observer to report the creation, modification, and erasure of
/// instructions. GISelChangeObserver will automatically report certain
/// kinds of operations. These operations are:
/// * Instructions that are newly inserted into the MachineFunction
/// * Instructions that are erased from the MachineFunction.
///
/// However, it is important to report instruction modification and this is
/// not automatic.
virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI,
MachineIRBuilder &B) const = 0;
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,140 @@
//===----- llvm/CodeGen/GlobalISel/GISelChangeObserver.h --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This contains common code to allow clients to notify changes to machine
/// instr.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H
#define LLVM_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/CodeGen/MachineFunction.h"
namespace llvm {
class MachineInstr;
class MachineRegisterInfo;
/// Abstract class that contains various methods for clients to notify about
/// changes. This should be the preferred way for APIs to notify changes.
/// Typically calling erasingInstr/createdInstr multiple times should not affect
/// the result. The observer would likely need to check if it was already
/// notified earlier (consider using GISelWorkList).
class GISelChangeObserver {
SmallPtrSet<MachineInstr *, 4> ChangingAllUsesOfReg;
public:
virtual ~GISelChangeObserver() = default;
/// An instruction is about to be erased.
virtual void erasingInstr(MachineInstr &MI) = 0;
/// An instruction has been created and inserted into the function.
/// Note that the instruction might not be a fully fledged instruction at this
/// point and won't be if the MachineFunction::Delegate is calling it. This is
/// because the delegate only sees the construction of the MachineInstr before
/// operands have been added.
virtual void createdInstr(MachineInstr &MI) = 0;
/// This instruction is about to be mutated in some way.
virtual void changingInstr(MachineInstr &MI) = 0;
/// This instruction was mutated in some way.
virtual void changedInstr(MachineInstr &MI) = 0;
/// All the instructions using the given register are being changed.
/// For convenience, finishedChangingAllUsesOfReg() will report the completion
/// of the changes. The use list may change between this call and
/// finishedChangingAllUsesOfReg().
void changingAllUsesOfReg(const MachineRegisterInfo &MRI, Register Reg);
/// All instructions reported as changing by changingAllUsesOfReg() have
/// finished being changed.
void finishedChangingAllUsesOfReg();
};
/// Simple wrapper observer that takes several observers, and calls
/// each one for each event. If there are multiple observers (say CSE,
/// Legalizer, Combiner), it's sufficient to register this to the machine
/// function as the delegate.
class GISelObserverWrapper : public MachineFunction::Delegate,
public GISelChangeObserver {
SmallVector<GISelChangeObserver *, 4> Observers;
public:
GISelObserverWrapper() = default;
GISelObserverWrapper(ArrayRef<GISelChangeObserver *> Obs)
: Observers(Obs.begin(), Obs.end()) {}
// Adds an observer.
void addObserver(GISelChangeObserver *O) { Observers.push_back(O); }
// Removes an observer from the list and does nothing if observer is not
// present.
void removeObserver(GISelChangeObserver *O) {
auto It = std::find(Observers.begin(), Observers.end(), O);
if (It != Observers.end())
Observers.erase(It);
}
// API for Observer.
void erasingInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->erasingInstr(MI);
}
void createdInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->createdInstr(MI);
}
void changingInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->changingInstr(MI);
}
void changedInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->changedInstr(MI);
}
// API for MachineFunction::Delegate
void MF_HandleInsertion(MachineInstr &MI) override { createdInstr(MI); }
void MF_HandleRemoval(MachineInstr &MI) override { erasingInstr(MI); }
};
/// A simple RAII based Delegate installer.
/// Use this in a scope to install a delegate to the MachineFunction and reset
/// it at the end of the scope.
class RAIIDelegateInstaller {
MachineFunction &MF;
MachineFunction::Delegate *Delegate;
public:
RAIIDelegateInstaller(MachineFunction &MF, MachineFunction::Delegate *Del);
~RAIIDelegateInstaller();
};
/// A simple RAII based Observer installer.
/// Use this in a scope to install the Observer to the MachineFunction and reset
/// it at the end of the scope.
class RAIIMFObserverInstaller {
MachineFunction &MF;
public:
RAIIMFObserverInstaller(MachineFunction &MF, GISelChangeObserver &Observer);
~RAIIMFObserverInstaller();
};
/// Class to install both of the above.
class RAIIMFObsDelInstaller {
RAIIDelegateInstaller DelI;
RAIIMFObserverInstaller ObsI;
public:
RAIIMFObsDelInstaller(MachineFunction &MF, GISelObserverWrapper &Wrapper)
: DelI(MF, &Wrapper), ObsI(MF, Wrapper) {}
~RAIIMFObsDelInstaller() = default;
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,132 @@
//===- llvm/CodeGen/GlobalISel/GISelKnownBits.h ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Provides analysis for querying information about KnownBits during GISel
/// passes.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_GISELKNOWNBITS_H
#define LLVM_CODEGEN_GLOBALISEL_GISELKNOWNBITS_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/KnownBits.h"
namespace llvm {
class TargetLowering;
class DataLayout;
class GISelKnownBits : public GISelChangeObserver {
MachineFunction &MF;
MachineRegisterInfo &MRI;
const TargetLowering &TL;
const DataLayout &DL;
unsigned MaxDepth;
/// Cache maintained during a computeKnownBits request.
SmallDenseMap<Register, KnownBits, 16> ComputeKnownBitsCache;
void computeKnownBitsMin(Register Src0, Register Src1, KnownBits &Known,
const APInt &DemandedElts,
unsigned Depth = 0);
unsigned computeNumSignBitsMin(Register Src0, Register Src1,
const APInt &DemandedElts, unsigned Depth = 0);
public:
GISelKnownBits(MachineFunction &MF, unsigned MaxDepth = 6);
virtual ~GISelKnownBits() = default;
const MachineFunction &getMachineFunction() const {
return MF;
}
const DataLayout &getDataLayout() const {
return DL;
}
virtual void computeKnownBitsImpl(Register R, KnownBits &Known,
const APInt &DemandedElts,
unsigned Depth = 0);
unsigned computeNumSignBits(Register R, const APInt &DemandedElts,
unsigned Depth = 0);
unsigned computeNumSignBits(Register R, unsigned Depth = 0);
// KnownBitsAPI
KnownBits getKnownBits(Register R);
KnownBits getKnownBits(Register R, const APInt &DemandedElts,
unsigned Depth = 0);
// Calls getKnownBits for first operand def of MI.
KnownBits getKnownBits(MachineInstr &MI);
APInt getKnownZeroes(Register R);
APInt getKnownOnes(Register R);
/// \return true if 'V & Mask' is known to be zero in DemandedElts. We use
/// this predicate to simplify operations downstream.
/// Mask is known to be zero for bits that V cannot have.
bool maskedValueIsZero(Register Val, const APInt &Mask) {
return Mask.isSubsetOf(getKnownBits(Val).Zero);
}
/// \return true if the sign bit of Op is known to be zero. We use this
/// predicate to simplify operations downstream.
bool signBitIsZero(Register Op);
static void computeKnownBitsForAlignment(KnownBits &Known,
Align Alignment) {
// The low bits are known zero if the pointer is aligned.
Known.Zero.setLowBits(Log2(Alignment));
}
/// \return The known alignment for the pointer-like value \p R.
Align computeKnownAlignment(Register R, unsigned Depth = 0);
// Observer API. No-op for non-caching implementation.
void erasingInstr(MachineInstr &MI) override{};
void createdInstr(MachineInstr &MI) override{};
void changingInstr(MachineInstr &MI) override{};
void changedInstr(MachineInstr &MI) override{};
protected:
unsigned getMaxDepth() const { return MaxDepth; }
};
/// To use KnownBitsInfo analysis in a pass,
/// KnownBitsInfo &Info = getAnalysis<GISelKnownBitsInfoAnalysis>().get(MF);
/// Add to observer if the Info is caching.
/// WrapperObserver.addObserver(Info);
/// Eventually add other features such as caching/ser/deserializing
/// to MIR etc. Those implementations can derive from GISelKnownBits
/// and override computeKnownBitsImpl.
class GISelKnownBitsAnalysis : public MachineFunctionPass {
std::unique_ptr<GISelKnownBits> Info;
public:
static char ID;
GISelKnownBitsAnalysis() : MachineFunctionPass(ID) {
initializeGISelKnownBitsAnalysisPass(*PassRegistry::getPassRegistry());
}
GISelKnownBits &get(MachineFunction &MF) {
if (!Info)
Info = std::make_unique<GISelKnownBits>(MF);
return *Info.get();
}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &MF) override;
void releaseMemory() override { Info.reset(); }
};
} // namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_GISELKNOWNBITS_H

View File

@@ -0,0 +1,112 @@
//===- GISelWorkList.h - Worklist for GISel passes ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_GISELWORKLIST_H
#define LLVM_CODEGEN_GLOBALISEL_GISELWORKLIST_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
namespace llvm {
class MachineInstr;
// Worklist which mostly works similar to InstCombineWorkList, but on
// MachineInstrs. The main difference with something like a SetVector is that
// erasing an element doesn't move all elements over one place - instead just
// nulls out the element of the vector.
//
// FIXME: Does it make sense to factor out common code with the
// instcombinerWorkList?
template<unsigned N>
class GISelWorkList {
SmallVector<MachineInstr *, N> Worklist;
DenseMap<MachineInstr *, unsigned> WorklistMap;
#ifndef NDEBUG
bool Finalized = true;
#endif
public:
GISelWorkList() : WorklistMap(N) {}
bool empty() const { return WorklistMap.empty(); }
unsigned size() const { return WorklistMap.size(); }
// Since we don't know ahead of time how many instructions we're going to add
// to the worklist, and migrating densemap's elements is quite expensive
// every time we resize, only insert to the smallvector (typically during the
// initial phase of populating lists). Before the worklist can be used,
// finalize should be called. Also assert with NDEBUG if list is ever used
// without finalizing. Note that unlike insert, we won't check for duplicates
// - so the ideal place to use this is during the initial prepopulating phase
// of most passes.
void deferred_insert(MachineInstr *I) {
Worklist.push_back(I);
#ifndef NDEBUG
Finalized = false;
#endif
}
// This should only be called when using deferred_insert.
// This asserts that the WorklistMap is empty, and then
// inserts all the elements in the Worklist into the map.
// It also asserts if there are any duplicate elements found.
void finalize() {
assert(WorklistMap.empty() && "Expecting empty worklistmap");
if (Worklist.size() > N)
WorklistMap.reserve(Worklist.size());
for (unsigned i = 0; i < Worklist.size(); ++i)
if (!WorklistMap.try_emplace(Worklist[i], i).second)
llvm_unreachable("Duplicate elements in the list");
#ifndef NDEBUG
Finalized = true;
#endif
}
/// Add the specified instruction to the worklist if it isn't already in it.
void insert(MachineInstr *I) {
assert(Finalized && "GISelWorkList used without finalizing");
if (WorklistMap.try_emplace(I, Worklist.size()).second)
Worklist.push_back(I);
}
/// Remove I from the worklist if it exists.
void remove(const MachineInstr *I) {
assert((Finalized || WorklistMap.empty()) && "Neither finalized nor empty");
auto It = WorklistMap.find(I);
if (It == WorklistMap.end())
return; // Not in worklist.
// Don't bother moving everything down, just null out the slot.
Worklist[It->second] = nullptr;
WorklistMap.erase(It);
}
void clear() {
Worklist.clear();
WorklistMap.clear();
}
MachineInstr *pop_back_val() {
assert(Finalized && "GISelWorkList used without finalizing");
MachineInstr *I;
do {
I = Worklist.pop_back_val();
} while(!I);
assert(I && "Pop back on empty worklist");
WorklistMap.erase(I);
return I;
}
};
} // end namespace llvm.
#endif

View File

@@ -0,0 +1,231 @@
//===- llvm/CodeGen/GlobalISel/GenericMachineInstrs.h -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Declares convenience wrapper classes for interpreting MachineInstr instances
/// as specific generic operations.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H
#define LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/Support/Casting.h"
namespace llvm {
/// A base class for all GenericMachineInstrs.
class GenericMachineInstr : public MachineInstr {
public:
GenericMachineInstr() = delete;
/// Access the Idx'th operand as a register and return it.
/// This assumes that the Idx'th operand is a Register type.
Register getReg(unsigned Idx) const { return getOperand(Idx).getReg(); }
static bool classof(const MachineInstr *MI) {
return isPreISelGenericOpcode(MI->getOpcode());
}
};
/// Represents any type of generic load or store.
/// G_LOAD, G_STORE, G_ZEXTLOAD, G_SEXTLOAD.
class GLoadStore : public GenericMachineInstr {
public:
/// Get the source register of the pointer value.
Register getPointerReg() const { return getOperand(1).getReg(); }
/// Get the MachineMemOperand on this instruction.
MachineMemOperand &getMMO() const { return **memoperands_begin(); }
/// Returns true if the attached MachineMemOperand has the atomic flag set.
bool isAtomic() const { return getMMO().isAtomic(); }
/// Returns true if the attached MachineMemOpeand as the volatile flag set.
bool isVolatile() const { return getMMO().isVolatile(); }
/// Returns true if the memory operation is neither atomic or volatile.
bool isSimple() const { return !isAtomic() && !isVolatile(); }
/// Returns true if this memory operation doesn't have any ordering
/// constraints other than normal aliasing. Volatile and (ordered) atomic
/// memory operations can't be reordered.
bool isUnordered() const { return getMMO().isUnordered(); }
/// Returns the size in bytes of the memory access.
uint64_t getMemSize() const { return getMMO().getSize();
} /// Returns the size in bits of the memory access.
uint64_t getMemSizeInBits() const { return getMMO().getSizeInBits(); }
static bool classof(const MachineInstr *MI) {
switch (MI->getOpcode()) {
case TargetOpcode::G_LOAD:
case TargetOpcode::G_STORE:
case TargetOpcode::G_ZEXTLOAD:
case TargetOpcode::G_SEXTLOAD:
return true;
default:
return false;
}
}
};
/// Represents any generic load, including sign/zero extending variants.
class GAnyLoad : public GLoadStore {
public:
/// Get the definition register of the loaded value.
Register getDstReg() const { return getOperand(0).getReg(); }
static bool classof(const MachineInstr *MI) {
switch (MI->getOpcode()) {
case TargetOpcode::G_LOAD:
case TargetOpcode::G_ZEXTLOAD:
case TargetOpcode::G_SEXTLOAD:
return true;
default:
return false;
}
}
};
/// Represents a G_LOAD.
class GLoad : public GAnyLoad {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_LOAD;
}
};
/// Represents either a G_SEXTLOAD or G_ZEXTLOAD.
class GExtLoad : public GAnyLoad {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_SEXTLOAD ||
MI->getOpcode() == TargetOpcode::G_ZEXTLOAD;
}
};
/// Represents a G_SEXTLOAD.
class GSExtLoad : public GExtLoad {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_SEXTLOAD;
}
};
/// Represents a G_ZEXTLOAD.
class GZExtLoad : public GExtLoad {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_ZEXTLOAD;
}
};
/// Represents a G_STORE.
class GStore : public GLoadStore {
public:
/// Get the stored value register.
Register getValueReg() const { return getOperand(0).getReg(); }
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_STORE;
}
};
/// Represents a G_UNMERGE_VALUES.
class GUnmerge : public GenericMachineInstr {
public:
/// Returns the number of def registers.
unsigned getNumDefs() const { return getNumOperands() - 1; }
/// Get the unmerge source register.
Register getSourceReg() const { return getOperand(getNumDefs()).getReg(); }
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES;
}
};
/// Represents G_BUILD_VECTOR, G_CONCAT_VECTORS or G_MERGE_VALUES.
/// All these have the common property of generating a single value from
/// multiple sources.
class GMergeLikeOp : public GenericMachineInstr {
public:
/// Returns the number of source registers.
unsigned getNumSources() const { return getNumOperands() - 1; }
/// Returns the I'th source register.
Register getSourceReg(unsigned I) const { return getReg(I + 1); }
static bool classof(const MachineInstr *MI) {
switch (MI->getOpcode()) {
case TargetOpcode::G_MERGE_VALUES:
case TargetOpcode::G_CONCAT_VECTORS:
case TargetOpcode::G_BUILD_VECTOR:
return true;
default:
return false;
}
}
};
/// Represents a G_MERGE_VALUES.
class GMerge : public GMergeLikeOp {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_MERGE_VALUES;
}
};
/// Represents a G_CONCAT_VECTORS.
class GConcatVectors : public GMergeLikeOp {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_CONCAT_VECTORS;
}
};
/// Represents a G_BUILD_VECTOR.
class GBuildVector : public GMergeLikeOp {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR;
}
};
/// Represents a G_PTR_ADD.
class GPtrAdd : public GenericMachineInstr {
public:
Register getBaseReg() const { return getReg(1); }
Register getOffsetReg() const { return getReg(2); }
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_PTR_ADD;
}
};
/// Represents a G_IMPLICIT_DEF.
class GImplicitDef : public GenericMachineInstr {
public:
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF;
}
};
/// Represents a G_SELECT.
class GSelect : public GenericMachineInstr {
public:
Register getCondReg() const { return getReg(1); }
Register getTrueReg() const { return getReg(2); }
Register getFalseReg() const { return getReg(3); }
static bool classof(const MachineInstr *MI) {
return MI->getOpcode() == TargetOpcode::G_SELECT;
}
};
} // namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H

View File

@@ -0,0 +1,731 @@
//===- llvm/CodeGen/GlobalISel/IRTranslator.h - IRTranslator ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file declares the IRTranslator pass.
/// This pass is responsible for translating LLVM IR into MachineInstr.
/// It uses target hooks to lower the ABI but aside from that, the pass
/// generated code is generic. This is the default translator used for
/// GlobalISel.
///
/// \todo Replace the comments with actual doxygen comments.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_IRTRANSLATOR_H
#define LLVM_CODEGEN_GLOBALISEL_IRTRANSLATOR_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/CodeGenCommonISel.h"
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/SwiftErrorValueTracking.h"
#include "llvm/CodeGen/SwitchLoweringUtils.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/CodeGen.h"
#include <memory>
#include <utility>
namespace llvm {
class AllocaInst;
class BasicBlock;
class CallInst;
class CallLowering;
class Constant;
class ConstrainedFPIntrinsic;
class DataLayout;
class Instruction;
class MachineBasicBlock;
class MachineFunction;
class MachineInstr;
class MachineRegisterInfo;
class OptimizationRemarkEmitter;
class PHINode;
class TargetPassConfig;
class User;
class Value;
// Technically the pass should run on an hypothetical MachineModule,
// since it should translate Global into some sort of MachineGlobal.
// The MachineGlobal should ultimately just be a transfer of ownership of
// the interesting bits that are relevant to represent a global value.
// That being said, we could investigate what would it cost to just duplicate
// the information from the LLVM IR.
// The idea is that ultimately we would be able to free up the memory used
// by the LLVM IR as soon as the translation is over.
class IRTranslator : public MachineFunctionPass {
public:
static char ID;
private:
/// Interface used to lower the everything related to calls.
const CallLowering *CLI;
/// This class contains the mapping between the Values to vreg related data.
class ValueToVRegInfo {
public:
ValueToVRegInfo() = default;
using VRegListT = SmallVector<Register, 1>;
using OffsetListT = SmallVector<uint64_t, 1>;
using const_vreg_iterator =
DenseMap<const Value *, VRegListT *>::const_iterator;
using const_offset_iterator =
DenseMap<const Value *, OffsetListT *>::const_iterator;
inline const_vreg_iterator vregs_end() const { return ValToVRegs.end(); }
VRegListT *getVRegs(const Value &V) {
auto It = ValToVRegs.find(&V);
if (It != ValToVRegs.end())
return It->second;
return insertVRegs(V);
}
OffsetListT *getOffsets(const Value &V) {
auto It = TypeToOffsets.find(V.getType());
if (It != TypeToOffsets.end())
return It->second;
return insertOffsets(V);
}
const_vreg_iterator findVRegs(const Value &V) const {
return ValToVRegs.find(&V);
}
bool contains(const Value &V) const {
return ValToVRegs.find(&V) != ValToVRegs.end();
}
void reset() {
ValToVRegs.clear();
TypeToOffsets.clear();
VRegAlloc.DestroyAll();
OffsetAlloc.DestroyAll();
}
private:
VRegListT *insertVRegs(const Value &V) {
assert(ValToVRegs.find(&V) == ValToVRegs.end() && "Value already exists");
// We placement new using our fast allocator since we never try to free
// the vectors until translation is finished.
auto *VRegList = new (VRegAlloc.Allocate()) VRegListT();
ValToVRegs[&V] = VRegList;
return VRegList;
}
OffsetListT *insertOffsets(const Value &V) {
assert(TypeToOffsets.find(V.getType()) == TypeToOffsets.end() &&
"Type already exists");
auto *OffsetList = new (OffsetAlloc.Allocate()) OffsetListT();
TypeToOffsets[V.getType()] = OffsetList;
return OffsetList;
}
SpecificBumpPtrAllocator<VRegListT> VRegAlloc;
SpecificBumpPtrAllocator<OffsetListT> OffsetAlloc;
// We store pointers to vectors here since references may be invalidated
// while we hold them if we stored the vectors directly.
DenseMap<const Value *, VRegListT*> ValToVRegs;
DenseMap<const Type *, OffsetListT*> TypeToOffsets;
};
/// Mapping of the values of the current LLVM IR function to the related
/// virtual registers and offsets.
ValueToVRegInfo VMap;
// N.b. it's not completely obvious that this will be sufficient for every
// LLVM IR construct (with "invoke" being the obvious candidate to mess up our
// lives.
DenseMap<const BasicBlock *, MachineBasicBlock *> BBToMBB;
// One BasicBlock can be translated to multiple MachineBasicBlocks. For such
// BasicBlocks translated to multiple MachineBasicBlocks, MachinePreds retains
// a mapping between the edges arriving at the BasicBlock to the corresponding
// created MachineBasicBlocks. Some BasicBlocks that get translated to a
// single MachineBasicBlock may also end up in this Map.
using CFGEdge = std::pair<const BasicBlock *, const BasicBlock *>;
DenseMap<CFGEdge, SmallVector<MachineBasicBlock *, 1>> MachinePreds;
// List of stubbed PHI instructions, for values and basic blocks to be filled
// in once all MachineBasicBlocks have been created.
SmallVector<std::pair<const PHINode *, SmallVector<MachineInstr *, 1>>, 4>
PendingPHIs;
/// Record of what frame index has been allocated to specified allocas for
/// this function.
DenseMap<const AllocaInst *, int> FrameIndices;
SwiftErrorValueTracking SwiftError;
/// \name Methods for translating form LLVM IR to MachineInstr.
/// \see ::translate for general information on the translate methods.
/// @{
/// Translate \p Inst into its corresponding MachineInstr instruction(s).
/// Insert the newly translated instruction(s) right where the CurBuilder
/// is set.
///
/// The general algorithm is:
/// 1. Look for a virtual register for each operand or
/// create one.
/// 2 Update the VMap accordingly.
/// 2.alt. For constant arguments, if they are compile time constants,
/// produce an immediate in the right operand and do not touch
/// ValToReg. Actually we will go with a virtual register for each
/// constants because it may be expensive to actually materialize the
/// constant. Moreover, if the constant spans on several instructions,
/// CSE may not catch them.
/// => Update ValToVReg and remember that we saw a constant in Constants.
/// We will materialize all the constants in finalize.
/// Note: we would need to do something so that we can recognize such operand
/// as constants.
/// 3. Create the generic instruction.
///
/// \return true if the translation succeeded.
bool translate(const Instruction &Inst);
/// Materialize \p C into virtual-register \p Reg. The generic instructions
/// performing this materialization will be inserted into the entry block of
/// the function.
///
/// \return true if the materialization succeeded.
bool translate(const Constant &C, Register Reg);
// Translate U as a copy of V.
bool translateCopy(const User &U, const Value &V,
MachineIRBuilder &MIRBuilder);
/// Translate an LLVM bitcast into generic IR. Either a COPY or a G_BITCAST is
/// emitted.
bool translateBitCast(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate an LLVM load instruction into generic IR.
bool translateLoad(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate an LLVM store instruction into generic IR.
bool translateStore(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate an LLVM string intrinsic (memcpy, memset, ...).
bool translateMemFunc(const CallInst &CI, MachineIRBuilder &MIRBuilder,
unsigned Opcode);
void getStackGuard(Register DstReg, MachineIRBuilder &MIRBuilder);
bool translateOverflowIntrinsic(const CallInst &CI, unsigned Op,
MachineIRBuilder &MIRBuilder);
bool translateFixedPointIntrinsic(unsigned Op, const CallInst &CI,
MachineIRBuilder &MIRBuilder);
/// Helper function for translateSimpleIntrinsic.
/// \return The generic opcode for \p IntrinsicID if \p IntrinsicID is a
/// simple intrinsic (ceil, fabs, etc.). Otherwise, returns
/// Intrinsic::not_intrinsic.
unsigned getSimpleIntrinsicOpcode(Intrinsic::ID ID);
/// Translates the intrinsics defined in getSimpleIntrinsicOpcode.
/// \return true if the translation succeeded.
bool translateSimpleIntrinsic(const CallInst &CI, Intrinsic::ID ID,
MachineIRBuilder &MIRBuilder);
bool translateConstrainedFPIntrinsic(const ConstrainedFPIntrinsic &FPI,
MachineIRBuilder &MIRBuilder);
bool translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
MachineIRBuilder &MIRBuilder);
bool translateInlineAsm(const CallBase &CB, MachineIRBuilder &MIRBuilder);
/// Returns true if the value should be split into multiple LLTs.
/// If \p Offsets is given then the split type's offsets will be stored in it.
/// If \p Offsets is not empty it will be cleared first.
bool valueIsSplit(const Value &V,
SmallVectorImpl<uint64_t> *Offsets = nullptr);
/// Common code for translating normal calls or invokes.
bool translateCallBase(const CallBase &CB, MachineIRBuilder &MIRBuilder);
/// Translate call instruction.
/// \pre \p U is a call instruction.
bool translateCall(const User &U, MachineIRBuilder &MIRBuilder);
/// When an invoke or a cleanupret unwinds to the next EH pad, there are
/// many places it could ultimately go. In the IR, we have a single unwind
/// destination, but in the machine CFG, we enumerate all the possible blocks.
/// This function skips over imaginary basic blocks that hold catchswitch
/// instructions, and finds all the "real" machine
/// basic block destinations. As those destinations may not be successors of
/// EHPadBB, here we also calculate the edge probability to those
/// destinations. The passed-in Prob is the edge probability to EHPadBB.
bool findUnwindDestinations(
const BasicBlock *EHPadBB, BranchProbability Prob,
SmallVectorImpl<std::pair<MachineBasicBlock *, BranchProbability>>
&UnwindDests);
bool translateInvoke(const User &U, MachineIRBuilder &MIRBuilder);
bool translateCallBr(const User &U, MachineIRBuilder &MIRBuilder);
bool translateLandingPad(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate one of LLVM's cast instructions into MachineInstrs, with the
/// given generic Opcode.
bool translateCast(unsigned Opcode, const User &U,
MachineIRBuilder &MIRBuilder);
/// Translate a phi instruction.
bool translatePHI(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate a comparison (icmp or fcmp) instruction or constant.
bool translateCompare(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate an integer compare instruction (or constant).
bool translateICmp(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCompare(U, MIRBuilder);
}
/// Translate a floating-point compare instruction (or constant).
bool translateFCmp(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCompare(U, MIRBuilder);
}
/// Add remaining operands onto phis we've translated. Executed after all
/// MachineBasicBlocks for the function have been created.
void finishPendingPhis();
/// Translate \p Inst into a unary operation \p Opcode.
/// \pre \p U is a unary operation.
bool translateUnaryOp(unsigned Opcode, const User &U,
MachineIRBuilder &MIRBuilder);
/// Translate \p Inst into a binary operation \p Opcode.
/// \pre \p U is a binary operation.
bool translateBinaryOp(unsigned Opcode, const User &U,
MachineIRBuilder &MIRBuilder);
/// If the set of cases should be emitted as a series of branches, return
/// true. If we should emit this as a bunch of and/or'd together conditions,
/// return false.
bool shouldEmitAsBranches(const std::vector<SwitchCG::CaseBlock> &Cases);
/// Helper method for findMergedConditions.
/// This function emits a branch and is used at the leaves of an OR or an
/// AND operator tree.
void emitBranchForMergedCondition(const Value *Cond, MachineBasicBlock *TBB,
MachineBasicBlock *FBB,
MachineBasicBlock *CurBB,
MachineBasicBlock *SwitchBB,
BranchProbability TProb,
BranchProbability FProb, bool InvertCond);
/// Used during condbr translation to find trees of conditions that can be
/// optimized.
void findMergedConditions(const Value *Cond, MachineBasicBlock *TBB,
MachineBasicBlock *FBB, MachineBasicBlock *CurBB,
MachineBasicBlock *SwitchBB,
Instruction::BinaryOps Opc, BranchProbability TProb,
BranchProbability FProb, bool InvertCond);
/// Translate branch (br) instruction.
/// \pre \p U is a branch instruction.
bool translateBr(const User &U, MachineIRBuilder &MIRBuilder);
// Begin switch lowering functions.
bool emitJumpTableHeader(SwitchCG::JumpTable &JT,
SwitchCG::JumpTableHeader &JTH,
MachineBasicBlock *HeaderBB);
void emitJumpTable(SwitchCG::JumpTable &JT, MachineBasicBlock *MBB);
void emitSwitchCase(SwitchCG::CaseBlock &CB, MachineBasicBlock *SwitchBB,
MachineIRBuilder &MIB);
/// Generate for for the BitTest header block, which precedes each sequence of
/// BitTestCases.
void emitBitTestHeader(SwitchCG::BitTestBlock &BTB,
MachineBasicBlock *SwitchMBB);
/// Generate code to produces one "bit test" for a given BitTestCase \p B.
void emitBitTestCase(SwitchCG::BitTestBlock &BB, MachineBasicBlock *NextMBB,
BranchProbability BranchProbToNext, Register Reg,
SwitchCG::BitTestCase &B, MachineBasicBlock *SwitchBB);
bool lowerJumpTableWorkItem(
SwitchCG::SwitchWorkListItem W, MachineBasicBlock *SwitchMBB,
MachineBasicBlock *CurMBB, MachineBasicBlock *DefaultMBB,
MachineIRBuilder &MIB, MachineFunction::iterator BBI,
BranchProbability UnhandledProbs, SwitchCG::CaseClusterIt I,
MachineBasicBlock *Fallthrough, bool FallthroughUnreachable);
bool lowerSwitchRangeWorkItem(SwitchCG::CaseClusterIt I, Value *Cond,
MachineBasicBlock *Fallthrough,
bool FallthroughUnreachable,
BranchProbability UnhandledProbs,
MachineBasicBlock *CurMBB,
MachineIRBuilder &MIB,
MachineBasicBlock *SwitchMBB);
bool lowerBitTestWorkItem(
SwitchCG::SwitchWorkListItem W, MachineBasicBlock *SwitchMBB,
MachineBasicBlock *CurMBB, MachineBasicBlock *DefaultMBB,
MachineIRBuilder &MIB, MachineFunction::iterator BBI,
BranchProbability DefaultProb, BranchProbability UnhandledProbs,
SwitchCG::CaseClusterIt I, MachineBasicBlock *Fallthrough,
bool FallthroughUnreachable);
bool lowerSwitchWorkItem(SwitchCG::SwitchWorkListItem W, Value *Cond,
MachineBasicBlock *SwitchMBB,
MachineBasicBlock *DefaultMBB,
MachineIRBuilder &MIB);
bool translateSwitch(const User &U, MachineIRBuilder &MIRBuilder);
// End switch lowering section.
bool translateIndirectBr(const User &U, MachineIRBuilder &MIRBuilder);
bool translateExtractValue(const User &U, MachineIRBuilder &MIRBuilder);
bool translateInsertValue(const User &U, MachineIRBuilder &MIRBuilder);
bool translateSelect(const User &U, MachineIRBuilder &MIRBuilder);
bool translateGetElementPtr(const User &U, MachineIRBuilder &MIRBuilder);
bool translateAlloca(const User &U, MachineIRBuilder &MIRBuilder);
/// Translate return (ret) instruction.
/// The target needs to implement CallLowering::lowerReturn for
/// this to succeed.
/// \pre \p U is a return instruction.
bool translateRet(const User &U, MachineIRBuilder &MIRBuilder);
bool translateFNeg(const User &U, MachineIRBuilder &MIRBuilder);
bool translateAdd(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_ADD, U, MIRBuilder);
}
bool translateSub(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_SUB, U, MIRBuilder);
}
bool translateAnd(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_AND, U, MIRBuilder);
}
bool translateMul(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_MUL, U, MIRBuilder);
}
bool translateOr(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_OR, U, MIRBuilder);
}
bool translateXor(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_XOR, U, MIRBuilder);
}
bool translateUDiv(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_UDIV, U, MIRBuilder);
}
bool translateSDiv(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_SDIV, U, MIRBuilder);
}
bool translateURem(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_UREM, U, MIRBuilder);
}
bool translateSRem(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_SREM, U, MIRBuilder);
}
bool translateIntToPtr(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_INTTOPTR, U, MIRBuilder);
}
bool translatePtrToInt(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_PTRTOINT, U, MIRBuilder);
}
bool translateTrunc(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_TRUNC, U, MIRBuilder);
}
bool translateFPTrunc(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_FPTRUNC, U, MIRBuilder);
}
bool translateFPExt(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_FPEXT, U, MIRBuilder);
}
bool translateFPToUI(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_FPTOUI, U, MIRBuilder);
}
bool translateFPToSI(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_FPTOSI, U, MIRBuilder);
}
bool translateUIToFP(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_UITOFP, U, MIRBuilder);
}
bool translateSIToFP(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_SITOFP, U, MIRBuilder);
}
bool translateUnreachable(const User &U, MachineIRBuilder &MIRBuilder);
bool translateSExt(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_SEXT, U, MIRBuilder);
}
bool translateZExt(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_ZEXT, U, MIRBuilder);
}
bool translateShl(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_SHL, U, MIRBuilder);
}
bool translateLShr(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_LSHR, U, MIRBuilder);
}
bool translateAShr(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_ASHR, U, MIRBuilder);
}
bool translateFAdd(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_FADD, U, MIRBuilder);
}
bool translateFSub(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_FSUB, U, MIRBuilder);
}
bool translateFMul(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_FMUL, U, MIRBuilder);
}
bool translateFDiv(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_FDIV, U, MIRBuilder);
}
bool translateFRem(const User &U, MachineIRBuilder &MIRBuilder) {
return translateBinaryOp(TargetOpcode::G_FREM, U, MIRBuilder);
}
bool translateVAArg(const User &U, MachineIRBuilder &MIRBuilder);
bool translateInsertElement(const User &U, MachineIRBuilder &MIRBuilder);
bool translateExtractElement(const User &U, MachineIRBuilder &MIRBuilder);
bool translateShuffleVector(const User &U, MachineIRBuilder &MIRBuilder);
bool translateAtomicCmpXchg(const User &U, MachineIRBuilder &MIRBuilder);
bool translateAtomicRMW(const User &U, MachineIRBuilder &MIRBuilder);
bool translateFence(const User &U, MachineIRBuilder &MIRBuilder);
bool translateFreeze(const User &U, MachineIRBuilder &MIRBuilder);
// Stubs to keep the compiler happy while we implement the rest of the
// translation.
bool translateResume(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateCleanupRet(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateCatchRet(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateCatchSwitch(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateAddrSpaceCast(const User &U, MachineIRBuilder &MIRBuilder) {
return translateCast(TargetOpcode::G_ADDRSPACE_CAST, U, MIRBuilder);
}
bool translateCleanupPad(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateCatchPad(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateUserOp1(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
bool translateUserOp2(const User &U, MachineIRBuilder &MIRBuilder) {
return false;
}
/// @}
// Builder for machine instruction a la IRBuilder.
// I.e., compared to regular MIBuilder, this one also inserts the instruction
// in the current block, it can creates block, etc., basically a kind of
// IRBuilder, but for Machine IR.
// CSEMIRBuilder CurBuilder;
std::unique_ptr<MachineIRBuilder> CurBuilder;
// Builder set to the entry block (just after ABI lowering instructions). Used
// as a convenient location for Constants.
// CSEMIRBuilder EntryBuilder;
std::unique_ptr<MachineIRBuilder> EntryBuilder;
// The MachineFunction currently being translated.
MachineFunction *MF;
/// MachineRegisterInfo used to create virtual registers.
MachineRegisterInfo *MRI = nullptr;
const DataLayout *DL;
/// Current target configuration. Controls how the pass handles errors.
const TargetPassConfig *TPC;
CodeGenOpt::Level OptLevel;
/// Current optimization remark emitter. Used to report failures.
std::unique_ptr<OptimizationRemarkEmitter> ORE;
FunctionLoweringInfo FuncInfo;
// True when either the Target Machine specifies no optimizations or the
// function has the optnone attribute.
bool EnableOpts = false;
/// True when the block contains a tail call. This allows the IRTranslator to
/// stop translating such blocks early.
bool HasTailCall = false;
StackProtectorDescriptor SPDescriptor;
/// Switch analysis and optimization.
class GISelSwitchLowering : public SwitchCG::SwitchLowering {
public:
GISelSwitchLowering(IRTranslator *irt, FunctionLoweringInfo &funcinfo)
: SwitchLowering(funcinfo), IRT(irt) {
assert(irt && "irt is null!");
}
virtual void addSuccessorWithProb(
MachineBasicBlock *Src, MachineBasicBlock *Dst,
BranchProbability Prob = BranchProbability::getUnknown()) override {
IRT->addSuccessorWithProb(Src, Dst, Prob);
}
virtual ~GISelSwitchLowering() = default;
private:
IRTranslator *IRT;
};
std::unique_ptr<GISelSwitchLowering> SL;
// * Insert all the code needed to materialize the constants
// at the proper place. E.g., Entry block or dominator block
// of each constant depending on how fancy we want to be.
// * Clear the different maps.
void finalizeFunction();
// Processing steps done per block. E.g. emitting jump tables, stack
// protectors etc. Returns true if no errors, false if there was a problem
// that caused an abort.
bool finalizeBasicBlock(const BasicBlock &BB, MachineBasicBlock &MBB);
/// Codegen a new tail for a stack protector check ParentMBB which has had its
/// tail spliced into a stack protector check success bb.
///
/// For a high level explanation of how this fits into the stack protector
/// generation see the comment on the declaration of class
/// StackProtectorDescriptor.
///
/// \return true if there were no problems.
bool emitSPDescriptorParent(StackProtectorDescriptor &SPD,
MachineBasicBlock *ParentBB);
/// Codegen the failure basic block for a stack protector check.
///
/// A failure stack protector machine basic block consists simply of a call to
/// __stack_chk_fail().
///
/// For a high level explanation of how this fits into the stack protector
/// generation see the comment on the declaration of class
/// StackProtectorDescriptor.
///
/// \return true if there were no problems.
bool emitSPDescriptorFailure(StackProtectorDescriptor &SPD,
MachineBasicBlock *FailureBB);
/// Get the VRegs that represent \p Val.
/// Non-aggregate types have just one corresponding VReg and the list can be
/// used as a single "unsigned". Aggregates get flattened. If such VRegs do
/// not exist, they are created.
ArrayRef<Register> getOrCreateVRegs(const Value &Val);
Register getOrCreateVReg(const Value &Val) {
auto Regs = getOrCreateVRegs(Val);
if (Regs.empty())
return 0;
assert(Regs.size() == 1 &&
"attempt to get single VReg for aggregate or void");
return Regs[0];
}
/// Allocate some vregs and offsets in the VMap. Then populate just the
/// offsets while leaving the vregs empty.
ValueToVRegInfo::VRegListT &allocateVRegs(const Value &Val);
/// Get the frame index that represents \p Val.
/// If such VReg does not exist, it is created.
int getOrCreateFrameIndex(const AllocaInst &AI);
/// Get the alignment of the given memory operation instruction. This will
/// either be the explicitly specified value or the ABI-required alignment for
/// the type being accessed (according to the Module's DataLayout).
Align getMemOpAlign(const Instruction &I);
/// Get the MachineBasicBlock that represents \p BB. Specifically, the block
/// returned will be the head of the translated block (suitable for branch
/// destinations).
MachineBasicBlock &getMBB(const BasicBlock &BB);
/// Record \p NewPred as a Machine predecessor to `Edge.second`, corresponding
/// to `Edge.first` at the IR level. This is used when IRTranslation creates
/// multiple MachineBasicBlocks for a given IR block and the CFG is no longer
/// represented simply by the IR-level CFG.
void addMachineCFGPred(CFGEdge Edge, MachineBasicBlock *NewPred);
/// Returns the Machine IR predecessors for the given IR CFG edge. Usually
/// this is just the single MachineBasicBlock corresponding to the predecessor
/// in the IR. More complex lowering can result in multiple MachineBasicBlocks
/// preceding the original though (e.g. switch instructions).
SmallVector<MachineBasicBlock *, 1> getMachinePredBBs(CFGEdge Edge) {
auto RemappedEdge = MachinePreds.find(Edge);
if (RemappedEdge != MachinePreds.end())
return RemappedEdge->second;
return SmallVector<MachineBasicBlock *, 4>(1, &getMBB(*Edge.first));
}
/// Return branch probability calculated by BranchProbabilityInfo for IR
/// blocks.
BranchProbability getEdgeProbability(const MachineBasicBlock *Src,
const MachineBasicBlock *Dst) const;
void addSuccessorWithProb(
MachineBasicBlock *Src, MachineBasicBlock *Dst,
BranchProbability Prob = BranchProbability::getUnknown());
public:
IRTranslator(CodeGenOpt::Level OptLevel = CodeGenOpt::None);
StringRef getPassName() const override { return "IRTranslator"; }
void getAnalysisUsage(AnalysisUsage &AU) const override;
// Algo:
// CallLowering = MF.subtarget.getCallLowering()
// F = MF.getParent()
// MIRBuilder.reset(MF)
// getMBB(F.getEntryBB())
// CallLowering->translateArguments(MIRBuilder, F, ValToVReg)
// for each bb in F
// getMBB(bb)
// for each inst in bb
// if (!translate(MIRBuilder, inst, ValToVReg, ConstantToSequence))
// report_fatal_error("Don't know how to translate input");
// finalize()
bool runOnMachineFunction(MachineFunction &MF) override;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_IRTRANSLATOR_H

View File

@@ -0,0 +1,67 @@
//===- llvm/CodeGen/GlobalISel/InlineAsmLowering.h --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file describes how to lower LLVM inline asm to machine code INLINEASM.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_INLINEASMLOWERING_H
#define LLVM_CODEGEN_GLOBALISEL_INLINEASMLOWERING_H
#include "llvm/ADT/ArrayRef.h"
#include <functional>
namespace llvm {
class CallBase;
class MachineIRBuilder;
class MachineOperand;
class Register;
class TargetLowering;
class Value;
class InlineAsmLowering {
const TargetLowering *TLI;
virtual void anchor();
public:
/// Lower the given inline asm call instruction
/// \p GetOrCreateVRegs is a callback to materialize a register for the
/// input and output operands of the inline asm
/// \return True if the lowering succeeds, false otherwise.
bool lowerInlineAsm(MachineIRBuilder &MIRBuilder, const CallBase &CB,
std::function<ArrayRef<Register>(const Value &Val)>
GetOrCreateVRegs) const;
/// Lower the specified operand into the Ops vector.
/// \p Val is the IR input value to be lowered
/// \p Constraint is the user supplied constraint string
/// \p Ops is the vector to be filled with the lowered operands
/// \return True if the lowering succeeds, false otherwise.
virtual bool lowerAsmOperandForConstraint(Value *Val, StringRef Constraint,
std::vector<MachineOperand> &Ops,
MachineIRBuilder &MIRBuilder) const;
protected:
/// Getter for generic TargetLowering class.
const TargetLowering *getTLI() const { return TLI; }
/// Getter for target specific TargetLowering class.
template <class XXXTargetLowering> const XXXTargetLowering *getTLI() const {
return static_cast<const XXXTargetLowering *>(TLI);
}
public:
InlineAsmLowering(const TargetLowering *TLI) : TLI(TLI) {}
virtual ~InlineAsmLowering() = default;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_INLINEASMLOWERING_H

View File

@@ -0,0 +1,63 @@
//== llvm/CodeGen/GlobalISel/InstructionSelect.h -----------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file This file describes the interface of the MachineFunctionPass
/// responsible for selecting (possibly generic) machine instructions to
/// target-specific instructions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H
#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
namespace llvm {
class BlockFrequencyInfo;
class ProfileSummaryInfo;
/// This pass is responsible for selecting generic machine instructions to
/// target-specific instructions. It relies on the InstructionSelector provided
/// by the target.
/// Selection is done by examining blocks in post-order, and instructions in
/// reverse order.
///
/// \post for all inst in MF: not isPreISelGenericOpcode(inst.opcode)
class InstructionSelect : public MachineFunctionPass {
public:
static char ID;
StringRef getPassName() const override { return "InstructionSelect"; }
void getAnalysisUsage(AnalysisUsage &AU) const override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties()
.set(MachineFunctionProperties::Property::IsSSA)
.set(MachineFunctionProperties::Property::Legalized)
.set(MachineFunctionProperties::Property::RegBankSelected);
}
MachineFunctionProperties getSetProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::Selected);
}
InstructionSelect(CodeGenOpt::Level OL);
InstructionSelect();
bool runOnMachineFunction(MachineFunction &MF) override;
protected:
BlockFrequencyInfo *BFI = nullptr;
ProfileSummaryInfo *PSI = nullptr;
CodeGenOpt::Level OptLevel = CodeGenOpt::None;
};
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,569 @@
//===- llvm/CodeGen/GlobalISel/InstructionSelector.h ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file This file declares the API for the instruction selector.
/// This class is responsible for selecting machine instructions.
/// It's implemented by the target. It's used by the InstructionSelect pass.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/Support/CodeGenCoverage.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include <bitset>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <vector>
namespace llvm {
class APInt;
class APFloat;
class GISelKnownBits;
class MachineInstr;
class MachineInstrBuilder;
class MachineFunction;
class MachineOperand;
class MachineRegisterInfo;
class RegisterBankInfo;
class TargetInstrInfo;
class TargetRegisterInfo;
/// Container class for CodeGen predicate results.
/// This is convenient because std::bitset does not have a constructor
/// with an initializer list of set bits.
///
/// Each InstructionSelector subclass should define a PredicateBitset class
/// with:
/// const unsigned MAX_SUBTARGET_PREDICATES = 192;
/// using PredicateBitset = PredicateBitsetImpl<MAX_SUBTARGET_PREDICATES>;
/// and updating the constant to suit the target. Tablegen provides a suitable
/// definition for the predicates in use in <Target>GenGlobalISel.inc when
/// GET_GLOBALISEL_PREDICATE_BITSET is defined.
template <std::size_t MaxPredicates>
class PredicateBitsetImpl : public std::bitset<MaxPredicates> {
public:
// Cannot inherit constructors because it's not supported by VC++..
PredicateBitsetImpl() = default;
PredicateBitsetImpl(const std::bitset<MaxPredicates> &B)
: std::bitset<MaxPredicates>(B) {}
PredicateBitsetImpl(std::initializer_list<unsigned> Init) {
for (auto I : Init)
std::bitset<MaxPredicates>::set(I);
}
};
enum {
/// Begin a try-block to attempt a match and jump to OnFail if it is
/// unsuccessful.
/// - OnFail - The MatchTable entry at which to resume if the match fails.
///
/// FIXME: This ought to take an argument indicating the number of try-blocks
/// to exit on failure. It's usually one but the last match attempt of
/// a block will need more. The (implemented) alternative is to tack a
/// GIM_Reject on the end of each try-block which is simpler but
/// requires an extra opcode and iteration in the interpreter on each
/// failed match.
GIM_Try,
/// Switch over the opcode on the specified instruction
/// - InsnID - Instruction ID
/// - LowerBound - numerically minimum opcode supported
/// - UpperBound - numerically maximum + 1 opcode supported
/// - Default - failure jump target
/// - JumpTable... - (UpperBound - LowerBound) (at least 2) jump targets
GIM_SwitchOpcode,
/// Switch over the LLT on the specified instruction operand
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - LowerBound - numerically minimum Type ID supported
/// - UpperBound - numerically maximum + 1 Type ID supported
/// - Default - failure jump target
/// - JumpTable... - (UpperBound - LowerBound) (at least 2) jump targets
GIM_SwitchType,
/// Record the specified instruction
/// - NewInsnID - Instruction ID to define
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
GIM_RecordInsn,
/// Check the feature bits
/// - Expected features
GIM_CheckFeatures,
/// Check the opcode on the specified instruction
/// - InsnID - Instruction ID
/// - Expected opcode
GIM_CheckOpcode,
/// Check the opcode on the specified instruction, checking 2 acceptable
/// alternatives.
/// - InsnID - Instruction ID
/// - Expected opcode
/// - Alternative expected opcode
GIM_CheckOpcodeIsEither,
/// Check the instruction has the right number of operands
/// - InsnID - Instruction ID
/// - Expected number of operands
GIM_CheckNumOperands,
/// Check an immediate predicate on the specified instruction
/// - InsnID - Instruction ID
/// - The predicate to test
GIM_CheckI64ImmPredicate,
/// Check an immediate predicate on the specified instruction via an APInt.
/// - InsnID - Instruction ID
/// - The predicate to test
GIM_CheckAPIntImmPredicate,
/// Check a floating point immediate predicate on the specified instruction.
/// - InsnID - Instruction ID
/// - The predicate to test
GIM_CheckAPFloatImmPredicate,
/// Check an immediate predicate on the specified instruction
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - The predicate to test
GIM_CheckImmOperandPredicate,
/// Check a memory operation has the specified atomic ordering.
/// - InsnID - Instruction ID
/// - Ordering - The AtomicOrdering value
GIM_CheckAtomicOrdering,
GIM_CheckAtomicOrderingOrStrongerThan,
GIM_CheckAtomicOrderingWeakerThan,
/// Check the size of the memory access for the given machine memory operand.
/// - InsnID - Instruction ID
/// - MMOIdx - MMO index
/// - Size - The size in bytes of the memory access
GIM_CheckMemorySizeEqualTo,
/// Check the address space of the memory access for the given machine memory
/// operand.
/// - InsnID - Instruction ID
/// - MMOIdx - MMO index
/// - NumAddrSpace - Number of valid address spaces
/// - AddrSpaceN - An allowed space of the memory access
/// - AddrSpaceN+1 ...
GIM_CheckMemoryAddressSpace,
/// Check the minimum alignment of the memory access for the given machine
/// memory operand.
/// - InsnID - Instruction ID
/// - MMOIdx - MMO index
/// - MinAlign - Minimum acceptable alignment
GIM_CheckMemoryAlignment,
/// Check the size of the memory access for the given machine memory operand
/// against the size of an operand.
/// - InsnID - Instruction ID
/// - MMOIdx - MMO index
/// - OpIdx - The operand index to compare the MMO against
GIM_CheckMemorySizeEqualToLLT,
GIM_CheckMemorySizeLessThanLLT,
GIM_CheckMemorySizeGreaterThanLLT,
/// Check if this is a vector that can be treated as a vector splat
/// constant. This is valid for both G_BUILD_VECTOR as well as
/// G_BUILD_VECTOR_TRUNC. For AllOnes refers to individual bits, so a -1
/// element.
/// - InsnID - Instruction ID
GIM_CheckIsBuildVectorAllOnes,
GIM_CheckIsBuildVectorAllZeros,
/// Check a generic C++ instruction predicate
/// - InsnID - Instruction ID
/// - PredicateID - The ID of the predicate function to call
GIM_CheckCxxInsnPredicate,
/// Check the type for the specified operand
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected type
GIM_CheckType,
/// Check the type of a pointer to any address space.
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - SizeInBits - The size of the pointer value in bits.
GIM_CheckPointerToAny,
/// Check the register bank for the specified operand
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected register bank (specified as a register class)
GIM_CheckRegBankForClass,
/// Check the operand matches a complex predicate
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - RendererID - The renderer to hold the result
/// - Complex predicate ID
GIM_CheckComplexPattern,
/// Check the operand is a specific integer
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected integer
GIM_CheckConstantInt,
/// Check the operand is a specific literal integer (i.e. MO.isImm() or
/// MO.isCImm() is true).
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected integer
GIM_CheckLiteralInt,
/// Check the operand is a specific intrinsic ID
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected Intrinsic ID
GIM_CheckIntrinsicID,
/// Check the operand is a specific predicate
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - Expected predicate
GIM_CheckCmpPredicate,
/// Check the specified operand is an MBB
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
GIM_CheckIsMBB,
/// Check the specified operand is an Imm
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
GIM_CheckIsImm,
/// Check if the specified operand is safe to fold into the current
/// instruction.
/// - InsnID - Instruction ID
GIM_CheckIsSafeToFold,
/// Check the specified operands are identical.
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - OtherInsnID - Other instruction ID
/// - OtherOpIdx - Other operand index
GIM_CheckIsSameOperand,
/// Predicates with 'let PredicateCodeUsesOperands = 1' need to examine some
/// named operands that will be recorded in RecordedOperands. Names of these
/// operands are referenced in predicate argument list. Emitter determines
/// StoreIdx(corresponds to the order in which names appear in argument list).
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - StoreIdx - Store location in RecordedOperands.
GIM_RecordNamedOperand,
/// Fail the current try-block, or completely fail to match if there is no
/// current try-block.
GIM_Reject,
//=== Renderers ===
/// Mutate an instruction
/// - NewInsnID - Instruction ID to define
/// - OldInsnID - Instruction ID to mutate
/// - NewOpcode - The new opcode to use
GIR_MutateOpcode,
/// Build a new instruction
/// - InsnID - Instruction ID to define
/// - Opcode - The new opcode to use
GIR_BuildMI,
/// Copy an operand to the specified instruction
/// - NewInsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to copy from
/// - OpIdx - The operand to copy
GIR_Copy,
/// Copy an operand to the specified instruction or add a zero register if the
/// operand is a zero immediate.
/// - NewInsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to copy from
/// - OpIdx - The operand to copy
/// - ZeroReg - The zero register to use
GIR_CopyOrAddZeroReg,
/// Copy an operand to the specified instruction
/// - NewInsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to copy from
/// - OpIdx - The operand to copy
/// - SubRegIdx - The subregister to copy
GIR_CopySubReg,
/// Add an implicit register def to the specified instruction
/// - InsnID - Instruction ID to modify
/// - RegNum - The register to add
GIR_AddImplicitDef,
/// Add an implicit register use to the specified instruction
/// - InsnID - Instruction ID to modify
/// - RegNum - The register to add
GIR_AddImplicitUse,
/// Add an register to the specified instruction
/// - InsnID - Instruction ID to modify
/// - RegNum - The register to add
GIR_AddRegister,
/// Add a temporary register to the specified instruction
/// - InsnID - Instruction ID to modify
/// - TempRegID - The temporary register ID to add
/// - TempRegFlags - The register flags to set
GIR_AddTempRegister,
/// Add a temporary register to the specified instruction
/// - InsnID - Instruction ID to modify
/// - TempRegID - The temporary register ID to add
/// - TempRegFlags - The register flags to set
/// - SubRegIndex - The subregister index to set
GIR_AddTempSubRegister,
/// Add an immediate to the specified instruction
/// - InsnID - Instruction ID to modify
/// - Imm - The immediate to add
GIR_AddImm,
/// Render complex operands to the specified instruction
/// - InsnID - Instruction ID to modify
/// - RendererID - The renderer to call
GIR_ComplexRenderer,
/// Render sub-operands of complex operands to the specified instruction
/// - InsnID - Instruction ID to modify
/// - RendererID - The renderer to call
/// - RenderOpID - The suboperand to render.
GIR_ComplexSubOperandRenderer,
/// Render operands to the specified instruction using a custom function
/// - InsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to get the matched operand from
/// - RendererFnID - Custom renderer function to call
GIR_CustomRenderer,
/// Render operands to the specified instruction using a custom function,
/// reading from a specific operand.
/// - InsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to get the matched operand from
/// - OpIdx - Operand index in OldInsnID the render function should read from..
/// - RendererFnID - Custom renderer function to call
GIR_CustomOperandRenderer,
/// Render a G_CONSTANT operator as a sign-extended immediate.
/// - NewInsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to copy from
/// The operand index is implicitly 1.
GIR_CopyConstantAsSImm,
/// Render a G_FCONSTANT operator as a sign-extended immediate.
/// - NewInsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to copy from
/// The operand index is implicitly 1.
GIR_CopyFConstantAsFPImm,
/// Constrain an instruction operand to a register class.
/// - InsnID - Instruction ID to modify
/// - OpIdx - Operand index
/// - RCEnum - Register class enumeration value
GIR_ConstrainOperandRC,
/// Constrain an instructions operands according to the instruction
/// description.
/// - InsnID - Instruction ID to modify
GIR_ConstrainSelectedInstOperands,
/// Merge all memory operands into instruction.
/// - InsnID - Instruction ID to modify
/// - MergeInsnID... - One or more Instruction ID to merge into the result.
/// - GIU_MergeMemOperands_EndOfList - Terminates the list of instructions to
/// merge.
GIR_MergeMemOperands,
/// Erase from parent.
/// - InsnID - Instruction ID to erase
GIR_EraseFromParent,
/// Create a new temporary register that's not constrained.
/// - TempRegID - The temporary register ID to initialize.
/// - Expected type
GIR_MakeTempReg,
/// A successful emission
GIR_Done,
/// Increment the rule coverage counter.
/// - RuleID - The ID of the rule that was covered.
GIR_Coverage,
/// Keeping track of the number of the GI opcodes. Must be the last entry.
GIU_NumOpcodes,
};
enum {
/// Indicates the end of the variable-length MergeInsnID list in a
/// GIR_MergeMemOperands opcode.
GIU_MergeMemOperands_EndOfList = -1,
};
/// Provides the logic to select generic machine instructions.
class InstructionSelector {
public:
virtual ~InstructionSelector() = default;
/// Select the (possibly generic) instruction \p I to only use target-specific
/// opcodes. It is OK to insert multiple instructions, but they cannot be
/// generic pre-isel instructions.
///
/// \returns whether selection succeeded.
/// \pre I.getParent() && I.getParent()->getParent()
/// \post
/// if returns true:
/// for I in all mutated/inserted instructions:
/// !isPreISelGenericOpcode(I.getOpcode())
virtual bool select(MachineInstr &I) = 0;
CodeGenCoverage *CoverageInfo = nullptr;
GISelKnownBits *KnownBits = nullptr;
MachineFunction *MF = nullptr;
ProfileSummaryInfo *PSI = nullptr;
BlockFrequencyInfo *BFI = nullptr;
// For some predicates, we need to track the current MBB.
MachineBasicBlock *CurMBB = nullptr;
virtual void setupGeneratedPerFunctionState(MachineFunction &MF) {
llvm_unreachable("TableGen should have emitted implementation");
}
/// Setup per-MF selector state.
virtual void setupMF(MachineFunction &mf, GISelKnownBits *KB,
CodeGenCoverage &covinfo, ProfileSummaryInfo *psi,
BlockFrequencyInfo *bfi) {
CoverageInfo = &covinfo;
KnownBits = KB;
MF = &mf;
PSI = psi;
BFI = bfi;
CurMBB = nullptr;
setupGeneratedPerFunctionState(mf);
}
protected:
using ComplexRendererFns =
Optional<SmallVector<std::function<void(MachineInstrBuilder &)>, 4>>;
using RecordedMIVector = SmallVector<MachineInstr *, 4>;
using NewMIVector = SmallVector<MachineInstrBuilder, 4>;
struct MatcherState {
std::vector<ComplexRendererFns::value_type> Renderers;
RecordedMIVector MIs;
DenseMap<unsigned, unsigned> TempRegisters;
/// Named operands that predicate with 'let PredicateCodeUsesOperands = 1'
/// referenced in its argument list. Operands are inserted at index set by
/// emitter, it corresponds to the order in which names appear in argument
/// list. Currently such predicates don't have more than 3 arguments.
std::array<const MachineOperand *, 3> RecordedOperands;
MatcherState(unsigned MaxRenderers);
};
bool shouldOptForSize(const MachineFunction *MF) const {
const auto &F = MF->getFunction();
return F.hasOptSize() || F.hasMinSize() ||
(PSI && BFI && CurMBB && llvm::shouldOptForSize(*CurMBB, PSI, BFI));
}
public:
template <class PredicateBitset, class ComplexMatcherMemFn,
class CustomRendererFn>
struct ISelInfoTy {
ISelInfoTy(const LLT *TypeObjects, size_t NumTypeObjects,
const PredicateBitset *FeatureBitsets,
const ComplexMatcherMemFn *ComplexPredicates,
const CustomRendererFn *CustomRenderers)
: TypeObjects(TypeObjects),
FeatureBitsets(FeatureBitsets),
ComplexPredicates(ComplexPredicates),
CustomRenderers(CustomRenderers) {
for (size_t I = 0; I < NumTypeObjects; ++I)
TypeIDMap[TypeObjects[I]] = I;
}
const LLT *TypeObjects;
const PredicateBitset *FeatureBitsets;
const ComplexMatcherMemFn *ComplexPredicates;
const CustomRendererFn *CustomRenderers;
SmallDenseMap<LLT, unsigned, 64> TypeIDMap;
};
protected:
InstructionSelector();
/// Execute a given matcher table and return true if the match was successful
/// and false otherwise.
template <class TgtInstructionSelector, class PredicateBitset,
class ComplexMatcherMemFn, class CustomRendererFn>
bool executeMatchTable(
TgtInstructionSelector &ISel, NewMIVector &OutMIs, MatcherState &State,
const ISelInfoTy<PredicateBitset, ComplexMatcherMemFn, CustomRendererFn>
&ISelInfo,
const int64_t *MatchTable, const TargetInstrInfo &TII,
MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures,
CodeGenCoverage &CoverageInfo) const;
virtual const int64_t *getMatchTable() const {
llvm_unreachable("Should have been overridden by tablegen if used");
}
virtual bool testImmPredicate_I64(unsigned, int64_t) const {
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
virtual bool testImmPredicate_APInt(unsigned, const APInt &) const {
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
virtual bool testImmPredicate_APFloat(unsigned, const APFloat &) const {
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
virtual bool testMIPredicate_MI(
unsigned, const MachineInstr &,
const std::array<const MachineOperand *, 3> &Operands) const {
llvm_unreachable(
"Subclasses must override this with a tablegen-erated function");
}
bool isOperandImmEqual(const MachineOperand &MO, int64_t Value,
const MachineRegisterInfo &MRI) const;
/// Return true if the specified operand is a G_PTR_ADD with a G_CONSTANT on the
/// right-hand side. GlobalISel's separation of pointer and integer types
/// means that we don't need to worry about G_OR with equivalent semantics.
bool isBaseWithConstantOffset(const MachineOperand &Root,
const MachineRegisterInfo &MRI) const;
/// Return true if MI can obviously be folded into IntoMI.
/// MI and IntoMI do not need to be in the same basic blocks, but MI must
/// precede IntoMI.
bool isObviouslySafeToFold(MachineInstr &MI, MachineInstr &IntoMI) const;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H

View File

@@ -0,0 +1,481 @@
//===- llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Interface for Targets to specify which operations they can successfully
/// select and how the others should be expanded most efficiently.
/// This implementation has been deprecated for a long time but it still in use
/// in a few places.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
#define LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include <unordered_map>
namespace llvm {
struct LegalityQuery;
namespace LegacyLegalizeActions {
enum LegacyLegalizeAction : std::uint8_t {
/// The operation is expected to be selectable directly by the target, and
/// no transformation is necessary.
Legal,
/// The operation should be synthesized from multiple instructions acting on
/// a narrower scalar base-type. For example a 64-bit add might be
/// implemented in terms of 32-bit add-with-carry.
NarrowScalar,
/// The operation should be implemented in terms of a wider scalar
/// base-type. For example a <2 x s8> add could be implemented as a <2
/// x s32> add (ignoring the high bits).
WidenScalar,
/// The (vector) operation should be implemented by splitting it into
/// sub-vectors where the operation is legal. For example a <8 x s64> add
/// might be implemented as 4 separate <2 x s64> adds.
FewerElements,
/// The (vector) operation should be implemented by widening the input
/// vector and ignoring the lanes added by doing so. For example <2 x i8> is
/// rarely legal, but you might perform an <8 x i8> and then only look at
/// the first two results.
MoreElements,
/// Perform the operation on a different, but equivalently sized type.
Bitcast,
/// The operation itself must be expressed in terms of simpler actions on
/// this target. E.g. a SREM replaced by an SDIV and subtraction.
Lower,
/// The operation should be implemented as a call to some kind of runtime
/// support library. For example this usually happens on machines that don't
/// support floating-point operations natively.
Libcall,
/// The target wants to do something special with this combination of
/// operand and type. A callback will be issued when it is needed.
Custom,
/// This operation is completely unsupported on the target. A programming
/// error has occurred.
Unsupported,
/// Sentinel value for when no action was found in the specified table.
NotFound,
};
} // end namespace LegacyLegalizeActions
raw_ostream &operator<<(raw_ostream &OS,
LegacyLegalizeActions::LegacyLegalizeAction Action);
/// Legalization is decided based on an instruction's opcode, which type slot
/// we're considering, and what the existing type is. These aspects are gathered
/// together for convenience in the InstrAspect class.
struct InstrAspect {
unsigned Opcode;
unsigned Idx = 0;
LLT Type;
InstrAspect(unsigned Opcode, LLT Type) : Opcode(Opcode), Type(Type) {}
InstrAspect(unsigned Opcode, unsigned Idx, LLT Type)
: Opcode(Opcode), Idx(Idx), Type(Type) {}
bool operator==(const InstrAspect &RHS) const {
return Opcode == RHS.Opcode && Idx == RHS.Idx && Type == RHS.Type;
}
};
/// The result of a query. It either indicates a final answer of Legal or
/// Unsupported or describes an action that must be taken to make an operation
/// more legal.
struct LegacyLegalizeActionStep {
/// The action to take or the final answer.
LegacyLegalizeActions::LegacyLegalizeAction Action;
/// If describing an action, the type index to change. Otherwise zero.
unsigned TypeIdx;
/// If describing an action, the new type for TypeIdx. Otherwise LLT{}.
LLT NewType;
LegacyLegalizeActionStep(LegacyLegalizeActions::LegacyLegalizeAction Action,
unsigned TypeIdx, const LLT NewType)
: Action(Action), TypeIdx(TypeIdx), NewType(NewType) {}
bool operator==(const LegacyLegalizeActionStep &RHS) const {
return std::tie(Action, TypeIdx, NewType) ==
std::tie(RHS.Action, RHS.TypeIdx, RHS.NewType);
}
};
class LegacyLegalizerInfo {
public:
using SizeAndAction =
std::pair<uint16_t, LegacyLegalizeActions::LegacyLegalizeAction>;
using SizeAndActionsVec = std::vector<SizeAndAction>;
using SizeChangeStrategy =
std::function<SizeAndActionsVec(const SizeAndActionsVec &v)>;
LegacyLegalizerInfo();
static bool needsLegalizingToDifferentSize(
const LegacyLegalizeActions::LegacyLegalizeAction Action) {
using namespace LegacyLegalizeActions;
switch (Action) {
case NarrowScalar:
case WidenScalar:
case FewerElements:
case MoreElements:
case Unsupported:
return true;
default:
return false;
}
}
/// Compute any ancillary tables needed to quickly decide how an operation
/// should be handled. This must be called after all "set*Action"methods but
/// before any query is made or incorrect results may be returned.
void computeTables();
/// More friendly way to set an action for common types that have an LLT
/// representation.
/// The LegacyLegalizeAction must be one for which
/// NeedsLegalizingToDifferentSize returns false.
void setAction(const InstrAspect &Aspect,
LegacyLegalizeActions::LegacyLegalizeAction Action) {
assert(!needsLegalizingToDifferentSize(Action));
TablesInitialized = false;
const unsigned OpcodeIdx = Aspect.Opcode - FirstOp;
if (SpecifiedActions[OpcodeIdx].size() <= Aspect.Idx)
SpecifiedActions[OpcodeIdx].resize(Aspect.Idx + 1);
SpecifiedActions[OpcodeIdx][Aspect.Idx][Aspect.Type] = Action;
}
/// The setAction calls record the non-size-changing legalization actions
/// to take on specifically-sized types. The SizeChangeStrategy defines what
/// to do when the size of the type needs to be changed to reach a legally
/// sized type (i.e., one that was defined through a setAction call).
/// e.g.
/// setAction ({G_ADD, 0, LLT::scalar(32)}, Legal);
/// setLegalizeScalarToDifferentSizeStrategy(
/// G_ADD, 0, widenToLargerTypesAndNarrowToLargest);
/// will end up defining getAction({G_ADD, 0, T}) to return the following
/// actions for different scalar types T:
/// LLT::scalar(1)..LLT::scalar(31): {WidenScalar, 0, LLT::scalar(32)}
/// LLT::scalar(32): {Legal, 0, LLT::scalar(32)}
/// LLT::scalar(33)..: {NarrowScalar, 0, LLT::scalar(32)}
///
/// If no SizeChangeAction gets defined, through this function,
/// the default is unsupportedForDifferentSizes.
void setLegalizeScalarToDifferentSizeStrategy(const unsigned Opcode,
const unsigned TypeIdx,
SizeChangeStrategy S) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (ScalarSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
ScalarSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
ScalarSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
}
/// See also setLegalizeScalarToDifferentSizeStrategy.
/// This function allows to set the SizeChangeStrategy for vector elements.
void setLegalizeVectorElementToDifferentSizeStrategy(const unsigned Opcode,
const unsigned TypeIdx,
SizeChangeStrategy S) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (VectorElementSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
VectorElementSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
VectorElementSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
}
/// A SizeChangeStrategy for the common case where legalization for a
/// particular operation consists of only supporting a specific set of type
/// sizes. E.g.
/// setAction ({G_DIV, 0, LLT::scalar(32)}, Legal);
/// setAction ({G_DIV, 0, LLT::scalar(64)}, Legal);
/// setLegalizeScalarToDifferentSizeStrategy(
/// G_DIV, 0, unsupportedForDifferentSizes);
/// will result in getAction({G_DIV, 0, T}) to return Legal for s32 and s64,
/// and Unsupported for all other scalar types T.
static SizeAndActionsVec
unsupportedForDifferentSizes(const SizeAndActionsVec &v) {
using namespace LegacyLegalizeActions;
return increaseToLargerTypesAndDecreaseToLargest(v, Unsupported,
Unsupported);
}
/// A SizeChangeStrategy for the common case where legalization for a
/// particular operation consists of widening the type to a large legal type,
/// unless there is no such type and then instead it should be narrowed to the
/// largest legal type.
static SizeAndActionsVec
widenToLargerTypesAndNarrowToLargest(const SizeAndActionsVec &v) {
using namespace LegacyLegalizeActions;
assert(v.size() > 0 &&
"At least one size that can be legalized towards is needed"
" for this SizeChangeStrategy");
return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
NarrowScalar);
}
static SizeAndActionsVec
widenToLargerTypesUnsupportedOtherwise(const SizeAndActionsVec &v) {
using namespace LegacyLegalizeActions;
return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
Unsupported);
}
static SizeAndActionsVec
narrowToSmallerAndUnsupportedIfTooSmall(const SizeAndActionsVec &v) {
using namespace LegacyLegalizeActions;
return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
Unsupported);
}
static SizeAndActionsVec
narrowToSmallerAndWidenToSmallest(const SizeAndActionsVec &v) {
using namespace LegacyLegalizeActions;
assert(v.size() > 0 &&
"At least one size that can be legalized towards is needed"
" for this SizeChangeStrategy");
return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
WidenScalar);
}
/// A SizeChangeStrategy for the common case where legalization for a
/// particular vector operation consists of having more elements in the
/// vector, to a type that is legal. Unless there is no such type and then
/// instead it should be legalized towards the widest vector that's still
/// legal. E.g.
/// setAction({G_ADD, LLT::vector(8, 8)}, Legal);
/// setAction({G_ADD, LLT::vector(16, 8)}, Legal);
/// setAction({G_ADD, LLT::vector(2, 32)}, Legal);
/// setAction({G_ADD, LLT::vector(4, 32)}, Legal);
/// setLegalizeVectorElementToDifferentSizeStrategy(
/// G_ADD, 0, moreToWiderTypesAndLessToWidest);
/// will result in the following getAction results:
/// * getAction({G_ADD, LLT::vector(8,8)}) returns
/// (Legal, vector(8,8)).
/// * getAction({G_ADD, LLT::vector(9,8)}) returns
/// (MoreElements, vector(16,8)).
/// * getAction({G_ADD, LLT::vector(8,32)}) returns
/// (FewerElements, vector(4,32)).
static SizeAndActionsVec
moreToWiderTypesAndLessToWidest(const SizeAndActionsVec &v) {
using namespace LegacyLegalizeActions;
return increaseToLargerTypesAndDecreaseToLargest(v, MoreElements,
FewerElements);
}
/// Helper function to implement many typical SizeChangeStrategy functions.
static SizeAndActionsVec increaseToLargerTypesAndDecreaseToLargest(
const SizeAndActionsVec &v,
LegacyLegalizeActions::LegacyLegalizeAction IncreaseAction,
LegacyLegalizeActions::LegacyLegalizeAction DecreaseAction);
/// Helper function to implement many typical SizeChangeStrategy functions.
static SizeAndActionsVec decreaseToSmallerTypesAndIncreaseToSmallest(
const SizeAndActionsVec &v,
LegacyLegalizeActions::LegacyLegalizeAction DecreaseAction,
LegacyLegalizeActions::LegacyLegalizeAction IncreaseAction);
LegacyLegalizeActionStep getAction(const LegalityQuery &Query) const;
unsigned getOpcodeIdxForOpcode(unsigned Opcode) const;
private:
/// Determine what action should be taken to legalize the given generic
/// instruction opcode, type-index and type. Requires computeTables to have
/// been called.
///
/// \returns a pair consisting of the kind of legalization that should be
/// performed and the destination type.
std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
getAspectAction(const InstrAspect &Aspect) const;
/// The SizeAndActionsVec is a representation mapping between all natural
/// numbers and an Action. The natural number represents the bit size of
/// the InstrAspect. For example, for a target with native support for 32-bit
/// and 64-bit additions, you'd express that as:
/// setScalarAction(G_ADD, 0,
/// {{1, WidenScalar}, // bit sizes [ 1, 31[
/// {32, Legal}, // bit sizes [32, 33[
/// {33, WidenScalar}, // bit sizes [33, 64[
/// {64, Legal}, // bit sizes [64, 65[
/// {65, NarrowScalar} // bit sizes [65, +inf[
/// });
/// It may be that only 64-bit pointers are supported on your target:
/// setPointerAction(G_PTR_ADD, 0, LLT:pointer(1),
/// {{1, Unsupported}, // bit sizes [ 1, 63[
/// {64, Legal}, // bit sizes [64, 65[
/// {65, Unsupported}, // bit sizes [65, +inf[
/// });
void setScalarAction(const unsigned Opcode, const unsigned TypeIndex,
const SizeAndActionsVec &SizeAndActions) {
const unsigned OpcodeIdx = Opcode - FirstOp;
SmallVector<SizeAndActionsVec, 1> &Actions = ScalarActions[OpcodeIdx];
setActions(TypeIndex, Actions, SizeAndActions);
}
void setPointerAction(const unsigned Opcode, const unsigned TypeIndex,
const unsigned AddressSpace,
const SizeAndActionsVec &SizeAndActions) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace) ==
AddrSpace2PointerActions[OpcodeIdx].end())
AddrSpace2PointerActions[OpcodeIdx][AddressSpace] = {{}};
SmallVector<SizeAndActionsVec, 1> &Actions =
AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace)->second;
setActions(TypeIndex, Actions, SizeAndActions);
}
/// If an operation on a given vector type (say <M x iN>) isn't explicitly
/// specified, we proceed in 2 stages. First we legalize the underlying scalar
/// (so that there's at least one legal vector with that scalar), then we
/// adjust the number of elements in the vector so that it is legal. The
/// desired action in the first step is controlled by this function.
void setScalarInVectorAction(const unsigned Opcode, const unsigned TypeIndex,
const SizeAndActionsVec &SizeAndActions) {
unsigned OpcodeIdx = Opcode - FirstOp;
SmallVector<SizeAndActionsVec, 1> &Actions =
ScalarInVectorActions[OpcodeIdx];
setActions(TypeIndex, Actions, SizeAndActions);
}
/// See also setScalarInVectorAction.
/// This function let's you specify the number of elements in a vector that
/// are legal for a legal element size.
void setVectorNumElementAction(const unsigned Opcode,
const unsigned TypeIndex,
const unsigned ElementSize,
const SizeAndActionsVec &SizeAndActions) {
const unsigned OpcodeIdx = Opcode - FirstOp;
if (NumElements2Actions[OpcodeIdx].find(ElementSize) ==
NumElements2Actions[OpcodeIdx].end())
NumElements2Actions[OpcodeIdx][ElementSize] = {{}};
SmallVector<SizeAndActionsVec, 1> &Actions =
NumElements2Actions[OpcodeIdx].find(ElementSize)->second;
setActions(TypeIndex, Actions, SizeAndActions);
}
/// A partial SizeAndActionsVec potentially doesn't cover all bit sizes,
/// i.e. it's OK if it doesn't start from size 1.
static void checkPartialSizeAndActionsVector(const SizeAndActionsVec& v) {
using namespace LegacyLegalizeActions;
#ifndef NDEBUG
// The sizes should be in increasing order
int prev_size = -1;
for(auto SizeAndAction: v) {
assert(SizeAndAction.first > prev_size);
prev_size = SizeAndAction.first;
}
// - for every Widen action, there should be a larger bitsize that
// can be legalized towards (e.g. Legal, Lower, Libcall or Custom
// action).
// - for every Narrow action, there should be a smaller bitsize that
// can be legalized towards.
int SmallestNarrowIdx = -1;
int LargestWidenIdx = -1;
int SmallestLegalizableToSameSizeIdx = -1;
int LargestLegalizableToSameSizeIdx = -1;
for(size_t i=0; i<v.size(); ++i) {
switch (v[i].second) {
case FewerElements:
case NarrowScalar:
if (SmallestNarrowIdx == -1)
SmallestNarrowIdx = i;
break;
case WidenScalar:
case MoreElements:
LargestWidenIdx = i;
break;
case Unsupported:
break;
default:
if (SmallestLegalizableToSameSizeIdx == -1)
SmallestLegalizableToSameSizeIdx = i;
LargestLegalizableToSameSizeIdx = i;
}
}
if (SmallestNarrowIdx != -1) {
assert(SmallestLegalizableToSameSizeIdx != -1);
assert(SmallestNarrowIdx > SmallestLegalizableToSameSizeIdx);
}
if (LargestWidenIdx != -1)
assert(LargestWidenIdx < LargestLegalizableToSameSizeIdx);
#endif
}
/// A full SizeAndActionsVec must cover all bit sizes, i.e. must start with
/// from size 1.
static void checkFullSizeAndActionsVector(const SizeAndActionsVec& v) {
#ifndef NDEBUG
// Data structure invariant: The first bit size must be size 1.
assert(v.size() >= 1);
assert(v[0].first == 1);
checkPartialSizeAndActionsVector(v);
#endif
}
/// Sets actions for all bit sizes on a particular generic opcode, type
/// index and scalar or pointer type.
void setActions(unsigned TypeIndex,
SmallVector<SizeAndActionsVec, 1> &Actions,
const SizeAndActionsVec &SizeAndActions) {
checkFullSizeAndActionsVector(SizeAndActions);
if (Actions.size() <= TypeIndex)
Actions.resize(TypeIndex + 1);
Actions[TypeIndex] = SizeAndActions;
}
static SizeAndAction findAction(const SizeAndActionsVec &Vec,
const uint32_t Size);
/// Returns the next action needed to get the scalar or pointer type closer
/// to being legal
/// E.g. findLegalAction({G_REM, 13}) should return
/// (WidenScalar, 32). After that, findLegalAction({G_REM, 32}) will
/// probably be called, which should return (Lower, 32).
/// This is assuming the setScalarAction on G_REM was something like:
/// setScalarAction(G_REM, 0,
/// {{1, WidenScalar}, // bit sizes [ 1, 31[
/// {32, Lower}, // bit sizes [32, 33[
/// {33, NarrowScalar} // bit sizes [65, +inf[
/// });
std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
findScalarLegalAction(const InstrAspect &Aspect) const;
/// Returns the next action needed towards legalizing the vector type.
std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
findVectorLegalAction(const InstrAspect &Aspect) const;
static const int FirstOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START;
static const int LastOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
// Data structures used temporarily during construction of legality data:
using TypeMap = DenseMap<LLT, LegacyLegalizeActions::LegacyLegalizeAction>;
SmallVector<TypeMap, 1> SpecifiedActions[LastOp - FirstOp + 1];
SmallVector<SizeChangeStrategy, 1>
ScalarSizeChangeStrategies[LastOp - FirstOp + 1];
SmallVector<SizeChangeStrategy, 1>
VectorElementSizeChangeStrategies[LastOp - FirstOp + 1];
bool TablesInitialized = false;
// Data structures used by getAction:
SmallVector<SizeAndActionsVec, 1> ScalarActions[LastOp - FirstOp + 1];
SmallVector<SizeAndActionsVec, 1> ScalarInVectorActions[LastOp - FirstOp + 1];
std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
AddrSpace2PointerActions[LastOp - FirstOp + 1];
std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
NumElements2Actions[LastOp - FirstOp + 1];
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H

View File

@@ -0,0 +1,76 @@
//== llvm/CodeGen/GlobalISel/Legalizer.h ---------------- -*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file A pass to convert the target-illegal operations created by IR -> MIR
/// translation into ones the target expects to be able to select. This may
/// occur in multiple phases, for example G_ADD <2 x i8> -> G_ADD <2 x i16> ->
/// G_ADD <4 x i16>.
///
/// The LegalizeHelper class is where most of the work happens, and is designed
/// to be callable from other passes that find themselves with an illegal
/// instruction.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LEGALIZER_H
#define LLVM_CODEGEN_GLOBALISEL_LEGALIZER_H
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
namespace llvm {
class LostDebugLocObserver;
class Legalizer : public MachineFunctionPass {
public:
static char ID;
struct MFResult {
bool Changed;
const MachineInstr *FailedOn;
};
private:
/// Initialize the field members using \p MF.
void init(MachineFunction &MF);
public:
// Ctor, nothing fancy.
Legalizer();
StringRef getPassName() const override { return "Legalizer"; }
void getAnalysisUsage(AnalysisUsage &AU) const override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::IsSSA);
}
MachineFunctionProperties getSetProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::Legalized);
}
MachineFunctionProperties getClearedProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoPHIs);
}
bool runOnMachineFunction(MachineFunction &MF) override;
static MFResult
legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
ArrayRef<GISelChangeObserver *> AuxObservers,
LostDebugLocObserver &LocObserver,
MachineIRBuilder &MIRBuilder);
};
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,431 @@
//== llvm/CodeGen/GlobalISel/LegalizerHelper.h ---------------- -*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file A pass to convert the target-illegal operations created by IR -> MIR
/// translation into ones the target expects to be able to select. This may
/// occur in multiple phases, for example G_ADD <2 x i8> -> G_ADD <2 x i16> ->
/// G_ADD <4 x i16>.
///
/// The LegalizerHelper class is where most of the work happens, and is
/// designed to be callable from other passes that find themselves with an
/// illegal instruction.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LEGALIZERHELPER_H
#define LLVM_CODEGEN_GLOBALISEL_LEGALIZERHELPER_H
#include "llvm/CodeGen/GlobalISel/CallLowering.h"
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/LowLevelType.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/RuntimeLibcalls.h"
namespace llvm {
// Forward declarations.
class LegalizerInfo;
class MachineRegisterInfo;
class GISelChangeObserver;
class LostDebugLocObserver;
class TargetLowering;
class LegalizerHelper {
public:
/// Expose MIRBuilder so clients can set their own RecordInsertInstruction
/// functions
MachineIRBuilder &MIRBuilder;
/// To keep track of changes made by the LegalizerHelper.
GISelChangeObserver &Observer;
private:
MachineRegisterInfo &MRI;
const LegalizerInfo &LI;
const TargetLowering &TLI;
public:
enum LegalizeResult {
/// Instruction was already legal and no change was made to the
/// MachineFunction.
AlreadyLegal,
/// Instruction has been legalized and the MachineFunction changed.
Legalized,
/// Some kind of error has occurred and we could not legalize this
/// instruction.
UnableToLegalize,
};
/// Expose LegalizerInfo so the clients can re-use.
const LegalizerInfo &getLegalizerInfo() const { return LI; }
const TargetLowering &getTargetLowering() const { return TLI; }
LegalizerHelper(MachineFunction &MF, GISelChangeObserver &Observer,
MachineIRBuilder &B);
LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
GISelChangeObserver &Observer, MachineIRBuilder &B);
/// Replace \p MI by a sequence of legal instructions that can implement the
/// same operation. Note that this means \p MI may be deleted, so any iterator
/// steps should be performed before calling this function. \p Helper should
/// be initialized to the MachineFunction containing \p MI.
///
/// Considered as an opaque blob, the legal code will use and define the same
/// registers as \p MI.
LegalizeResult legalizeInstrStep(MachineInstr &MI,
LostDebugLocObserver &LocObserver);
/// Legalize an instruction by emitting a runtime library call instead.
LegalizeResult libcall(MachineInstr &MI, LostDebugLocObserver &LocObserver);
/// Legalize an instruction by reducing the width of the underlying scalar
/// type.
LegalizeResult narrowScalar(MachineInstr &MI, unsigned TypeIdx, LLT NarrowTy);
/// Legalize an instruction by performing the operation on a wider scalar type
/// (for example a 16-bit addition can be safely performed at 32-bits
/// precision, ignoring the unused bits).
LegalizeResult widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
/// Legalize an instruction by replacing the value type
LegalizeResult bitcast(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
/// Legalize an instruction by splitting it into simpler parts, hopefully
/// understood by the target.
LegalizeResult lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
/// Legalize a vector instruction by splitting into multiple components, each
/// acting on the same scalar type as the original but with fewer elements.
LegalizeResult fewerElementsVector(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy);
/// Legalize a vector instruction by increasing the number of vector elements
/// involved and ignoring the added elements later.
LegalizeResult moreElementsVector(MachineInstr &MI, unsigned TypeIdx,
LLT MoreTy);
/// Cast the given value to an LLT::scalar with an equivalent size. Returns
/// the register to use if an instruction was inserted. Returns the original
/// register if no coercion was necessary.
//
// This may also fail and return Register() if there is no legal way to cast.
Register coerceToScalar(Register Val);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// Use by extending the operand's type to \p WideTy using the specified \p
/// ExtOpcode for the extension instruction, and replacing the vreg of the
/// operand in place.
void widenScalarSrc(MachineInstr &MI, LLT WideTy, unsigned OpIdx,
unsigned ExtOpcode);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// Use by truncating the operand's type to \p NarrowTy using G_TRUNC, and
/// replacing the vreg of the operand in place.
void narrowScalarSrc(MachineInstr &MI, LLT NarrowTy, unsigned OpIdx);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// Def by extending the operand's type to \p WideTy and truncating it back
/// with the \p TruncOpcode, and replacing the vreg of the operand in place.
void widenScalarDst(MachineInstr &MI, LLT WideTy, unsigned OpIdx = 0,
unsigned TruncOpcode = TargetOpcode::G_TRUNC);
// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
// Def by truncating the operand's type to \p NarrowTy, replacing in place and
// extending back with \p ExtOpcode.
void narrowScalarDst(MachineInstr &MI, LLT NarrowTy, unsigned OpIdx,
unsigned ExtOpcode);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// Def by performing it with additional vector elements and extracting the
/// result elements, and replacing the vreg of the operand in place.
void moreElementsVectorDst(MachineInstr &MI, LLT MoreTy, unsigned OpIdx);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// Use by producing a vector with undefined high elements, extracting the
/// original vector type, and replacing the vreg of the operand in place.
void moreElementsVectorSrc(MachineInstr &MI, LLT MoreTy, unsigned OpIdx);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// use by inserting a G_BITCAST to \p CastTy
void bitcastSrc(MachineInstr &MI, LLT CastTy, unsigned OpIdx);
/// Legalize a single operand \p OpIdx of the machine instruction \p MI as a
/// def by inserting a G_BITCAST from \p CastTy
void bitcastDst(MachineInstr &MI, LLT CastTy, unsigned OpIdx);
/// Widen \p OrigReg to \p WideTy by merging to a wider type, padding with
/// G_IMPLICIT_DEF, and producing dead results.
Register widenWithUnmerge(LLT WideTy, Register OrigReg);
private:
LegalizeResult
widenScalarMergeValues(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
LegalizeResult
widenScalarUnmergeValues(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
LegalizeResult
widenScalarExtract(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
LegalizeResult
widenScalarInsert(MachineInstr &MI, unsigned TypeIdx, LLT WideTy);
LegalizeResult widenScalarAddSubOverflow(MachineInstr &MI, unsigned TypeIdx,
LLT WideTy);
LegalizeResult widenScalarAddSubShlSat(MachineInstr &MI, unsigned TypeIdx,
LLT WideTy);
LegalizeResult widenScalarMulo(MachineInstr &MI, unsigned TypeIdx,
LLT WideTy);
/// Helper function to split a wide generic register into bitwise blocks with
/// the given Type (which implies the number of blocks needed). The generic
/// registers created are appended to Ops, starting at bit 0 of Reg.
void extractParts(Register Reg, LLT Ty, int NumParts,
SmallVectorImpl<Register> &VRegs);
/// Version which handles irregular splits.
bool extractParts(Register Reg, LLT RegTy, LLT MainTy,
LLT &LeftoverTy,
SmallVectorImpl<Register> &VRegs,
SmallVectorImpl<Register> &LeftoverVRegs);
/// Version which handles irregular sub-vector splits.
void extractVectorParts(Register Reg, unsigned NumElst,
SmallVectorImpl<Register> &VRegs);
/// Helper function to build a wide generic register \p DstReg of type \p
/// RegTy from smaller parts. This will produce a G_MERGE_VALUES,
/// G_BUILD_VECTOR, G_CONCAT_VECTORS, or sequence of G_INSERT as appropriate
/// for the types.
///
/// \p PartRegs must be registers of type \p PartTy.
///
/// If \p ResultTy does not evenly break into \p PartTy sized pieces, the
/// remainder must be specified with \p LeftoverRegs of type \p LeftoverTy.
void insertParts(Register DstReg, LLT ResultTy,
LLT PartTy, ArrayRef<Register> PartRegs,
LLT LeftoverTy = LLT(), ArrayRef<Register> LeftoverRegs = {});
/// Merge \p PartRegs with different types into \p DstReg.
void mergeMixedSubvectors(Register DstReg, ArrayRef<Register> PartRegs);
void appendVectorElts(SmallVectorImpl<Register> &Elts, Register Reg);
/// Unmerge \p SrcReg into smaller sized values, and append them to \p
/// Parts. The elements of \p Parts will be the greatest common divisor type
/// of \p DstTy, \p NarrowTy and the type of \p SrcReg. This will compute and
/// return the GCD type.
LLT extractGCDType(SmallVectorImpl<Register> &Parts, LLT DstTy,
LLT NarrowTy, Register SrcReg);
/// Unmerge \p SrcReg into \p GCDTy typed registers. This will append all of
/// the unpacked registers to \p Parts. This version is if the common unmerge
/// type is already known.
void extractGCDType(SmallVectorImpl<Register> &Parts, LLT GCDTy,
Register SrcReg);
/// Produce a merge of values in \p VRegs to define \p DstReg. Perform a merge
/// from the least common multiple type, and convert as appropriate to \p
/// DstReg.
///
/// \p VRegs should each have type \p GCDTy. This type should be greatest
/// common divisor type of \p DstReg, \p NarrowTy, and an undetermined source
/// type.
///
/// \p NarrowTy is the desired result merge source type. If the source value
/// needs to be widened to evenly cover \p DstReg, inserts high bits
/// corresponding to the extension opcode \p PadStrategy.
///
/// \p VRegs will be cleared, and the result \p NarrowTy register pieces
/// will replace it. Returns The complete LCMTy that \p VRegs will cover when
/// merged.
LLT buildLCMMergePieces(LLT DstTy, LLT NarrowTy, LLT GCDTy,
SmallVectorImpl<Register> &VRegs,
unsigned PadStrategy = TargetOpcode::G_ANYEXT);
/// Merge the values in \p RemergeRegs to an \p LCMTy typed value. Extract the
/// low bits into \p DstReg. This is intended to use the outputs from
/// buildLCMMergePieces after processing.
void buildWidenedRemergeToDst(Register DstReg, LLT LCMTy,
ArrayRef<Register> RemergeRegs);
/// Perform generic multiplication of values held in multiple registers.
/// Generated instructions use only types NarrowTy and i1.
/// Destination can be same or two times size of the source.
void multiplyRegisters(SmallVectorImpl<Register> &DstRegs,
ArrayRef<Register> Src1Regs,
ArrayRef<Register> Src2Regs, LLT NarrowTy);
void changeOpcode(MachineInstr &MI, unsigned NewOpcode);
LegalizeResult tryNarrowPow2Reduction(MachineInstr &MI, Register SrcReg,
LLT SrcTy, LLT NarrowTy,
unsigned ScalarOpc);
// Memcpy family legalization helpers.
LegalizeResult lowerMemset(MachineInstr &MI, Register Dst, Register Val,
uint64_t KnownLen, Align Alignment,
bool IsVolatile);
LegalizeResult lowerMemcpyInline(MachineInstr &MI, Register Dst, Register Src,
uint64_t KnownLen, Align DstAlign,
Align SrcAlign, bool IsVolatile);
LegalizeResult lowerMemcpy(MachineInstr &MI, Register Dst, Register Src,
uint64_t KnownLen, uint64_t Limit, Align DstAlign,
Align SrcAlign, bool IsVolatile);
LegalizeResult lowerMemmove(MachineInstr &MI, Register Dst, Register Src,
uint64_t KnownLen, Align DstAlign, Align SrcAlign,
bool IsVolatile);
public:
/// Return the alignment to use for a stack temporary object with the given
/// type.
Align getStackTemporaryAlignment(LLT Type, Align MinAlign = Align()) const;
/// Create a stack temporary based on the size in bytes and the alignment
MachineInstrBuilder createStackTemporary(TypeSize Bytes, Align Alignment,
MachinePointerInfo &PtrInfo);
/// Get a pointer to vector element \p Index located in memory for a vector of
/// type \p VecTy starting at a base address of \p VecPtr. If \p Index is out
/// of bounds the returned pointer is unspecified, but will be within the
/// vector bounds.
Register getVectorElementPointer(Register VecPtr, LLT VecTy, Register Index);
/// Handles most opcodes. Split \p MI into same instruction on sub-vectors or
/// scalars with \p NumElts elements (1 for scalar). Supports uneven splits:
/// there can be leftover sub-vector with fewer then \p NumElts or a leftover
/// scalar. To avoid this use moreElements first and set MI number of elements
/// to multiple of \p NumElts. Non-vector operands that should be used on all
/// sub-instructions without split are listed in \p NonVecOpIndices.
LegalizeResult fewerElementsVectorMultiEltType(
GenericMachineInstr &MI, unsigned NumElts,
std::initializer_list<unsigned> NonVecOpIndices = {});
LegalizeResult fewerElementsVectorPhi(GenericMachineInstr &MI,
unsigned NumElts);
LegalizeResult moreElementsVectorPhi(MachineInstr &MI, unsigned TypeIdx,
LLT MoreTy);
LegalizeResult moreElementsVectorShuffle(MachineInstr &MI, unsigned TypeIdx,
LLT MoreTy);
LegalizeResult fewerElementsVectorUnmergeValues(MachineInstr &MI,
unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult fewerElementsVectorMerge(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult fewerElementsVectorExtractInsertVectorElt(MachineInstr &MI,
unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult reduceLoadStoreWidth(GLoadStore &MI, unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult fewerElementsVectorSextInReg(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult narrowScalarShiftByConstant(MachineInstr &MI, const APInt &Amt,
LLT HalfTy, LLT ShiftAmtTy);
LegalizeResult fewerElementsVectorReductions(MachineInstr &MI,
unsigned TypeIdx, LLT NarrowTy);
LegalizeResult fewerElementsVectorShuffle(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult narrowScalarShift(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarAddSub(MachineInstr &MI, unsigned TypeIdx,
LLT NarrowTy);
LegalizeResult narrowScalarMul(MachineInstr &MI, LLT Ty);
LegalizeResult narrowScalarFPTOI(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarExtract(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarExt(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarSelect(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarCTLZ(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarCTTZ(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
LegalizeResult narrowScalarCTPOP(MachineInstr &MI, unsigned TypeIdx, LLT Ty);
/// Perform Bitcast legalize action on G_EXTRACT_VECTOR_ELT.
LegalizeResult bitcastExtractVectorElt(MachineInstr &MI, unsigned TypeIdx,
LLT CastTy);
/// Perform Bitcast legalize action on G_INSERT_VECTOR_ELT.
LegalizeResult bitcastInsertVectorElt(MachineInstr &MI, unsigned TypeIdx,
LLT CastTy);
LegalizeResult lowerBitcast(MachineInstr &MI);
LegalizeResult lowerLoad(GAnyLoad &MI);
LegalizeResult lowerStore(GStore &MI);
LegalizeResult lowerBitCount(MachineInstr &MI);
LegalizeResult lowerFunnelShiftWithInverse(MachineInstr &MI);
LegalizeResult lowerFunnelShiftAsShifts(MachineInstr &MI);
LegalizeResult lowerFunnelShift(MachineInstr &MI);
LegalizeResult lowerRotateWithReverseRotate(MachineInstr &MI);
LegalizeResult lowerRotate(MachineInstr &MI);
LegalizeResult lowerU64ToF32BitOps(MachineInstr &MI);
LegalizeResult lowerUITOFP(MachineInstr &MI);
LegalizeResult lowerSITOFP(MachineInstr &MI);
LegalizeResult lowerFPTOUI(MachineInstr &MI);
LegalizeResult lowerFPTOSI(MachineInstr &MI);
LegalizeResult lowerFPTRUNC_F64_TO_F16(MachineInstr &MI);
LegalizeResult lowerFPTRUNC(MachineInstr &MI);
LegalizeResult lowerFPOWI(MachineInstr &MI);
LegalizeResult lowerMinMax(MachineInstr &MI);
LegalizeResult lowerFCopySign(MachineInstr &MI);
LegalizeResult lowerFMinNumMaxNum(MachineInstr &MI);
LegalizeResult lowerFMad(MachineInstr &MI);
LegalizeResult lowerIntrinsicRound(MachineInstr &MI);
LegalizeResult lowerFFloor(MachineInstr &MI);
LegalizeResult lowerMergeValues(MachineInstr &MI);
LegalizeResult lowerUnmergeValues(MachineInstr &MI);
LegalizeResult lowerExtractInsertVectorElt(MachineInstr &MI);
LegalizeResult lowerShuffleVector(MachineInstr &MI);
LegalizeResult lowerDynStackAlloc(MachineInstr &MI);
LegalizeResult lowerExtract(MachineInstr &MI);
LegalizeResult lowerInsert(MachineInstr &MI);
LegalizeResult lowerSADDO_SSUBO(MachineInstr &MI);
LegalizeResult lowerAddSubSatToMinMax(MachineInstr &MI);
LegalizeResult lowerAddSubSatToAddoSubo(MachineInstr &MI);
LegalizeResult lowerShlSat(MachineInstr &MI);
LegalizeResult lowerBswap(MachineInstr &MI);
LegalizeResult lowerBitreverse(MachineInstr &MI);
LegalizeResult lowerReadWriteRegister(MachineInstr &MI);
LegalizeResult lowerSMULH_UMULH(MachineInstr &MI);
LegalizeResult lowerSelect(MachineInstr &MI);
LegalizeResult lowerDIVREM(MachineInstr &MI);
LegalizeResult lowerAbsToAddXor(MachineInstr &MI);
LegalizeResult lowerAbsToMaxNeg(MachineInstr &MI);
LegalizeResult lowerVectorReduction(MachineInstr &MI);
LegalizeResult lowerMemcpyInline(MachineInstr &MI);
LegalizeResult lowerMemCpyFamily(MachineInstr &MI, unsigned MaxLen = 0);
};
/// Helper function that creates a libcall to the given \p Name using the given
/// calling convention \p CC.
LegalizerHelper::LegalizeResult
createLibcall(MachineIRBuilder &MIRBuilder, const char *Name,
const CallLowering::ArgInfo &Result,
ArrayRef<CallLowering::ArgInfo> Args, CallingConv::ID CC);
/// Helper function that creates the given libcall.
LegalizerHelper::LegalizeResult
createLibcall(MachineIRBuilder &MIRBuilder, RTLIB::Libcall Libcall,
const CallLowering::ArgInfo &Result,
ArrayRef<CallLowering::ArgInfo> Args);
/// Create a libcall to memcpy et al.
LegalizerHelper::LegalizeResult
createMemLibcall(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
MachineInstr &MI, LostDebugLocObserver &LocObserver);
} // End namespace llvm.
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,164 @@
//== llvm/CodeGen/GlobalISel/LoadStoreOpt.h - LoadStoreOpt -------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// This is an optimization pass for GlobalISel generic memory operations.
/// Specifically, it focuses on merging stores and loads to consecutive
/// addresses.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LOADSTOREOPT_H
#define LLVM_CODEGEN_GLOBALISEL_LOADSTOREOPT_H
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
namespace llvm {
// Forward declarations.
class MachineRegisterInfo;
namespace GISelAddressing {
/// Helper struct to store a base, index and offset that forms an address
struct BaseIndexOffset {
Register BaseReg;
Register IndexReg;
int64_t Offset = 0;
bool IsIndexSignExt = false;
};
/// Returns a BaseIndexOffset which describes the pointer in \p Ptr.
BaseIndexOffset getPointerInfo(Register Ptr, MachineRegisterInfo &MRI);
/// Compute whether or not a memory access at \p MI1 aliases with an access at
/// \p MI2 \returns true if either alias/no-alias is known. Sets \p IsAlias
/// accordingly.
bool aliasIsKnownForLoadStore(const MachineInstr &MI1, const MachineInstr &MI2,
bool &IsAlias, MachineRegisterInfo &MRI);
/// Returns true if the instruction \p MI may alias \p Other.
/// This function uses multiple strategies to detect aliasing, whereas
/// aliasIsKnownForLoadStore just looks at the addresses of load/stores and is
/// tries to reason about base/index/offsets.
bool instMayAlias(const MachineInstr &MI, const MachineInstr &Other,
MachineRegisterInfo &MRI, AliasAnalysis *AA);
} // namespace GISelAddressing
using namespace GISelAddressing;
class LoadStoreOpt : public MachineFunctionPass {
public:
static char ID;
private:
/// An input function to decide if the pass should run or not
/// on the given MachineFunction.
std::function<bool(const MachineFunction &)> DoNotRunPass;
MachineRegisterInfo *MRI;
const TargetLowering *TLI;
MachineFunction *MF;
AliasAnalysis *AA;
const LegalizerInfo *LI;
MachineIRBuilder Builder;
/// Initialize the field members using \p MF.
void init(MachineFunction &MF);
class StoreMergeCandidate {
public:
// The base pointer used as the base for all stores in this candidate.
Register BasePtr;
// Our algorithm is very simple at the moment. We assume that in instruction
// order stores are writing to incremeneting consecutive addresses. So when
// we walk the block in reverse order, the next eligible store must write to
// an offset one store width lower than CurrentLowestOffset.
uint64_t CurrentLowestOffset;
SmallVector<GStore *> Stores;
// A vector of MachineInstr/unsigned pairs to denote potential aliases that
// need to be checked before the candidate is considered safe to merge. The
// unsigned value is an index into the Stores vector. The indexed store is
// the highest-indexed store that has already been checked to not have an
// alias with the instruction. We record this so we don't have to repeat
// alias checks that have been already done, only those with stores added
// after the potential alias is recorded.
SmallVector<std::pair<MachineInstr *, unsigned>> PotentialAliases;
void addPotentialAlias(MachineInstr &MI);
/// Reset this candidate back to an empty one.
void reset() {
Stores.clear();
PotentialAliases.clear();
CurrentLowestOffset = 0;
BasePtr = Register();
}
};
bool isLegalOrBeforeLegalizer(const LegalityQuery &Query,
MachineFunction &MF) const;
/// If the given store is valid to be a member of the candidate, add it and
/// return true. Otherwise, returns false.
bool addStoreToCandidate(GStore &MI, StoreMergeCandidate &C);
/// Returns true if the instruction \p MI would potentially alias with any
/// stores in the candidate \p C.
bool operationAliasesWithCandidate(MachineInstr &MI, StoreMergeCandidate &C);
/// Merges the stores in the given vector into a wide store.
/// \p returns true if at least some of the stores were merged.
/// This may decide not to merge stores if heuristics predict it will not be
/// worth it.
bool mergeStores(SmallVectorImpl<GStore *> &StoresToMerge);
/// Perform a merge of all the stores in \p Stores into a single store.
/// Erases the old stores from the block when finished.
/// \returns true if merging was done. It may fail to perform a merge if
/// there are issues with materializing legal wide values.
bool doSingleStoreMerge(SmallVectorImpl<GStore *> &Stores);
bool processMergeCandidate(StoreMergeCandidate &C);
bool mergeBlockStores(MachineBasicBlock &MBB);
bool mergeFunctionStores(MachineFunction &MF);
/// Initialize some target-specific data structures for the store merging
/// optimization. \p AddrSpace indicates which address space to use when
/// probing the legalizer info for legal stores.
void initializeStoreMergeTargetInfo(unsigned AddrSpace = 0);
/// A map between address space numbers and a bitvector of supported stores
/// sizes. Each bit in the bitvector represents whether a store size of
/// that bit's value is legal. E.g. if bit 64 is set, then 64 bit scalar
/// stores are legal.
DenseMap<unsigned, BitVector> LegalStoreSizes;
bool IsPreLegalizer;
/// Contains instructions to be erased at the end of a block scan.
SmallSet<MachineInstr *, 16> InstsToErase;
public:
LoadStoreOpt();
LoadStoreOpt(std::function<bool(const MachineFunction &)>);
StringRef getPassName() const override { return "LoadStoreOpt"; }
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties()
.set(MachineFunctionProperties::Property::IsSSA);
}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &MF) override;
};
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,97 @@
//== llvm/CodeGen/GlobalISel/Localizer.h - Localizer -------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file This file describes the interface of the Localizer pass.
/// This pass moves/duplicates constant-like instructions close to their uses.
/// Its primarily goal is to workaround the deficiencies of the fast register
/// allocator.
/// With GlobalISel constants are all materialized in the entry block of
/// a function. However, the fast allocator cannot rematerialize constants and
/// has a lot more live-ranges to deal with and will most likely end up
/// spilling a lot.
/// By pushing the constants close to their use, we only create small
/// live-ranges.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LOCALIZER_H
#define LLVM_CODEGEN_GLOBALISEL_LOCALIZER_H
#include "llvm/ADT/SetVector.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
namespace llvm {
// Forward declarations.
class MachineRegisterInfo;
class TargetTransformInfo;
/// This pass implements the localization mechanism described at the
/// top of this file. One specificity of the implementation is that
/// it will materialize one and only one instance of a constant per
/// basic block, thus enabling reuse of that constant within that block.
/// Moreover, it only materializes constants in blocks where they
/// are used. PHI uses are considered happening at the end of the
/// related predecessor.
class Localizer : public MachineFunctionPass {
public:
static char ID;
private:
/// An input function to decide if the pass should run or not
/// on the given MachineFunction.
std::function<bool(const MachineFunction &)> DoNotRunPass;
/// MRI contains all the register class/bank information that this
/// pass uses and updates.
MachineRegisterInfo *MRI;
/// TTI used for getting remat costs for instructions.
TargetTransformInfo *TTI;
/// Check if \p MOUse is used in the same basic block as \p Def.
/// If the use is in the same block, we say it is local.
/// When the use is not local, \p InsertMBB will contain the basic
/// block when to insert \p Def to have a local use.
static bool isLocalUse(MachineOperand &MOUse, const MachineInstr &Def,
MachineBasicBlock *&InsertMBB);
/// Initialize the field members using \p MF.
void init(MachineFunction &MF);
typedef SmallSetVector<MachineInstr *, 32> LocalizedSetVecT;
/// If \p Op is a phi operand and not unique in that phi, that is,
/// there are other operands in the phi with the same register,
/// return true.
bool isNonUniquePhiValue(MachineOperand &Op) const;
/// Do inter-block localization from the entry block.
bool localizeInterBlock(MachineFunction &MF,
LocalizedSetVecT &LocalizedInstrs);
/// Do intra-block localization of already localized instructions.
bool localizeIntraBlock(LocalizedSetVecT &LocalizedInstrs);
public:
Localizer();
Localizer(std::function<bool(const MachineFunction &)>);
StringRef getPassName() const override { return "Localizer"; }
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties()
.set(MachineFunctionProperties::Property::IsSSA);
}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &MF) override;
};
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,50 @@
//===----- llvm/CodeGen/GlobalISel/LostDebugLocObserver.h -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Tracks DebugLocs between checkpoints and verifies that they are transferred.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_LOSTDEBUGLOCOBSERVER_H
#define LLVM_CODEGEN_GLOBALISEL_LOSTDEBUGLOCOBSERVER_H
#include "llvm/ADT/SmallSet.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
namespace llvm {
class LostDebugLocObserver : public GISelChangeObserver {
StringRef DebugType;
SmallSet<DebugLoc, 4> LostDebugLocs;
SmallPtrSet<MachineInstr *, 4> PotentialMIsForDebugLocs;
unsigned NumLostDebugLocs = 0;
public:
LostDebugLocObserver(StringRef DebugType) : DebugType(DebugType) {}
unsigned getNumLostDebugLocs() const { return NumLostDebugLocs; }
/// Call this to indicate that it's a good point to assess whether locations
/// have been lost. Typically this will be when a logical change has been
/// completed such as the caller has finished replacing some instructions with
/// alternatives. When CheckDebugLocs is true, the locations will be checked
/// to see if any have been lost since the last checkpoint. When
/// CheckDebugLocs is false, it will just reset ready for the next checkpoint
/// without checking anything. This can be helpful to limit the detection to
/// easy-to-fix portions of an algorithm before allowing more difficult ones.
void checkpoint(bool CheckDebugLocs = true);
void createdInstr(MachineInstr &MI) override;
void erasingInstr(MachineInstr &MI) override;
void changingInstr(MachineInstr &MI) override;
void changedInstr(MachineInstr &MI) override;
private:
void analyzeDebugLocations();
};
} // namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_LOSTDEBUGLOCOBSERVER_H

View File

@@ -0,0 +1,661 @@
//==------ llvm/CodeGen/GlobalISel/MIPatternMatch.h -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Contains matchers for matching SSA Machine Instructions.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_MIPATTERNMATCH_H
#define LLVM_CODEGEN_GLOBALISEL_MIPATTERNMATCH_H
#include "llvm/ADT/APInt.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/InstrTypes.h"
namespace llvm {
namespace MIPatternMatch {
template <typename Reg, typename Pattern>
bool mi_match(Reg R, const MachineRegisterInfo &MRI, Pattern &&P) {
return P.match(MRI, R);
}
template <typename Pattern>
bool mi_match(MachineInstr &MI, const MachineRegisterInfo &MRI, Pattern &&P) {
return P.match(MRI, &MI);
}
// TODO: Extend for N use.
template <typename SubPatternT> struct OneUse_match {
SubPatternT SubPat;
OneUse_match(const SubPatternT &SP) : SubPat(SP) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
return MRI.hasOneUse(Reg) && SubPat.match(MRI, Reg);
}
};
template <typename SubPat>
inline OneUse_match<SubPat> m_OneUse(const SubPat &SP) {
return SP;
}
template <typename SubPatternT> struct OneNonDBGUse_match {
SubPatternT SubPat;
OneNonDBGUse_match(const SubPatternT &SP) : SubPat(SP) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
return MRI.hasOneNonDBGUse(Reg) && SubPat.match(MRI, Reg);
}
};
template <typename SubPat>
inline OneNonDBGUse_match<SubPat> m_OneNonDBGUse(const SubPat &SP) {
return SP;
}
template <typename ConstT>
inline Optional<ConstT> matchConstant(Register, const MachineRegisterInfo &);
template <>
inline Optional<APInt> matchConstant(Register Reg,
const MachineRegisterInfo &MRI) {
return getIConstantVRegVal(Reg, MRI);
}
template <>
inline Optional<int64_t> matchConstant(Register Reg,
const MachineRegisterInfo &MRI) {
return getIConstantVRegSExtVal(Reg, MRI);
}
template <typename ConstT> struct ConstantMatch {
ConstT &CR;
ConstantMatch(ConstT &C) : CR(C) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
if (auto MaybeCst = matchConstant<ConstT>(Reg, MRI)) {
CR = *MaybeCst;
return true;
}
return false;
}
};
inline ConstantMatch<APInt> m_ICst(APInt &Cst) {
return ConstantMatch<APInt>(Cst);
}
inline ConstantMatch<int64_t> m_ICst(int64_t &Cst) {
return ConstantMatch<int64_t>(Cst);
}
struct GCstAndRegMatch {
Optional<ValueAndVReg> &ValReg;
GCstAndRegMatch(Optional<ValueAndVReg> &ValReg) : ValReg(ValReg) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
ValReg = getIConstantVRegValWithLookThrough(Reg, MRI);
return ValReg ? true : false;
}
};
inline GCstAndRegMatch m_GCst(Optional<ValueAndVReg> &ValReg) {
return GCstAndRegMatch(ValReg);
}
struct GFCstAndRegMatch {
Optional<FPValueAndVReg> &FPValReg;
GFCstAndRegMatch(Optional<FPValueAndVReg> &FPValReg) : FPValReg(FPValReg) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI);
return FPValReg ? true : false;
}
};
inline GFCstAndRegMatch m_GFCst(Optional<FPValueAndVReg> &FPValReg) {
return GFCstAndRegMatch(FPValReg);
}
struct GFCstOrSplatGFCstMatch {
Optional<FPValueAndVReg> &FPValReg;
GFCstOrSplatGFCstMatch(Optional<FPValueAndVReg> &FPValReg)
: FPValReg(FPValReg) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
return (FPValReg = getFConstantSplat(Reg, MRI)) ||
(FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI));
};
};
inline GFCstOrSplatGFCstMatch
m_GFCstOrSplat(Optional<FPValueAndVReg> &FPValReg) {
return GFCstOrSplatGFCstMatch(FPValReg);
}
/// Matcher for a specific constant value.
struct SpecificConstantMatch {
int64_t RequestedVal;
SpecificConstantMatch(int64_t RequestedVal) : RequestedVal(RequestedVal) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
int64_t MatchedVal;
return mi_match(Reg, MRI, m_ICst(MatchedVal)) && MatchedVal == RequestedVal;
}
};
/// Matches a constant equal to \p RequestedValue.
inline SpecificConstantMatch m_SpecificICst(int64_t RequestedValue) {
return SpecificConstantMatch(RequestedValue);
}
/// Matcher for a specific constant splat.
struct SpecificConstantSplatMatch {
int64_t RequestedVal;
SpecificConstantSplatMatch(int64_t RequestedVal)
: RequestedVal(RequestedVal) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
return isBuildVectorConstantSplat(Reg, MRI, RequestedVal,
/* AllowUndef */ false);
}
};
/// Matches a constant splat of \p RequestedValue.
inline SpecificConstantSplatMatch m_SpecificICstSplat(int64_t RequestedValue) {
return SpecificConstantSplatMatch(RequestedValue);
}
/// Matcher for a specific constant or constant splat.
struct SpecificConstantOrSplatMatch {
int64_t RequestedVal;
SpecificConstantOrSplatMatch(int64_t RequestedVal)
: RequestedVal(RequestedVal) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
int64_t MatchedVal;
if (mi_match(Reg, MRI, m_ICst(MatchedVal)) && MatchedVal == RequestedVal)
return true;
return isBuildVectorConstantSplat(Reg, MRI, RequestedVal,
/* AllowUndef */ false);
}
};
/// Matches a \p RequestedValue constant or a constant splat of \p
/// RequestedValue.
inline SpecificConstantOrSplatMatch
m_SpecificICstOrSplat(int64_t RequestedValue) {
return SpecificConstantOrSplatMatch(RequestedValue);
}
///{
/// Convenience matchers for specific integer values.
inline SpecificConstantMatch m_ZeroInt() { return SpecificConstantMatch(0); }
inline SpecificConstantMatch m_AllOnesInt() {
return SpecificConstantMatch(-1);
}
///}
// TODO: Rework this for different kinds of MachineOperand.
// Currently assumes the Src for a match is a register.
// We might want to support taking in some MachineOperands and call getReg on
// that.
struct operand_type_match {
bool match(const MachineRegisterInfo &MRI, Register Reg) { return true; }
bool match(const MachineRegisterInfo &MRI, MachineOperand *MO) {
return MO->isReg();
}
};
inline operand_type_match m_Reg() { return operand_type_match(); }
/// Matching combinators.
template <typename... Preds> struct And {
template <typename MatchSrc>
bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
return true;
}
};
template <typename Pred, typename... Preds>
struct And<Pred, Preds...> : And<Preds...> {
Pred P;
And(Pred &&p, Preds &&... preds)
: And<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {
}
template <typename MatchSrc>
bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
return P.match(MRI, src) && And<Preds...>::match(MRI, src);
}
};
template <typename... Preds> struct Or {
template <typename MatchSrc>
bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
return false;
}
};
template <typename Pred, typename... Preds>
struct Or<Pred, Preds...> : Or<Preds...> {
Pred P;
Or(Pred &&p, Preds &&... preds)
: Or<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {}
template <typename MatchSrc>
bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
return P.match(MRI, src) || Or<Preds...>::match(MRI, src);
}
};
template <typename... Preds> And<Preds...> m_all_of(Preds &&... preds) {
return And<Preds...>(std::forward<Preds>(preds)...);
}
template <typename... Preds> Or<Preds...> m_any_of(Preds &&... preds) {
return Or<Preds...>(std::forward<Preds>(preds)...);
}
template <typename BindTy> struct bind_helper {
static bool bind(const MachineRegisterInfo &MRI, BindTy &VR, BindTy &V) {
VR = V;
return true;
}
};
template <> struct bind_helper<MachineInstr *> {
static bool bind(const MachineRegisterInfo &MRI, MachineInstr *&MI,
Register Reg) {
MI = MRI.getVRegDef(Reg);
if (MI)
return true;
return false;
}
static bool bind(const MachineRegisterInfo &MRI, MachineInstr *&MI,
MachineInstr *Inst) {
MI = Inst;
return MI;
}
};
template <> struct bind_helper<LLT> {
static bool bind(const MachineRegisterInfo &MRI, LLT Ty, Register Reg) {
Ty = MRI.getType(Reg);
if (Ty.isValid())
return true;
return false;
}
};
template <> struct bind_helper<const ConstantFP *> {
static bool bind(const MachineRegisterInfo &MRI, const ConstantFP *&F,
Register Reg) {
F = getConstantFPVRegVal(Reg, MRI);
if (F)
return true;
return false;
}
};
template <typename Class> struct bind_ty {
Class &VR;
bind_ty(Class &V) : VR(V) {}
template <typename ITy> bool match(const MachineRegisterInfo &MRI, ITy &&V) {
return bind_helper<Class>::bind(MRI, VR, V);
}
};
inline bind_ty<Register> m_Reg(Register &R) { return R; }
inline bind_ty<MachineInstr *> m_MInstr(MachineInstr *&MI) { return MI; }
inline bind_ty<LLT> m_Type(LLT Ty) { return Ty; }
inline bind_ty<CmpInst::Predicate> m_Pred(CmpInst::Predicate &P) { return P; }
inline operand_type_match m_Pred() { return operand_type_match(); }
// Helper for matching G_FCONSTANT
inline bind_ty<const ConstantFP *> m_GFCst(const ConstantFP *&C) { return C; }
// General helper for all the binary generic MI such as G_ADD/G_SUB etc
template <typename LHS_P, typename RHS_P, unsigned Opcode,
bool Commutable = false>
struct BinaryOp_match {
LHS_P L;
RHS_P R;
BinaryOp_match(const LHS_P &LHS, const RHS_P &RHS) : L(LHS), R(RHS) {}
template <typename OpTy>
bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
MachineInstr *TmpMI;
if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 3) {
return (L.match(MRI, TmpMI->getOperand(1).getReg()) &&
R.match(MRI, TmpMI->getOperand(2).getReg())) ||
(Commutable && (R.match(MRI, TmpMI->getOperand(1).getReg()) &&
L.match(MRI, TmpMI->getOperand(2).getReg())));
}
}
return false;
}
};
// Helper for (commutative) binary generic MI that checks Opcode.
template <typename LHS_P, typename RHS_P, bool Commutable = false>
struct BinaryOpc_match {
unsigned Opc;
LHS_P L;
RHS_P R;
BinaryOpc_match(unsigned Opcode, const LHS_P &LHS, const RHS_P &RHS)
: Opc(Opcode), L(LHS), R(RHS) {}
template <typename OpTy>
bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
MachineInstr *TmpMI;
if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
if (TmpMI->getOpcode() == Opc && TmpMI->getNumDefs() == 1 &&
TmpMI->getNumOperands() == 3) {
return (L.match(MRI, TmpMI->getOperand(1).getReg()) &&
R.match(MRI, TmpMI->getOperand(2).getReg())) ||
(Commutable && (R.match(MRI, TmpMI->getOperand(1).getReg()) &&
L.match(MRI, TmpMI->getOperand(2).getReg())));
}
}
return false;
}
};
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_BinOp(unsigned Opcode, const LHS &L,
const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(Opcode, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true>
m_CommutativeBinOp(unsigned Opcode, const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(Opcode, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_ADD, true>
m_GAdd(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_ADD, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_PTR_ADD, false>
m_GPtrAdd(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_PTR_ADD, false>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SUB> m_GSub(const LHS &L,
const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_SUB>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_MUL, true>
m_GMul(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_MUL, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FADD, true>
m_GFAdd(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_FADD, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FMUL, true>
m_GFMul(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_FMUL, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FSUB, false>
m_GFSub(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_FSUB, false>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_AND, true>
m_GAnd(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_AND, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_XOR, true>
m_GXor(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_XOR, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true> m_GOr(const LHS &L,
const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>
m_GShl(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_LSHR, false>
m_GLShr(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_LSHR, false>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_ASHR, false>
m_GAShr(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_ASHR, false>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SMAX, false>
m_GSMax(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_SMAX, false>(L, R);
}
template <typename LHS, typename RHS>
inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SMIN, false>
m_GSMin(const LHS &L, const RHS &R) {
return BinaryOp_match<LHS, RHS, TargetOpcode::G_SMIN, false>(L, R);
}
// Helper for unary instructions (G_[ZSA]EXT/G_TRUNC) etc
template <typename SrcTy, unsigned Opcode> struct UnaryOp_match {
SrcTy L;
UnaryOp_match(const SrcTy &LHS) : L(LHS) {}
template <typename OpTy>
bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
MachineInstr *TmpMI;
if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 2) {
return L.match(MRI, TmpMI->getOperand(1).getReg());
}
}
return false;
}
};
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_ANYEXT>
m_GAnyExt(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_ANYEXT>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_SEXT> m_GSExt(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_SEXT>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_ZEXT> m_GZExt(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_ZEXT>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_FPEXT> m_GFPExt(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_FPEXT>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_TRUNC> m_GTrunc(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_TRUNC>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_BITCAST>
m_GBitcast(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_BITCAST>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_PTRTOINT>
m_GPtrToInt(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_PTRTOINT>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_INTTOPTR>
m_GIntToPtr(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_INTTOPTR>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_FPTRUNC>
m_GFPTrunc(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_FPTRUNC>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_FABS> m_GFabs(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_FABS>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_FNEG> m_GFNeg(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_FNEG>(Src);
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::COPY> m_Copy(SrcTy &&Src) {
return UnaryOp_match<SrcTy, TargetOpcode::COPY>(std::forward<SrcTy>(Src));
}
template <typename SrcTy>
inline UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT> m_GFSqrt(const SrcTy &Src) {
return UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT>(Src);
}
// General helper for generic MI compares, i.e. G_ICMP and G_FCMP
// TODO: Allow checking a specific predicate.
template <typename Pred_P, typename LHS_P, typename RHS_P, unsigned Opcode>
struct CompareOp_match {
Pred_P P;
LHS_P L;
RHS_P R;
CompareOp_match(const Pred_P &Pred, const LHS_P &LHS, const RHS_P &RHS)
: P(Pred), L(LHS), R(RHS) {}
template <typename OpTy>
bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
MachineInstr *TmpMI;
if (!mi_match(Op, MRI, m_MInstr(TmpMI)) || TmpMI->getOpcode() != Opcode)
return false;
auto TmpPred =
static_cast<CmpInst::Predicate>(TmpMI->getOperand(1).getPredicate());
if (!P.match(MRI, TmpPred))
return false;
return L.match(MRI, TmpMI->getOperand(2).getReg()) &&
R.match(MRI, TmpMI->getOperand(3).getReg());
}
};
template <typename Pred, typename LHS, typename RHS>
inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP>
m_GICmp(const Pred &P, const LHS &L, const RHS &R) {
return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP>(P, L, R);
}
template <typename Pred, typename LHS, typename RHS>
inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP>
m_GFCmp(const Pred &P, const LHS &L, const RHS &R) {
return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP>(P, L, R);
}
// Helper for checking if a Reg is of specific type.
struct CheckType {
LLT Ty;
CheckType(const LLT Ty) : Ty(Ty) {}
bool match(const MachineRegisterInfo &MRI, Register Reg) {
return MRI.getType(Reg) == Ty;
}
};
inline CheckType m_SpecificType(LLT Ty) { return Ty; }
template <typename Src0Ty, typename Src1Ty, typename Src2Ty, unsigned Opcode>
struct TernaryOp_match {
Src0Ty Src0;
Src1Ty Src1;
Src2Ty Src2;
TernaryOp_match(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2)
: Src0(Src0), Src1(Src1), Src2(Src2) {}
template <typename OpTy>
bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
MachineInstr *TmpMI;
if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 4) {
return (Src0.match(MRI, TmpMI->getOperand(1).getReg()) &&
Src1.match(MRI, TmpMI->getOperand(2).getReg()) &&
Src2.match(MRI, TmpMI->getOperand(3).getReg()));
}
}
return false;
}
};
template <typename Src0Ty, typename Src1Ty, typename Src2Ty>
inline TernaryOp_match<Src0Ty, Src1Ty, Src2Ty,
TargetOpcode::G_INSERT_VECTOR_ELT>
m_GInsertVecElt(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2) {
return TernaryOp_match<Src0Ty, Src1Ty, Src2Ty,
TargetOpcode::G_INSERT_VECTOR_ELT>(Src0, Src1, Src2);
}
template <typename Src0Ty, typename Src1Ty, typename Src2Ty>
inline TernaryOp_match<Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT>
m_GISelect(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2) {
return TernaryOp_match<Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT>(
Src0, Src1, Src2);
}
/// Matches a register negated by a G_SUB.
/// G_SUB 0, %negated_reg
template <typename SrcTy>
inline BinaryOp_match<SpecificConstantMatch, SrcTy, TargetOpcode::G_SUB>
m_Neg(const SrcTy &&Src) {
return m_GSub(m_ZeroInt(), Src);
}
/// Matches a register not-ed by a G_XOR.
/// G_XOR %not_reg, -1
template <typename SrcTy>
inline BinaryOp_match<SrcTy, SpecificConstantMatch, TargetOpcode::G_XOR, true>
m_Not(const SrcTy &&Src) {
return m_GXor(Src, m_AllOnesInt());
}
} // namespace MIPatternMatch
} // namespace llvm
#endif

View File

@@ -0,0 +1,670 @@
//=- llvm/CodeGen/GlobalISel/RegBankSelect.h - Reg Bank Selector --*- C++ -*-=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file
/// This file describes the interface of the MachineFunctionPass
/// responsible for assigning the generic virtual registers to register bank.
///
/// By default, the reg bank selector relies on local decisions to
/// assign the register bank. In other words, it looks at one instruction
/// at a time to decide where the operand of that instruction should live.
///
/// At higher optimization level, we could imagine that the reg bank selector
/// would use more global analysis and do crazier thing like duplicating
/// instructions and so on. This is future work.
///
/// For now, the pass uses a greedy algorithm to decide where the operand
/// of an instruction should live. It asks the target which banks may be
/// used for each operand of the instruction and what is the cost. Then,
/// it chooses the solution which minimize the cost of the instruction plus
/// the cost of any move that may be needed to the values into the right
/// register bank.
/// In other words, the cost for an instruction on a register bank RegBank
/// is: Cost of I on RegBank plus the sum of the cost for bringing the
/// input operands from their current register bank to RegBank.
/// Thus, the following formula:
/// cost(I, RegBank) = cost(I.Opcode, RegBank) +
/// sum(for each arg in I.arguments: costCrossCopy(arg.RegBank, RegBank))
///
/// E.g., Let say we are assigning the register bank for the instruction
/// defining v2.
/// v0(A_REGBANK) = ...
/// v1(A_REGBANK) = ...
/// v2 = G_ADD i32 v0, v1 <-- MI
///
/// The target may say it can generate G_ADD i32 on register bank A and B
/// with a cost of respectively 5 and 1.
/// Then, let say the cost of a cross register bank copies from A to B is 1.
/// The reg bank selector would compare the following two costs:
/// cost(MI, A_REGBANK) = cost(G_ADD, A_REGBANK) + cost(v0.RegBank, A_REGBANK) +
/// cost(v1.RegBank, A_REGBANK)
/// = 5 + cost(A_REGBANK, A_REGBANK) + cost(A_REGBANK,
/// A_REGBANK)
/// = 5 + 0 + 0 = 5
/// cost(MI, B_REGBANK) = cost(G_ADD, B_REGBANK) + cost(v0.RegBank, B_REGBANK) +
/// cost(v1.RegBank, B_REGBANK)
/// = 1 + cost(A_REGBANK, B_REGBANK) + cost(A_REGBANK,
/// B_REGBANK)
/// = 1 + 1 + 1 = 3
/// Therefore, in this specific example, the reg bank selector would choose
/// bank B for MI.
/// v0(A_REGBANK) = ...
/// v1(A_REGBANK) = ...
/// tmp0(B_REGBANK) = COPY v0
/// tmp1(B_REGBANK) = COPY v1
/// v2(B_REGBANK) = G_ADD i32 tmp0, tmp1
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_REGBANKSELECT_H
#define LLVM_CODEGEN_GLOBALISEL_REGBANKSELECT_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
#include <cassert>
#include <cstdint>
#include <memory>
namespace llvm {
class BlockFrequency;
class MachineBlockFrequencyInfo;
class MachineBranchProbabilityInfo;
class MachineOperand;
class MachineRegisterInfo;
class Pass;
class raw_ostream;
class TargetPassConfig;
class TargetRegisterInfo;
/// This pass implements the reg bank selector pass used in the GlobalISel
/// pipeline. At the end of this pass, all register operands have been assigned
class RegBankSelect : public MachineFunctionPass {
public:
static char ID;
/// List of the modes supported by the RegBankSelect pass.
enum Mode {
/// Assign the register banks as fast as possible (default).
Fast,
/// Greedily minimize the cost of assigning register banks.
/// This should produce code of greater quality, but will
/// require more compile time.
Greedy
};
/// Abstract class used to represent an insertion point in a CFG.
/// This class records an insertion point and materializes it on
/// demand.
/// It allows to reason about the frequency of this insertion point,
/// without having to logically materialize it (e.g., on an edge),
/// before we actually need to insert something.
class InsertPoint {
protected:
/// Tell if the insert point has already been materialized.
bool WasMaterialized = false;
/// Materialize the insertion point.
///
/// If isSplit() is true, this involves actually splitting
/// the block or edge.
///
/// \post getPointImpl() returns a valid iterator.
/// \post getInsertMBBImpl() returns a valid basic block.
/// \post isSplit() == false ; no more splitting should be required.
virtual void materialize() = 0;
/// Return the materialized insertion basic block.
/// Code will be inserted into that basic block.
///
/// \pre ::materialize has been called.
virtual MachineBasicBlock &getInsertMBBImpl() = 0;
/// Return the materialized insertion point.
/// Code will be inserted before that point.
///
/// \pre ::materialize has been called.
virtual MachineBasicBlock::iterator getPointImpl() = 0;
public:
virtual ~InsertPoint() = default;
/// The first call to this method will cause the splitting to
/// happen if need be, then sub sequent calls just return
/// the iterator to that point. I.e., no more splitting will
/// occur.
///
/// \return The iterator that should be used with
/// MachineBasicBlock::insert. I.e., additional code happens
/// before that point.
MachineBasicBlock::iterator getPoint() {
if (!WasMaterialized) {
WasMaterialized = true;
assert(canMaterialize() && "Impossible to materialize this point");
materialize();
}
// When we materialized the point we should have done the splitting.
assert(!isSplit() && "Wrong pre-condition");
return getPointImpl();
}
/// The first call to this method will cause the splitting to
/// happen if need be, then sub sequent calls just return
/// the basic block that contains the insertion point.
/// I.e., no more splitting will occur.
///
/// \return The basic block should be used with
/// MachineBasicBlock::insert and ::getPoint. The new code should
/// happen before that point.
MachineBasicBlock &getInsertMBB() {
if (!WasMaterialized) {
WasMaterialized = true;
assert(canMaterialize() && "Impossible to materialize this point");
materialize();
}
// When we materialized the point we should have done the splitting.
assert(!isSplit() && "Wrong pre-condition");
return getInsertMBBImpl();
}
/// Insert \p MI in the just before ::getPoint()
MachineBasicBlock::iterator insert(MachineInstr &MI) {
return getInsertMBB().insert(getPoint(), &MI);
}
/// Does this point involve splitting an edge or block?
/// As soon as ::getPoint is called and thus, the point
/// materialized, the point will not require splitting anymore,
/// i.e., this will return false.
virtual bool isSplit() const { return false; }
/// Frequency of the insertion point.
/// \p P is used to access the various analysis that will help to
/// get that information, like MachineBlockFrequencyInfo. If \p P
/// does not contain enough enough to return the actual frequency,
/// this returns 1.
virtual uint64_t frequency(const Pass &P) const { return 1; }
/// Check whether this insertion point can be materialized.
/// As soon as ::getPoint is called and thus, the point materialized
/// calling this method does not make sense.
virtual bool canMaterialize() const { return false; }
};
/// Insertion point before or after an instruction.
class InstrInsertPoint : public InsertPoint {
private:
/// Insertion point.
MachineInstr &Instr;
/// Does the insertion point is before or after Instr.
bool Before;
void materialize() override;
MachineBasicBlock::iterator getPointImpl() override {
if (Before)
return Instr;
return Instr.getNextNode() ? *Instr.getNextNode()
: Instr.getParent()->end();
}
MachineBasicBlock &getInsertMBBImpl() override {
return *Instr.getParent();
}
public:
/// Create an insertion point before (\p Before=true) or after \p Instr.
InstrInsertPoint(MachineInstr &Instr, bool Before = true);
bool isSplit() const override;
uint64_t frequency(const Pass &P) const override;
// Worst case, we need to slice the basic block, but that is still doable.
bool canMaterialize() const override { return true; }
};
/// Insertion point at the beginning or end of a basic block.
class MBBInsertPoint : public InsertPoint {
private:
/// Insertion point.
MachineBasicBlock &MBB;
/// Does the insertion point is at the beginning or end of MBB.
bool Beginning;
void materialize() override { /*Nothing to do to materialize*/
}
MachineBasicBlock::iterator getPointImpl() override {
return Beginning ? MBB.begin() : MBB.end();
}
MachineBasicBlock &getInsertMBBImpl() override { return MBB; }
public:
MBBInsertPoint(MachineBasicBlock &MBB, bool Beginning = true)
: MBB(MBB), Beginning(Beginning) {
// If we try to insert before phis, we should use the insertion
// points on the incoming edges.
assert((!Beginning || MBB.getFirstNonPHI() == MBB.begin()) &&
"Invalid beginning point");
// If we try to insert after the terminators, we should use the
// points on the outcoming edges.
assert((Beginning || MBB.getFirstTerminator() == MBB.end()) &&
"Invalid end point");
}
bool isSplit() const override { return false; }
uint64_t frequency(const Pass &P) const override;
bool canMaterialize() const override { return true; };
};
/// Insertion point on an edge.
class EdgeInsertPoint : public InsertPoint {
private:
/// Source of the edge.
MachineBasicBlock &Src;
/// Destination of the edge.
/// After the materialization is done, this hold the basic block
/// that resulted from the splitting.
MachineBasicBlock *DstOrSplit;
/// P is used to update the analysis passes as applicable.
Pass &P;
void materialize() override;
MachineBasicBlock::iterator getPointImpl() override {
// DstOrSplit should be the Split block at this point.
// I.e., it should have one predecessor, Src, and one successor,
// the original Dst.
assert(DstOrSplit && DstOrSplit->isPredecessor(&Src) &&
DstOrSplit->pred_size() == 1 && DstOrSplit->succ_size() == 1 &&
"Did not split?!");
return DstOrSplit->begin();
}
MachineBasicBlock &getInsertMBBImpl() override { return *DstOrSplit; }
public:
EdgeInsertPoint(MachineBasicBlock &Src, MachineBasicBlock &Dst, Pass &P)
: Src(Src), DstOrSplit(&Dst), P(P) {}
bool isSplit() const override {
return Src.succ_size() > 1 && DstOrSplit->pred_size() > 1;
}
uint64_t frequency(const Pass &P) const override;
bool canMaterialize() const override;
};
/// Struct used to represent the placement of a repairing point for
/// a given operand.
class RepairingPlacement {
public:
/// Define the kind of action this repairing needs.
enum RepairingKind {
/// Nothing to repair, just drop this action.
None,
/// Reparing code needs to happen before InsertPoints.
Insert,
/// (Re)assign the register bank of the operand.
Reassign,
/// Mark this repairing placement as impossible.
Impossible
};
/// \name Convenient types for a list of insertion points.
/// @{
using InsertionPoints = SmallVector<std::unique_ptr<InsertPoint>, 2>;
using insertpt_iterator = InsertionPoints::iterator;
using const_insertpt_iterator = InsertionPoints::const_iterator;
/// @}
private:
/// Kind of repairing.
RepairingKind Kind;
/// Index of the operand that will be repaired.
unsigned OpIdx;
/// Are all the insert points materializeable?
bool CanMaterialize;
/// Is there any of the insert points needing splitting?
bool HasSplit = false;
/// Insertion point for the repair code.
/// The repairing code needs to happen just before these points.
InsertionPoints InsertPoints;
/// Some insertion points may need to update the liveness and such.
Pass &P;
public:
/// Create a repairing placement for the \p OpIdx-th operand of
/// \p MI. \p TRI is used to make some checks on the register aliases
/// if the machine operand is a physical register. \p P is used to
/// to update liveness information and such when materializing the
/// points.
RepairingPlacement(MachineInstr &MI, unsigned OpIdx,
const TargetRegisterInfo &TRI, Pass &P,
RepairingKind Kind = RepairingKind::Insert);
/// \name Getters.
/// @{
RepairingKind getKind() const { return Kind; }
unsigned getOpIdx() const { return OpIdx; }
bool canMaterialize() const { return CanMaterialize; }
bool hasSplit() { return HasSplit; }
/// @}
/// \name Overloaded methods to add an insertion point.
/// @{
/// Add a MBBInsertionPoint to the list of InsertPoints.
void addInsertPoint(MachineBasicBlock &MBB, bool Beginning);
/// Add a InstrInsertionPoint to the list of InsertPoints.
void addInsertPoint(MachineInstr &MI, bool Before);
/// Add an EdgeInsertionPoint (\p Src, \p Dst) to the list of InsertPoints.
void addInsertPoint(MachineBasicBlock &Src, MachineBasicBlock &Dst);
/// Add an InsertPoint to the list of insert points.
/// This method takes the ownership of &\p Point.
void addInsertPoint(InsertPoint &Point);
/// @}
/// \name Accessors related to the insertion points.
/// @{
insertpt_iterator begin() { return InsertPoints.begin(); }
insertpt_iterator end() { return InsertPoints.end(); }
const_insertpt_iterator begin() const { return InsertPoints.begin(); }
const_insertpt_iterator end() const { return InsertPoints.end(); }
unsigned getNumInsertPoints() const { return InsertPoints.size(); }
/// @}
/// Change the type of this repairing placement to \p NewKind.
/// It is not possible to switch a repairing placement to the
/// RepairingKind::Insert. There is no fundamental problem with
/// that, but no uses as well, so do not support it for now.
///
/// \pre NewKind != RepairingKind::Insert
/// \post getKind() == NewKind
void switchTo(RepairingKind NewKind) {
assert(NewKind != Kind && "Already of the right Kind");
Kind = NewKind;
InsertPoints.clear();
CanMaterialize = NewKind != RepairingKind::Impossible;
HasSplit = false;
assert(NewKind != RepairingKind::Insert &&
"We would need more MI to switch to Insert");
}
};
private:
/// Helper class used to represent the cost for mapping an instruction.
/// When mapping an instruction, we may introduce some repairing code.
/// In most cases, the repairing code is local to the instruction,
/// thus, we can omit the basic block frequency from the cost.
/// However, some alternatives may produce non-local cost, e.g., when
/// repairing a phi, and thus we then need to scale the local cost
/// to the non-local cost. This class does this for us.
/// \note: We could simply always scale the cost. The problem is that
/// there are higher chances that we saturate the cost easier and end
/// up having the same cost for actually different alternatives.
/// Another option would be to use APInt everywhere.
class MappingCost {
private:
/// Cost of the local instructions.
/// This cost is free of basic block frequency.
uint64_t LocalCost = 0;
/// Cost of the non-local instructions.
/// This cost should include the frequency of the related blocks.
uint64_t NonLocalCost = 0;
/// Frequency of the block where the local instructions live.
uint64_t LocalFreq;
MappingCost(uint64_t LocalCost, uint64_t NonLocalCost, uint64_t LocalFreq)
: LocalCost(LocalCost), NonLocalCost(NonLocalCost),
LocalFreq(LocalFreq) {}
/// Check if this cost is saturated.
bool isSaturated() const;
public:
/// Create a MappingCost assuming that most of the instructions
/// will occur in a basic block with \p LocalFreq frequency.
MappingCost(const BlockFrequency &LocalFreq);
/// Add \p Cost to the local cost.
/// \return true if this cost is saturated, false otherwise.
bool addLocalCost(uint64_t Cost);
/// Add \p Cost to the non-local cost.
/// Non-local cost should reflect the frequency of their placement.
/// \return true if this cost is saturated, false otherwise.
bool addNonLocalCost(uint64_t Cost);
/// Saturate the cost to the maximal representable value.
void saturate();
/// Return an instance of MappingCost that represents an
/// impossible mapping.
static MappingCost ImpossibleCost();
/// Check if this is less than \p Cost.
bool operator<(const MappingCost &Cost) const;
/// Check if this is equal to \p Cost.
bool operator==(const MappingCost &Cost) const;
/// Check if this is not equal to \p Cost.
bool operator!=(const MappingCost &Cost) const { return !(*this == Cost); }
/// Check if this is greater than \p Cost.
bool operator>(const MappingCost &Cost) const {
return *this != Cost && Cost < *this;
}
/// Print this on dbgs() stream.
void dump() const;
/// Print this on \p OS;
void print(raw_ostream &OS) const;
/// Overload the stream operator for easy debug printing.
friend raw_ostream &operator<<(raw_ostream &OS, const MappingCost &Cost) {
Cost.print(OS);
return OS;
}
};
/// Interface to the target lowering info related
/// to register banks.
const RegisterBankInfo *RBI = nullptr;
/// MRI contains all the register class/bank information that this
/// pass uses and updates.
MachineRegisterInfo *MRI = nullptr;
/// Information on the register classes for the current function.
const TargetRegisterInfo *TRI = nullptr;
/// Get the frequency of blocks.
/// This is required for non-fast mode.
MachineBlockFrequencyInfo *MBFI = nullptr;
/// Get the frequency of the edges.
/// This is required for non-fast mode.
MachineBranchProbabilityInfo *MBPI = nullptr;
/// Current optimization remark emitter. Used to report failures.
std::unique_ptr<MachineOptimizationRemarkEmitter> MORE;
/// Helper class used for every code morphing.
MachineIRBuilder MIRBuilder;
/// Optimization mode of the pass.
Mode OptMode;
/// Current target configuration. Controls how the pass handles errors.
const TargetPassConfig *TPC;
/// Assign the register bank of each operand of \p MI.
/// \return True on success, false otherwise.
bool assignInstr(MachineInstr &MI);
/// Initialize the field members using \p MF.
void init(MachineFunction &MF);
/// Check if \p Reg is already assigned what is described by \p ValMapping.
/// \p OnlyAssign == true means that \p Reg just needs to be assigned a
/// register bank. I.e., no repairing is necessary to have the
/// assignment match.
bool assignmentMatch(Register Reg,
const RegisterBankInfo::ValueMapping &ValMapping,
bool &OnlyAssign) const;
/// Insert repairing code for \p Reg as specified by \p ValMapping.
/// The repairing placement is specified by \p RepairPt.
/// \p NewVRegs contains all the registers required to remap \p Reg.
/// In other words, the number of registers in NewVRegs must be equal
/// to ValMapping.BreakDown.size().
///
/// The transformation could be sketched as:
/// \code
/// ... = op Reg
/// \endcode
/// Becomes
/// \code
/// <NewRegs> = COPY or extract Reg
/// ... = op Reg
/// \endcode
///
/// and
/// \code
/// Reg = op ...
/// \endcode
/// Becomes
/// \code
/// Reg = op ...
/// Reg = COPY or build_sequence <NewRegs>
/// \endcode
///
/// \pre NewVRegs.size() == ValMapping.BreakDown.size()
///
/// \note The caller is supposed to do the rewriting of op if need be.
/// I.e., Reg = op ... => <NewRegs> = NewOp ...
///
/// \return True if the repairing worked, false otherwise.
bool repairReg(MachineOperand &MO,
const RegisterBankInfo::ValueMapping &ValMapping,
RegBankSelect::RepairingPlacement &RepairPt,
const iterator_range<SmallVectorImpl<Register>::const_iterator>
&NewVRegs);
/// Return the cost of the instruction needed to map \p MO to \p ValMapping.
/// The cost is free of basic block frequencies.
/// \pre MO.isReg()
/// \pre MO is assigned to a register bank.
/// \pre ValMapping is a valid mapping for MO.
uint64_t
getRepairCost(const MachineOperand &MO,
const RegisterBankInfo::ValueMapping &ValMapping) const;
/// Find the best mapping for \p MI from \p PossibleMappings.
/// \return a reference on the best mapping in \p PossibleMappings.
const RegisterBankInfo::InstructionMapping &
findBestMapping(MachineInstr &MI,
RegisterBankInfo::InstructionMappings &PossibleMappings,
SmallVectorImpl<RepairingPlacement> &RepairPts);
/// Compute the cost of mapping \p MI with \p InstrMapping and
/// compute the repairing placement for such mapping in \p
/// RepairPts.
/// \p BestCost is used to specify when the cost becomes too high
/// and thus it is not worth computing the RepairPts. Moreover if
/// \p BestCost == nullptr, the mapping cost is actually not
/// computed.
MappingCost
computeMapping(MachineInstr &MI,
const RegisterBankInfo::InstructionMapping &InstrMapping,
SmallVectorImpl<RepairingPlacement> &RepairPts,
const MappingCost *BestCost = nullptr);
/// When \p RepairPt involves splitting to repair \p MO for the
/// given \p ValMapping, try to change the way we repair such that
/// the splitting is not required anymore.
///
/// \pre \p RepairPt.hasSplit()
/// \pre \p MO == MO.getParent()->getOperand(\p RepairPt.getOpIdx())
/// \pre \p ValMapping is the mapping of \p MO for MO.getParent()
/// that implied \p RepairPt.
void tryAvoidingSplit(RegBankSelect::RepairingPlacement &RepairPt,
const MachineOperand &MO,
const RegisterBankInfo::ValueMapping &ValMapping) const;
/// Apply \p Mapping to \p MI. \p RepairPts represents the different
/// mapping action that need to happen for the mapping to be
/// applied.
/// \return True if the mapping was applied successfully, false otherwise.
bool applyMapping(MachineInstr &MI,
const RegisterBankInfo::InstructionMapping &InstrMapping,
SmallVectorImpl<RepairingPlacement> &RepairPts);
public:
/// Create a RegBankSelect pass with the specified \p RunningMode.
RegBankSelect(Mode RunningMode = Fast);
StringRef getPassName() const override { return "RegBankSelect"; }
void getAnalysisUsage(AnalysisUsage &AU) const override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties()
.set(MachineFunctionProperties::Property::IsSSA)
.set(MachineFunctionProperties::Property::Legalized);
}
MachineFunctionProperties getSetProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::RegBankSelected);
}
MachineFunctionProperties getClearedProperties() const override {
return MachineFunctionProperties()
.set(MachineFunctionProperties::Property::NoPHIs);
}
/// Walk through \p MF and assign a register bank to every virtual register
/// that are still mapped to nothing.
/// The target needs to provide a RegisterBankInfo and in particular
/// override RegisterBankInfo::getInstrMapping.
///
/// Simplified algo:
/// \code
/// RBI = MF.subtarget.getRegBankInfo()
/// MIRBuilder.setMF(MF)
/// for each bb in MF
/// for each inst in bb
/// MIRBuilder.setInstr(inst)
/// MappingCosts = RBI.getMapping(inst);
/// Idx = findIdxOfMinCost(MappingCosts)
/// CurRegBank = MappingCosts[Idx].RegBank
/// MRI.setRegBank(inst.getOperand(0).getReg(), CurRegBank)
/// for each argument in inst
/// if (CurRegBank != argument.RegBank)
/// ArgReg = argument.getReg()
/// Tmp = MRI.createNewVirtual(MRI.getSize(ArgReg), CurRegBank)
/// MIRBuilder.buildInstr(COPY, Tmp, ArgReg)
/// inst.getOperand(argument.getOperandNo()).setReg(Tmp)
/// \endcode
bool runOnMachineFunction(MachineFunction &MF) override;
};
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_REGBANKSELECT_H

View File

@@ -0,0 +1,98 @@
//==-- llvm/CodeGen/GlobalISel/RegisterBank.h - Register Bank ----*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file This file declares the API of register banks.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_REGISTERBANK_H
#define LLVM_CODEGEN_GLOBALISEL_REGISTERBANK_H
#include "llvm/ADT/BitVector.h"
namespace llvm {
// Forward declarations.
class RegisterBankInfo;
class raw_ostream;
class TargetRegisterClass;
class TargetRegisterInfo;
/// This class implements the register bank concept.
/// Two instances of RegisterBank must have different ID.
/// This property is enforced by the RegisterBankInfo class.
class RegisterBank {
private:
unsigned ID;
const char *Name;
unsigned Size;
BitVector ContainedRegClasses;
/// Sentinel value used to recognize register bank not properly
/// initialized yet.
static const unsigned InvalidID;
/// Only the RegisterBankInfo can initialize RegisterBank properly.
friend RegisterBankInfo;
public:
RegisterBank(unsigned ID, const char *Name, unsigned Size,
const uint32_t *CoveredClasses, unsigned NumRegClasses);
/// Get the identifier of this register bank.
unsigned getID() const { return ID; }
/// Get a user friendly name of this register bank.
/// Should be used only for debugging purposes.
const char *getName() const { return Name; }
/// Get the maximal size in bits that fits in this register bank.
unsigned getSize() const { return Size; }
/// Check whether this instance is ready to be used.
bool isValid() const;
/// Check if this register bank is valid. In other words,
/// if it has been properly constructed.
///
/// \note This method does not check anything when assertions are disabled.
///
/// \return True is the check was successful.
bool verify(const TargetRegisterInfo &TRI) const;
/// Check whether this register bank covers \p RC.
/// In other words, check if this register bank fully covers
/// the registers that \p RC contains.
/// \pre isValid()
bool covers(const TargetRegisterClass &RC) const;
/// Check whether \p OtherRB is the same as this.
bool operator==(const RegisterBank &OtherRB) const;
bool operator!=(const RegisterBank &OtherRB) const {
return !this->operator==(OtherRB);
}
/// Dump the register mask on dbgs() stream.
/// The dump is verbose.
void dump(const TargetRegisterInfo *TRI = nullptr) const;
/// Print the register mask on OS.
/// If IsForDebug is false, then only the name of the register bank
/// is printed. Otherwise, all the fields are printing.
/// TRI is then used to print the name of the register classes that
/// this register bank covers.
void print(raw_ostream &OS, bool IsForDebug = false,
const TargetRegisterInfo *TRI = nullptr) const;
};
inline raw_ostream &operator<<(raw_ostream &OS, const RegisterBank &RegBank) {
RegBank.print(OS);
return OS;
}
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,775 @@
//===- llvm/CodeGen/GlobalISel/RegisterBankInfo.h ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file This file declares the API for the register bank info.
/// This API is responsible for handling the register banks.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_REGISTERBANKINFO_H
#define LLVM_CODEGEN_GLOBALISEL_REGISTERBANKINFO_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include <cassert>
#include <initializer_list>
#include <memory>
namespace llvm {
class MachineInstr;
class MachineRegisterInfo;
class raw_ostream;
class RegisterBank;
class TargetInstrInfo;
class TargetRegisterClass;
class TargetRegisterInfo;
/// Holds all the information related to register banks.
class RegisterBankInfo {
public:
/// Helper struct that represents how a value is partially mapped
/// into a register.
/// The StartIdx and Length represent what region of the original
/// value this partial mapping covers.
/// This can be represented as a Mask of contiguous bit starting
/// at StartIdx bit and spanning Length bits.
/// StartIdx is the number of bits from the less significant bits.
struct PartialMapping {
/// Number of bits at which this partial mapping starts in the
/// original value. The bits are counted from less significant
/// bits to most significant bits.
unsigned StartIdx;
/// Length of this mapping in bits. This is how many bits this
/// partial mapping covers in the original value:
/// from StartIdx to StartIdx + Length -1.
unsigned Length;
/// Register bank where the partial value lives.
const RegisterBank *RegBank;
PartialMapping() = default;
/// Provide a shortcut for quickly building PartialMapping.
PartialMapping(unsigned StartIdx, unsigned Length,
const RegisterBank &RegBank)
: StartIdx(StartIdx), Length(Length), RegBank(&RegBank) {}
/// \return the index of in the original value of the most
/// significant bit that this partial mapping covers.
unsigned getHighBitIdx() const { return StartIdx + Length - 1; }
/// Print this partial mapping on dbgs() stream.
void dump() const;
/// Print this partial mapping on \p OS;
void print(raw_ostream &OS) const;
/// Check that the Mask is compatible with the RegBank.
/// Indeed, if the RegBank cannot accommodate the "active bits" of the mask,
/// there is no way this mapping is valid.
///
/// \note This method does not check anything when assertions are disabled.
///
/// \return True is the check was successful.
bool verify() const;
};
/// Helper struct that represents how a value is mapped through
/// different register banks.
///
/// \note: So far we do not have any users of the complex mappings
/// (mappings with more than one partial mapping), but when we do,
/// we would have needed to duplicate partial mappings.
/// The alternative could be to use an array of pointers of partial
/// mapping (i.e., PartialMapping **BreakDown) and duplicate the
/// pointers instead.
///
/// E.g.,
/// Let say we have a 32-bit add and a <2 x 32-bit> vadd. We
/// can expand the
/// <2 x 32-bit> add into 2 x 32-bit add.
///
/// Currently the TableGen-like file would look like:
/// \code
/// PartialMapping[] = {
/// /*32-bit add*/ {0, 32, GPR}, // Scalar entry repeated for first
/// // vec elt.
/// /*2x32-bit add*/ {0, 32, GPR}, {32, 32, GPR},
/// /*<2x32-bit> vadd*/ {0, 64, VPR}
/// }; // PartialMapping duplicated.
///
/// ValueMapping[] {
/// /*plain 32-bit add*/ {&PartialMapping[0], 1},
/// /*expanded vadd on 2xadd*/ {&PartialMapping[1], 2},
/// /*plain <2x32-bit> vadd*/ {&PartialMapping[3], 1}
/// };
/// \endcode
///
/// With the array of pointer, we would have:
/// \code
/// PartialMapping[] = {
/// /*32-bit add lower */ { 0, 32, GPR},
/// /*32-bit add upper */ {32, 32, GPR},
/// /*<2x32-bit> vadd */ { 0, 64, VPR}
/// }; // No more duplication.
///
/// BreakDowns[] = {
/// /*AddBreakDown*/ &PartialMapping[0],
/// /*2xAddBreakDown*/ &PartialMapping[0], &PartialMapping[1],
/// /*VAddBreakDown*/ &PartialMapping[2]
/// }; // Addresses of PartialMapping duplicated (smaller).
///
/// ValueMapping[] {
/// /*plain 32-bit add*/ {&BreakDowns[0], 1},
/// /*expanded vadd on 2xadd*/ {&BreakDowns[1], 2},
/// /*plain <2x32-bit> vadd*/ {&BreakDowns[3], 1}
/// };
/// \endcode
///
/// Given that a PartialMapping is actually small, the code size
/// impact is actually a degradation. Moreover the compile time will
/// be hit by the additional indirection.
/// If PartialMapping gets bigger we may reconsider.
struct ValueMapping {
/// How the value is broken down between the different register banks.
const PartialMapping *BreakDown;
/// Number of partial mapping to break down this value.
unsigned NumBreakDowns;
/// The default constructor creates an invalid (isValid() == false)
/// instance.
ValueMapping() : ValueMapping(nullptr, 0) {}
/// Initialize a ValueMapping with the given parameter.
/// \p BreakDown needs to have a life time at least as long
/// as this instance.
ValueMapping(const PartialMapping *BreakDown, unsigned NumBreakDowns)
: BreakDown(BreakDown), NumBreakDowns(NumBreakDowns) {}
/// Iterators through the PartialMappings.
const PartialMapping *begin() const { return BreakDown; }
const PartialMapping *end() const { return BreakDown + NumBreakDowns; }
/// \return true if all partial mappings are the same size and register
/// bank.
bool partsAllUniform() const;
/// Check if this ValueMapping is valid.
bool isValid() const { return BreakDown && NumBreakDowns; }
/// Verify that this mapping makes sense for a value of
/// \p MeaningfulBitWidth.
/// \note This method does not check anything when assertions are disabled.
///
/// \return True is the check was successful.
bool verify(unsigned MeaningfulBitWidth) const;
/// Print this on dbgs() stream.
void dump() const;
/// Print this on \p OS;
void print(raw_ostream &OS) const;
};
/// Helper class that represents how the value of an instruction may be
/// mapped and what is the related cost of such mapping.
class InstructionMapping {
/// Identifier of the mapping.
/// This is used to communicate between the target and the optimizers
/// which mapping should be realized.
unsigned ID = InvalidMappingID;
/// Cost of this mapping.
unsigned Cost = 0;
/// Mapping of all the operands.
const ValueMapping *OperandsMapping = nullptr;
/// Number of operands.
unsigned NumOperands = 0;
const ValueMapping &getOperandMapping(unsigned i) {
assert(i < getNumOperands() && "Out of bound operand");
return OperandsMapping[i];
}
public:
/// Constructor for the mapping of an instruction.
/// \p NumOperands must be equal to number of all the operands of
/// the related instruction.
/// The rationale is that it is more efficient for the optimizers
/// to be able to assume that the mapping of the ith operand is
/// at the index i.
InstructionMapping(unsigned ID, unsigned Cost,
const ValueMapping *OperandsMapping,
unsigned NumOperands)
: ID(ID), Cost(Cost), OperandsMapping(OperandsMapping),
NumOperands(NumOperands) {
}
/// Default constructor.
/// Use this constructor to express that the mapping is invalid.
InstructionMapping() = default;
/// Get the cost.
unsigned getCost() const { return Cost; }
/// Get the ID.
unsigned getID() const { return ID; }
/// Get the number of operands.
unsigned getNumOperands() const { return NumOperands; }
/// Get the value mapping of the ith operand.
/// \pre The mapping for the ith operand has been set.
/// \pre The ith operand is a register.
const ValueMapping &getOperandMapping(unsigned i) const {
const ValueMapping &ValMapping =
const_cast<InstructionMapping *>(this)->getOperandMapping(i);
return ValMapping;
}
/// Set the mapping for all the operands.
/// In other words, OpdsMapping should hold at least getNumOperands
/// ValueMapping.
void setOperandsMapping(const ValueMapping *OpdsMapping) {
OperandsMapping = OpdsMapping;
}
/// Check whether this object is valid.
/// This is a lightweight check for obvious wrong instance.
bool isValid() const {
return getID() != InvalidMappingID && OperandsMapping;
}
/// Verify that this mapping makes sense for \p MI.
/// \pre \p MI must be connected to a MachineFunction.
///
/// \note This method does not check anything when assertions are disabled.
///
/// \return True is the check was successful.
bool verify(const MachineInstr &MI) const;
/// Print this on dbgs() stream.
void dump() const;
/// Print this on \p OS;
void print(raw_ostream &OS) const;
};
/// Convenient type to represent the alternatives for mapping an
/// instruction.
/// \todo When we move to TableGen this should be an array ref.
using InstructionMappings = SmallVector<const InstructionMapping *, 4>;
/// Helper class used to get/create the virtual registers that will be used
/// to replace the MachineOperand when applying a mapping.
class OperandsMapper {
/// The OpIdx-th cell contains the index in NewVRegs where the VRegs of the
/// OpIdx-th operand starts. -1 means we do not have such mapping yet.
/// Note: We use a SmallVector to avoid heap allocation for most cases.
SmallVector<int, 8> OpToNewVRegIdx;
/// Hold the registers that will be used to map MI with InstrMapping.
SmallVector<Register, 8> NewVRegs;
/// Current MachineRegisterInfo, used to create new virtual registers.
MachineRegisterInfo &MRI;
/// Instruction being remapped.
MachineInstr &MI;
/// New mapping of the instruction.
const InstructionMapping &InstrMapping;
/// Constant value identifying that the index in OpToNewVRegIdx
/// for an operand has not been set yet.
static const int DontKnowIdx;
/// Get the range in NewVRegs to store all the partial
/// values for the \p OpIdx-th operand.
///
/// \return The iterator range for the space created.
//
/// \pre getMI().getOperand(OpIdx).isReg()
iterator_range<SmallVectorImpl<Register>::iterator>
getVRegsMem(unsigned OpIdx);
/// Get the end iterator for a range starting at \p StartIdx and
/// spannig \p NumVal in NewVRegs.
/// \pre StartIdx + NumVal <= NewVRegs.size()
SmallVectorImpl<Register>::const_iterator
getNewVRegsEnd(unsigned StartIdx, unsigned NumVal) const;
SmallVectorImpl<Register>::iterator getNewVRegsEnd(unsigned StartIdx,
unsigned NumVal);
public:
/// Create an OperandsMapper that will hold the information to apply \p
/// InstrMapping to \p MI.
/// \pre InstrMapping.verify(MI)
OperandsMapper(MachineInstr &MI, const InstructionMapping &InstrMapping,
MachineRegisterInfo &MRI);
/// \name Getters.
/// @{
/// The MachineInstr being remapped.
MachineInstr &getMI() const { return MI; }
/// The final mapping of the instruction.
const InstructionMapping &getInstrMapping() const { return InstrMapping; }
/// The MachineRegisterInfo we used to realize the mapping.
MachineRegisterInfo &getMRI() const { return MRI; }
/// @}
/// Create as many new virtual registers as needed for the mapping of the \p
/// OpIdx-th operand.
/// The number of registers is determined by the number of breakdown for the
/// related operand in the instruction mapping.
/// The type of the new registers is a plain scalar of the right size.
/// The proper type is expected to be set when the mapping is applied to
/// the instruction(s) that realizes the mapping.
///
/// \pre getMI().getOperand(OpIdx).isReg()
///
/// \post All the partial mapping of the \p OpIdx-th operand have been
/// assigned a new virtual register.
void createVRegs(unsigned OpIdx);
/// Set the virtual register of the \p PartialMapIdx-th partial mapping of
/// the OpIdx-th operand to \p NewVReg.
///
/// \pre getMI().getOperand(OpIdx).isReg()
/// \pre getInstrMapping().getOperandMapping(OpIdx).BreakDown.size() >
/// PartialMapIdx
/// \pre NewReg != 0
///
/// \post the \p PartialMapIdx-th register of the value mapping of the \p
/// OpIdx-th operand has been set.
void setVRegs(unsigned OpIdx, unsigned PartialMapIdx, Register NewVReg);
/// Get all the virtual registers required to map the \p OpIdx-th operand of
/// the instruction.
///
/// This return an empty range when createVRegs or setVRegs has not been
/// called.
/// The iterator may be invalidated by a call to setVRegs or createVRegs.
///
/// When \p ForDebug is true, we will not check that the list of new virtual
/// registers does not contain uninitialized values.
///
/// \pre getMI().getOperand(OpIdx).isReg()
/// \pre ForDebug || All partial mappings have been set a register
iterator_range<SmallVectorImpl<Register>::const_iterator>
getVRegs(unsigned OpIdx, bool ForDebug = false) const;
/// Print this operands mapper on dbgs() stream.
void dump() const;
/// Print this operands mapper on \p OS stream.
void print(raw_ostream &OS, bool ForDebug = false) const;
};
protected:
/// Hold the set of supported register banks.
RegisterBank **RegBanks;
/// Total number of register banks.
unsigned NumRegBanks;
/// Keep dynamically allocated PartialMapping in a separate map.
/// This shouldn't be needed when everything gets TableGen'ed.
mutable DenseMap<unsigned, std::unique_ptr<const PartialMapping>>
MapOfPartialMappings;
/// Keep dynamically allocated ValueMapping in a separate map.
/// This shouldn't be needed when everything gets TableGen'ed.
mutable DenseMap<unsigned, std::unique_ptr<const ValueMapping>>
MapOfValueMappings;
/// Keep dynamically allocated array of ValueMapping in a separate map.
/// This shouldn't be needed when everything gets TableGen'ed.
mutable DenseMap<unsigned, std::unique_ptr<ValueMapping[]>>
MapOfOperandsMappings;
/// Keep dynamically allocated InstructionMapping in a separate map.
/// This shouldn't be needed when everything gets TableGen'ed.
mutable DenseMap<unsigned, std::unique_ptr<const InstructionMapping>>
MapOfInstructionMappings;
/// Getting the minimal register class of a physreg is expensive.
/// Cache this information as we get it.
mutable DenseMap<unsigned, const TargetRegisterClass *> PhysRegMinimalRCs;
/// Create a RegisterBankInfo that can accommodate up to \p NumRegBanks
/// RegisterBank instances.
RegisterBankInfo(RegisterBank **RegBanks, unsigned NumRegBanks);
/// This constructor is meaningless.
/// It just provides a default constructor that can be used at link time
/// when GlobalISel is not built.
/// That way, targets can still inherit from this class without doing
/// crazy gymnastic to avoid link time failures.
/// \note That works because the constructor is inlined.
RegisterBankInfo() {
llvm_unreachable("This constructor should not be executed");
}
/// Get the register bank identified by \p ID.
RegisterBank &getRegBank(unsigned ID) {
assert(ID < getNumRegBanks() && "Accessing an unknown register bank");
return *RegBanks[ID];
}
/// Get the MinimalPhysRegClass for Reg.
/// \pre Reg is a physical register.
const TargetRegisterClass &
getMinimalPhysRegClass(Register Reg, const TargetRegisterInfo &TRI) const;
/// Try to get the mapping of \p MI.
/// See getInstrMapping for more details on what a mapping represents.
///
/// Unlike getInstrMapping the returned InstructionMapping may be invalid
/// (isValid() == false).
/// This means that the target independent code is not smart enough
/// to get the mapping of \p MI and thus, the target has to provide the
/// information for \p MI.
///
/// This implementation is able to get the mapping of:
/// - Target specific instructions by looking at the encoding constraints.
/// - Any instruction if all the register operands have already been assigned
/// a register, a register class, or a register bank.
/// - Copies and phis if at least one of the operands has been assigned a
/// register, a register class, or a register bank.
/// In other words, this method will likely fail to find a mapping for
/// any generic opcode that has not been lowered by target specific code.
const InstructionMapping &getInstrMappingImpl(const MachineInstr &MI) const;
/// Get the uniquely generated PartialMapping for the
/// given arguments.
const PartialMapping &getPartialMapping(unsigned StartIdx, unsigned Length,
const RegisterBank &RegBank) const;
/// \name Methods to get a uniquely generated ValueMapping.
/// @{
/// The most common ValueMapping consists of a single PartialMapping.
/// Feature a method for that.
const ValueMapping &getValueMapping(unsigned StartIdx, unsigned Length,
const RegisterBank &RegBank) const;
/// Get the ValueMapping for the given arguments.
const ValueMapping &getValueMapping(const PartialMapping *BreakDown,
unsigned NumBreakDowns) const;
/// @}
/// \name Methods to get a uniquely generated array of ValueMapping.
/// @{
/// Get the uniquely generated array of ValueMapping for the
/// elements of between \p Begin and \p End.
///
/// Elements that are nullptr will be replaced by
/// invalid ValueMapping (ValueMapping::isValid == false).
///
/// \pre The pointers on ValueMapping between \p Begin and \p End
/// must uniquely identify a ValueMapping. Otherwise, there is no
/// guarantee that the return instance will be unique, i.e., another
/// OperandsMapping could have the same content.
template <typename Iterator>
const ValueMapping *getOperandsMapping(Iterator Begin, Iterator End) const;
/// Get the uniquely generated array of ValueMapping for the
/// elements of \p OpdsMapping.
///
/// Elements of \p OpdsMapping that are nullptr will be replaced by
/// invalid ValueMapping (ValueMapping::isValid == false).
const ValueMapping *getOperandsMapping(
const SmallVectorImpl<const ValueMapping *> &OpdsMapping) const;
/// Get the uniquely generated array of ValueMapping for the
/// given arguments.
///
/// Arguments that are nullptr will be replaced by invalid
/// ValueMapping (ValueMapping::isValid == false).
const ValueMapping *getOperandsMapping(
std::initializer_list<const ValueMapping *> OpdsMapping) const;
/// @}
/// \name Methods to get a uniquely generated InstructionMapping.
/// @{
private:
/// Method to get a uniquely generated InstructionMapping.
const InstructionMapping &
getInstructionMappingImpl(bool IsInvalid, unsigned ID = InvalidMappingID,
unsigned Cost = 0,
const ValueMapping *OperandsMapping = nullptr,
unsigned NumOperands = 0) const;
public:
/// Method to get a uniquely generated InstructionMapping.
const InstructionMapping &
getInstructionMapping(unsigned ID, unsigned Cost,
const ValueMapping *OperandsMapping,
unsigned NumOperands) const {
return getInstructionMappingImpl(/*IsInvalid*/ false, ID, Cost,
OperandsMapping, NumOperands);
}
/// Method to get a uniquely generated invalid InstructionMapping.
const InstructionMapping &getInvalidInstructionMapping() const {
return getInstructionMappingImpl(/*IsInvalid*/ true);
}
/// @}
/// Get the register bank for the \p OpIdx-th operand of \p MI form
/// the encoding constraints, if any.
///
/// \return A register bank that covers the register class of the
/// related encoding constraints or nullptr if \p MI did not provide
/// enough information to deduce it.
const RegisterBank *
getRegBankFromConstraints(const MachineInstr &MI, unsigned OpIdx,
const TargetInstrInfo &TII,
const MachineRegisterInfo &MRI) const;
/// Helper method to apply something that is like the default mapping.
/// Basically, that means that \p OpdMapper.getMI() is left untouched
/// aside from the reassignment of the register operand that have been
/// remapped.
///
/// The type of all the new registers that have been created by the
/// mapper are properly remapped to the type of the original registers
/// they replace. In other words, the semantic of the instruction does
/// not change, only the register banks.
///
/// If the mapping of one of the operand spans several registers, this
/// method will abort as this is not like a default mapping anymore.
///
/// \pre For OpIdx in {0..\p OpdMapper.getMI().getNumOperands())
/// the range OpdMapper.getVRegs(OpIdx) is empty or of size 1.
static void applyDefaultMapping(const OperandsMapper &OpdMapper);
/// See ::applyMapping.
virtual void applyMappingImpl(const OperandsMapper &OpdMapper) const {
llvm_unreachable("The target has to implement that part");
}
public:
virtual ~RegisterBankInfo() = default;
/// Get the register bank identified by \p ID.
const RegisterBank &getRegBank(unsigned ID) const {
return const_cast<RegisterBankInfo *>(this)->getRegBank(ID);
}
/// Get the register bank of \p Reg.
/// If Reg has not been assigned a register, a register class,
/// or a register bank, then this returns nullptr.
///
/// \pre Reg != 0 (NoRegister)
const RegisterBank *getRegBank(Register Reg, const MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI) const;
/// Get the total number of register banks.
unsigned getNumRegBanks() const { return NumRegBanks; }
/// Get a register bank that covers \p RC.
///
/// \pre \p RC is a user-defined register class (as opposed as one
/// generated by TableGen).
///
/// \note The mapping RC -> RegBank could be built while adding the
/// coverage for the register banks. However, we do not do it, because,
/// at least for now, we only need this information for register classes
/// that are used in the description of instruction. In other words,
/// there are just a handful of them and we do not want to waste space.
///
/// \todo This should be TableGen'ed.
virtual const RegisterBank &
getRegBankFromRegClass(const TargetRegisterClass &RC, LLT Ty) const {
llvm_unreachable("The target must override this method");
}
/// Get the cost of a copy from \p B to \p A, or put differently,
/// get the cost of A = COPY B. Since register banks may cover
/// different size, \p Size specifies what will be the size in bits
/// that will be copied around.
///
/// \note Since this is a copy, both registers have the same size.
virtual unsigned copyCost(const RegisterBank &A, const RegisterBank &B,
unsigned Size) const {
// Optimistically assume that copies are coalesced. I.e., when
// they are on the same bank, they are free.
// Otherwise assume a non-zero cost of 1. The targets are supposed
// to override that properly anyway if they care.
return &A != &B;
}
/// \returns true if emitting a copy from \p Src to \p Dst is impossible.
bool cannotCopy(const RegisterBank &Dst, const RegisterBank &Src,
unsigned Size) const {
return copyCost(Dst, Src, Size) == std::numeric_limits<unsigned>::max();
}
/// Get the cost of using \p ValMapping to decompose a register. This is
/// similar to ::copyCost, except for cases where multiple copy-like
/// operations need to be inserted. If the register is used as a source
/// operand and already has a bank assigned, \p CurBank is non-null.
virtual unsigned getBreakDownCost(const ValueMapping &ValMapping,
const RegisterBank *CurBank = nullptr) const {
return std::numeric_limits<unsigned>::max();
}
/// Constrain the (possibly generic) virtual register \p Reg to \p RC.
///
/// \pre \p Reg is a virtual register that either has a bank or a class.
/// \returns The constrained register class, or nullptr if there is none.
/// \note This is a generic variant of MachineRegisterInfo::constrainRegClass
/// \note Use MachineRegisterInfo::constrainRegAttrs instead for any non-isel
/// purpose, including non-select passes of GlobalISel
static const TargetRegisterClass *
constrainGenericRegister(Register Reg, const TargetRegisterClass &RC,
MachineRegisterInfo &MRI);
/// Identifier used when the related instruction mapping instance
/// is generated by target independent code.
/// Make sure not to use that identifier to avoid possible collision.
static const unsigned DefaultMappingID;
/// Identifier used when the related instruction mapping instance
/// is generated by the default constructor.
/// Make sure not to use that identifier.
static const unsigned InvalidMappingID;
/// Get the mapping of the different operands of \p MI
/// on the register bank.
/// This mapping should be the direct translation of \p MI.
/// In other words, when \p MI is mapped with the returned mapping,
/// only the register banks of the operands of \p MI need to be updated.
/// In particular, neither the opcode nor the type of \p MI needs to be
/// updated for this direct mapping.
///
/// The target independent implementation gives a mapping based on
/// the register classes for the target specific opcode.
/// It uses the ID RegisterBankInfo::DefaultMappingID for that mapping.
/// Make sure you do not use that ID for the alternative mapping
/// for MI. See getInstrAlternativeMappings for the alternative
/// mappings.
///
/// For instance, if \p MI is a vector add, the mapping should
/// not be a scalarization of the add.
///
/// \post returnedVal.verify(MI).
///
/// \note If returnedVal does not verify MI, this would probably mean
/// that the target does not support that instruction.
virtual const InstructionMapping &
getInstrMapping(const MachineInstr &MI) const;
/// Get the alternative mappings for \p MI.
/// Alternative in the sense different from getInstrMapping.
virtual InstructionMappings
getInstrAlternativeMappings(const MachineInstr &MI) const;
/// Get the possible mapping for \p MI.
/// A mapping defines where the different operands may live and at what cost.
/// For instance, let us consider:
/// v0(16) = G_ADD <2 x i8> v1, v2
/// The possible mapping could be:
///
/// {/*ID*/VectorAdd, /*Cost*/1, /*v0*/{(0xFFFF, VPR)}, /*v1*/{(0xFFFF, VPR)},
/// /*v2*/{(0xFFFF, VPR)}}
/// {/*ID*/ScalarAddx2, /*Cost*/2, /*v0*/{(0x00FF, GPR),(0xFF00, GPR)},
/// /*v1*/{(0x00FF, GPR),(0xFF00, GPR)},
/// /*v2*/{(0x00FF, GPR),(0xFF00, GPR)}}
///
/// \note The first alternative of the returned mapping should be the
/// direct translation of \p MI current form.
///
/// \post !returnedVal.empty().
InstructionMappings getInstrPossibleMappings(const MachineInstr &MI) const;
/// Apply \p OpdMapper.getInstrMapping() to \p OpdMapper.getMI().
/// After this call \p OpdMapper.getMI() may not be valid anymore.
/// \p OpdMapper.getInstrMapping().getID() carries the information of
/// what has been chosen to map \p OpdMapper.getMI(). This ID is set
/// by the various getInstrXXXMapping method.
///
/// Therefore, getting the mapping and applying it should be kept in
/// sync.
void applyMapping(const OperandsMapper &OpdMapper) const {
// The only mapping we know how to handle is the default mapping.
if (OpdMapper.getInstrMapping().getID() == DefaultMappingID)
return applyDefaultMapping(OpdMapper);
// For other mapping, the target needs to do the right thing.
// If that means calling applyDefaultMapping, fine, but this
// must be explicitly stated.
applyMappingImpl(OpdMapper);
}
/// Get the size in bits of \p Reg.
/// Utility method to get the size of any registers. Unlike
/// MachineRegisterInfo::getSize, the register does not need to be a
/// virtual register.
///
/// \pre \p Reg != 0 (NoRegister).
unsigned getSizeInBits(Register Reg, const MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI) const;
/// Check that information hold by this instance make sense for the
/// given \p TRI.
///
/// \note This method does not check anything when assertions are disabled.
///
/// \return True is the check was successful.
bool verify(const TargetRegisterInfo &TRI) const;
};
inline raw_ostream &
operator<<(raw_ostream &OS,
const RegisterBankInfo::PartialMapping &PartMapping) {
PartMapping.print(OS);
return OS;
}
inline raw_ostream &
operator<<(raw_ostream &OS, const RegisterBankInfo::ValueMapping &ValMapping) {
ValMapping.print(OS);
return OS;
}
inline raw_ostream &
operator<<(raw_ostream &OS,
const RegisterBankInfo::InstructionMapping &InstrMapping) {
InstrMapping.print(OS);
return OS;
}
inline raw_ostream &
operator<<(raw_ostream &OS, const RegisterBankInfo::OperandsMapper &OpdMapper) {
OpdMapper.print(OS, /*ForDebug*/ false);
return OS;
}
/// Hashing function for PartialMapping.
/// It is required for the hashing of ValueMapping.
hash_code hash_value(const RegisterBankInfo::PartialMapping &PartMapping);
} // end namespace llvm
#endif // LLVM_CODEGEN_GLOBALISEL_REGISTERBANKINFO_H

View File

@@ -0,0 +1,474 @@
//==-- llvm/CodeGen/GlobalISel/Utils.h ---------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file This file declares the API of helper functions used throughout the
/// GlobalISel pipeline.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_UTILS_H
#define LLVM_CODEGEN_GLOBALISEL_UTILS_H
#include "GISelWorkList.h"
#include "LostDebugLocObserver.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include <cstdint>
namespace llvm {
class AnalysisUsage;
class BlockFrequencyInfo;
class GISelKnownBits;
class MachineFunction;
class MachineInstr;
class MachineOperand;
class MachineOptimizationRemarkEmitter;
class MachineOptimizationRemarkMissed;
struct MachinePointerInfo;
class MachineRegisterInfo;
class MCInstrDesc;
class ProfileSummaryInfo;
class RegisterBankInfo;
class TargetInstrInfo;
class TargetLowering;
class TargetPassConfig;
class TargetRegisterInfo;
class TargetRegisterClass;
class ConstantFP;
class APFloat;
class MachineIRBuilder;
// Convenience macros for dealing with vector reduction opcodes.
#define GISEL_VECREDUCE_CASES_ALL \
case TargetOpcode::G_VECREDUCE_SEQ_FADD: \
case TargetOpcode::G_VECREDUCE_SEQ_FMUL: \
case TargetOpcode::G_VECREDUCE_FADD: \
case TargetOpcode::G_VECREDUCE_FMUL: \
case TargetOpcode::G_VECREDUCE_FMAX: \
case TargetOpcode::G_VECREDUCE_FMIN: \
case TargetOpcode::G_VECREDUCE_ADD: \
case TargetOpcode::G_VECREDUCE_MUL: \
case TargetOpcode::G_VECREDUCE_AND: \
case TargetOpcode::G_VECREDUCE_OR: \
case TargetOpcode::G_VECREDUCE_XOR: \
case TargetOpcode::G_VECREDUCE_SMAX: \
case TargetOpcode::G_VECREDUCE_SMIN: \
case TargetOpcode::G_VECREDUCE_UMAX: \
case TargetOpcode::G_VECREDUCE_UMIN:
#define GISEL_VECREDUCE_CASES_NONSEQ \
case TargetOpcode::G_VECREDUCE_FADD: \
case TargetOpcode::G_VECREDUCE_FMUL: \
case TargetOpcode::G_VECREDUCE_FMAX: \
case TargetOpcode::G_VECREDUCE_FMIN: \
case TargetOpcode::G_VECREDUCE_ADD: \
case TargetOpcode::G_VECREDUCE_MUL: \
case TargetOpcode::G_VECREDUCE_AND: \
case TargetOpcode::G_VECREDUCE_OR: \
case TargetOpcode::G_VECREDUCE_XOR: \
case TargetOpcode::G_VECREDUCE_SMAX: \
case TargetOpcode::G_VECREDUCE_SMIN: \
case TargetOpcode::G_VECREDUCE_UMAX: \
case TargetOpcode::G_VECREDUCE_UMIN:
/// Try to constrain Reg to the specified register class. If this fails,
/// create a new virtual register in the correct class.
///
/// \return The virtual register constrained to the right register class.
Register constrainRegToClass(MachineRegisterInfo &MRI,
const TargetInstrInfo &TII,
const RegisterBankInfo &RBI, Register Reg,
const TargetRegisterClass &RegClass);
/// Constrain the Register operand OpIdx, so that it is now constrained to the
/// TargetRegisterClass passed as an argument (RegClass).
/// If this fails, create a new virtual register in the correct class and insert
/// a COPY before \p InsertPt if it is a use or after if it is a definition.
/// In both cases, the function also updates the register of RegMo. The debug
/// location of \p InsertPt is used for the new copy.
///
/// \return The virtual register constrained to the right register class.
Register constrainOperandRegClass(const MachineFunction &MF,
const TargetRegisterInfo &TRI,
MachineRegisterInfo &MRI,
const TargetInstrInfo &TII,
const RegisterBankInfo &RBI,
MachineInstr &InsertPt,
const TargetRegisterClass &RegClass,
MachineOperand &RegMO);
/// Try to constrain Reg so that it is usable by argument OpIdx of the provided
/// MCInstrDesc \p II. If this fails, create a new virtual register in the
/// correct class and insert a COPY before \p InsertPt if it is a use or after
/// if it is a definition. In both cases, the function also updates the register
/// of RegMo.
/// This is equivalent to constrainOperandRegClass(..., RegClass, ...)
/// with RegClass obtained from the MCInstrDesc. The debug location of \p
/// InsertPt is used for the new copy.
///
/// \return The virtual register constrained to the right register class.
Register constrainOperandRegClass(const MachineFunction &MF,
const TargetRegisterInfo &TRI,
MachineRegisterInfo &MRI,
const TargetInstrInfo &TII,
const RegisterBankInfo &RBI,
MachineInstr &InsertPt, const MCInstrDesc &II,
MachineOperand &RegMO, unsigned OpIdx);
/// Mutate the newly-selected instruction \p I to constrain its (possibly
/// generic) virtual register operands to the instruction's register class.
/// This could involve inserting COPYs before (for uses) or after (for defs).
/// This requires the number of operands to match the instruction description.
/// \returns whether operand regclass constraining succeeded.
///
// FIXME: Not all instructions have the same number of operands. We should
// probably expose a constrain helper per operand and let the target selector
// constrain individual registers, like fast-isel.
bool constrainSelectedInstRegOperands(MachineInstr &I,
const TargetInstrInfo &TII,
const TargetRegisterInfo &TRI,
const RegisterBankInfo &RBI);
/// Check if DstReg can be replaced with SrcReg depending on the register
/// constraints.
bool canReplaceReg(Register DstReg, Register SrcReg, MachineRegisterInfo &MRI);
/// Check whether an instruction \p MI is dead: it only defines dead virtual
/// registers, and doesn't have other side effects.
bool isTriviallyDead(const MachineInstr &MI, const MachineRegisterInfo &MRI);
/// Report an ISel error as a missed optimization remark to the LLVMContext's
/// diagnostic stream. Set the FailedISel MachineFunction property.
void reportGISelFailure(MachineFunction &MF, const TargetPassConfig &TPC,
MachineOptimizationRemarkEmitter &MORE,
MachineOptimizationRemarkMissed &R);
void reportGISelFailure(MachineFunction &MF, const TargetPassConfig &TPC,
MachineOptimizationRemarkEmitter &MORE,
const char *PassName, StringRef Msg,
const MachineInstr &MI);
/// Report an ISel warning as a missed optimization remark to the LLVMContext's
/// diagnostic stream.
void reportGISelWarning(MachineFunction &MF, const TargetPassConfig &TPC,
MachineOptimizationRemarkEmitter &MORE,
MachineOptimizationRemarkMissed &R);
/// If \p VReg is defined by a G_CONSTANT, return the corresponding value.
Optional<APInt> getIConstantVRegVal(Register VReg,
const MachineRegisterInfo &MRI);
/// If \p VReg is defined by a G_CONSTANT fits in int64_t returns it.
Optional<int64_t> getIConstantVRegSExtVal(Register VReg,
const MachineRegisterInfo &MRI);
/// Simple struct used to hold a constant integer value and a virtual
/// register.
struct ValueAndVReg {
APInt Value;
Register VReg;
};
/// If \p VReg is defined by a statically evaluable chain of instructions rooted
/// on a G_CONSTANT returns its APInt value and def register.
Optional<ValueAndVReg>
getIConstantVRegValWithLookThrough(Register VReg,
const MachineRegisterInfo &MRI,
bool LookThroughInstrs = true);
/// If \p VReg is defined by a statically evaluable chain of instructions rooted
/// on a G_CONSTANT or G_FCONSTANT returns its value as APInt and def register.
Optional<ValueAndVReg> getAnyConstantVRegValWithLookThrough(
Register VReg, const MachineRegisterInfo &MRI,
bool LookThroughInstrs = true, bool LookThroughAnyExt = false);
struct FPValueAndVReg {
APFloat Value;
Register VReg;
};
/// If \p VReg is defined by a statically evaluable chain of instructions rooted
/// on a G_FCONSTANT returns its APFloat value and def register.
Optional<FPValueAndVReg>
getFConstantVRegValWithLookThrough(Register VReg,
const MachineRegisterInfo &MRI,
bool LookThroughInstrs = true);
const ConstantFP* getConstantFPVRegVal(Register VReg,
const MachineRegisterInfo &MRI);
/// See if Reg is defined by an single def instruction that is
/// Opcode. Also try to do trivial folding if it's a COPY with
/// same types. Returns null otherwise.
MachineInstr *getOpcodeDef(unsigned Opcode, Register Reg,
const MachineRegisterInfo &MRI);
/// Simple struct used to hold a Register value and the instruction which
/// defines it.
struct DefinitionAndSourceRegister {
MachineInstr *MI;
Register Reg;
};
/// Find the def instruction for \p Reg, and underlying value Register folding
/// away any copies.
///
/// Also walks through hints such as G_ASSERT_ZEXT.
Optional<DefinitionAndSourceRegister>
getDefSrcRegIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI);
/// Find the def instruction for \p Reg, folding away any trivial copies. May
/// return nullptr if \p Reg is not a generic virtual register.
///
/// Also walks through hints such as G_ASSERT_ZEXT.
MachineInstr *getDefIgnoringCopies(Register Reg,
const MachineRegisterInfo &MRI);
/// Find the source register for \p Reg, folding away any trivial copies. It
/// will be an output register of the instruction that getDefIgnoringCopies
/// returns. May return an invalid register if \p Reg is not a generic virtual
/// register.
///
/// Also walks through hints such as G_ASSERT_ZEXT.
Register getSrcRegIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI);
// Templated variant of getOpcodeDef returning a MachineInstr derived T.
/// See if Reg is defined by an single def instruction of type T
/// Also try to do trivial folding if it's a COPY with
/// same types. Returns null otherwise.
template <class T>
T *getOpcodeDef(Register Reg, const MachineRegisterInfo &MRI) {
MachineInstr *DefMI = getDefIgnoringCopies(Reg, MRI);
return dyn_cast_or_null<T>(DefMI);
}
/// Returns an APFloat from Val converted to the appropriate size.
APFloat getAPFloatFromSize(double Val, unsigned Size);
/// Modify analysis usage so it preserves passes required for the SelectionDAG
/// fallback.
void getSelectionDAGFallbackAnalysisUsage(AnalysisUsage &AU);
Optional<APInt> ConstantFoldBinOp(unsigned Opcode, const Register Op1,
const Register Op2,
const MachineRegisterInfo &MRI);
Optional<APFloat> ConstantFoldFPBinOp(unsigned Opcode, const Register Op1,
const Register Op2,
const MachineRegisterInfo &MRI);
/// Tries to constant fold a vector binop with sources \p Op1 and \p Op2.
/// If successful, returns the G_BUILD_VECTOR representing the folded vector
/// constant. \p MIB should have an insertion point already set to create new
/// G_CONSTANT instructions as needed.
Register ConstantFoldVectorBinop(unsigned Opcode, const Register Op1,
const Register Op2,
const MachineRegisterInfo &MRI,
MachineIRBuilder &MIB);
Optional<APInt> ConstantFoldExtOp(unsigned Opcode, const Register Op1,
uint64_t Imm, const MachineRegisterInfo &MRI);
Optional<APFloat> ConstantFoldIntToFloat(unsigned Opcode, LLT DstTy,
Register Src,
const MachineRegisterInfo &MRI);
/// Tries to constant fold a G_CTLZ operation on \p Src. If \p Src is a vector
/// then it tries to do an element-wise constant fold.
Optional<SmallVector<unsigned>>
ConstantFoldCTLZ(Register Src, const MachineRegisterInfo &MRI);
/// Test if the given value is known to have exactly one bit set. This differs
/// from computeKnownBits in that it doesn't necessarily determine which bit is
/// set.
bool isKnownToBeAPowerOfTwo(Register Val, const MachineRegisterInfo &MRI,
GISelKnownBits *KnownBits = nullptr);
/// Returns true if \p Val can be assumed to never be a NaN. If \p SNaN is true,
/// this returns if \p Val can be assumed to never be a signaling NaN.
bool isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
bool SNaN = false);
/// Returns true if \p Val can be assumed to never be a signaling NaN.
inline bool isKnownNeverSNaN(Register Val, const MachineRegisterInfo &MRI) {
return isKnownNeverNaN(Val, MRI, true);
}
Align inferAlignFromPtrInfo(MachineFunction &MF, const MachinePointerInfo &MPO);
/// Return a virtual register corresponding to the incoming argument register \p
/// PhysReg. This register is expected to have class \p RC, and optional type \p
/// RegTy. This assumes all references to the register will use the same type.
///
/// If there is an existing live-in argument register, it will be returned.
/// This will also ensure there is a valid copy
Register getFunctionLiveInPhysReg(MachineFunction &MF,
const TargetInstrInfo &TII,
MCRegister PhysReg,
const TargetRegisterClass &RC,
const DebugLoc &DL, LLT RegTy = LLT());
/// Return the least common multiple type of \p OrigTy and \p TargetTy, by changing the
/// number of vector elements or scalar bitwidth. The intent is a
/// G_MERGE_VALUES, G_BUILD_VECTOR, or G_CONCAT_VECTORS can be constructed from
/// \p OrigTy elements, and unmerged into \p TargetTy
LLVM_READNONE
LLT getLCMType(LLT OrigTy, LLT TargetTy);
LLVM_READNONE
/// Return smallest type that covers both \p OrigTy and \p TargetTy and is
/// multiple of TargetTy.
LLT getCoverTy(LLT OrigTy, LLT TargetTy);
/// Return a type where the total size is the greatest common divisor of \p
/// OrigTy and \p TargetTy. This will try to either change the number of vector
/// elements, or bitwidth of scalars. The intent is the result type can be used
/// as the result of a G_UNMERGE_VALUES from \p OrigTy, and then some
/// combination of G_MERGE_VALUES, G_BUILD_VECTOR and G_CONCAT_VECTORS (possibly
/// with intermediate casts) can re-form \p TargetTy.
///
/// If these are vectors with different element types, this will try to produce
/// a vector with a compatible total size, but the element type of \p OrigTy. If
/// this can't be satisfied, this will produce a scalar smaller than the
/// original vector elements.
///
/// In the worst case, this returns LLT::scalar(1)
LLVM_READNONE
LLT getGCDType(LLT OrigTy, LLT TargetTy);
/// Represents a value which can be a Register or a constant.
///
/// This is useful in situations where an instruction may have an interesting
/// register operand or interesting constant operand. For a concrete example,
/// \see getVectorSplat.
class RegOrConstant {
int64_t Cst;
Register Reg;
bool IsReg;
public:
explicit RegOrConstant(Register Reg) : Reg(Reg), IsReg(true) {}
explicit RegOrConstant(int64_t Cst) : Cst(Cst), IsReg(false) {}
bool isReg() const { return IsReg; }
bool isCst() const { return !IsReg; }
Register getReg() const {
assert(isReg() && "Expected a register!");
return Reg;
}
int64_t getCst() const {
assert(isCst() && "Expected a constant!");
return Cst;
}
};
/// \returns The splat index of a G_SHUFFLE_VECTOR \p MI when \p MI is a splat.
/// If \p MI is not a splat, returns None.
Optional<int> getSplatIndex(MachineInstr &MI);
/// Returns a scalar constant of a G_BUILD_VECTOR splat if it exists.
Optional<int64_t> getBuildVectorConstantSplat(const MachineInstr &MI,
const MachineRegisterInfo &MRI);
/// Returns a floating point scalar constant of a build vector splat if it
/// exists. When \p AllowUndef == true some elements can be undef but not all.
Optional<FPValueAndVReg> getFConstantSplat(Register VReg,
const MachineRegisterInfo &MRI,
bool AllowUndef = true);
/// Return true if the specified register is defined by G_BUILD_VECTOR or
/// G_BUILD_VECTOR_TRUNC where all of the elements are \p SplatValue or undef.
bool isBuildVectorConstantSplat(const Register Reg,
const MachineRegisterInfo &MRI,
int64_t SplatValue, bool AllowUndef);
/// Return true if the specified instruction is a G_BUILD_VECTOR or
/// G_BUILD_VECTOR_TRUNC where all of the elements are \p SplatValue or undef.
bool isBuildVectorConstantSplat(const MachineInstr &MI,
const MachineRegisterInfo &MRI,
int64_t SplatValue, bool AllowUndef);
/// Return true if the specified instruction is a G_BUILD_VECTOR or
/// G_BUILD_VECTOR_TRUNC where all of the elements are 0 or undef.
bool isBuildVectorAllZeros(const MachineInstr &MI,
const MachineRegisterInfo &MRI,
bool AllowUndef = false);
/// Return true if the specified instruction is a G_BUILD_VECTOR or
/// G_BUILD_VECTOR_TRUNC where all of the elements are ~0 or undef.
bool isBuildVectorAllOnes(const MachineInstr &MI,
const MachineRegisterInfo &MRI,
bool AllowUndef = false);
/// \returns a value when \p MI is a vector splat. The splat can be either a
/// Register or a constant.
///
/// Examples:
///
/// \code
/// %reg = COPY $physreg
/// %reg_splat = G_BUILD_VECTOR %reg, %reg, ..., %reg
/// \endcode
///
/// If called on the G_BUILD_VECTOR above, this will return a RegOrConstant
/// containing %reg.
///
/// \code
/// %cst = G_CONSTANT iN 4
/// %constant_splat = G_BUILD_VECTOR %cst, %cst, ..., %cst
/// \endcode
///
/// In the above case, this will return a RegOrConstant containing 4.
Optional<RegOrConstant> getVectorSplat(const MachineInstr &MI,
const MachineRegisterInfo &MRI);
/// Determines if \p MI defines a constant integer or a build vector of
/// constant integers. Treats undef values as constants.
bool isConstantOrConstantVector(MachineInstr &MI,
const MachineRegisterInfo &MRI);
/// Determines if \p MI defines a constant integer or a splat vector of
/// constant integers.
/// \returns the scalar constant or None.
Optional<APInt> isConstantOrConstantSplatVector(MachineInstr &MI,
const MachineRegisterInfo &MRI);
/// Attempt to match a unary predicate against a scalar/splat constant or every
/// element of a constant G_BUILD_VECTOR. If \p ConstVal is null, the source
/// value was undef.
bool matchUnaryPredicate(const MachineRegisterInfo &MRI, Register Reg,
std::function<bool(const Constant *ConstVal)> Match,
bool AllowUndefs = false);
/// Returns true if given the TargetLowering's boolean contents information,
/// the value \p Val contains a true value.
bool isConstTrueVal(const TargetLowering &TLI, int64_t Val, bool IsVector,
bool IsFP);
/// Returns an integer representing true, as defined by the
/// TargetBooleanContents.
int64_t getICmpTrueVal(const TargetLowering &TLI, bool IsVector, bool IsFP);
/// Returns true if the given block should be optimized for size.
bool shouldOptForSize(const MachineBasicBlock &MBB, ProfileSummaryInfo *PSI,
BlockFrequencyInfo *BFI);
using SmallInstListTy = GISelWorkList<4>;
void saveUsesAndErase(MachineInstr &MI, MachineRegisterInfo &MRI,
LostDebugLocObserver *LocObserver,
SmallInstListTy &DeadInstChain);
void eraseInstrs(ArrayRef<MachineInstr *> DeadInstrs, MachineRegisterInfo &MRI,
LostDebugLocObserver *LocObserver = nullptr);
void eraseInstr(MachineInstr &MI, MachineRegisterInfo &MRI,
LostDebugLocObserver *LocObserver = nullptr);
} // End namespace llvm.
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
//===---- IndirectThunks.h - Indirect Thunk Base Class ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Contains a base class for Passes that inject an MI thunk.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_INDIRECTTHUNKS_H
#define LLVM_CODEGEN_INDIRECTTHUNKS_H
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
namespace llvm {
template <typename Derived> class ThunkInserter {
Derived &getDerived() { return *static_cast<Derived *>(this); }
protected:
bool InsertedThunks;
void doInitialization(Module &M) {}
void createThunkFunction(MachineModuleInfo &MMI, StringRef Name,
bool Comdat = true);
public:
void init(Module &M) {
InsertedThunks = false;
getDerived().doInitialization(M);
}
// return `true` if `MMI` or `MF` was modified
bool run(MachineModuleInfo &MMI, MachineFunction &MF);
};
template <typename Derived>
void ThunkInserter<Derived>::createThunkFunction(MachineModuleInfo &MMI,
StringRef Name, bool Comdat) {
assert(Name.startswith(getDerived().getThunkPrefix()) &&
"Created a thunk with an unexpected prefix!");
Module &M = const_cast<Module &>(*MMI.getModule());
LLVMContext &Ctx = M.getContext();
auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);
Function *F = Function::Create(Type,
Comdat ? GlobalValue::LinkOnceODRLinkage
: GlobalValue::InternalLinkage,
Name, &M);
if (Comdat) {
F->setVisibility(GlobalValue::HiddenVisibility);
F->setComdat(M.getOrInsertComdat(Name));
}
// Add Attributes so that we don't create a frame, unwind information, or
// inline.
AttrBuilder B(Ctx);
B.addAttribute(llvm::Attribute::NoUnwind);
B.addAttribute(llvm::Attribute::Naked);
F->addFnAttrs(B);
// Populate our function a bit so that we can verify.
BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
IRBuilder<> Builder(Entry);
Builder.CreateRetVoid();
// MachineFunctions aren't created automatically for the IR-level constructs
// we already made. Create them and insert them into the module.
MachineFunction &MF = MMI.getOrCreateMachineFunction(*F);
// A MachineBasicBlock must not be created for the Entry block; code
// generation from an empty naked function in C source code also does not
// generate one. At least GlobalISel asserts if this invariant isn't
// respected.
// Set MF properties. We never use vregs...
MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs);
}
template <typename Derived>
bool ThunkInserter<Derived>::run(MachineModuleInfo &MMI, MachineFunction &MF) {
// If MF is not a thunk, check to see if we need to insert a thunk.
if (!MF.getName().startswith(getDerived().getThunkPrefix())) {
// If we've already inserted a thunk, nothing else to do.
if (InsertedThunks)
return false;
// Only add a thunk if one of the functions has the corresponding feature
// enabled in its subtarget, and doesn't enable external thunks.
// FIXME: Conditionalize on indirect calls so we don't emit a thunk when
// nothing will end up calling it.
// FIXME: It's a little silly to look at every function just to enumerate
// the subtargets, but eventually we'll want to look at them for indirect
// calls, so maybe this is OK.
if (!getDerived().mayUseThunk(MF))
return false;
getDerived().insertThunks(MMI);
InsertedThunks = true;
return true;
}
// If this *is* a thunk function, we need to populate it with the correct MI.
getDerived().populateThunk(MF);
return true;
}
} // namespace llvm
#endif

Some files were not shown because too many files have changed in this diff Show More