From 3ee60685571449ad8b52451e010ddbe2091dd758 Mon Sep 17 00:00:00 2001 From: Jason Eckhardt Date: Fri, 14 Oct 2022 17:50:49 -0500 Subject: [PATCH 001/114] [SOL] Initial squash commit for new SBF LLVM back-end and related. - New SBF back-end subdirectory and corresponding test directories. - Corresponding changes in ADT, Support, MC, Object, BinaryFormat for new SBF architecture and EM_SBF ELF object. - Corresponding changes in clang to instantiate SBF for Triple::sbf rather than the old BPF back-end, etc. - Corresponding changes in lld to add SBF arch recognition, etc. - Corresponding changes in llvm-readelf to and opt. --- clang/lib/Basic/Targets/BPF.cpp | 8 + lld/ELF/Arch/SBF.cpp | 140 ++ lld/ELF/CMakeLists.txt | 1 + lld/ELF/Target.cpp | 4 + lld/ELF/Target.h | 1 + llvm/CMakeLists.txt | 1 + llvm/include/llvm/BinaryFormat/ELF.h | 11 + .../llvm/BinaryFormat/ELFRelocs/SBF.def | 12 + llvm/include/llvm/Object/ELFObjectFile.h | 4 + llvm/include/llvm/TargetParser/Triple.h | 6 + llvm/include/module.modulemap | 1 + llvm/lib/Analysis/TargetLibraryInfo.cpp | 7 + llvm/lib/BinaryFormat/ELF.cpp | 3 + llvm/lib/Object/ELF.cpp | 9 + llvm/lib/Object/RelocationResolver.cpp | 24 + llvm/lib/ObjectYAML/ELFYAML.cpp | 4 + llvm/lib/Target/BPF/BPFSubtarget.cpp | 8 +- llvm/lib/Target/BPF/BPFTargetMachine.cpp | 15 +- .../lib/Target/BPF/TargetInfo/BPFTargetInfo.h | 1 - llvm/lib/Target/SBF/AsmParser/CMakeLists.txt | 13 + .../lib/Target/SBF/AsmParser/SBFAsmParser.cpp | 509 ++++++ llvm/lib/Target/SBF/BTF.def | 37 + llvm/lib/Target/SBF/BTF.h | 261 +++ llvm/lib/Target/SBF/BTFDebug.cpp | 1514 +++++++++++++++++ llvm/lib/Target/SBF/BTFDebug.h | 409 +++++ llvm/lib/Target/SBF/CMakeLists.txt | 60 + .../Target/SBF/Disassembler/CMakeLists.txt | 12 + .../SBF/Disassembler/SBFDisassembler.cpp | 219 +++ .../Target/SBF/MCTargetDesc/CMakeLists.txt | 15 + .../Target/SBF/MCTargetDesc/SBFAsmBackend.cpp | 120 ++ .../SBF/MCTargetDesc/SBFELFObjectWriter.cpp | 109 ++ .../SBF/MCTargetDesc/SBFInstPrinter.cpp | 108 ++ .../Target/SBF/MCTargetDesc/SBFInstPrinter.h | 41 + .../Target/SBF/MCTargetDesc/SBFMCAsmInfo.h | 58 + .../SBF/MCTargetDesc/SBFMCCodeEmitter.cpp | 180 ++ .../SBF/MCTargetDesc/SBFMCTargetDesc.cpp | 148 ++ .../Target/SBF/MCTargetDesc/SBFMCTargetDesc.h | 64 + llvm/lib/Target/SBF/SBF.h | 75 + llvm/lib/Target/SBF/SBF.td | 72 + .../Target/SBF/SBFAbstractMemberAccess.cpp | 1125 ++++++++++++ llvm/lib/Target/SBF/SBFAdjustOpt.cpp | 387 +++++ llvm/lib/Target/SBF/SBFAsmPrinter.cpp | 156 ++ llvm/lib/Target/SBF/SBFCORE.h | 76 + llvm/lib/Target/SBF/SBFCallingConv.td | 68 + llvm/lib/Target/SBF/SBFCheckAndAdjustIR.cpp | 173 ++ llvm/lib/Target/SBF/SBFFrameLowering.cpp | 70 + llvm/lib/Target/SBF/SBFFrameLowering.h | 40 + llvm/lib/Target/SBF/SBFIRPeephole.cpp | 118 ++ llvm/lib/Target/SBF/SBFISelDAGToDAG.cpp | 542 ++++++ llvm/lib/Target/SBF/SBFISelLowering.cpp | 1197 +++++++++++++ llvm/lib/Target/SBF/SBFISelLowering.h | 174 ++ llvm/lib/Target/SBF/SBFInstrFormats.td | 124 ++ llvm/lib/Target/SBF/SBFInstrInfo.cpp | 260 +++ llvm/lib/Target/SBF/SBFInstrInfo.h | 65 + llvm/lib/Target/SBF/SBFInstrInfo.td | 1008 +++++++++++ llvm/lib/Target/SBF/SBFMCInstLower.cpp | 81 + llvm/lib/Target/SBF/SBFMCInstLower.h | 41 + llvm/lib/Target/SBF/SBFMIChecking.cpp | 251 +++ llvm/lib/Target/SBF/SBFMIPeephole.cpp | 564 ++++++ .../lib/Target/SBF/SBFMISimplifyPatchable.cpp | 312 ++++ llvm/lib/Target/SBF/SBFPreserveDIType.cpp | 153 ++ llvm/lib/Target/SBF/SBFRegisterInfo.cpp | 150 ++ llvm/lib/Target/SBF/SBFRegisterInfo.h | 40 + llvm/lib/Target/SBF/SBFRegisterInfo.td | 51 + llvm/lib/Target/SBF/SBFSelectionDAGInfo.cpp | 42 + llvm/lib/Target/SBF/SBFSelectionDAGInfo.h | 44 + llvm/lib/Target/SBF/SBFSubtarget.cpp | 80 + llvm/lib/Target/SBF/SBFSubtarget.h | 110 ++ llvm/lib/Target/SBF/SBFTargetMachine.cpp | 184 ++ llvm/lib/Target/SBF/SBFTargetMachine.h | 48 + llvm/lib/Target/SBF/SBFTargetTransformInfo.h | 78 + llvm/lib/Target/SBF/TargetInfo/CMakeLists.txt | 10 + .../Target/SBF/TargetInfo/SBFTargetInfo.cpp | 22 + .../lib/Target/SBF/TargetInfo/SBFTargetInfo.h | 19 + llvm/lib/TargetParser/Triple.cpp | 1 + llvm/test/CodeGen/SBF/32-bit-subreg-alu.ll | 299 ++++ .../CodeGen/SBF/32-bit-subreg-cond-select.ll | 117 ++ .../CodeGen/SBF/32-bit-subreg-load-store.ll | 107 ++ .../SBF/32-bit-subreg-peephole-phi-1.ll | 34 + .../SBF/32-bit-subreg-peephole-phi-2.ll | 34 + .../SBF/32-bit-subreg-peephole-phi-3.ll | 53 + .../CodeGen/SBF/32-bit-subreg-peephole.ll | 126 ++ llvm/test/CodeGen/SBF/32-bit-subreg-zext.ll | 21 + llvm/test/CodeGen/SBF/TODO-NOTES | 12 + llvm/test/CodeGen/SBF/adjust-opt-icmp1.ll | 95 ++ llvm/test/CodeGen/SBF/adjust-opt-icmp2.ll | 98 ++ llvm/test/CodeGen/SBF/adjust-opt-icmp3.ll | 85 + llvm/test/CodeGen/SBF/adjust-opt-icmp4.ll | 85 + .../CodeGen/SBF/adjust-opt-speculative1.ll | 84 + .../CodeGen/SBF/adjust-opt-speculative2.ll | 93 + llvm/test/CodeGen/SBF/alu8.ll | 45 + llvm/test/CodeGen/SBF/atomics_sbf.ll | 276 +++ llvm/test/CodeGen/SBF/basictest.ll | 28 + llvm/test/CodeGen/SBF/byval.ll | 31 + llvm/test/CodeGen/SBF/callx.ll | 20 + llvm/test/CodeGen/SBF/cc_args.ll | 93 + llvm/test/CodeGen/SBF/cc_ret.ll | 48 + llvm/test/CodeGen/SBF/cmp.ll | 119 ++ llvm/test/CodeGen/SBF/dwarfdump.ll | 66 + .../CodeGen/SBF/elf-symbol-information.ll | 8 + llvm/test/CodeGen/SBF/ex1.ll | 46 + llvm/test/CodeGen/SBF/fi_ri.ll | 25 + llvm/test/CodeGen/SBF/i128.ll | 68 + llvm/test/CodeGen/SBF/inline_asm.ll | 53 + .../CodeGen/SBF/inlineasm-output-template.ll | 26 + llvm/test/CodeGen/SBF/inlineasm-wreg.ll | 17 + llvm/test/CodeGen/SBF/intrinsics.ll | 100 ++ llvm/test/CodeGen/SBF/is_trunc_free.ll | 80 + llvm/test/CodeGen/SBF/is_zext_free.ll | 26 + llvm/test/CodeGen/SBF/lit.local.cfg | 4 + llvm/test/CodeGen/SBF/load.ll | 43 + llvm/test/CodeGen/SBF/loop-exit-cond.ll | 131 ++ llvm/test/CodeGen/SBF/loops.ll | 111 ++ llvm/test/CodeGen/SBF/many_args1.ll | 18 + llvm/test/CodeGen/SBF/many_args2.ll | 17 + llvm/test/CodeGen/SBF/mem_offset.ll | 17 + .../CodeGen/SBF/memcpy-expand-in-order.ll | 78 + llvm/test/CodeGen/SBF/objdump_atomics.ll | 21 + llvm/test/CodeGen/SBF/objdump_cond_op.ll | 69 + llvm/test/CodeGen/SBF/objdump_cond_op_2.ll | 39 + llvm/test/CodeGen/SBF/objdump_dis_all.ll | 26 + llvm/test/CodeGen/SBF/objdump_imm_hex.ll | 65 + llvm/test/CodeGen/SBF/objdump_intrinsics.ll | 37 + llvm/test/CodeGen/SBF/objdump_nop.ll | 19 + llvm/test/CodeGen/SBF/objdump_static_var.ll | 39 + llvm/test/CodeGen/SBF/objdump_trivial.ll | 18 + llvm/test/CodeGen/SBF/objdump_two_funcs.ll | 68 + llvm/test/CodeGen/SBF/optnone-1.ll | 52 + llvm/test/CodeGen/SBF/optnone-2.ll | 53 + llvm/test/CodeGen/SBF/preprocess-loads.ll | 35 + llvm/test/CodeGen/SBF/reloc-2.ll | 58 + llvm/test/CodeGen/SBF/reloc-3.ll | 23 + llvm/test/CodeGen/SBF/reloc-abs64-sbf.ll | 20 + llvm/test/CodeGen/SBF/reloc-btf-2.ll | 63 + llvm/test/CodeGen/SBF/reloc-btf.ll | 33 + llvm/test/CodeGen/SBF/reloc.ll | 43 + llvm/test/CodeGen/SBF/remove_truncate_1.ll | 87 + llvm/test/CodeGen/SBF/remove_truncate_2.ll | 65 + llvm/test/CodeGen/SBF/remove_truncate_3.ll | 106 ++ llvm/test/CodeGen/SBF/remove_truncate_5.ll | 50 + llvm/test/CodeGen/SBF/remove_truncate_6.ll | 80 + llvm/test/CodeGen/SBF/remove_truncate_7.ll | 55 + llvm/test/CodeGen/SBF/remove_truncate_8.ll | 41 + llvm/test/CodeGen/SBF/rodata_1.ll | 55 + llvm/test/CodeGen/SBF/rodata_2.ll | 51 + llvm/test/CodeGen/SBF/rodata_3.ll | 40 + llvm/test/CodeGen/SBF/rodata_4.ll | 43 + llvm/test/CodeGen/SBF/rodata_5.ll | 49 + llvm/test/CodeGen/SBF/rodata_6.ll | 25 + llvm/test/CodeGen/SBF/rodata_7.ll | 25 + llvm/test/CodeGen/SBF/sanity.ll | 117 ++ llvm/test/CodeGen/SBF/sdiv.ll | 10 + llvm/test/CodeGen/SBF/select_ri.ll | 66 + llvm/test/CodeGen/SBF/selectiondag-bug.ll | 82 + llvm/test/CodeGen/SBF/setcc.ll | 112 ++ llvm/test/CodeGen/SBF/shifts.ll | 100 ++ llvm/test/CodeGen/SBF/simplifycfg.ll | 139 ++ llvm/test/CodeGen/SBF/spill-alu32.ll | 35 + llvm/test/CodeGen/SBF/struct_ret1.ll | 21 + llvm/test/CodeGen/SBF/struct_ret2.ll | 16 + llvm/test/CodeGen/SBF/undef.ll | 55 + llvm/test/CodeGen/SBF/vararg1.ll | 9 + llvm/test/CodeGen/SBF/vla.ll | 115 ++ llvm/test/CodeGen/SBF/warn-stack.ll | 75 + llvm/test/CodeGen/SBF/xadd.ll | 60 + llvm/test/CodeGen/SBF/xadd_legal.ll | 30 + llvm/test/DebugInfo/SBF/extern-void.ll | 80 + llvm/test/DebugInfo/SBF/lit.local.cfg | 2 + llvm/test/MC/SBF/insn-unit-32.s | 103 ++ llvm/test/MC/SBF/insn-unit.s | 178 ++ llvm/test/MC/SBF/lit.local.cfg | 2 + llvm/test/MC/SBF/load-store-32.s | 28 + llvm/test/MC/SBF/sbf-sdiv.s | 15 + llvm/test/Object/SBF/lit.local.cfg | 2 + .../test/Object/SBF/yaml2obj-elf-sbf-rel.yaml | 86 + .../ELF/file-header-machine-types.test | 3 + llvm/tools/llvm-readobj/ELFDumper.cpp | 1 + llvm/tools/opt/opt.cpp | 2 - llvm/unittests/Object/ELFObjectFileTest.cpp | 8 + llvm/unittests/TargetParser/TripleTest.cpp | 32 + 180 files changed, 18213 insertions(+), 9 deletions(-) create mode 100644 lld/ELF/Arch/SBF.cpp create mode 100644 llvm/include/llvm/BinaryFormat/ELFRelocs/SBF.def create mode 100644 llvm/lib/Target/SBF/AsmParser/CMakeLists.txt create mode 100644 llvm/lib/Target/SBF/AsmParser/SBFAsmParser.cpp create mode 100644 llvm/lib/Target/SBF/BTF.def create mode 100644 llvm/lib/Target/SBF/BTF.h create mode 100644 llvm/lib/Target/SBF/BTFDebug.cpp create mode 100644 llvm/lib/Target/SBF/BTFDebug.h create mode 100644 llvm/lib/Target/SBF/CMakeLists.txt create mode 100644 llvm/lib/Target/SBF/Disassembler/CMakeLists.txt create mode 100644 llvm/lib/Target/SBF/Disassembler/SBFDisassembler.cpp create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/CMakeLists.txt create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFAsmBackend.cpp create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFELFObjectWriter.cpp create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.cpp create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.h create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFMCAsmInfo.h create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFMCCodeEmitter.cpp create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.cpp create mode 100644 llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.h create mode 100644 llvm/lib/Target/SBF/SBF.h create mode 100644 llvm/lib/Target/SBF/SBF.td create mode 100644 llvm/lib/Target/SBF/SBFAbstractMemberAccess.cpp create mode 100644 llvm/lib/Target/SBF/SBFAdjustOpt.cpp create mode 100644 llvm/lib/Target/SBF/SBFAsmPrinter.cpp create mode 100644 llvm/lib/Target/SBF/SBFCORE.h create mode 100644 llvm/lib/Target/SBF/SBFCallingConv.td create mode 100644 llvm/lib/Target/SBF/SBFCheckAndAdjustIR.cpp create mode 100644 llvm/lib/Target/SBF/SBFFrameLowering.cpp create mode 100644 llvm/lib/Target/SBF/SBFFrameLowering.h create mode 100644 llvm/lib/Target/SBF/SBFIRPeephole.cpp create mode 100644 llvm/lib/Target/SBF/SBFISelDAGToDAG.cpp create mode 100644 llvm/lib/Target/SBF/SBFISelLowering.cpp create mode 100644 llvm/lib/Target/SBF/SBFISelLowering.h create mode 100644 llvm/lib/Target/SBF/SBFInstrFormats.td create mode 100644 llvm/lib/Target/SBF/SBFInstrInfo.cpp create mode 100644 llvm/lib/Target/SBF/SBFInstrInfo.h create mode 100644 llvm/lib/Target/SBF/SBFInstrInfo.td create mode 100644 llvm/lib/Target/SBF/SBFMCInstLower.cpp create mode 100644 llvm/lib/Target/SBF/SBFMCInstLower.h create mode 100644 llvm/lib/Target/SBF/SBFMIChecking.cpp create mode 100644 llvm/lib/Target/SBF/SBFMIPeephole.cpp create mode 100644 llvm/lib/Target/SBF/SBFMISimplifyPatchable.cpp create mode 100644 llvm/lib/Target/SBF/SBFPreserveDIType.cpp create mode 100644 llvm/lib/Target/SBF/SBFRegisterInfo.cpp create mode 100644 llvm/lib/Target/SBF/SBFRegisterInfo.h create mode 100644 llvm/lib/Target/SBF/SBFRegisterInfo.td create mode 100644 llvm/lib/Target/SBF/SBFSelectionDAGInfo.cpp create mode 100644 llvm/lib/Target/SBF/SBFSelectionDAGInfo.h create mode 100644 llvm/lib/Target/SBF/SBFSubtarget.cpp create mode 100644 llvm/lib/Target/SBF/SBFSubtarget.h create mode 100644 llvm/lib/Target/SBF/SBFTargetMachine.cpp create mode 100644 llvm/lib/Target/SBF/SBFTargetMachine.h create mode 100644 llvm/lib/Target/SBF/SBFTargetTransformInfo.h create mode 100644 llvm/lib/Target/SBF/TargetInfo/CMakeLists.txt create mode 100644 llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.cpp create mode 100644 llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.h create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-alu.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-cond-select.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-load-store.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-1.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-2.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-3.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-peephole.ll create mode 100644 llvm/test/CodeGen/SBF/32-bit-subreg-zext.ll create mode 100644 llvm/test/CodeGen/SBF/TODO-NOTES create mode 100644 llvm/test/CodeGen/SBF/adjust-opt-icmp1.ll create mode 100644 llvm/test/CodeGen/SBF/adjust-opt-icmp2.ll create mode 100644 llvm/test/CodeGen/SBF/adjust-opt-icmp3.ll create mode 100644 llvm/test/CodeGen/SBF/adjust-opt-icmp4.ll create mode 100644 llvm/test/CodeGen/SBF/adjust-opt-speculative1.ll create mode 100644 llvm/test/CodeGen/SBF/adjust-opt-speculative2.ll create mode 100644 llvm/test/CodeGen/SBF/alu8.ll create mode 100644 llvm/test/CodeGen/SBF/atomics_sbf.ll create mode 100644 llvm/test/CodeGen/SBF/basictest.ll create mode 100644 llvm/test/CodeGen/SBF/byval.ll create mode 100644 llvm/test/CodeGen/SBF/callx.ll create mode 100644 llvm/test/CodeGen/SBF/cc_args.ll create mode 100644 llvm/test/CodeGen/SBF/cc_ret.ll create mode 100644 llvm/test/CodeGen/SBF/cmp.ll create mode 100644 llvm/test/CodeGen/SBF/dwarfdump.ll create mode 100644 llvm/test/CodeGen/SBF/elf-symbol-information.ll create mode 100644 llvm/test/CodeGen/SBF/ex1.ll create mode 100644 llvm/test/CodeGen/SBF/fi_ri.ll create mode 100644 llvm/test/CodeGen/SBF/i128.ll create mode 100644 llvm/test/CodeGen/SBF/inline_asm.ll create mode 100644 llvm/test/CodeGen/SBF/inlineasm-output-template.ll create mode 100644 llvm/test/CodeGen/SBF/inlineasm-wreg.ll create mode 100644 llvm/test/CodeGen/SBF/intrinsics.ll create mode 100644 llvm/test/CodeGen/SBF/is_trunc_free.ll create mode 100644 llvm/test/CodeGen/SBF/is_zext_free.ll create mode 100644 llvm/test/CodeGen/SBF/lit.local.cfg create mode 100644 llvm/test/CodeGen/SBF/load.ll create mode 100644 llvm/test/CodeGen/SBF/loop-exit-cond.ll create mode 100644 llvm/test/CodeGen/SBF/loops.ll create mode 100644 llvm/test/CodeGen/SBF/many_args1.ll create mode 100644 llvm/test/CodeGen/SBF/many_args2.ll create mode 100644 llvm/test/CodeGen/SBF/mem_offset.ll create mode 100644 llvm/test/CodeGen/SBF/memcpy-expand-in-order.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_atomics.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_cond_op.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_cond_op_2.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_dis_all.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_imm_hex.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_intrinsics.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_nop.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_static_var.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_trivial.ll create mode 100644 llvm/test/CodeGen/SBF/objdump_two_funcs.ll create mode 100644 llvm/test/CodeGen/SBF/optnone-1.ll create mode 100644 llvm/test/CodeGen/SBF/optnone-2.ll create mode 100644 llvm/test/CodeGen/SBF/preprocess-loads.ll create mode 100644 llvm/test/CodeGen/SBF/reloc-2.ll create mode 100644 llvm/test/CodeGen/SBF/reloc-3.ll create mode 100644 llvm/test/CodeGen/SBF/reloc-abs64-sbf.ll create mode 100644 llvm/test/CodeGen/SBF/reloc-btf-2.ll create mode 100644 llvm/test/CodeGen/SBF/reloc-btf.ll create mode 100644 llvm/test/CodeGen/SBF/reloc.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_1.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_2.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_3.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_5.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_6.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_7.ll create mode 100644 llvm/test/CodeGen/SBF/remove_truncate_8.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_1.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_2.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_3.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_4.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_5.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_6.ll create mode 100644 llvm/test/CodeGen/SBF/rodata_7.ll create mode 100644 llvm/test/CodeGen/SBF/sanity.ll create mode 100644 llvm/test/CodeGen/SBF/sdiv.ll create mode 100644 llvm/test/CodeGen/SBF/select_ri.ll create mode 100644 llvm/test/CodeGen/SBF/selectiondag-bug.ll create mode 100644 llvm/test/CodeGen/SBF/setcc.ll create mode 100644 llvm/test/CodeGen/SBF/shifts.ll create mode 100644 llvm/test/CodeGen/SBF/simplifycfg.ll create mode 100644 llvm/test/CodeGen/SBF/spill-alu32.ll create mode 100644 llvm/test/CodeGen/SBF/struct_ret1.ll create mode 100644 llvm/test/CodeGen/SBF/struct_ret2.ll create mode 100644 llvm/test/CodeGen/SBF/undef.ll create mode 100644 llvm/test/CodeGen/SBF/vararg1.ll create mode 100644 llvm/test/CodeGen/SBF/vla.ll create mode 100644 llvm/test/CodeGen/SBF/warn-stack.ll create mode 100644 llvm/test/CodeGen/SBF/xadd.ll create mode 100644 llvm/test/CodeGen/SBF/xadd_legal.ll create mode 100644 llvm/test/DebugInfo/SBF/extern-void.ll create mode 100644 llvm/test/DebugInfo/SBF/lit.local.cfg create mode 100644 llvm/test/MC/SBF/insn-unit-32.s create mode 100644 llvm/test/MC/SBF/insn-unit.s create mode 100644 llvm/test/MC/SBF/lit.local.cfg create mode 100644 llvm/test/MC/SBF/load-store-32.s create mode 100644 llvm/test/MC/SBF/sbf-sdiv.s create mode 100644 llvm/test/Object/SBF/lit.local.cfg create mode 100644 llvm/test/Object/SBF/yaml2obj-elf-sbf-rel.yaml diff --git a/clang/lib/Basic/Targets/BPF.cpp b/clang/lib/Basic/Targets/BPF.cpp index f4684765b7ffb..7af9965f81c6e 100644 --- a/clang/lib/Basic/Targets/BPF.cpp +++ b/clang/lib/Basic/Targets/BPF.cpp @@ -12,8 +12,10 @@ #include "BPF.h" #include "Targets.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/MacroBuilder.h" #include "clang/Basic/TargetBuiltins.h" +#include "clang/Driver/DriverDiagnostic.h" #include "llvm/ADT/StringRef.h" using namespace clang; @@ -88,6 +90,12 @@ ArrayRef BPFTargetInfo::getTargetBuiltins() const { bool BPFTargetInfo::handleTargetFeatures(std::vector &Features, DiagnosticsEngine &Diags) { + // TODO: The SBF back-end now provides the sbf target. Issue deprecation + // warning directing use of '-target sbf' instead. Eventually remove the + // +solana support from the BPF back-end. + if (getTriple().getArch() != llvm::Triple::sbf && HasSolanaFeature) + Diags.Report(diag::warn_drv_no_solana_with_bpf); + for (const auto &Feature : Features) { if (Feature == "+alu32") { HasAlu32 = true; diff --git a/lld/ELF/Arch/SBF.cpp b/lld/ELF/Arch/SBF.cpp new file mode 100644 index 0000000000000..b5358c5e3d587 --- /dev/null +++ b/lld/ELF/Arch/SBF.cpp @@ -0,0 +1,140 @@ +//===- SBF.cpp ------------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; + +namespace lld { +namespace elf { + +namespace { +class SBF final : public TargetInfo { +public: + SBF(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + uint32_t calcEFlags() const override; +}; +} // namespace + +SBF::SBF(Ctx &ctx) : TargetInfo(ctx) { + relativeRel = R_SBF_64_RELATIVE; + symbolicRel = R_SBF_64_64; +} + +RelExpr SBF::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_SBF_64_32: + return R_PC; + case R_SBF_64_ABS32: + case R_SBF_64_NODYLD32: + case R_SBF_64_ABS64: + case R_SBF_64_64: + return R_ABS; + default: + error(getErrorLocation(loc) + "unrecognized reloc " + toString(type)); + } + return R_NONE; +} + +RelType SBF::getDynRel(RelType type) const { + switch (type) { + case R_SBF_64_ABS64: + // R_SBF_64_ABS64 is symbolic like R_SBF_64_64, which is set as our + // symbolicRel in the constructor. Return R_SBF_64_64 here so that if + // the symbol isn't preemptible, we emit a _RELATIVE relocation instead + // and skip emitting the symbol. + // + // See https://github.com/solana-labs/llvm-project/blob/6b6aef5dbacef31a3c7b3a54f7f1ba54cafc7077/lld/ELF/Relocations.cpp#L1179 + return R_SBF_64_64; + default: + return type; + } +} + +int64_t SBF::getImplicitAddend(const uint8_t *buf, RelType type) const { + return 0; +} + +void SBF::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { + case R_SBF_64_32: { + // Relocation of a symbol + write32le(loc + 4, ((val - 8) / 8) & 0xFFFFFFFF); + break; + } + case R_SBF_64_ABS32: + case R_SBF_64_NODYLD32: { + // Relocation used by .BTF.ext and DWARF + write32le(loc, val & 0xFFFFFFFF); + break; + } + case R_SBF_64_64: { + // Relocation of a lddw instruction + // 64 bit address is divided into the imm of this and the following + // instructions, lower 32 first. + write32le(loc + 4, val & 0xFFFFFFFF); + write32le(loc + 8 + 4, val >> 32); + break; + } + case R_SBF_64_ABS64: { + // The relocation type is used for normal 64-bit data. The + // actual to-be-relocated data is stored at r_offset and the + // read/write data bitsize is 64 (8 bytes). The relocation can + // be resolved with the symbol value plus implicit addend. + write64le(loc, val); + break; + } + default: + error(getErrorLocation(loc) + "unrecognized reloc " + toString(rel.type)); + } +} + +static uint32_t getEFlags(InputFile *file) { + if (config->ekind == ELF64BEKind) + return cast>(file)->getObj().getHeader().e_flags; + return cast>(file)->getObj().getHeader().e_flags; +} + +uint32_t SBF::calcEFlags() const { + uint32_t ret = 0; + + // Ensure that all the object files were compiled with the same flags, as + // different flags indicate different ABIs. + for (InputFile *f : objectFiles) { + uint32_t flags = getEFlags(f); + if (ret == 0) { + ret = flags; + } else if (ret != flags) { + error("can not link object files with incompatible flags"); + } + } + + return ret; +} + +void setSBFTargetInfo(Ctx &ctx) { + ctx.target.reset(new SBF(ctx)); +} + +} // namespace elf +} // namespace lld diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 83d816ddb0601..00182c9f146e4 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -32,6 +32,7 @@ add_lld_library(lldELF Arch/PPC.cpp Arch/PPC64.cpp Arch/RISCV.cpp + Arch/SBF.cpp Arch/SPARCV9.cpp Arch/SystemZ.cpp Arch/X86.cpp diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index c90ef8505aadd..86072ef06f539 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -63,6 +63,10 @@ void elf::setTarget(Ctx &ctx) { return setARMTargetInfo(ctx); case EM_AVR: return setAVRTargetInfo(ctx); + case EM_BPF: + return setSBFTargetInfo(ctx); + case EM_SBF: + return setSBFTargetInfo(ctx); case EM_HEXAGON: return setHexagonTargetInfo(ctx); case EM_LOONGARCH: diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index fd1e5d33c438a..227089c9bfe3e 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -194,6 +194,7 @@ void setSPARCV9TargetInfo(Ctx &); void setSystemZTargetInfo(Ctx &); void setX86TargetInfo(Ctx &); void setX86_64TargetInfo(Ctx &); +void setSBFTargetInfo(Ctx &); struct ErrorPlace { InputSectionBase *isec; diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index f5293e8663243..e58be29f1f664 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -492,6 +492,7 @@ set(LLVM_ALL_TARGETS NVPTX PowerPC RISCV + SBF Sparc SPIRV SystemZ diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 8853c4a88b0b5..b594a7734cbc7 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -323,6 +323,7 @@ enum { EM_VE = 251, // NEC SX-Aurora VE EM_CSKY = 252, // C-SKY 32-bit processor EM_LOONGARCH = 258, // LoongArch + EM_SBF = 263 // Solana Bytecode Format }; // Object file classes. @@ -950,6 +951,16 @@ enum : unsigned { EF_CUDA_VIRTUAL_SM = 0xff0000, }; +// SBF specific e_flags +enum : unsigned { + EF_SBF_V2 = 0x20, +}; + +// ELF Relocation types for SBF. +enum { +#include "ELFRelocs/SBF.def" +}; + // ELF Relocation types for BPF enum { #include "ELFRelocs/BPF.def" diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/SBF.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/SBF.def new file mode 100644 index 0000000000000..01a7eb09e8ec6 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/SBF.def @@ -0,0 +1,12 @@ +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +// Currently these match one-to-one with BPF relocations. +ELF_RELOC(R_SBF_NONE, 0) +ELF_RELOC(R_SBF_64_64, 1) +ELF_RELOC(R_SBF_64_ABS64, 2) +ELF_RELOC(R_SBF_64_ABS32, 3) +ELF_RELOC(R_SBF_64_NODYLD32, 4) +ELF_RELOC(R_SBF_64_RELATIVE, 8) // B + A +ELF_RELOC(R_SBF_64_32, 10) diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 60c72062a3f6a..aae09c5bed3b1 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1343,6 +1343,8 @@ StringRef ELFObjectFile::getFileFormatName() const { return "elf64-amdgpu"; case ELF::EM_BPF: return "elf64-bpf"; + case ELF::EM_SBF: + return "elf64-sbf"; case ELF::EM_VE: return "elf64-ve"; case ELF::EM_LOONGARCH: @@ -1432,6 +1434,8 @@ template Triple::ArchType ELFObjectFile::getArch() const { case ELF::EM_BPF: return IsLittleEndian ? Triple::bpfel : Triple::bpfeb; + case ELF::EM_SBF: + return Triple::sbf; case ELF::EM_VE: return Triple::ve; diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h index 7d67966d17256..c1b366cd73849 100644 --- a/llvm/include/llvm/TargetParser/Triple.h +++ b/llvm/include/llvm/TargetParser/Triple.h @@ -1089,6 +1089,7 @@ class Triple { } /// Tests whether the target is eBPF. + /// TODO/TBD: For new sbf backend, we should probably remove sbf check here. bool isBPF() const { return getArch() == Triple::bpfel || getArch() == Triple::bpfeb; } @@ -1109,6 +1110,11 @@ class Triple { Env == llvm::Triple::EABIHF; } + /// Tests whether the target is SBF (little endian). + bool isSBF() const { + return getArch() == Triple::sbf; + } + /// Tests whether the target supports comdat bool supportsCOMDAT() const { return !(isOSBinFormatMachO() || isOSBinFormatXCOFF() || diff --git a/llvm/include/module.modulemap b/llvm/include/module.modulemap index b378023453241..adfb9a1cfa47b 100644 --- a/llvm/include/module.modulemap +++ b/llvm/include/module.modulemap @@ -97,6 +97,7 @@ module LLVM_BinaryFormat { textual header "llvm/BinaryFormat/ELFRelocs/PowerPC.def" textual header "llvm/BinaryFormat/ELFRelocs/RISCV.def" textual header "llvm/BinaryFormat/ELFRelocs/RISCV_nonstandard.def" + textual header "llvm/BinaryFormat/ELFRelocs/SBF.def" textual header "llvm/BinaryFormat/ELFRelocs/Sparc.def" textual header "llvm/BinaryFormat/ELFRelocs/SystemZ.def" textual header "llvm/BinaryFormat/ELFRelocs/VE.def" diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp index 8557901192e40..136e333673b7e 100644 --- a/llvm/lib/Analysis/TargetLibraryInfo.cpp +++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp @@ -908,6 +908,13 @@ static void initializeLibCalls(TargetLibraryInfoImpl &TLI, const Triple &T, if (T.isOSAIX()) TLI.setUnavailable(LibFunc_memrchr); + + if (T.isBPF() || T.isSBF()) { + TLI.setUnavailable(LibFunc_rust_alloc); + TLI.setUnavailable(LibFunc_rust_dealloc); + TLI.setUnavailable(LibFunc_rust_realloc); + } + TLI.addVectorizableFunctionsFromVecLib(ClVectorLibrary, T); } diff --git a/llvm/lib/BinaryFormat/ELF.cpp b/llvm/lib/BinaryFormat/ELF.cpp index 9878f5769087e..f60d0be94c191 100644 --- a/llvm/lib/BinaryFormat/ELF.cpp +++ b/llvm/lib/BinaryFormat/ELF.cpp @@ -194,6 +194,7 @@ uint16_t ELF::convertArchNameToEMachine(StringRef Arch) { .Case("riscv", EM_RISCV) .Case("lanai", EM_LANAI) .Case("bpf", EM_BPF) + .Case("sbf", EM_SBF) .Case("ve", EM_VE) .Case("csky", EM_CSKY) .Case("loongarch", EM_LOONGARCH) @@ -557,6 +558,8 @@ StringRef ELF::convertEMachineToArchName(uint16_t EMachine) { return "lanai"; case EM_BPF: return "bpf"; + case EM_SBF: + return "sbf"; case EM_VE: return "ve"; case EM_CSKY: diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index b6d0699ee4fe0..fa3c93d45c43f 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -147,6 +147,13 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, break; } break; + case ELF::EM_SBF: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/SBF.def" + default: + break; + } + break; case ELF::EM_MSP430: switch (Type) { #include "llvm/BinaryFormat/ELFRelocs/MSP430.def" @@ -230,6 +237,8 @@ uint32_t llvm::object::getELFRelativeRelocationType(uint32_t Machine) { return ELF::R_VE_RELATIVE; case ELF::EM_AMDGPU: break; + case ELF::EM_SBF: + break; case ELF::EM_BPF: break; case ELF::EM_LOONGARCH: diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp index a28f5943c320d..5a7391a51d414 100644 --- a/llvm/lib/Object/RelocationResolver.cpp +++ b/llvm/lib/Object/RelocationResolver.cpp @@ -125,6 +125,28 @@ static uint64_t resolveBPF(uint64_t Type, uint64_t Offset, uint64_t S, } } +static bool supportsSBF(uint64_t Type) { + switch (Type) { + case ELF::R_SBF_64_ABS32: + case ELF::R_SBF_64_ABS64: + return true; + default: + return false; + } +} + +static uint64_t resolveSBF(uint64_t Type, uint64_t Offset, uint64_t S, + uint64_t LocData, int64_t /*Addend*/) { + switch (Type) { + case ELF::R_SBF_64_ABS32: + return (S + LocData) & 0xFFFFFFFF; + case ELF::R_SBF_64_ABS64: + return S + LocData; + default: + llvm_unreachable("Invalid relocation type"); + } +} + static bool supportsMips64(uint64_t Type) { switch (Type) { case ELF::R_MIPS_32: @@ -797,6 +819,8 @@ getRelocationResolver(const ObjectFile &Obj) { return {supportsBPF, resolveBPF}; case Triple::loongarch64: return {supportsLoongArch, resolveLoongArch}; + case Triple::sbf: + return {supportsSBF, resolveSBF}; case Triple::mips64el: case Triple::mips64: return {supportsMips64, resolveMips64}; diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 539834fc8d4db..6007cf187f548 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -355,6 +355,7 @@ void ScalarEnumerationTraits::enumeration( ECase(EM_RISCV); ECase(EM_LANAI); ECase(EM_BPF); + ECase(EM_SBF); ECase(EM_VE); ECase(EM_CSKY); ECase(EM_LOONGARCH); @@ -940,6 +941,9 @@ void ScalarEnumerationTraits::enumeration( case ELF::EM_BPF: #include "llvm/BinaryFormat/ELFRelocs/BPF.def" break; + case ELF::EM_SBF: +#include "llvm/BinaryFormat/ELFRelocs/SBF.def" + break; case ELF::EM_VE: #include "llvm/BinaryFormat/ELFRelocs/VE.def" break; diff --git a/llvm/lib/Target/BPF/BPFSubtarget.cpp b/llvm/lib/Target/BPF/BPFSubtarget.cpp index 305e9a2bf2cda..a110cb2c8fff6 100644 --- a/llvm/lib/Target/BPF/BPFSubtarget.cpp +++ b/llvm/lib/Target/BPF/BPFSubtarget.cpp @@ -51,7 +51,9 @@ BPFSubtarget &BPFSubtarget::initializeSubtargetDependencies(StringRef CPU, return *this; } -void BPFSubtarget::initializeEnvironment() { +void BPFSubtarget::initializeEnvironment(const Triple &TT) { + // TODO: jle: remove, sbf is now provided by the SBF backend. + IsSolana = false; HasJmpExt = false; HasJmp32 = false; HasAlu32 = false; @@ -101,6 +103,10 @@ BPFSubtarget::BPFSubtarget(const Triple &TT, const std::string &CPU, FrameLowering(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this) { IsLittleEndian = TT.isLittleEndian(); +<<<<<<< HEAD +======= + TSInfo.setSolanaFlag(false); +>>>>>>> 3d16ab60503b ([SOL] Initial squash commit for new SBF LLVM back-end and related.) CallLoweringInfo.reset(new BPFCallLowering(*getTargetLowering())); Legalizer.reset(new BPFLegalizerInfo(*this)); diff --git a/llvm/lib/Target/BPF/BPFTargetMachine.cpp b/llvm/lib/Target/BPF/BPFTargetMachine.cpp index 3379af6fe8744..0ad56ecb4a4d4 100644 --- a/llvm/lib/Target/BPF/BPFTargetMachine.cpp +++ b/llvm/lib/Target/BPF/BPFTargetMachine.cpp @@ -51,11 +51,16 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFTarget() { } // DataLayout: little or big endian -static std::string computeDataLayout(const Triple &TT) { - if (TT.getArch() == Triple::bpfeb) - return "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; - else - return "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; +static std::string computeDataLayout(const Triple &TT, StringRef FS) { + // TODO: jle: remove 'solana', sbf is now provided by the SBF backend. + bool IsSolana = FS.contains("solana"); + if (TT.getArch() == Triple::bpfeb) { + return IsSolana ? "E-m:e-p:64:64-i64:64-n32:64-S128" + : "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; + } else { + return IsSolana ? "e-m:e-p:64:64-i64:64-n32:64-S128" + : "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; + } } static Reloc::Model getEffectiveRelocModel(std::optional RM) { diff --git a/llvm/lib/Target/BPF/TargetInfo/BPFTargetInfo.h b/llvm/lib/Target/BPF/TargetInfo/BPFTargetInfo.h index 150526c1a9db6..81d1be39d6232 100644 --- a/llvm/lib/Target/BPF/TargetInfo/BPFTargetInfo.h +++ b/llvm/lib/Target/BPF/TargetInfo/BPFTargetInfo.h @@ -16,7 +16,6 @@ class Target; Target &getTheBPFleTarget(); Target &getTheBPFbeTarget(); Target &getTheBPFTarget(); - } // namespace llvm #endif // LLVM_LIB_TARGET_BPF_TARGETINFO_BPFTARGETINFO_H diff --git a/llvm/lib/Target/SBF/AsmParser/CMakeLists.txt b/llvm/lib/Target/SBF/AsmParser/CMakeLists.txt new file mode 100644 index 0000000000000..1a8255366fb32 --- /dev/null +++ b/llvm/lib/Target/SBF/AsmParser/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_component_library(LLVMSBFAsmParser + SBFAsmParser.cpp + + LINK_COMPONENTS + MC + MCParser + SBFDesc + SBFInfo + Support + + ADD_TO_COMPONENT + SBF + ) diff --git a/llvm/lib/Target/SBF/AsmParser/SBFAsmParser.cpp b/llvm/lib/Target/SBF/AsmParser/SBFAsmParser.cpp new file mode 100644 index 0000000000000..927593a749247 --- /dev/null +++ b/llvm/lib/Target/SBF/AsmParser/SBFAsmParser.cpp @@ -0,0 +1,509 @@ +//===-- SBFAsmParser.cpp - Parse SBF assembly to MCInst 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 +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "TargetInfo/SBFTargetInfo.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" +#include "llvm/MC/MCParser/MCParsedAsmOperand.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Casting.h" + +using namespace llvm; + +namespace { +struct SBFOperand; + +class SBFAsmParser : public MCTargetAsmParser { + + SMLoc getLoc() const { return getParser().getTok().getLoc(); } + + bool PreMatchCheck(OperandVector &Operands); + + bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) override; + + bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; + OperandMatchResultTy tryParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) override; + + bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) override; + + bool ParseDirective(AsmToken DirectiveID) override; + + // "=" is used as assignment operator for assembly statment, so can't be used + // for symbol assignment. + bool equalIsAsmAssignment() override { return false; } + // "*" is used for dereferencing memory that it will be the start of + // statement. + bool starIsStartOfStatement() override { return true; } + +#define GET_ASSEMBLER_HEADER +#include "SBFGenAsmMatcher.inc" + + OperandMatchResultTy parseImmediate(OperandVector &Operands); + OperandMatchResultTy parseRegister(OperandVector &Operands); + OperandMatchResultTy parseOperandAsOperator(OperandVector &Operands); + +public: + enum SBFMatchResultTy { + Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, +#define GET_OPERAND_DIAGNOSTIC_TYPES +#include "SBFGenAsmMatcher.inc" +#undef GET_OPERAND_DIAGNOSTIC_TYPES + }; + + SBFAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, + const MCInstrInfo &MII, const MCTargetOptions &Options) + : MCTargetAsmParser(Options, STI, MII) { + setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); + } +}; + +/// SBFOperand - Instances of this class represent a parsed machine +/// instruction +struct SBFOperand : public MCParsedAsmOperand { + + enum KindTy { + Token, + Register, + Immediate, + } Kind; + + struct RegOp { + unsigned RegNum; + }; + + struct ImmOp { + const MCExpr *Val; + }; + + SMLoc StartLoc, EndLoc; + union { + StringRef Tok; + RegOp Reg; + ImmOp Imm; + }; + + SBFOperand(KindTy K) : Kind(K) {} + +public: + SBFOperand(const SBFOperand &o) : MCParsedAsmOperand() { + Kind = o.Kind; + StartLoc = o.StartLoc; + EndLoc = o.EndLoc; + + switch (Kind) { + case Register: + Reg = o.Reg; + break; + case Immediate: + Imm = o.Imm; + break; + case Token: + Tok = o.Tok; + break; + } + } + + bool isToken() const override { return Kind == Token; } + bool isReg() const override { return Kind == Register; } + bool isImm() const override { return Kind == Immediate; } + bool isMem() const override { return false; } + + bool isConstantImm() const { + return isImm() && isa(getImm()); + } + + int64_t getConstantImm() const { + const MCExpr *Val = getImm(); + return static_cast(Val)->getValue(); + } + + bool isSImm12() const { + return (isConstantImm() && isInt<12>(getConstantImm())); + } + + /// getStartLoc - Gets location of the first token of this operand + SMLoc getStartLoc() const override { return StartLoc; } + /// getEndLoc - Gets location of the last token of this operand + SMLoc getEndLoc() const override { return EndLoc; } + + unsigned getReg() const override { + assert(Kind == Register && "Invalid type access!"); + return Reg.RegNum; + } + + const MCExpr *getImm() const { + assert(Kind == Immediate && "Invalid type access!"); + return Imm.Val; + } + + StringRef getToken() const { + assert(Kind == Token && "Invalid type access!"); + return Tok; + } + + void print(raw_ostream &OS) const override { + switch (Kind) { + case Immediate: + OS << *getImm(); + break; + case Register: + OS << ""; + break; + case Token: + OS << "'" << getToken() << "'"; + break; + } + } + + void addExpr(MCInst &Inst, const MCExpr *Expr) const { + assert(Expr && "Expr shouldn't be null!"); + + if (auto *CE = dyn_cast(Expr)) + Inst.addOperand(MCOperand::createImm(CE->getValue())); + else + Inst.addOperand(MCOperand::createExpr(Expr)); + } + + // Used by the TableGen Code + void addRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getReg())); + } + + void addImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + addExpr(Inst, getImm()); + } + + static std::unique_ptr createToken(StringRef Str, SMLoc S) { + auto Op = std::make_unique(Token); + Op->Tok = Str; + Op->StartLoc = S; + Op->EndLoc = S; + return Op; + } + + static std::unique_ptr createReg(unsigned RegNo, SMLoc S, + SMLoc E) { + auto Op = std::make_unique(Register); + Op->Reg.RegNum = RegNo; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + static std::unique_ptr createImm(const MCExpr *Val, SMLoc S, + SMLoc E) { + auto Op = std::make_unique(Immediate); + Op->Imm.Val = Val; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + // Identifiers that can be used at the start of a statment. + static bool isValidIdAtStart(StringRef Name) { + return StringSwitch(Name.lower()) + .Case("if", true) + .Case("call", true) + .Case("callx", true) + .Case("goto", true) + .Case("*", true) + .Case("exit", true) + .Case("lock", true) + .Case("ld_pseudo", true) + .Default(false); + } + + // Identifiers that can be used in the middle of a statment. + static bool isValidIdInMiddle(StringRef Name) { + return StringSwitch(Name.lower()) + .Case("u64", true) + .Case("u32", true) + .Case("u16", true) + .Case("u8", true) + .Case("be64", true) + .Case("be32", true) + .Case("be16", true) + .Case("le64", true) + .Case("le32", true) + .Case("le16", true) + .Case("goto", true) + .Case("ll", true) + .Case("skb", true) + .Case("s", true) + .Default(false); + } +}; +} // end anonymous namespace. + +#define GET_REGISTER_MATCHER +#define GET_MATCHER_IMPLEMENTATION +#include "SBFGenAsmMatcher.inc" + +bool SBFAsmParser::PreMatchCheck(OperandVector &Operands) { + + if (Operands.size() == 4) { + // check "reg1 = -reg2" and "reg1 = be16/be32/be64/le16/le32/le64 reg2", + // reg1 must be the same as reg2 + SBFOperand &Op0 = (SBFOperand &)*Operands[0]; + SBFOperand &Op1 = (SBFOperand &)*Operands[1]; + SBFOperand &Op2 = (SBFOperand &)*Operands[2]; + SBFOperand &Op3 = (SBFOperand &)*Operands[3]; + if (Op0.isReg() && Op1.isToken() && Op2.isToken() && Op3.isReg() + && Op1.getToken() == "=" + && (Op2.getToken() == "-" || Op2.getToken() == "be16" + || Op2.getToken() == "be32" || Op2.getToken() == "be64" + || Op2.getToken() == "le16" || Op2.getToken() == "le32" + || Op2.getToken() == "le64") + && Op0.getReg() != Op3.getReg()) + return true; + } + + return false; +} + +bool SBFAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, + MCStreamer &Out, uint64_t &ErrorInfo, + bool MatchingInlineAsm) { + MCInst Inst; + SMLoc ErrorLoc; + + if (PreMatchCheck(Operands)) + return Error(IDLoc, "additional inst constraint not met"); + + switch (MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm)) { + default: + break; + case Match_Success: + Inst.setLoc(IDLoc); + Out.emitInstruction(Inst, getSTI()); + return false; + case Match_MissingFeature: + return Error(IDLoc, "instruction use requires an option to be enabled"); + case Match_MnemonicFail: + return Error(IDLoc, "unrecognized instruction mnemonic"); + case Match_InvalidOperand: + ErrorLoc = IDLoc; + + if (ErrorInfo != ~0U) { + if (ErrorInfo >= Operands.size()) + return Error(ErrorLoc, "too few operands for instruction"); + + ErrorLoc = ((SBFOperand &)*Operands[ErrorInfo]).getStartLoc(); + + if (ErrorLoc == SMLoc()) + ErrorLoc = IDLoc; + } + + return Error(ErrorLoc, "invalid operand for instruction"); + } + + llvm_unreachable("Unknown match type detected!"); +} + +bool SBFAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) { + if (tryParseRegister(RegNo, StartLoc, EndLoc) != MatchOperand_Success) + return Error(StartLoc, "invalid register name"); + return false; +} + +OperandMatchResultTy SBFAsmParser::tryParseRegister(unsigned &RegNo, + SMLoc &StartLoc, + SMLoc &EndLoc) { + const AsmToken &Tok = getParser().getTok(); + StartLoc = Tok.getLoc(); + EndLoc = Tok.getEndLoc(); + RegNo = 0; + StringRef Name = getLexer().getTok().getIdentifier(); + + if (!MatchRegisterName(Name)) { + getParser().Lex(); // Eat identifier token. + return MatchOperand_Success; + } + + return MatchOperand_NoMatch; +} + +OperandMatchResultTy +SBFAsmParser::parseOperandAsOperator(OperandVector &Operands) { + SMLoc S = getLoc(); + + if (getLexer().getKind() == AsmToken::Identifier) { + StringRef Name = getLexer().getTok().getIdentifier(); + + if (SBFOperand::isValidIdInMiddle(Name)) { + getLexer().Lex(); + Operands.push_back(SBFOperand::createToken(Name, S)); + return MatchOperand_Success; + } + + return MatchOperand_NoMatch; + } + + switch (getLexer().getKind()) { + case AsmToken::Minus: + case AsmToken::Plus: { + if (getLexer().peekTok().is(AsmToken::Integer)) + return MatchOperand_NoMatch; + LLVM_FALLTHROUGH; + } + + case AsmToken::Equal: + case AsmToken::Greater: + case AsmToken::Less: + case AsmToken::Pipe: + case AsmToken::Star: + case AsmToken::LParen: + case AsmToken::RParen: + case AsmToken::LBrac: + case AsmToken::RBrac: + case AsmToken::Slash: + case AsmToken::Amp: + case AsmToken::Percent: + case AsmToken::Caret: { + StringRef Name = getLexer().getTok().getString(); + getLexer().Lex(); + Operands.push_back(SBFOperand::createToken(Name, S)); + + return MatchOperand_Success; + } + + case AsmToken::EqualEqual: + case AsmToken::ExclaimEqual: + case AsmToken::GreaterEqual: + case AsmToken::GreaterGreater: + case AsmToken::LessEqual: + case AsmToken::LessLess: { + Operands.push_back(SBFOperand::createToken( + getLexer().getTok().getString().substr(0, 1), S)); + Operands.push_back(SBFOperand::createToken( + getLexer().getTok().getString().substr(1, 1), S)); + getLexer().Lex(); + + return MatchOperand_Success; + } + + default: + break; + } + + return MatchOperand_NoMatch; +} + +OperandMatchResultTy SBFAsmParser::parseRegister(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::Identifier: + StringRef Name = getLexer().getTok().getIdentifier(); + unsigned RegNo = MatchRegisterName(Name); + + if (RegNo == 0) + return MatchOperand_NoMatch; + + getLexer().Lex(); + Operands.push_back(SBFOperand::createReg(RegNo, S, E)); + } + return MatchOperand_Success; +} + +OperandMatchResultTy SBFAsmParser::parseImmediate(OperandVector &Operands) { + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::LParen: + case AsmToken::Minus: + case AsmToken::Plus: + case AsmToken::Integer: + case AsmToken::String: + case AsmToken::Identifier: + break; + } + + const MCExpr *IdVal; + SMLoc S = getLoc(); + + if (getParser().parseExpression(IdVal)) + return MatchOperand_ParseFail; + + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + Operands.push_back(SBFOperand::createImm(IdVal, S, E)); + + return MatchOperand_Success; +} + +/// ParseInstruction - Parse an SBF instruction which is in SBF verifier +/// format. +bool SBFAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) { + // The first operand could be either register or actually an operator. + unsigned RegNo = MatchRegisterName(Name); + + if (RegNo != 0) { + SMLoc E = SMLoc::getFromPointer(NameLoc.getPointer() - 1); + Operands.push_back(SBFOperand::createReg(RegNo, NameLoc, E)); + } else if (SBFOperand::isValidIdAtStart (Name)) + Operands.push_back(SBFOperand::createToken(Name, NameLoc)); + else + return Error(NameLoc, "invalid register/token name"); + + while (!getLexer().is(AsmToken::EndOfStatement)) { + // Attempt to parse token as operator + if (parseOperandAsOperator(Operands) == MatchOperand_Success) + continue; + + // Attempt to parse token as register + if (parseRegister(Operands) == MatchOperand_Success) + continue; + + // Attempt to parse token as an immediate + if (parseImmediate(Operands) != MatchOperand_Success) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token"); + } + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + + getParser().eatToEndOfStatement(); + + return Error(Loc, "unexpected token"); + } + + // Consume the EndOfStatement. + getParser().Lex(); + return false; +} + +bool SBFAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSBFAsmParser() { + RegisterMCAsmParser XX(getTheSBFXTarget()); +} diff --git a/llvm/lib/Target/SBF/BTF.def b/llvm/lib/Target/SBF/BTF.def new file mode 100644 index 0000000000000..0ae4194bc512e --- /dev/null +++ b/llvm/lib/Target/SBF/BTF.def @@ -0,0 +1,37 @@ +//===- BTF.def - BTF definitions --------------------------------*- 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 BTF. +// +//===----------------------------------------------------------------------===// + +#if !defined(HANDLE_BTF_KIND) +#error "Missing macro definition of HANDLE_BTF_*" +#endif + +HANDLE_BTF_KIND(0, UNKN) +HANDLE_BTF_KIND(1, INT) +HANDLE_BTF_KIND(2, PTR) +HANDLE_BTF_KIND(3, ARRAY) +HANDLE_BTF_KIND(4, STRUCT) +HANDLE_BTF_KIND(5, UNION) +HANDLE_BTF_KIND(6, ENUM) +HANDLE_BTF_KIND(7, FWD) +HANDLE_BTF_KIND(8, TYPEDEF) +HANDLE_BTF_KIND(9, VOLATILE) +HANDLE_BTF_KIND(10, CONST) +HANDLE_BTF_KIND(11, RESTRICT) +HANDLE_BTF_KIND(12, FUNC) +HANDLE_BTF_KIND(13, FUNC_PROTO) +HANDLE_BTF_KIND(14, VAR) +HANDLE_BTF_KIND(15, DATASEC) +HANDLE_BTF_KIND(16, FLOAT) +HANDLE_BTF_KIND(17, DECL_TAG) +HANDLE_BTF_KIND(18, TYPE_TAG) + +#undef HANDLE_BTF_KIND diff --git a/llvm/lib/Target/SBF/BTF.h b/llvm/lib/Target/SBF/BTF.h new file mode 100644 index 0000000000000..89fe9e9f4c9d3 --- /dev/null +++ b/llvm/lib/Target/SBF/BTF.h @@ -0,0 +1,261 @@ +//===-- BTF.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 contains the layout of .BTF and .BTF.ext ELF sections. +/// +/// The binary layout for .BTF section: +/// struct Header +/// Type and Str subsections +/// The Type subsection is a collection of types with type id starting with 1. +/// The Str subsection is simply a collection of strings. +/// +/// The binary layout for .BTF.ext section: +/// struct ExtHeader +/// FuncInfo, LineInfo, FieldReloc and ExternReloc subsections +/// The FuncInfo subsection is defined as below: +/// BTFFuncInfo Size +/// struct SecFuncInfo for ELF section #1 +/// A number of struct SBFFuncInfo for ELF section #1 +/// struct SecFuncInfo for ELF section #2 +/// A number of struct SBFFuncInfo for ELF section #2 +/// ... +/// The LineInfo subsection is defined as below: +/// SBFLineInfo Size +/// struct SecLineInfo for ELF section #1 +/// A number of struct SBFLineInfo for ELF section #1 +/// struct SecLineInfo for ELF section #2 +/// A number of struct SBFLineInfo for ELF section #2 +/// ... +/// The FieldReloc subsection is defined as below: +/// SBFFieldReloc Size +/// struct SecFieldReloc for ELF section #1 +/// A number of struct SBFFieldReloc for ELF section #1 +/// struct SecFieldReloc for ELF section #2 +/// A number of struct SBFFieldReloc for ELF section #2 +/// ... +/// +/// The section formats are also defined at +/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/btf.h +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_BTF_H +#define LLVM_LIB_TARGET_SBF_BTF_H + +namespace llvm { +namespace BTF { + +enum : uint32_t { MAGIC = 0xeB9F, VERSION = 1 }; + +/// Sizes in bytes of various things in the BTF format. +enum { + HeaderSize = 24, + ExtHeaderSize = 32, + CommonTypeSize = 12, + BTFArraySize = 12, + BTFEnumSize = 8, + BTFMemberSize = 12, + BTFParamSize = 8, + BTFDataSecVarSize = 12, + SecFuncInfoSize = 8, + SecLineInfoSize = 8, + SecFieldRelocSize = 8, + SBFFuncInfoSize = 8, + SBFLineInfoSize = 16, + SBFFieldRelocSize = 16, +}; + +/// The .BTF section header definition. +struct Header { + uint16_t Magic; ///< Magic value + uint8_t Version; ///< Version number + uint8_t Flags; ///< Extra flags + uint32_t HdrLen; ///< Length of this header + + /// All offsets are in bytes relative to the end of this header. + uint32_t TypeOff; ///< Offset of type section + uint32_t TypeLen; ///< Length of type section + uint32_t StrOff; ///< Offset of string section + uint32_t StrLen; ///< Length of string section +}; + +enum : uint32_t { + MAX_VLEN = 0xffff ///< Max # of struct/union/enum members or func args +}; + +enum TypeKinds : uint8_t { +#define HANDLE_BTF_KIND(ID, NAME) BTF_KIND_##NAME = ID, +#include "BTF.def" +}; + +/// The BTF common type definition. Different kinds may have +/// additional information after this structure data. +struct CommonType { + /// Type name offset in the string table. + uint32_t NameOff; + + /// "Info" bits arrangement: + /// Bits 0-15: vlen (e.g. # of struct's members) + /// Bits 16-23: unused + /// Bits 24-27: kind (e.g. int, ptr, array...etc) + /// Bits 28-30: unused + /// Bit 31: kind_flag, currently used by + /// struct, union and fwd + uint32_t Info; + + /// "Size" is used by INT, ENUM, STRUCT and UNION. + /// "Size" tells the size of the type it is describing. + /// + /// "Type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, + /// FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG. + /// "Type" is a type_id referring to another type. + union { + uint32_t Size; + uint32_t Type; + }; +}; + +// For some specific BTF_KIND, "struct CommonType" is immediately +// followed by extra data. + +// BTF_KIND_INT is followed by a u32 and the following +// is the 32 bits arrangement: +// BTF_INT_ENCODING(VAL) : (((VAL) & 0x0f000000) >> 24) +// BTF_INT_OFFSET(VAL) : (((VAL & 0x00ff0000)) >> 16) +// BTF_INT_BITS(VAL) : ((VAL) & 0x000000ff) + +/// Attributes stored in the INT_ENCODING. +enum : uint8_t { + INT_SIGNED = (1 << 0), + INT_CHAR = (1 << 1), + INT_BOOL = (1 << 2) +}; + +/// BTF_KIND_ENUM is followed by multiple "struct BTFEnum". +/// The exact number of btf_enum is stored in the vlen (of the +/// info in "struct CommonType"). +struct BTFEnum { + uint32_t NameOff; ///< Enum name offset in the string table + int32_t Val; ///< Enum member value +}; + +/// BTF_KIND_ARRAY is followed by one "struct BTFArray". +struct BTFArray { + uint32_t ElemType; ///< Element type + uint32_t IndexType; ///< Index type + uint32_t Nelems; ///< Number of elements for this array +}; + +/// BTF_KIND_STRUCT and BTF_KIND_UNION are followed +/// by multiple "struct BTFMember". The exact number +/// of BTFMember is stored in the vlen (of the info in +/// "struct CommonType"). +/// +/// If the struct/union contains any bitfield member, +/// the Offset below represents BitOffset (bits 0 - 23) +/// and BitFieldSize(bits 24 - 31) with BitFieldSize = 0 +/// for non bitfield members. Otherwise, the Offset +/// represents the BitOffset. +struct BTFMember { + uint32_t NameOff; ///< Member name offset in the string table + uint32_t Type; ///< Member type + uint32_t Offset; ///< BitOffset or BitFieldSize+BitOffset +}; + +/// BTF_KIND_FUNC_PROTO are followed by multiple "struct BTFParam". +/// The exist number of BTFParam is stored in the vlen (of the info +/// in "struct CommonType"). +struct BTFParam { + uint32_t NameOff; + uint32_t Type; +}; + +/// BTF_KIND_FUNC can be global, static or extern. +enum : uint8_t { + FUNC_STATIC = 0, + FUNC_GLOBAL = 1, + FUNC_EXTERN = 2, +}; + +/// Variable scoping information. +enum : uint8_t { + VAR_STATIC = 0, ///< Linkage: InternalLinkage + VAR_GLOBAL_ALLOCATED = 1, ///< Linkage: ExternalLinkage + VAR_GLOBAL_EXTERNAL = 2, ///< Linkage: ExternalLinkage +}; + +/// BTF_KIND_DATASEC are followed by multiple "struct BTFDataSecVar". +/// The exist number of BTFDataSec is stored in the vlen (of the info +/// in "struct CommonType"). +struct BTFDataSec { + uint32_t Type; ///< A BTF_KIND_VAR type + uint32_t Offset; ///< In-section offset + uint32_t Size; ///< Occupied memory size +}; + +/// The .BTF.ext section header definition. +struct ExtHeader { + uint16_t Magic; + uint8_t Version; + uint8_t Flags; + uint32_t HdrLen; + + uint32_t FuncInfoOff; ///< Offset of func info section + uint32_t FuncInfoLen; ///< Length of func info section + uint32_t LineInfoOff; ///< Offset of line info section + uint32_t LineInfoLen; ///< Length of line info section + uint32_t FieldRelocOff; ///< Offset of offset reloc section + uint32_t FieldRelocLen; ///< Length of offset reloc section +}; + +/// Specifying one function info. +struct SBFFuncInfo { + uint32_t InsnOffset; ///< Byte offset in the section + uint32_t TypeId; ///< Type id referring to .BTF type section +}; + +/// Specifying function info's in one section. +struct SecFuncInfo { + uint32_t SecNameOff; ///< Section name index in the .BTF string table + uint32_t NumFuncInfo; ///< Number of func info's in this section +}; + +/// Specifying one line info. +struct SBFLineInfo { + uint32_t InsnOffset; ///< Byte offset in this section + uint32_t FileNameOff; ///< File name index in the .BTF string table + uint32_t LineOff; ///< Line index in the .BTF string table + uint32_t LineCol; ///< Line num: line_col >> 10, + /// col num: line_col & 0x3ff +}; + +/// Specifying line info's in one section. +struct SecLineInfo { + uint32_t SecNameOff; ///< Section name index in the .BTF string table + uint32_t NumLineInfo; ///< Number of line info's in this section +}; + +/// Specifying one offset relocation. +struct SBFFieldReloc { + uint32_t InsnOffset; ///< Byte offset in this section + uint32_t TypeID; ///< TypeID for the relocation + uint32_t OffsetNameOff; ///< The string to traverse types + uint32_t RelocKind; ///< What to patch the instruction +}; + +/// Specifying offset relocation's in one section. +struct SecFieldReloc { + uint32_t SecNameOff; ///< Section name index in the .BTF string table + uint32_t NumFieldReloc; ///< Number of offset reloc's in this section +}; + +} // End namespace BTF. +} // End namespace llvm. + +#endif diff --git a/llvm/lib/Target/SBF/BTFDebug.cpp b/llvm/lib/Target/SBF/BTFDebug.cpp new file mode 100644 index 0000000000000..b3f6e626011d7 --- /dev/null +++ b/llvm/lib/Target/SBF/BTFDebug.cpp @@ -0,0 +1,1514 @@ +//===- BTFDebug.cpp - BTF Generator ---------------------------------------===// +// +// 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 support for writing BTF debug info. +// +//===----------------------------------------------------------------------===// + +#include "BTFDebug.h" +#include "SBF.h" +#include "SBFCORE.h" +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + +using namespace llvm; +using namespace BTFX; + +static const char *BTFKindStr[] = { +#define HANDLE_BTF_KIND(ID, NAME) "BTF_KIND_" #NAME, +#include "BTF.def" +}; + +/// Emit a BTF common type. +void BTFTypeBase::emitType(MCStreamer &OS) { + OS.AddComment(std::string(BTFKindStr[Kind]) + "(id = " + std::to_string(Id) + + ")"); + OS.emitInt32(BTFType.NameOff); + OS.AddComment("0x" + Twine::utohexstr(BTFType.Info)); + OS.emitInt32(BTFType.Info); + OS.emitInt32(BTFType.Size); +} + +BTFTypeDerived::BTFTypeDerived(const DIDerivedType *DTy, unsigned Tag, + bool NeedsFixup) + : DTy(DTy), NeedsFixup(NeedsFixup), Name(DTy->getName()) { + switch (Tag) { + case dwarf::DW_TAG_pointer_type: + Kind = BTF::BTF_KIND_PTR; + break; + case dwarf::DW_TAG_const_type: + Kind = BTF::BTF_KIND_CONST; + break; + case dwarf::DW_TAG_volatile_type: + Kind = BTF::BTF_KIND_VOLATILE; + break; + case dwarf::DW_TAG_typedef: + Kind = BTF::BTF_KIND_TYPEDEF; + break; + case dwarf::DW_TAG_restrict_type: + Kind = BTF::BTF_KIND_RESTRICT; + break; + default: + llvm_unreachable("Unknown DIDerivedType Tag"); + } + BTFType.Info = Kind << 24; +} + +/// Used by DW_TAG_pointer_type only. +BTFTypeDerived::BTFTypeDerived(unsigned NextTypeId, unsigned Tag, + StringRef Name) + : DTy(nullptr), NeedsFixup(false), Name(Name) { + Kind = BTF::BTF_KIND_PTR; + BTFType.Info = Kind << 24; + BTFType.Type = NextTypeId; +} + +void BTFTypeDerived::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(Name); + + if (NeedsFixup || !DTy) + return; + + // The base type for PTR/CONST/VOLATILE could be void. + const DIType *ResolvedType = DTy->getBaseType(); + if (!ResolvedType) { + assert((Kind == BTF::BTF_KIND_PTR || Kind == BTF::BTF_KIND_CONST || + Kind == BTF::BTF_KIND_VOLATILE) && + "Invalid null basetype"); + BTFType.Type = 0; + } else { + BTFType.Type = BDebug.getTypeId(ResolvedType); + } +} + +void BTFTypeDerived::emitType(MCStreamer &OS) { BTFTypeBase::emitType(OS); } + +void BTFTypeDerived::setPointeeType(uint32_t PointeeType) { + BTFType.Type = PointeeType; +} + +/// Represent a struct/union forward declaration. +BTFTypeFwd::BTFTypeFwd(StringRef Name, bool IsUnion) : Name(Name) { + Kind = BTF::BTF_KIND_FWD; + BTFType.Info = IsUnion << 31 | Kind << 24; + BTFType.Type = 0; +} + +void BTFTypeFwd::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(Name); +} + +void BTFTypeFwd::emitType(MCStreamer &OS) { BTFTypeBase::emitType(OS); } + +BTFTypeInt::BTFTypeInt(uint32_t Encoding, uint32_t SizeInBits, + uint32_t OffsetInBits, StringRef TypeName) + : Name(TypeName) { + // Translate IR int encoding to BTF int encoding. + uint8_t BTFEncoding; + switch (Encoding) { + case dwarf::DW_ATE_boolean: + BTFEncoding = BTF::INT_BOOL; + break; + case dwarf::DW_ATE_signed: + case dwarf::DW_ATE_signed_char: + BTFEncoding = BTF::INT_SIGNED; + break; + case dwarf::DW_ATE_unsigned: + case dwarf::DW_ATE_unsigned_char: + BTFEncoding = 0; + break; + default: + llvm_unreachable("Unknown BTFTypeInt Encoding"); + } + + Kind = BTF::BTF_KIND_INT; + BTFType.Info = Kind << 24; + BTFType.Size = roundupToBytes(SizeInBits); + IntVal = (BTFEncoding << 24) | OffsetInBits << 16 | SizeInBits; +} + +void BTFTypeInt::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(Name); +} + +void BTFTypeInt::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + OS.AddComment("0x" + Twine::utohexstr(IntVal)); + OS.emitInt32(IntVal); +} + +BTFTypeEnum::BTFTypeEnum(const DICompositeType *ETy, uint32_t VLen) : ETy(ETy) { + Kind = BTF::BTF_KIND_ENUM; + BTFType.Info = Kind << 24 | VLen; + BTFType.Size = roundupToBytes(ETy->getSizeInBits()); +} + +void BTFTypeEnum::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(ETy->getName()); + + DINodeArray Elements = ETy->getElements(); + for (const auto Element : Elements) { + const auto *Enum = cast(Element); + + struct BTF::BTFEnum BTFEnum; + BTFEnum.NameOff = BDebug.addString(Enum->getName()); + // BTF enum value is 32bit, enforce it. + uint32_t Value; + if (Enum->isUnsigned()) + Value = static_cast(Enum->getValue().getZExtValue()); + else + Value = static_cast(Enum->getValue().getSExtValue()); + BTFEnum.Val = Value; + EnumValues.push_back(BTFEnum); + } +} + +void BTFTypeEnum::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + for (const auto &Enum : EnumValues) { + OS.emitInt32(Enum.NameOff); + OS.emitInt32(Enum.Val); + } +} + +BTFTypeArray::BTFTypeArray(uint32_t ElemTypeId, uint32_t NumElems) { + Kind = BTF::BTF_KIND_ARRAY; + BTFType.NameOff = 0; + BTFType.Info = Kind << 24; + BTFType.Size = 0; + + ArrayInfo.ElemType = ElemTypeId; + ArrayInfo.Nelems = NumElems; +} + +/// Represent a BTF array. +void BTFTypeArray::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + // The IR does not really have a type for the index. + // A special type for array index should have been + // created during initial type traversal. Just + // retrieve that type id. + ArrayInfo.IndexType = BDebug.getArrayIndexTypeId(); +} + +void BTFTypeArray::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + OS.emitInt32(ArrayInfo.ElemType); + OS.emitInt32(ArrayInfo.IndexType); + OS.emitInt32(ArrayInfo.Nelems); +} + +/// Represent either a struct or a union. +BTFTypeStruct::BTFTypeStruct(const DICompositeType *STy, bool IsStruct, + bool HasBitField, uint32_t Vlen) + : STy(STy), HasBitField(HasBitField) { + Kind = IsStruct ? BTF::BTF_KIND_STRUCT : BTF::BTF_KIND_UNION; + BTFType.Size = roundupToBytes(STy->getSizeInBits()); + BTFType.Info = (HasBitField << 31) | (Kind << 24) | Vlen; +} + +void BTFTypeStruct::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(STy->getName()); + + // Add struct/union members. + const DINodeArray Elements = STy->getElements(); + for (const auto *Element : Elements) { + struct BTF::BTFMember BTFMember; + const auto *DDTy = cast(Element); + + BTFMember.NameOff = BDebug.addString(DDTy->getName()); + if (HasBitField) { + uint8_t BitFieldSize = DDTy->isBitField() ? DDTy->getSizeInBits() : 0; + BTFMember.Offset = BitFieldSize << 24 | DDTy->getOffsetInBits(); + } else { + BTFMember.Offset = DDTy->getOffsetInBits(); + } + const auto *BaseTy = DDTy->getBaseType(); + BTFMember.Type = BDebug.getTypeId(BaseTy); + Members.push_back(BTFMember); + } +} + +void BTFTypeStruct::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + for (const auto &Member : Members) { + OS.emitInt32(Member.NameOff); + OS.emitInt32(Member.Type); + OS.AddComment("0x" + Twine::utohexstr(Member.Offset)); + OS.emitInt32(Member.Offset); + } +} + +std::string BTFTypeStruct::getName() { return std::string(STy->getName()); } + +/// The Func kind represents both subprogram and pointee of function +/// pointers. If the FuncName is empty, it represents a pointee of function +/// pointer. Otherwise, it represents a subprogram. The func arg names +/// are empty for pointee of function pointer case, and are valid names +/// for subprogram. +BTFTypeFuncProto::BTFTypeFuncProto( + const DISubroutineType *STy, uint32_t VLen, + const std::unordered_map &FuncArgNames) + : STy(STy), FuncArgNames(FuncArgNames) { + Kind = BTF::BTF_KIND_FUNC_PROTO; + BTFType.Info = (Kind << 24) | VLen; +} + +void BTFTypeFuncProto::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + DITypeRefArray Elements = STy->getTypeArray(); + auto RetType = Elements[0]; + BTFType.Type = RetType ? BDebug.getTypeId(RetType) : 0; + BTFType.NameOff = 0; + + // For null parameter which is typically the last one + // to represent the vararg, encode the NameOff/Type to be 0. + for (unsigned I = 1, N = Elements.size(); I < N; ++I) { + struct BTF::BTFParam Param; + auto Element = Elements[I]; + if (Element) { + Param.NameOff = BDebug.addString(FuncArgNames[I]); + Param.Type = BDebug.getTypeId(Element); + } else { + Param.NameOff = 0; + Param.Type = 0; + } + Parameters.push_back(Param); + } +} + +void BTFTypeFuncProto::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + for (const auto &Param : Parameters) { + OS.emitInt32(Param.NameOff); + OS.emitInt32(Param.Type); + } +} + +BTFTypeFunc::BTFTypeFunc(StringRef FuncName, uint32_t ProtoTypeId, + uint32_t Scope) + : Name(FuncName) { + Kind = BTF::BTF_KIND_FUNC; + BTFType.Info = (Kind << 24) | Scope; + BTFType.Type = ProtoTypeId; +} + +void BTFTypeFunc::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(Name); +} + +void BTFTypeFunc::emitType(MCStreamer &OS) { BTFTypeBase::emitType(OS); } + +BTFKindVar::BTFKindVar(StringRef VarName, uint32_t TypeId, uint32_t VarInfo) + : Name(VarName) { + Kind = BTF::BTF_KIND_VAR; + BTFType.Info = Kind << 24; + BTFType.Type = TypeId; + Info = VarInfo; +} + +void BTFKindVar::completeType(BTFDebug &BDebug) { + BTFType.NameOff = BDebug.addString(Name); +} + +void BTFKindVar::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + OS.emitInt32(Info); +} + +BTFKindDataSec::BTFKindDataSec(AsmPrinter *AsmPrt, std::string SecName) + : Asm(AsmPrt), Name(SecName) { + Kind = BTF::BTF_KIND_DATASEC; + BTFType.Info = Kind << 24; + BTFType.Size = 0; +} + +void BTFKindDataSec::completeType(BTFDebug &BDebug) { + BTFType.NameOff = BDebug.addString(Name); + BTFType.Info |= Vars.size(); +} + +void BTFKindDataSec::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + + for (const auto &V : Vars) { + OS.emitInt32(std::get<0>(V)); + Asm->emitLabelReference(std::get<1>(V), 4); + OS.emitInt32(std::get<2>(V)); + } +} + +BTFTypeFloat::BTFTypeFloat(uint32_t SizeInBits, StringRef TypeName) + : Name(TypeName) { + Kind = BTF::BTF_KIND_FLOAT; + BTFType.Info = Kind << 24; + BTFType.Size = roundupToBytes(SizeInBits); +} + +void BTFTypeFloat::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(Name); +} + +BTFTypeDeclTag::BTFTypeDeclTag(uint32_t BaseTypeId, int ComponentIdx, + StringRef Tag) + : Tag(Tag) { + Kind = BTF::BTF_KIND_DECL_TAG; + BTFType.Info = Kind << 24; + BTFType.Type = BaseTypeId; + Info = ComponentIdx; +} + +void BTFTypeDeclTag::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + + BTFType.NameOff = BDebug.addString(Tag); +} + +void BTFTypeDeclTag::emitType(MCStreamer &OS) { + BTFTypeBase::emitType(OS); + OS.emitInt32(Info); +} + +BTFTypeTypeTag::BTFTypeTypeTag(uint32_t NextTypeId, StringRef Tag) + : DTy(nullptr), Tag(Tag) { + Kind = BTF::BTF_KIND_TYPE_TAG; + BTFType.Info = Kind << 24; + BTFType.Type = NextTypeId; +} + +BTFTypeTypeTag::BTFTypeTypeTag(const DIDerivedType *DTy, StringRef Tag) + : DTy(DTy), Tag(Tag) { + Kind = BTF::BTF_KIND_TYPE_TAG; + BTFType.Info = Kind << 24; +} + +void BTFTypeTypeTag::completeType(BTFDebug &BDebug) { + if (IsCompleted) + return; + IsCompleted = true; + BTFType.NameOff = BDebug.addString(Tag); + if (DTy) { + const DIType *ResolvedType = DTy->getBaseType(); + if (!ResolvedType) + BTFType.Type = 0; + else + BTFType.Type = BDebug.getTypeId(ResolvedType); + } +} + +uint32_t BTFStringTable::addString(StringRef S) { + // Check whether the string already exists. + for (auto &OffsetM : OffsetToIdMap) { + if (Table[OffsetM.second] == S) + return OffsetM.first; + } + // Not find, add to the string table. + uint32_t Offset = Size; + OffsetToIdMap[Offset] = Table.size(); + Table.push_back(std::string(S)); + Size += S.size() + 1; + return Offset; +} + +BTFDebug::BTFDebug(AsmPrinter *AP) + : DebugHandlerBase(AP), OS(*Asm->OutStreamer), SkipInstruction(false), + LineInfoGenerated(false), SecNameOff(0), ArrayIndexTypeId(0), + MapDefNotCollected(true) { + addString("\0"); +} + +uint32_t BTFDebug::addType(std::unique_ptr TypeEntry, + const DIType *Ty) { + TypeEntry->setId(TypeEntries.size() + 1); + uint32_t Id = TypeEntry->getId(); + DIToIdMap[Ty] = Id; + TypeEntries.push_back(std::move(TypeEntry)); + return Id; +} + +uint32_t BTFDebug::addType(std::unique_ptr TypeEntry) { + TypeEntry->setId(TypeEntries.size() + 1); + uint32_t Id = TypeEntry->getId(); + TypeEntries.push_back(std::move(TypeEntry)); + return Id; +} + +void BTFDebug::visitBasicType(const DIBasicType *BTy, uint32_t &TypeId) { + // Only int and binary floating point types are supported in BTF. + uint32_t Encoding = BTy->getEncoding(); + std::unique_ptr TypeEntry; + switch (Encoding) { + case dwarf::DW_ATE_boolean: + case dwarf::DW_ATE_signed: + case dwarf::DW_ATE_signed_char: + case dwarf::DW_ATE_unsigned: + case dwarf::DW_ATE_unsigned_char: + // Create a BTF type instance for this DIBasicType and put it into + // DIToIdMap for cross-type reference check. + TypeEntry = std::make_unique( + Encoding, BTy->getSizeInBits(), BTy->getOffsetInBits(), BTy->getName()); + break; + case dwarf::DW_ATE_float: + TypeEntry = + std::make_unique(BTy->getSizeInBits(), BTy->getName()); + break; + default: + return; + } + + TypeId = addType(std::move(TypeEntry), BTy); +} + +/// Handle subprogram or subroutine types. +void BTFDebug::visitSubroutineType( + const DISubroutineType *STy, bool ForSubprog, + const std::unordered_map &FuncArgNames, + uint32_t &TypeId) { + DITypeRefArray Elements = STy->getTypeArray(); + uint32_t VLen = Elements.size() - 1; + if (VLen > BTF::MAX_VLEN) + return; + + // Subprogram has a valid non-zero-length name, and the pointee of + // a function pointer has an empty name. The subprogram type will + // not be added to DIToIdMap as it should not be referenced by + // any other types. + auto TypeEntry = std::make_unique(STy, VLen, FuncArgNames); + if (ForSubprog) + TypeId = addType(std::move(TypeEntry)); // For subprogram + else + TypeId = addType(std::move(TypeEntry), STy); // For func ptr + + // Visit return type and func arg types. + for (const auto Element : Elements) { + visitTypeEntry(Element); + } +} + +void BTFDebug::processDeclAnnotations(DINodeArray Annotations, + uint32_t BaseTypeId, + int ComponentIdx) { + if (!Annotations) + return; + + for (const Metadata *Annotation : Annotations->operands()) { + const MDNode *MD = cast(Annotation); + const MDString *Name = cast(MD->getOperand(0)); + if (!Name->getString().equals("btf_decl_tag")) + continue; + + const MDString *Value = cast(MD->getOperand(1)); + auto TypeEntry = std::make_unique(BaseTypeId, ComponentIdx, + Value->getString()); + addType(std::move(TypeEntry)); + } +} + +/// Handle structure/union types. +void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct, + uint32_t &TypeId) { + const DINodeArray Elements = CTy->getElements(); + uint32_t VLen = Elements.size(); + if (VLen > BTF::MAX_VLEN) + return; + + // Check whether we have any bitfield members or not + bool HasBitField = false; + for (const auto *Element : Elements) { + auto E = cast(Element); + if (E->isBitField()) { + HasBitField = true; + break; + } + } + + auto TypeEntry = + std::make_unique(CTy, IsStruct, HasBitField, VLen); + StructTypes.push_back(TypeEntry.get()); + TypeId = addType(std::move(TypeEntry), CTy); + + // Check struct/union annotations + processDeclAnnotations(CTy->getAnnotations(), TypeId, -1); + + // Visit all struct members. + int FieldNo = 0; + for (const auto *Element : Elements) { + const auto Elem = cast(Element); + visitTypeEntry(Elem); + processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo); + FieldNo++; + } +} + +void BTFDebug::visitArrayType(const DICompositeType *CTy, uint32_t &TypeId) { + // Visit array element type. + uint32_t ElemTypeId; + const DIType *ElemType = CTy->getBaseType(); + visitTypeEntry(ElemType, ElemTypeId, false, false); + + // Visit array dimensions. + DINodeArray Elements = CTy->getElements(); + for (int I = Elements.size() - 1; I >= 0; --I) { + if (auto *Element = dyn_cast_or_null(Elements[I])) + if (Element->getTag() == dwarf::DW_TAG_subrange_type) { + const DISubrange *SR = cast(Element); + auto *CI = SR->getCount().dyn_cast(); + int64_t Count = CI->getSExtValue(); + + // For struct s { int b; char c[]; }, the c[] will be represented + // as an array with Count = -1. + auto TypeEntry = + std::make_unique(ElemTypeId, + Count >= 0 ? Count : 0); + if (I == 0) + ElemTypeId = addType(std::move(TypeEntry), CTy); + else + ElemTypeId = addType(std::move(TypeEntry)); + } + } + + // The array TypeId is the type id of the outermost dimension. + TypeId = ElemTypeId; + + // The IR does not have a type for array index while BTF wants one. + // So create an array index type if there is none. + if (!ArrayIndexTypeId) { + auto TypeEntry = std::make_unique(dwarf::DW_ATE_unsigned, 32, + 0, "__ARRAY_SIZE_TYPE__"); + ArrayIndexTypeId = addType(std::move(TypeEntry)); + } +} + +void BTFDebug::visitEnumType(const DICompositeType *CTy, uint32_t &TypeId) { + DINodeArray Elements = CTy->getElements(); + uint32_t VLen = Elements.size(); + if (VLen > BTF::MAX_VLEN) + return; + + auto TypeEntry = std::make_unique(CTy, VLen); + TypeId = addType(std::move(TypeEntry), CTy); + // No need to visit base type as BTF does not encode it. +} + +/// Handle structure/union forward declarations. +void BTFDebug::visitFwdDeclType(const DICompositeType *CTy, bool IsUnion, + uint32_t &TypeId) { + auto TypeEntry = std::make_unique(CTy->getName(), IsUnion); + TypeId = addType(std::move(TypeEntry), CTy); +} + +/// Handle structure, union, array and enumeration types. +void BTFDebug::visitCompositeType(const DICompositeType *CTy, + uint32_t &TypeId) { + auto Tag = CTy->getTag(); + if (Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type) { + // Handle forward declaration differently as it does not have members. + if (CTy->isForwardDecl()) + visitFwdDeclType(CTy, Tag == dwarf::DW_TAG_union_type, TypeId); + else + visitStructType(CTy, Tag == dwarf::DW_TAG_structure_type, TypeId); + } else if (Tag == dwarf::DW_TAG_array_type) + visitArrayType(CTy, TypeId); + else if (Tag == dwarf::DW_TAG_enumeration_type) + visitEnumType(CTy, TypeId); +} + +/// Handle pointer, typedef, const, volatile, restrict and member types. +void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId, + bool CheckPointer, bool SeenPointer) { + unsigned Tag = DTy->getTag(); + + /// Try to avoid chasing pointees, esp. structure pointees which may + /// unnecessary bring in a lot of types. + if (CheckPointer && !SeenPointer) { + SeenPointer = Tag == dwarf::DW_TAG_pointer_type; + } + + if (CheckPointer && SeenPointer) { + const DIType *Base = DTy->getBaseType(); + if (Base) { + if (const auto *CTy = dyn_cast(Base)) { + auto CTag = CTy->getTag(); + if ((CTag == dwarf::DW_TAG_structure_type || + CTag == dwarf::DW_TAG_union_type) && + !CTy->getName().empty() && !CTy->isForwardDecl()) { + /// Find a candidate, generate a fixup. Later on the struct/union + /// pointee type will be replaced with either a real type or + /// a forward declaration. + auto TypeEntry = std::make_unique(DTy, Tag, true); + auto &Fixup = FixupDerivedTypes[CTy->getName()]; + Fixup.first = CTag == dwarf::DW_TAG_union_type; + Fixup.second.push_back(TypeEntry.get()); + TypeId = addType(std::move(TypeEntry), DTy); + return; + } + } + } + } + + if (Tag == dwarf::DW_TAG_pointer_type) { + SmallVector MDStrs; + DINodeArray Annots = DTy->getAnnotations(); + if (Annots) { + // For type with "int __tag1 __tag2 *p", the MDStrs will have + // content: [__tag1, __tag2]. + for (const Metadata *Annotations : Annots->operands()) { + const MDNode *MD = cast(Annotations); + const MDString *Name = cast(MD->getOperand(0)); + if (!Name->getString().equals("btf_type_tag")) + continue; + MDStrs.push_back(cast(MD->getOperand(1))); + } + } + + if (MDStrs.size() > 0) { + // With MDStrs [__tag1, __tag2], the output type chain looks like + // PTR -> __tag2 -> __tag1 -> BaseType + // In the below, we construct BTF types with the order of __tag1, __tag2 + // and PTR. + auto TypeEntry = + std::make_unique(DTy, MDStrs[0]->getString()); + unsigned TmpTypeId = addType(std::move(TypeEntry)); + for (unsigned I = 1; I < MDStrs.size(); I++) { + const MDString *Value = MDStrs[I]; + TypeEntry = + std::make_unique(TmpTypeId, Value->getString()); + TmpTypeId = addType(std::move(TypeEntry)); + } + auto TypeDEntry = + std::make_unique(TmpTypeId, Tag, DTy->getName()); + TypeId = addType(std::move(TypeDEntry), DTy); + } else { + auto TypeEntry = std::make_unique(DTy, Tag, false); + TypeId = addType(std::move(TypeEntry), DTy); + } + } else if (Tag == dwarf::DW_TAG_typedef || Tag == dwarf::DW_TAG_const_type || + Tag == dwarf::DW_TAG_volatile_type || + Tag == dwarf::DW_TAG_restrict_type) { + auto TypeEntry = std::make_unique(DTy, Tag, false); + TypeId = addType(std::move(TypeEntry), DTy); + if (Tag == dwarf::DW_TAG_typedef) + processDeclAnnotations(DTy->getAnnotations(), TypeId, -1); + } else if (Tag != dwarf::DW_TAG_member) { + return; + } + + // Visit base type of pointer, typedef, const, volatile, restrict or + // struct/union member. + uint32_t TempTypeId = 0; + if (Tag == dwarf::DW_TAG_member) + visitTypeEntry(DTy->getBaseType(), TempTypeId, true, false); + else + visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer); +} + +void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId, + bool CheckPointer, bool SeenPointer) { + if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) { + TypeId = DIToIdMap[Ty]; + + // To handle the case like the following: + // struct t; + // typedef struct t _t; + // struct s1 { _t *c; }; + // int test1(struct s1 *arg) { ... } + // + // struct t { int a; int b; }; + // struct s2 { _t c; } + // int test2(struct s2 *arg) { ... } + // + // During traversing test1() argument, "_t" is recorded + // in DIToIdMap and a forward declaration fixup is created + // for "struct t" to avoid pointee type traversal. + // + // During traversing test2() argument, even if we see "_t" is + // already defined, we should keep moving to eventually + // bring in types for "struct t". Otherwise, the "struct s2" + // definition won't be correct. + if (Ty && (!CheckPointer || !SeenPointer)) { + if (const auto *DTy = dyn_cast(Ty)) { + unsigned Tag = DTy->getTag(); + if (Tag == dwarf::DW_TAG_typedef || Tag == dwarf::DW_TAG_const_type || + Tag == dwarf::DW_TAG_volatile_type || + Tag == dwarf::DW_TAG_restrict_type) { + uint32_t TmpTypeId; + visitTypeEntry(DTy->getBaseType(), TmpTypeId, CheckPointer, + SeenPointer); + } + } + } + + return; + } + + if (const auto *BTy = dyn_cast(Ty)) + visitBasicType(BTy, TypeId); + else if (const auto *STy = dyn_cast(Ty)) + visitSubroutineType(STy, false, std::unordered_map(), + TypeId); + else if (const auto *CTy = dyn_cast(Ty)) + visitCompositeType(CTy, TypeId); + else if (const auto *DTy = dyn_cast(Ty)) + visitDerivedType(DTy, TypeId, CheckPointer, SeenPointer); + else + llvm_unreachable("Unknown DIType"); +} + +void BTFDebug::visitTypeEntry(const DIType *Ty) { + uint32_t TypeId; + visitTypeEntry(Ty, TypeId, false, false); +} + +void BTFDebug::visitMapDefType(const DIType *Ty, uint32_t &TypeId) { + if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) { + TypeId = DIToIdMap[Ty]; + return; + } + + // MapDef type may be a struct type or a non-pointer derived type + const DIType *OrigTy = Ty; + while (auto *DTy = dyn_cast(Ty)) { + auto Tag = DTy->getTag(); + if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type && + Tag != dwarf::DW_TAG_volatile_type && + Tag != dwarf::DW_TAG_restrict_type) + break; + Ty = DTy->getBaseType(); + } + + const auto *CTy = dyn_cast(Ty); + if (!CTy) + return; + + auto Tag = CTy->getTag(); + if (Tag != dwarf::DW_TAG_structure_type || CTy->isForwardDecl()) + return; + + // Visit all struct members to ensure pointee type is visited + const DINodeArray Elements = CTy->getElements(); + for (const auto *Element : Elements) { + const auto *MemberType = cast(Element); + visitTypeEntry(MemberType->getBaseType()); + } + + // Visit this type, struct or a const/typedef/volatile/restrict type + visitTypeEntry(OrigTy, TypeId, false, false); +} + +/// Read file contents from the actual file or from the source +std::string BTFDebug::populateFileContent(const DISubprogram *SP) { + auto File = SP->getFile(); + std::string FileName; + + if (!File->getFilename().startswith("/") && File->getDirectory().size()) + FileName = File->getDirectory().str() + "/" + File->getFilename().str(); + else + FileName = std::string(File->getFilename()); + + // No need to populate the contends if it has been populated! + if (FileContent.find(FileName) != FileContent.end()) + return FileName; + + std::vector Content; + std::string Line; + Content.push_back(Line); // Line 0 for empty string + + std::unique_ptr Buf; + auto Source = File->getSource(); + if (Source) + Buf = MemoryBuffer::getMemBufferCopy(*Source); + else if (ErrorOr> BufOrErr = + MemoryBuffer::getFile(FileName)) + Buf = std::move(*BufOrErr); + if (Buf) + for (line_iterator I(*Buf, false), E; I != E; ++I) + Content.push_back(std::string(*I)); + + FileContent[FileName] = Content; + return FileName; +} + +void BTFDebug::constructLineInfo(const DISubprogram *SP, MCSymbol *Label, + uint32_t Line, uint32_t Column) { + std::string FileName = populateFileContent(SP); + BTFLineInfo LineInfo; + + LineInfo.Label = Label; + LineInfo.FileNameOff = addString(FileName); + // If file content is not available, let LineOff = 0. + if (Line < FileContent[FileName].size()) + LineInfo.LineOff = addString(FileContent[FileName][Line]); + else + LineInfo.LineOff = 0; + LineInfo.LineNum = Line; + LineInfo.ColumnNum = Column; + LineInfoTable[SecNameOff].push_back(LineInfo); +} + +void BTFDebug::emitCommonHeader() { + OS.AddComment("0x" + Twine::utohexstr(BTF::MAGIC)); + OS.emitIntValue(BTF::MAGIC, 2); + OS.emitInt8(BTF::VERSION); + OS.emitInt8(0); +} + +void BTFDebug::emitBTFSection() { + // Do not emit section if no types and only "" string. + if (!TypeEntries.size() && StringTable.getSize() == 1) + return; + + MCContext &Ctx = OS.getContext(); + MCSectionELF *Sec = Ctx.getELFSection(".BTF", ELF::SHT_PROGBITS, 0); + Sec->setAlignment(Align(4)); + OS.SwitchSection(Sec); + + // Emit header. + emitCommonHeader(); + OS.emitInt32(BTF::HeaderSize); + + uint32_t TypeLen = 0, StrLen; + for (const auto &TypeEntry : TypeEntries) + TypeLen += TypeEntry->getSize(); + StrLen = StringTable.getSize(); + + OS.emitInt32(0); + OS.emitInt32(TypeLen); + OS.emitInt32(TypeLen); + OS.emitInt32(StrLen); + + // Emit type table. + for (const auto &TypeEntry : TypeEntries) + TypeEntry->emitType(OS); + + // Emit string table. + uint32_t StringOffset = 0; + for (const auto &S : StringTable.getTable()) { + OS.AddComment("string offset=" + std::to_string(StringOffset)); + OS.emitBytes(S); + OS.emitBytes(StringRef("\0", 1)); + StringOffset += S.size() + 1; + } +} + +void BTFDebug::emitBTFExtSection() { + // Do not emit section if empty FuncInfoTable and LineInfoTable + // and FieldRelocTable. + if (!FuncInfoTable.size() && !LineInfoTable.size() && + !FieldRelocTable.size()) + return; + + MCContext &Ctx = OS.getContext(); + MCSectionELF *Sec = Ctx.getELFSection(".BTF.ext", ELF::SHT_PROGBITS, 0); + Sec->setAlignment(Align(4)); + OS.SwitchSection(Sec); + + // Emit header. + emitCommonHeader(); + OS.emitInt32(BTF::ExtHeaderSize); + + // Account for FuncInfo/LineInfo record size as well. + uint32_t FuncLen = 4, LineLen = 4; + // Do not account for optional FieldReloc. + uint32_t FieldRelocLen = 0; + for (const auto &FuncSec : FuncInfoTable) { + FuncLen += BTF::SecFuncInfoSize; + FuncLen += FuncSec.second.size() * BTF::SBFFuncInfoSize; + } + for (const auto &LineSec : LineInfoTable) { + LineLen += BTF::SecLineInfoSize; + LineLen += LineSec.second.size() * BTF::SBFLineInfoSize; + } + for (const auto &FieldRelocSec : FieldRelocTable) { + FieldRelocLen += BTF::SecFieldRelocSize; + FieldRelocLen += FieldRelocSec.second.size() * BTF::SBFFieldRelocSize; + } + + if (FieldRelocLen) + FieldRelocLen += 4; + + OS.emitInt32(0); + OS.emitInt32(FuncLen); + OS.emitInt32(FuncLen); + OS.emitInt32(LineLen); + OS.emitInt32(FuncLen + LineLen); + OS.emitInt32(FieldRelocLen); + + // Emit func_info table. + OS.AddComment("FuncInfo"); + OS.emitInt32(BTF::SBFFuncInfoSize); + for (const auto &FuncSec : FuncInfoTable) { + OS.AddComment("FuncInfo section string offset=" + + std::to_string(FuncSec.first)); + OS.emitInt32(FuncSec.first); + OS.emitInt32(FuncSec.second.size()); + for (const auto &FuncInfo : FuncSec.second) { + Asm->emitLabelReference(FuncInfo.Label, 4); + OS.emitInt32(FuncInfo.TypeId); + } + } + + // Emit line_info table. + OS.AddComment("LineInfo"); + OS.emitInt32(BTF::SBFLineInfoSize); + for (const auto &LineSec : LineInfoTable) { + OS.AddComment("LineInfo section string offset=" + + std::to_string(LineSec.first)); + OS.emitInt32(LineSec.first); + OS.emitInt32(LineSec.second.size()); + for (const auto &LineInfo : LineSec.second) { + Asm->emitLabelReference(LineInfo.Label, 4); + OS.emitInt32(LineInfo.FileNameOff); + OS.emitInt32(LineInfo.LineOff); + OS.AddComment("Line " + std::to_string(LineInfo.LineNum) + " Col " + + std::to_string(LineInfo.ColumnNum)); + OS.emitInt32(LineInfo.LineNum << 10 | LineInfo.ColumnNum); + } + } + + // Emit field reloc table. + if (FieldRelocLen) { + OS.AddComment("FieldReloc"); + OS.emitInt32(BTF::SBFFieldRelocSize); + for (const auto &FieldRelocSec : FieldRelocTable) { + OS.AddComment("Field reloc section string offset=" + + std::to_string(FieldRelocSec.first)); + OS.emitInt32(FieldRelocSec.first); + OS.emitInt32(FieldRelocSec.second.size()); + for (const auto &FieldRelocInfo : FieldRelocSec.second) { + Asm->emitLabelReference(FieldRelocInfo.Label, 4); + OS.emitInt32(FieldRelocInfo.TypeID); + OS.emitInt32(FieldRelocInfo.OffsetNameOff); + OS.emitInt32(FieldRelocInfo.RelocKind); + } + } + } +} + +void BTFDebug::beginFunctionImpl(const MachineFunction *MF) { + auto *SP = MF->getFunction().getSubprogram(); + auto *Unit = SP->getUnit(); + + if (Unit->getEmissionKind() == DICompileUnit::NoDebug) { + SkipInstruction = true; + return; + } + SkipInstruction = false; + + // Collect MapDef types. Map definition needs to collect + // pointee types. Do it first. Otherwise, for the following + // case: + // struct m { ...}; + // struct t { + // struct m *key; + // }; + // foo(struct t *arg); + // + // struct mapdef { + // ... + // struct m *key; + // ... + // } __attribute__((section(".maps"))) hash_map; + // + // If subroutine foo is traversed first, a type chain + // "ptr->struct m(fwd)" will be created and later on + // when traversing mapdef, since "ptr->struct m" exists, + // the traversal of "struct m" will be omitted. + if (MapDefNotCollected) { + processGlobals(true); + MapDefNotCollected = false; + } + + // Collect all types locally referenced in this function. + // Use RetainedNodes so we can collect all argument names + // even if the argument is not used. + std::unordered_map FuncArgNames; + for (const DINode *DN : SP->getRetainedNodes()) { + if (const auto *DV = dyn_cast(DN)) { + // Collect function arguments for subprogram func type. + uint32_t Arg = DV->getArg(); + if (Arg) { + visitTypeEntry(DV->getType()); + FuncArgNames[Arg] = DV->getName(); + } + } + } + + // Construct subprogram func proto type. + uint32_t ProtoTypeId; + visitSubroutineType(SP->getType(), true, FuncArgNames, ProtoTypeId); + + // Construct subprogram func type + uint8_t Scope = SP->isLocalToUnit() ? BTF::FUNC_STATIC : BTF::FUNC_GLOBAL; + auto FuncTypeEntry = + std::make_unique(SP->getName(), ProtoTypeId, Scope); + uint32_t FuncTypeId = addType(std::move(FuncTypeEntry)); + + // Process argument annotations. + for (const DINode *DN : SP->getRetainedNodes()) { + if (const auto *DV = dyn_cast(DN)) { + uint32_t Arg = DV->getArg(); + if (Arg) + processDeclAnnotations(DV->getAnnotations(), FuncTypeId, Arg - 1); + } + } + + processDeclAnnotations(SP->getAnnotations(), FuncTypeId, -1); + + for (const auto &TypeEntry : TypeEntries) + TypeEntry->completeType(*this); + + // Construct funcinfo and the first lineinfo for the function. + MCSymbol *FuncLabel = Asm->getFunctionBegin(); + BTFFuncInfo FuncInfo; + FuncInfo.Label = FuncLabel; + FuncInfo.TypeId = FuncTypeId; + if (FuncLabel->isInSection()) { + MCSection &Section = FuncLabel->getSection(); + const MCSectionELF *SectionELF = dyn_cast(&Section); + assert(SectionELF && "Null section for Function Label"); + SecNameOff = addString(SectionELF->getName()); + } else { + SecNameOff = addString(".text"); + } + FuncInfoTable[SecNameOff].push_back(FuncInfo); +} + +void BTFDebug::endFunctionImpl(const MachineFunction *MF) { + SkipInstruction = false; + LineInfoGenerated = false; + SecNameOff = 0; +} + +/// On-demand populate types as requested from abstract member +/// accessing or preserve debuginfo type. +unsigned BTFDebug::populateType(const DIType *Ty) { + unsigned Id; + visitTypeEntry(Ty, Id, false, false); + for (const auto &TypeEntry : TypeEntries) + TypeEntry->completeType(*this); + return Id; +} + +/// Generate a struct member field relocation. +void BTFDebug::generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, + const GlobalVariable *GVar, bool IsAma) { + BTFFieldReloc FieldReloc; + FieldReloc.Label = ORSym; + FieldReloc.TypeID = RootId; + + StringRef AccessPattern = GVar->getName(); + size_t FirstDollar = AccessPattern.find_first_of('$'); + if (IsAma) { + size_t FirstColon = AccessPattern.find_first_of(':'); + size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1); + StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1); + StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1, + SecondColon - FirstColon); + StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1, + FirstDollar - SecondColon); + + FieldReloc.OffsetNameOff = addString(IndexPattern); + FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr)); + PatchImms[GVar] = std::make_pair(std::stoll(std::string(PatchImmStr)), + FieldReloc.RelocKind); + } else { + StringRef RelocStr = AccessPattern.substr(FirstDollar + 1); + FieldReloc.OffsetNameOff = addString("0"); + FieldReloc.RelocKind = std::stoull(std::string(RelocStr)); + PatchImms[GVar] = std::make_pair(RootId, FieldReloc.RelocKind); + } + FieldRelocTable[SecNameOff].push_back(FieldReloc); +} + +void BTFDebug::processGlobalValue(const MachineOperand &MO) { + // check whether this is a candidate or not + if (MO.isGlobal()) { + const GlobalValue *GVal = MO.getGlobal(); + auto *GVar = dyn_cast(GVal); + if (!GVar) { + // Not a global variable. Maybe an extern function reference. + processFuncPrototypes(dyn_cast(GVal)); + return; + } + + if (!GVar->hasAttribute(SBFCoreSharedInfo::AmaAttr) && + !GVar->hasAttribute(SBFCoreSharedInfo::TypeIdAttr)) + return; + + MCSymbol *ORSym = OS.getContext().createTempSymbol(); + OS.emitLabel(ORSym); + + MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index); + uint32_t RootId = populateType(dyn_cast(MDN)); + generatePatchImmReloc(ORSym, RootId, GVar, + GVar->hasAttribute(SBFCoreSharedInfo::AmaAttr)); + } +} + +void BTFDebug::beginInstruction(const MachineInstr *MI) { + DebugHandlerBase::beginInstruction(MI); + + if (SkipInstruction || MI->isMetaInstruction() || + MI->getFlag(MachineInstr::FrameSetup)) + return; + + if (MI->isInlineAsm()) { + // Count the number of register definitions to find the asm string. + unsigned NumDefs = 0; + for (; MI->getOperand(NumDefs).isReg() && MI->getOperand(NumDefs).isDef(); + ++NumDefs) + ; + + // Skip this inline asm instruction if the asmstr is empty. + const char *AsmStr = MI->getOperand(NumDefs).getSymbolName(); + if (AsmStr[0] == 0) + return; + } + + if (MI->getOpcode() == SBF::LD_imm64) { + // If the insn is "r2 = LD_imm64 @", + // add this insn into the .BTF.ext FieldReloc subsection. + // Relocation looks like: + // . SecName: + // . InstOffset + // . TypeID + // . OffSetNameOff + // . RelocType + // Later, the insn is replaced with "r2 = " + // where "" equals to the offset based on current + // type definitions. + // + // If the insn is "r2 = LD_imm64 @", + // The LD_imm64 result will be replaced with a btf type id. + processGlobalValue(MI->getOperand(1)); + } else if (MI->getOpcode() == SBF::CORE_MEM || + MI->getOpcode() == SBF::CORE_ALU32_MEM || + MI->getOpcode() == SBF::CORE_SHIFT) { + // relocation insn is a load, store or shift insn. + processGlobalValue(MI->getOperand(3)); + } else if (MI->getOpcode() == SBF::JAL) { + // check extern function references + const MachineOperand &MO = MI->getOperand(0); + if (MO.isGlobal()) { + processFuncPrototypes(dyn_cast(MO.getGlobal())); + } + } + + if (!CurMI) // no debug info + return; + + // Skip this instruction if no DebugLoc or the DebugLoc + // is the same as the previous instruction. + const DebugLoc &DL = MI->getDebugLoc(); + if (!DL || PrevInstLoc == DL) { + // This instruction will be skipped, no LineInfo has + // been generated, construct one based on function signature. + if (LineInfoGenerated == false) { + auto *S = MI->getMF()->getFunction().getSubprogram(); + if (S) { + MCSymbol *FuncLabel = Asm->getFunctionBegin(); + constructLineInfo(S, FuncLabel, S->getLine(), 0); + LineInfoGenerated = true; + } + } + return; + } + + // Create a temporary label to remember the insn for lineinfo. + MCSymbol *LineSym = OS.getContext().createTempSymbol(); + OS.emitLabel(LineSym); + + // Construct the lineinfo. + auto SP = DL.get()->getScope()->getSubprogram(); + constructLineInfo(SP, LineSym, DL.getLine(), DL.getCol()); + + LineInfoGenerated = true; + PrevInstLoc = DL; +} + +void BTFDebug::processGlobals(bool ProcessingMapDef) { + // Collect all types referenced by globals. + const Module *M = MMI->getModule(); + for (const GlobalVariable &Global : M->globals()) { + // Decide the section name. + StringRef SecName; + if (Global.hasSection()) { + SecName = Global.getSection(); + } else if (Global.hasInitializer()) { + // data, bss, or readonly sections + if (Global.isConstant()) + SecName = ".rodata"; + else + SecName = Global.getInitializer()->isZeroValue() ? ".bss" : ".data"; + } + + if (ProcessingMapDef != SecName.startswith(".maps")) + continue; + + // Create a .rodata datasec if the global variable is an initialized + // constant with private linkage and if it won't be in .rodata.str<#> + // and .rodata.cst<#> sections. + if (SecName == ".rodata" && Global.hasPrivateLinkage() && + DataSecEntries.find(std::string(SecName)) == DataSecEntries.end()) { + SectionKind GVKind = + TargetLoweringObjectFile::getKindForGlobal(&Global, Asm->TM); + // skip .rodata.str<#> and .rodata.cst<#> sections + if (!GVKind.isMergeableCString() && !GVKind.isMergeableConst()) { + DataSecEntries[std::string(SecName)] = + std::make_unique(Asm, std::string(SecName)); + } + } + + SmallVector GVs; + Global.getDebugInfo(GVs); + + // No type information, mostly internal, skip it. + if (GVs.size() == 0) + continue; + + uint32_t GVTypeId = 0; + DIGlobalVariable *DIGlobal = nullptr; + for (auto *GVE : GVs) { + DIGlobal = GVE->getVariable(); + if (SecName.startswith(".maps")) + visitMapDefType(DIGlobal->getType(), GVTypeId); + else + visitTypeEntry(DIGlobal->getType(), GVTypeId, false, false); + break; + } + + // Only support the following globals: + // . static variables + // . non-static weak or non-weak global variables + // . weak or non-weak extern global variables + // Whether DataSec is readonly or not can be found from corresponding ELF + // section flags. Whether a BTF_KIND_VAR is a weak symbol or not + // can be found from the corresponding ELF symbol table. + auto Linkage = Global.getLinkage(); + if (Linkage != GlobalValue::InternalLinkage && + Linkage != GlobalValue::ExternalLinkage && + Linkage != GlobalValue::WeakAnyLinkage && + Linkage != GlobalValue::WeakODRLinkage && + Linkage != GlobalValue::ExternalWeakLinkage) + continue; + + uint32_t GVarInfo; + if (Linkage == GlobalValue::InternalLinkage) { + GVarInfo = BTF::VAR_STATIC; + } else if (Global.hasInitializer()) { + GVarInfo = BTF::VAR_GLOBAL_ALLOCATED; + } else { + GVarInfo = BTF::VAR_GLOBAL_EXTERNAL; + } + + auto VarEntry = + std::make_unique(Global.getName(), GVTypeId, GVarInfo); + uint32_t VarId = addType(std::move(VarEntry)); + + processDeclAnnotations(DIGlobal->getAnnotations(), VarId, -1); + + // An empty SecName means an extern variable without section attribute. + if (SecName.empty()) + continue; + + // Find or create a DataSec + if (DataSecEntries.find(std::string(SecName)) == DataSecEntries.end()) { + DataSecEntries[std::string(SecName)] = + std::make_unique(Asm, std::string(SecName)); + } + + // Calculate symbol size + const DataLayout &DL = Global.getParent()->getDataLayout(); + uint32_t Size = DL.getTypeAllocSize(Global.getValueType()); + + DataSecEntries[std::string(SecName)]->addDataSecEntry(VarId, + Asm->getSymbol(&Global), Size); + } +} + +/// Emit proper patchable instructions. +bool BTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) { + if (MI->getOpcode() == SBF::LD_imm64) { + const MachineOperand &MO = MI->getOperand(1); + if (MO.isGlobal()) { + const GlobalValue *GVal = MO.getGlobal(); + auto *GVar = dyn_cast(GVal); + if (GVar) { + // Emit "mov ri, " + int64_t Imm; + uint32_t Reloc; + if (GVar->hasAttribute(SBFCoreSharedInfo::AmaAttr) || + GVar->hasAttribute(SBFCoreSharedInfo::TypeIdAttr)) { + Imm = PatchImms[GVar].first; + Reloc = PatchImms[GVar].second; + } else { + return false; + } + + if (Reloc == SBFCoreSharedInfo::ENUM_VALUE_EXISTENCE || + Reloc == SBFCoreSharedInfo::ENUM_VALUE || + Reloc == SBFCoreSharedInfo::BTF_TYPE_ID_LOCAL || + Reloc == SBFCoreSharedInfo::BTF_TYPE_ID_REMOTE) + OutMI.setOpcode(SBF::LD_imm64); + else + OutMI.setOpcode(SBF::MOV_ri); + OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); + OutMI.addOperand(MCOperand::createImm(Imm)); + return true; + } + } + } else if (MI->getOpcode() == SBF::CORE_MEM || + MI->getOpcode() == SBF::CORE_ALU32_MEM || + MI->getOpcode() == SBF::CORE_SHIFT) { + const MachineOperand &MO = MI->getOperand(3); + if (MO.isGlobal()) { + const GlobalValue *GVal = MO.getGlobal(); + auto *GVar = dyn_cast(GVal); + if (GVar && GVar->hasAttribute(SBFCoreSharedInfo::AmaAttr)) { + uint32_t Imm = PatchImms[GVar].first; + OutMI.setOpcode(MI->getOperand(1).getImm()); + if (MI->getOperand(0).isImm()) + OutMI.addOperand(MCOperand::createImm(MI->getOperand(0).getImm())); + else + OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); + OutMI.addOperand(MCOperand::createReg(MI->getOperand(2).getReg())); + OutMI.addOperand(MCOperand::createImm(Imm)); + return true; + } + } + } + return false; +} + +void BTFDebug::processFuncPrototypes(const Function *F) { + if (!F) + return; + + const DISubprogram *SP = F->getSubprogram(); + if (!SP || SP->isDefinition()) + return; + + // Do not emit again if already emitted. + if (ProtoFunctions.find(F) != ProtoFunctions.end()) + return; + ProtoFunctions.insert(F); + + uint32_t ProtoTypeId; + const std::unordered_map FuncArgNames; + visitSubroutineType(SP->getType(), false, FuncArgNames, ProtoTypeId); + + uint8_t Scope = BTF::FUNC_EXTERN; + auto FuncTypeEntry = + std::make_unique(SP->getName(), ProtoTypeId, Scope); + uint32_t FuncId = addType(std::move(FuncTypeEntry)); + + processDeclAnnotations(SP->getAnnotations(), FuncId, -1); + + if (F->hasSection()) { + StringRef SecName = F->getSection(); + + if (DataSecEntries.find(std::string(SecName)) == DataSecEntries.end()) { + DataSecEntries[std::string(SecName)] = + std::make_unique(Asm, std::string(SecName)); + } + + // We really don't know func size, set it to 0. + DataSecEntries[std::string(SecName)]->addDataSecEntry(FuncId, + Asm->getSymbol(F), 0); + } +} + +void BTFDebug::endModule() { + // Collect MapDef globals if not collected yet. + if (MapDefNotCollected) { + processGlobals(true); + MapDefNotCollected = false; + } + + // Collect global types/variables except MapDef globals. + processGlobals(false); + + for (auto &DataSec : DataSecEntries) + addType(std::move(DataSec.second)); + + // Fixups + for (auto &Fixup : FixupDerivedTypes) { + StringRef TypeName = Fixup.first; + bool IsUnion = Fixup.second.first; + + // Search through struct types + uint32_t StructTypeId = 0; + for (const auto &StructType : StructTypes) { + if (StructType->getName() == TypeName) { + StructTypeId = StructType->getId(); + break; + } + } + + if (StructTypeId == 0) { + auto FwdTypeEntry = std::make_unique(TypeName, IsUnion); + StructTypeId = addType(std::move(FwdTypeEntry)); + } + + for (auto &DType : Fixup.second.second) { + DType->setPointeeType(StructTypeId); + } + } + + // Complete BTF type cross refereences. + for (const auto &TypeEntry : TypeEntries) + TypeEntry->completeType(*this); + + // Emit BTF sections. + emitBTFSection(); + emitBTFExtSection(); +} diff --git a/llvm/lib/Target/SBF/BTFDebug.h b/llvm/lib/Target/SBF/BTFDebug.h new file mode 100644 index 0000000000000..941e1bbae7f88 --- /dev/null +++ b/llvm/lib/Target/SBF/BTFDebug.h @@ -0,0 +1,409 @@ +//===- BTFDebug.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 contains support for writing BTF debug info. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_BTFDEBUG_H +#define LLVM_LIB_TARGET_SBF_BTFDEBUG_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/CodeGen/DebugHandlerBase.h" +#include +#include +#include +#include +#include "BTF.h" + +namespace llvm { + +class AsmPrinter; +class DIType; +class GlobalVariable; +class MachineFunction; +class MachineInstr; +class MachineOperand; +class MCInst; +class MCStreamer; +class MCSymbol; + +namespace BTFX { + +class BTFDebug; + +/// The base class for BTF type generation. +class BTFTypeBase { +protected: + uint8_t Kind; + bool IsCompleted; + uint32_t Id; + struct BTF::CommonType BTFType; + +public: + BTFTypeBase() : IsCompleted(false) {} + virtual ~BTFTypeBase() = default; + void setId(uint32_t Id) { this->Id = Id; } + uint32_t getId() { return Id; } + uint32_t roundupToBytes(uint32_t NumBits) { return (NumBits + 7) >> 3; } + /// Get the size of this BTF type entry. + virtual uint32_t getSize() { return BTF::CommonTypeSize; } + /// Complete BTF type generation after all related DebugInfo types + /// have been visited so their BTF type id's are available + /// for cross referece. + virtual void completeType(BTFDebug &BDebug) {} + /// Emit types for this BTF type entry. + virtual void emitType(MCStreamer &OS); +}; + +/// Handle several derived types include pointer, const, +/// volatile, typedef and restrict. +class BTFTypeDerived : public BTFTypeBase { + const DIDerivedType *DTy; + bool NeedsFixup; + StringRef Name; + +public: + BTFTypeDerived(const DIDerivedType *Ty, unsigned Tag, bool NeedsFixup); + BTFTypeDerived(unsigned NextTypeId, unsigned Tag, StringRef Name); + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; + void setPointeeType(uint32_t PointeeType); +}; + +/// Handle struct or union forward declaration. +class BTFTypeFwd : public BTFTypeBase { + StringRef Name; + +public: + BTFTypeFwd(StringRef Name, bool IsUnion); + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle int type. +class BTFTypeInt : public BTFTypeBase { + StringRef Name; + uint32_t IntVal; ///< Encoding, offset, bits + +public: + BTFTypeInt(uint32_t Encoding, uint32_t SizeInBits, uint32_t OffsetInBits, + StringRef TypeName); + uint32_t getSize() override { return BTFTypeBase::getSize() + sizeof(uint32_t); } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle enumerate type. +class BTFTypeEnum : public BTFTypeBase { + const DICompositeType *ETy; + std::vector EnumValues; + +public: + BTFTypeEnum(const DICompositeType *ETy, uint32_t NumValues); + uint32_t getSize() override { + return BTFTypeBase::getSize() + EnumValues.size() * BTF::BTFEnumSize; + } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle array type. +class BTFTypeArray : public BTFTypeBase { + struct BTF::BTFArray ArrayInfo; + +public: + BTFTypeArray(uint32_t ElemTypeId, uint32_t NumElems); + uint32_t getSize() override { return BTFTypeBase::getSize() + BTF::BTFArraySize; } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle struct/union type. +class BTFTypeStruct : public BTFTypeBase { + const DICompositeType *STy; + bool HasBitField; + std::vector Members; + +public: + BTFTypeStruct(const DICompositeType *STy, bool IsStruct, bool HasBitField, + uint32_t NumMembers); + uint32_t getSize() override { + return BTFTypeBase::getSize() + Members.size() * BTF::BTFMemberSize; + } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; + std::string getName(); +}; + +/// Handle function pointer. +class BTFTypeFuncProto : public BTFTypeBase { + const DISubroutineType *STy; + std::unordered_map FuncArgNames; + std::vector Parameters; + +public: + BTFTypeFuncProto(const DISubroutineType *STy, uint32_t NumParams, + const std::unordered_map &FuncArgNames); + uint32_t getSize() override { + return BTFTypeBase::getSize() + Parameters.size() * BTF::BTFParamSize; + } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle subprogram +class BTFTypeFunc : public BTFTypeBase { + StringRef Name; + +public: + BTFTypeFunc(StringRef FuncName, uint32_t ProtoTypeId, uint32_t Scope); + uint32_t getSize() override { return BTFTypeBase::getSize(); } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle variable instances +class BTFKindVar : public BTFTypeBase { + StringRef Name; + uint32_t Info; + +public: + BTFKindVar(StringRef VarName, uint32_t TypeId, uint32_t VarInfo); + uint32_t getSize() override { return BTFTypeBase::getSize() + 4; } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle data sections +class BTFKindDataSec : public BTFTypeBase { + AsmPrinter *Asm; + std::string Name; + std::vector> Vars; + +public: + BTFKindDataSec(AsmPrinter *AsmPrt, std::string SecName); + uint32_t getSize() override { + return BTFTypeBase::getSize() + BTF::BTFDataSecVarSize * Vars.size(); + } + void addDataSecEntry(uint32_t Id, const MCSymbol *Sym, uint32_t Size) { + Vars.push_back(std::make_tuple(Id, Sym, Size)); + } + std::string getName() { return Name; } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +/// Handle binary floating point type. +class BTFTypeFloat : public BTFTypeBase { + StringRef Name; + +public: + BTFTypeFloat(uint32_t SizeInBits, StringRef TypeName); + void completeType(BTFDebug &BDebug) override; +}; + +/// Handle decl tags. +class BTFTypeDeclTag : public BTFTypeBase { + uint32_t Info; + StringRef Tag; + +public: + BTFTypeDeclTag(uint32_t BaseTypeId, int ComponentId, StringRef Tag); + uint32_t getSize() override { return BTFTypeBase::getSize() + 4; } + void completeType(BTFDebug &BDebug) override; + void emitType(MCStreamer &OS) override; +}; + +class BTFTypeTypeTag : public BTFTypeBase { + const DIDerivedType *DTy; + StringRef Tag; + +public: + BTFTypeTypeTag(uint32_t NextTypeId, StringRef Tag); + BTFTypeTypeTag(const DIDerivedType *DTy, StringRef Tag); + void completeType(BTFDebug &BDebug) override; +}; + +/// String table. +class BTFStringTable { + /// String table size in bytes. + uint32_t Size; + /// A mapping from string table offset to the index + /// of the Table. It is used to avoid putting + /// duplicated strings in the table. + std::map OffsetToIdMap; + /// A vector of strings to represent the string table. + std::vector Table; + +public: + BTFStringTable() : Size(0) {} + uint32_t getSize() { return Size; } + std::vector &getTable() { return Table; } + /// Add a string to the string table and returns its offset + /// in the table. + uint32_t addString(StringRef S); +}; + +/// Represent one func and its type id. +struct BTFFuncInfo { + const MCSymbol *Label; ///< Func MCSymbol + uint32_t TypeId; ///< Type id referring to .BTF type section +}; + +/// Represent one line info. +struct BTFLineInfo { + MCSymbol *Label; ///< MCSymbol identifying insn for the lineinfo + uint32_t FileNameOff; ///< file name offset in the .BTF string table + uint32_t LineOff; ///< line offset in the .BTF string table + uint32_t LineNum; ///< the line number + uint32_t ColumnNum; ///< the column number +}; + +/// Represent one field relocation. +struct BTFFieldReloc { + const MCSymbol *Label; ///< MCSymbol identifying insn for the reloc + uint32_t TypeID; ///< Type ID + uint32_t OffsetNameOff; ///< The string to traverse types + uint32_t RelocKind; ///< What to patch the instruction +}; + +/// Collect and emit BTF information. +class BTFDebug : public DebugHandlerBase { + MCStreamer &OS; + bool SkipInstruction; + bool LineInfoGenerated; + uint32_t SecNameOff; + uint32_t ArrayIndexTypeId; + bool MapDefNotCollected; + BTFStringTable StringTable; + std::vector> TypeEntries; + std::unordered_map DIToIdMap; + std::map> FuncInfoTable; + std::map> LineInfoTable; + std::map> FieldRelocTable; + StringMap> FileContent; + std::map> DataSecEntries; + std::vector StructTypes; + std::map> PatchImms; + std::map>> + FixupDerivedTypes; + std::setProtoFunctions; + + /// Add types to TypeEntries. + /// @{ + /// Add types to TypeEntries and DIToIdMap. + uint32_t addType(std::unique_ptr TypeEntry, const DIType *Ty); + /// Add types to TypeEntries only and return type id. + uint32_t addType(std::unique_ptr TypeEntry); + /// @} + + /// IR type visiting functions. + /// @{ + void visitTypeEntry(const DIType *Ty); + void visitTypeEntry(const DIType *Ty, uint32_t &TypeId, bool CheckPointer, + bool SeenPointer); + void visitBasicType(const DIBasicType *BTy, uint32_t &TypeId); + void visitSubroutineType( + const DISubroutineType *STy, bool ForSubprog, + const std::unordered_map &FuncArgNames, + uint32_t &TypeId); + void visitFwdDeclType(const DICompositeType *CTy, bool IsUnion, + uint32_t &TypeId); + void visitCompositeType(const DICompositeType *CTy, uint32_t &TypeId); + void visitStructType(const DICompositeType *STy, bool IsStruct, + uint32_t &TypeId); + void visitArrayType(const DICompositeType *ATy, uint32_t &TypeId); + void visitEnumType(const DICompositeType *ETy, uint32_t &TypeId); + void visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId, + bool CheckPointer, bool SeenPointer); + void visitMapDefType(const DIType *Ty, uint32_t &TypeId); + /// @} + + /// Get the file content for the subprogram. Certain lines of the file + /// later may be put into string table and referenced by line info. + std::string populateFileContent(const DISubprogram *SP); + + /// Construct a line info. + void constructLineInfo(const DISubprogram *SP, MCSymbol *Label, uint32_t Line, + uint32_t Column); + + /// Generate types and variables for globals. + void processGlobals(bool ProcessingMapDef); + + /// Generate types for function prototypes. + void processFuncPrototypes(const Function *); + + /// Generate types for decl annotations. + void processDeclAnnotations(DINodeArray Annotations, uint32_t BaseTypeId, + int ComponentId); + + /// Generate one field relocation record. + void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId, + const GlobalVariable *, bool IsAma); + + /// Populating unprocessed type on demand. + unsigned populateType(const DIType *Ty); + + /// Process global variables referenced by relocation instructions + /// and extern function references. + void processGlobalValue(const MachineOperand &MO); + + /// Emit common header of .BTF and .BTF.ext sections. + void emitCommonHeader(); + + /// Emit the .BTF section. + void emitBTFSection(); + + /// Emit the .BTF.ext section. + void emitBTFExtSection(); + +protected: + /// Gather pre-function debug information. + void beginFunctionImpl(const MachineFunction *MF) override; + + /// Post process after all instructions in this function are processed. + void endFunctionImpl(const MachineFunction *MF) override; + +public: + BTFDebug(AsmPrinter *AP); + + /// + bool InstLower(const MachineInstr *MI, MCInst &OutMI); + + /// Get the special array index type id. + uint32_t getArrayIndexTypeId() { + assert(ArrayIndexTypeId); + return ArrayIndexTypeId; + } + + /// Add string to the string table. + size_t addString(StringRef S) { return StringTable.addString(S); } + + /// Get the type id for a particular DIType. + uint32_t getTypeId(const DIType *Ty) { + assert(Ty && "Invalid null Type"); + assert(DIToIdMap.find(Ty) != DIToIdMap.end() && + "DIType not added in the BDIToIdMap"); + return DIToIdMap[Ty]; + } + + void setSymbolSize(const MCSymbol *Symbol, uint64_t Size) override {} + + /// Process beginning of an instruction. + void beginInstruction(const MachineInstr *MI) override; + + /// Complete all the types and emit the BTF sections. + void endModule() override; +}; + +} // end namespace BTFX +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/SBF/CMakeLists.txt b/llvm/lib/Target/SBF/CMakeLists.txt new file mode 100644 index 0000000000000..cc8a9c7d1c0e9 --- /dev/null +++ b/llvm/lib/Target/SBF/CMakeLists.txt @@ -0,0 +1,60 @@ +add_llvm_component_group(SBF) + +set(LLVM_TARGET_DEFINITIONS SBF.td) + +tablegen(LLVM SBFGenAsmMatcher.inc -gen-asm-matcher) +tablegen(LLVM SBFGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM SBFGenCallingConv.inc -gen-callingconv) +tablegen(LLVM SBFGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM SBFGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM SBFGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM SBFGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM SBFGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM SBFGenSubtargetInfo.inc -gen-subtarget) + +add_public_tablegen_target(SBFCommonTableGen) + +add_llvm_target(SBFCodeGen + SBFAbstractMemberAccess.cpp + SBFAdjustOpt.cpp + SBFAsmPrinter.cpp + SBFCheckAndAdjustIR.cpp + SBFFrameLowering.cpp + SBFInstrInfo.cpp + SBFIRPeephole.cpp + SBFISelDAGToDAG.cpp + SBFISelLowering.cpp + SBFMCInstLower.cpp + SBFPreserveDIType.cpp + SBFRegisterInfo.cpp + SBFSelectionDAGInfo.cpp + SBFSubtarget.cpp + SBFTargetMachine.cpp + SBFMIPeephole.cpp + SBFMIChecking.cpp + SBFMISimplifyPatchable.cpp + BTFDebug.cpp + + LINK_COMPONENTS + Analysis + AsmPrinter + CodeGen + Core + MC + SBFDesc + SBFInfo + IPO + Scalar + SelectionDAG + Support + Target + TransformUtils + + ADD_TO_COMPONENT + SBF + ) + +add_subdirectory(AsmParser) +add_subdirectory(Disassembler) +add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) diff --git a/llvm/lib/Target/SBF/Disassembler/CMakeLists.txt b/llvm/lib/Target/SBF/Disassembler/CMakeLists.txt new file mode 100644 index 0000000000000..fb2764455d02d --- /dev/null +++ b/llvm/lib/Target/SBF/Disassembler/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_component_library(LLVMSBFDisassembler + SBFDisassembler.cpp + + LINK_COMPONENTS + MCDisassembler + SBFInfo + Support + + ADD_TO_COMPONENT + SBF +) + diff --git a/llvm/lib/Target/SBF/Disassembler/SBFDisassembler.cpp b/llvm/lib/Target/SBF/Disassembler/SBFDisassembler.cpp new file mode 100644 index 0000000000000..13f2ad814bbd8 --- /dev/null +++ b/llvm/lib/Target/SBF/Disassembler/SBFDisassembler.cpp @@ -0,0 +1,219 @@ +//===- SBFDisassembler.cpp - Disassembler for SBF ---------------*- 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 is part of the SBF Disassembler. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "TargetInfo/SBFTargetInfo.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCFixedLenDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/MathExtras.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "sbf-disassembler" + +typedef MCDisassembler::DecodeStatus DecodeStatus; + +namespace { + +/// A disassembler class for SBF. +class SBFDisassembler : public MCDisassembler { +public: + enum SBF_CLASS { + SBF_LD = 0x0, + SBF_LDX = 0x1, + SBF_ST = 0x2, + SBF_STX = 0x3, + SBF_ALU = 0x4, + SBF_JMP = 0x5, + SBF_JMP32 = 0x6, + SBF_ALU64 = 0x7 + }; + + enum SBF_SIZE { + SBF_W = 0x0, + SBF_H = 0x1, + SBF_B = 0x2, + SBF_DW = 0x3 + }; + + enum SBF_MODE { + SBF_IMM = 0x0, + SBF_ABS = 0x1, + SBF_IND = 0x2, + SBF_MEM = 0x3, + SBF_LEN = 0x4, + SBF_MSH = 0x5, + SBF_ATOMIC = 0x6 + }; + + SBFDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx) + : MCDisassembler(STI, Ctx) {} + ~SBFDisassembler() override = default; + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const override; + + uint8_t getInstClass(uint64_t Inst) const { return (Inst >> 56) & 0x7; }; + uint8_t getInstSize(uint64_t Inst) const { return (Inst >> 59) & 0x3; }; + uint8_t getInstMode(uint64_t Inst) const { return (Inst >> 61) & 0x7; }; +}; + +} // end anonymous namespace + +static MCDisassembler *createSBFDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new SBFDisassembler(STI, Ctx); +} + + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSBFDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheSBFXTarget(), + createSBFDisassembler); +} + +static const unsigned GPRDecoderTable[] = { + SBF::R0, SBF::R1, SBF::R2, SBF::R3, SBF::R4, SBF::R5, + SBF::R6, SBF::R7, SBF::R8, SBF::R9, SBF::R10, SBF::R11}; + +static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t /*Address*/, + const void * /*Decoder*/) { + if (RegNo > 11) + return MCDisassembler::Fail; + + unsigned Reg = GPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned GPR32DecoderTable[] = { + SBF::W0, SBF::W1, SBF::W2, SBF::W3, SBF::W4, SBF::W5, + SBF::W6, SBF::W7, SBF::W8, SBF::W9, SBF::W10, SBF::W11}; + +static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t /*Address*/, + const void * /*Decoder*/) { + if (RegNo > 11) + return MCDisassembler::Fail; + + unsigned Reg = GPR32DecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeMemoryOpValue(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + unsigned Register = (Insn >> 16) & 0xf; + if (Register > 11) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register])); + unsigned Offset = (Insn & 0xffff); + Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Offset))); + + return MCDisassembler::Success; +} + +#include "SBFGenDisassemblerTables.inc" +static DecodeStatus readInstruction64(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint64_t &Insn, + bool IsLittleEndian) { + uint64_t Lo, Hi; + + if (Bytes.size() < 8) { + Size = 0; + return MCDisassembler::Fail; + } + + Size = 8; + if (IsLittleEndian) { + Hi = (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 0) | (Bytes[3] << 8); + Lo = (Bytes[4] << 0) | (Bytes[5] << 8) | (Bytes[6] << 16) | (Bytes[7] << 24); + } else { + Hi = (Bytes[0] << 24) | ((Bytes[1] & 0x0F) << 20) | ((Bytes[1] & 0xF0) << 12) | + (Bytes[2] << 8) | (Bytes[3] << 0); + Lo = (Bytes[4] << 24) | (Bytes[5] << 16) | (Bytes[6] << 8) | (Bytes[7] << 0); + } + Insn = Make_64(Hi, Lo); + + return MCDisassembler::Success; +} + +DecodeStatus SBFDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, + uint64_t Address, + raw_ostream &CStream) const { + bool IsLittleEndian = getContext().getAsmInfo()->isLittleEndian(); + uint64_t Insn, Hi; + DecodeStatus Result; + + Result = readInstruction64(Bytes, Address, Size, Insn, IsLittleEndian); + if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; + + uint8_t InstClass = getInstClass(Insn); + uint8_t InstMode = getInstMode(Insn); + if ((InstClass == SBF_LDX || InstClass == SBF_STX) && + getInstSize(Insn) != SBF_DW && + (InstMode == SBF_MEM || InstMode == SBF_ATOMIC) && + STI.getFeatureBits()[SBF::ALU32]) + Result = decodeInstruction(DecoderTableSBFALU3264, Instr, Insn, Address, + this, STI); + else + Result = decodeInstruction(DecoderTableSBF64, Instr, Insn, Address, this, + STI); + + if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; + + switch (Instr.getOpcode()) { + case SBF::LD_imm64: + case SBF::LD_pseudo: { + if (Bytes.size() < 16) { + Size = 0; + return MCDisassembler::Fail; + } + Size = 16; + if (IsLittleEndian) + Hi = (Bytes[12] << 0) | (Bytes[13] << 8) | (Bytes[14] << 16) | (Bytes[15] << 24); + else + Hi = (Bytes[12] << 24) | (Bytes[13] << 16) | (Bytes[14] << 8) | (Bytes[15] << 0); + auto& Op = Instr.getOperand(1); + Op.setImm(Make_64(Hi, Op.getImm())); + break; + } + case SBF::LD_ABS_B: + case SBF::LD_ABS_H: + case SBF::LD_ABS_W: + case SBF::LD_IND_B: + case SBF::LD_IND_H: + case SBF::LD_IND_W: { + auto Op = Instr.getOperand(0); + Instr.clear(); + Instr.addOperand(MCOperand::createReg(SBF::R6)); + Instr.addOperand(Op); + break; + } + } + + return Result; +} + +typedef DecodeStatus (*DecodeFunc)(MCInst &MI, unsigned insn, uint64_t Address, + const void *Decoder); diff --git a/llvm/lib/Target/SBF/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/SBF/MCTargetDesc/CMakeLists.txt new file mode 100644 index 0000000000000..23c8c21c548ee --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,15 @@ +add_llvm_component_library(LLVMSBFDesc + SBFMCTargetDesc.cpp + SBFAsmBackend.cpp + SBFInstPrinter.cpp + SBFMCCodeEmitter.cpp + SBFELFObjectWriter.cpp + + LINK_COMPONENTS + MC + SBFInfo + Support + + ADD_TO_COMPONENT + SBF + ) diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFAsmBackend.cpp b/llvm/lib/Target/SBF/MCTargetDesc/SBFAsmBackend.cpp new file mode 100644 index 0000000000000..67e553265cba6 --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFAsmBackend.cpp @@ -0,0 +1,120 @@ +//===-- SBFAsmBackend.cpp - SBF Assembler Backend -------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/Support/EndianStream.h" +#include +#include + +using namespace llvm; + +namespace { + +class SBFAsmBackend : public MCAsmBackend { +public: + SBFAsmBackend(support::endianness Endian, const MCSubtargetInfo &STI) + : MCAsmBackend(Endian), + isSolana(STI.hasFeature(SBF::FeatureSolana) || + STI.getTargetTriple().getArch() == Triple::sbf), + relocAbs64(STI.hasFeature(SBF::FeatureRelocAbs64)) {} + ~SBFAsmBackend() override = default; + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override; + + std::unique_ptr + createObjectTargetWriter() const override; + + // No instruction requires relaxation + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override { + return false; + } + + unsigned getNumFixupKinds() const override { return 1; } + + bool writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const override; +private: + bool isSolana; + bool relocAbs64; +}; + +} // end anonymous namespace + +bool SBFAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const { + if ((Count % 8) != 0) + return false; + + for (uint64_t i = 0; i < Count; i += 8) + support::endian::write(OS, 0x15000000, Endian); + + return true; +} + +void SBFAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, + MutableArrayRef Data, uint64_t Value, + bool IsResolved, + const MCSubtargetInfo *STI) const { + if (Fixup.getKind() == FK_SecRel_8) { + // The Value is 0 for global variables, and the in-section offset + // for static variables. Write to the immediate field of the inst. + assert(Value <= UINT32_MAX); + support::endian::write(&Data[Fixup.getOffset() + 4], + static_cast(Value), + Endian); + } else if (Fixup.getKind() == FK_Data_4) { + support::endian::write(&Data[Fixup.getOffset()], Value, Endian); + } else if (Fixup.getKind() == FK_Data_8) { + support::endian::write(&Data[Fixup.getOffset()], Value, Endian); + } else if (Fixup.getKind() == FK_PCRel_4) { + Value = (uint32_t)((Value - 8) / 8); + if (Endian == support::little) { + Data[Fixup.getOffset() + 1] = 0x10; + support::endian::write32le(&Data[Fixup.getOffset() + 4], Value); + } else { + Data[Fixup.getOffset() + 1] = 0x1; + support::endian::write32be(&Data[Fixup.getOffset() + 4], Value); + } + } else { + assert(Fixup.getKind() == FK_PCRel_2); + Value = (uint16_t)((Value - 8) / 8); + support::endian::write(&Data[Fixup.getOffset() + 2], Value, + Endian); + } +} + +std::unique_ptr +SBFAsmBackend::createObjectTargetWriter() const { + return createSBFELFObjectWriter(0, isSolana, relocAbs64); +} + +MCAsmBackend *llvm::createSBFAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &) { + return new SBFAsmBackend(support::little, STI); +} + +MCAsmBackend *llvm::createSBFbeAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &) { + return new SBFAsmBackend(support::big, STI); +} diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFELFObjectWriter.cpp b/llvm/lib/Target/SBF/MCTargetDesc/SBFELFObjectWriter.cpp new file mode 100644 index 0000000000000..cbd7e3abe592a --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFELFObjectWriter.cpp @@ -0,0 +1,109 @@ +//===-- SBFELFObjectWriter.cpp - SBF ELF Writer ---------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" +#include + +using namespace llvm; + +namespace { + +class SBFELFObjectWriter : public MCELFObjectTargetWriter { +public: + SBFELFObjectWriter(uint8_t OSABI, bool isSolana, bool relocAbs64); + ~SBFELFObjectWriter() override = default; + +protected: + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; + + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override; +private: + bool isSolana; + bool relocAbs64; +}; + +} // end anonymous namespace + +// Avoid section relocations because the SBF backend can only handle +// section relocations with values (offset into the section containing +// the symbol being relocated). Forcing a relocation with a symbol +// will result in the symbol's index being used in the .o file instead. +bool SBFELFObjectWriter::needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const { + return isSolana; +} + +SBFELFObjectWriter::SBFELFObjectWriter(uint8_t OSABI, bool isSolana, + bool relocAbs64) + : MCELFObjectTargetWriter(/*Is64Bit*/ true, OSABI, ELF::EM_SBF, + /*HasRelocationAddend*/ false), + isSolana(isSolana), relocAbs64(relocAbs64) {} + +unsigned SBFELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + // determine the type of the relocation + switch (Fixup.getKind()) { + default: + llvm_unreachable("invalid fixup kind!"); + case FK_SecRel_8: + // LD_imm64 instruction. + return ELF::R_SBF_64_64; + case FK_PCRel_4: + // CALL instruction. + return ELF::R_SBF_64_32; + case FK_Data_8: + return (isSolana && !relocAbs64) ? ELF::R_SBF_64_64 : ELF::R_SBF_64_ABS64; + case FK_Data_4: + if (const MCSymbolRefExpr *A = Target.getSymA()) { + const MCSymbol &Sym = A->getSymbol(); + + if (Sym.isDefined()) { + MCSection &Section = Sym.getSection(); + const MCSectionELF *SectionELF = dyn_cast(&Section); + assert(SectionELF && "Null section for reloc symbol"); + + unsigned Flags = SectionELF->getFlags(); + + if (Sym.isTemporary()) { + // .BTF.ext generates FK_Data_4 relocations for + // insn offset by creating temporary labels. + // The reloc symbol should be in text section. + // Use a different relocation to instruct ExecutionEngine + // RuntimeDyld not to do relocation for it, yet still to + // allow lld to do proper adjustment when merging sections. + if ((Flags & ELF::SHF_ALLOC) && (Flags & ELF::SHF_EXECINSTR)) + return ELF::R_SBF_64_NODYLD32; + } else { + // .BTF generates FK_Data_4 relocations for variable + // offset in DataSec kind. + // The reloc symbol should be in data section. + if ((Flags & ELF::SHF_ALLOC) && (Flags & ELF::SHF_WRITE)) + return ELF::R_SBF_64_NODYLD32; + } + // .debug_* sections + if (!(Flags & ELF::SHF_ALLOC)) + return ELF::R_SBF_64_ABS32; + } + } + return isSolana ? ELF::R_SBF_64_32 : ELF::R_SBF_64_ABS32; + } +} + +std::unique_ptr +llvm::createSBFELFObjectWriter(uint8_t OSABI, bool isSolana, bool useRelocAbs64) { + return std::make_unique(OSABI, isSolana, useRelocAbs64); +} diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.cpp b/llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.cpp new file mode 100644 index 0000000000000..c158bc3101290 --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.cpp @@ -0,0 +1,108 @@ +//===-- SBFInstPrinter.cpp - Convert SBF MCInst to asm syntax -------------===// +// +// 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 prints an SBF MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFInstPrinter.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "SBFGenAsmWriter.inc" + +void SBFInstPrinter::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, const MCSubtargetInfo &STI, + raw_ostream &O) { + printInstruction(MI, Address, O); + printAnnotation(O, Annot); +} + +static void printExpr(const MCExpr *Expr, raw_ostream &O) { +#ifndef NDEBUG + const MCSymbolRefExpr *SRE; + + if (const MCBinaryExpr *BE = dyn_cast(Expr)) + SRE = dyn_cast(BE->getLHS()); + else + SRE = dyn_cast(Expr); + assert(SRE && "Unexpected MCExpr type."); + + MCSymbolRefExpr::VariantKind Kind = SRE->getKind(); + + assert(Kind == MCSymbolRefExpr::VK_None); +#endif + O << *Expr; +} + +void SBFInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O, const char *Modifier) { + assert((Modifier == nullptr || Modifier[0] == 0) && "No modifiers supported"); + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + O << getRegisterName(Op.getReg()); + } else if (Op.isImm()) { + O << formatImm((int32_t)Op.getImm()); + } else { + assert(Op.isExpr() && "Expected an expression"); + printExpr(Op.getExpr(), O); + } +} + +void SBFInstPrinter::printMemOperand(const MCInst *MI, int OpNo, raw_ostream &O, + const char *Modifier) { + const MCOperand &RegOp = MI->getOperand(OpNo); + const MCOperand &OffsetOp = MI->getOperand(OpNo + 1); + + // register + assert(RegOp.isReg() && "Register operand not a register"); + O << getRegisterName(RegOp.getReg()); + + // offset + if (OffsetOp.isImm()) { + auto Imm = OffsetOp.getImm(); + if (Imm >= 0) + O << " + " << formatImm(Imm); + else + O << " - " << formatImm(-Imm); + } else { + assert(0 && "Expected an immediate"); + } +} + +void SBFInstPrinter::printImm64Operand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isImm()) + O << formatImm(Op.getImm()); + else if (Op.isExpr()) + printExpr(Op.getExpr(), O); + else + O << Op; +} + +void SBFInstPrinter::printBrTargetOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isImm()) { + int16_t Imm = Op.getImm(); + O << ((Imm >= 0) ? "+" : "") << formatImm(Imm); + } else if (Op.isExpr()) { + printExpr(Op.getExpr(), O); + } else { + O << Op; + } +} diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.h b/llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.h new file mode 100644 index 0000000000000..1f1e069e62d94 --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFInstPrinter.h @@ -0,0 +1,41 @@ +//===-- SBFInstPrinter.h - Convert SBF MCInst to asm syntax -------*- 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 prints a SBF MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_MCTARGETDESC_SBFINSTPRINTER_H +#define LLVM_LIB_TARGET_SBF_MCTARGETDESC_SBFINSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { +class SBFInstPrinter : public MCInstPrinter { +public: + SBFInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &O) override; + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O, + const char *Modifier = nullptr); + void printMemOperand(const MCInst *MI, int OpNo, raw_ostream &O, + const char *Modifier = nullptr); + void printImm64Operand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printBrTargetOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + + // Autogenerated by tblgen. + std::pair getMnemonic(const MCInst *MI) override; + void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O); + static const char *getRegisterName(unsigned RegNo); +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFMCAsmInfo.h b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCAsmInfo.h new file mode 100644 index 0000000000000..08261d3aea67e --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCAsmInfo.h @@ -0,0 +1,58 @@ +//===-- SBFMCAsmInfo.h - SBF asm properties -------------------*- 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 the declaration of the SBFMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_MCTARGETDESC_SBFMCASMINFO_H +#define LLVM_LIB_TARGET_SBF_MCTARGETDESC_SBFMCASMINFO_H + +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmInfo.h" + +namespace llvm { + +class SBFMCAsmInfo : public MCAsmInfo { +public: + explicit SBFMCAsmInfo(const Triple &TT, const MCTargetOptions &Options) { + if (TT.getArch() == Triple::bpfeb) + IsLittleEndian = false; + + PrivateGlobalPrefix = ".L"; + WeakRefDirective = "\t.weak\t"; + + UsesELFSectionDirectiveForBSS = true; + HasSingleParameterDotFile = true; + HasDotTypeDotSizeDirective = true; + + SupportsDebugInformation = true; + ExceptionsType = ExceptionHandling::DwarfCFI; + MinInstAlignment = 8; + + // the default is 4 and it only affects dwarf elf output + // so if not set correctly, the dwarf data will be + // messed up in random places by 4 bytes. .debug_line + // section will be parsable, but with odd offsets and + // line numbers, etc. + CodePointerSize = 8; + + UseIntegratedAssembler = false; + } + + void setDwarfUsesRelocationsAcrossSections(bool enable) { + DwarfUsesRelocationsAcrossSections = enable; + } + + void setSupportsDebugInformation(bool enable) { + SupportsDebugInformation = enable; + } +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFMCCodeEmitter.cpp b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCCodeEmitter.cpp new file mode 100644 index 0000000000000..2b7fe22684b25 --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCCodeEmitter.cpp @@ -0,0 +1,180 @@ +//===-- SBFMCCodeEmitter.cpp - Convert SBF code to machine code -----------===// +// +// 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 SBFMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "mccodeemitter" + +namespace { + +class SBFMCCodeEmitter : public MCCodeEmitter { + const MCInstrInfo &MCII; + const MCRegisterInfo &MRI; + bool IsLittleEndian; + +public: + SBFMCCodeEmitter(const MCInstrInfo &mcii, const MCRegisterInfo &mri, + bool IsLittleEndian) + : MCII(mcii), MRI(mri), IsLittleEndian(IsLittleEndian) {} + SBFMCCodeEmitter(const SBFMCCodeEmitter &) = delete; + void operator=(const SBFMCCodeEmitter &) = delete; + ~SBFMCCodeEmitter() override = default; + + // getBinaryCodeForInstr - TableGen'erated function for getting the + // binary encoding for an instruction. + uint64_t getBinaryCodeForInstr(const MCInst &MI, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getMachineOpValue - Return binary encoding of operand. If the machin + // operand requires relocation, record the relocation and return zero. + unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint64_t getMemoryOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + +private: + FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) const; + void + verifyInstructionPredicates(const MCInst &MI, + const FeatureBitset &AvailableFeatures) const; +}; + +} // end anonymous namespace + +MCCodeEmitter *llvm::createSBFMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new SBFMCCodeEmitter(MCII, MRI, true); +} + +MCCodeEmitter *llvm::createSBFbeMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new SBFMCCodeEmitter(MCII, MRI, false); +} + +unsigned SBFMCCodeEmitter::getMachineOpValue(const MCInst &MI, + const MCOperand &MO, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + if (MO.isReg()) + return MRI.getEncodingValue(MO.getReg()); + if (MO.isImm()) + return static_cast(MO.getImm()); + + assert(MO.isExpr()); + + const MCExpr *Expr = MO.getExpr(); + + assert(Expr->getKind() == MCExpr::SymbolRef); + + if (MI.getOpcode() == SBF::JAL) + // func call name + Fixups.push_back(MCFixup::create(0, Expr, FK_PCRel_4)); + else if (MI.getOpcode() == SBF::LD_imm64) + Fixups.push_back(MCFixup::create(0, Expr, FK_SecRel_8)); + else + // bb label + Fixups.push_back(MCFixup::create(0, Expr, FK_PCRel_2)); + + return 0; +} + +static uint8_t SwapBits(uint8_t Val) +{ + return (Val & 0x0F) << 4 | (Val & 0xF0) >> 4; +} + +void SBFMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + verifyInstructionPredicates(MI, + computeAvailableFeatures(STI.getFeatureBits())); + + unsigned Opcode = MI.getOpcode(); + support::endian::Writer OSE(OS, + IsLittleEndian ? support::little : support::big); + + if (Opcode == SBF::LD_imm64 || Opcode == SBF::LD_pseudo) { + uint64_t Value = getBinaryCodeForInstr(MI, Fixups, STI); + OS << char(Value >> 56); + if (IsLittleEndian) + OS << char((Value >> 48) & 0xff); + else + OS << char(SwapBits((Value >> 48) & 0xff)); + OSE.write(0); + OSE.write(Value & 0xffffFFFF); + + const MCOperand &MO = MI.getOperand(1); + uint64_t Imm = MO.isImm() ? MO.getImm() : 0; + OSE.write(0); + OSE.write(0); + OSE.write(0); + OSE.write(Imm >> 32); + } else { + // Get instruction encoding and emit it + uint64_t Value = getBinaryCodeForInstr(MI, Fixups, STI); + OS << char(Value >> 56); + if (IsLittleEndian) + OS << char((Value >> 48) & 0xff); + else + OS << char(SwapBits((Value >> 48) & 0xff)); + OSE.write((Value >> 32) & 0xffff); + OSE.write(Value & 0xffffFFFF); + } +} + +// Encode SBF Memory Operand +uint64_t SBFMCCodeEmitter::getMemoryOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // For CMPXCHG instructions, output is implicitly in R0/W0, + // so memory operand starts from operand 0. + int MemOpStartIndex = 1, Opcode = MI.getOpcode(); + if (Opcode == SBF::CMPXCHGW32 || Opcode == SBF::CMPXCHGD) + MemOpStartIndex = 0; + + uint64_t Encoding; + const MCOperand Op1 = MI.getOperand(MemOpStartIndex); + assert(Op1.isReg() && "First operand is not register."); + Encoding = MRI.getEncodingValue(Op1.getReg()); + Encoding <<= 16; + MCOperand Op2 = MI.getOperand(MemOpStartIndex + 1); + assert(Op2.isImm() && "Second operand is not immediate."); + Encoding |= Op2.getImm() & 0xffff; + return Encoding; +} + +#define ENABLE_INSTR_PREDICATE_VERIFIER +#include "SBFGenMCCodeEmitter.inc" diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.cpp b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.cpp new file mode 100644 index 0000000000000..923d764efe5aa --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.cpp @@ -0,0 +1,148 @@ +//===-- SBFMCTargetDesc.cpp - SBF Target Descriptions ---------------------===// +// +// 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 provides SBF specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "MCTargetDesc/SBFInstPrinter.h" +#include "MCTargetDesc/SBFMCAsmInfo.h" +#include "TargetInfo/SBFTargetInfo.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInstrAnalysis.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Host.h" + +#define GET_INSTRINFO_MC_DESC +#include "SBFGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "SBFGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "SBFGenRegisterInfo.inc" + +using namespace llvm; + +static MCInstrInfo *createSBFMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitSBFMCInstrInfo(X); + return X; +} + +static MCRegisterInfo *createSBFMCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitSBFMCRegisterInfo(X, SBF::R11 /* RAReg doesn't exist */); + return X; +} + +static MCSubtargetInfo *createSBFMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + return createSBFMCSubtargetInfoImpl(TT, CPU, /*TuneCPU*/ CPU, FS); +} + +static MCStreamer *createSBFMCStreamer(const Triple &T, MCContext &Ctx, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, + bool RelaxAll) { + MCELFStreamer *S = + new MCELFStreamer(Ctx, std::move(MAB), std::move(OW), std::move(Emitter)); + MCAssembler &A = S->getAssembler(); + if (RelaxAll) + A.setRelaxAll(true); + + const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); + if (STI->getCPU() == "sbfv2") { + A.setELFHeaderEFlags(llvm::ELF::EF_SBF_V2); + } + + return S; +} + +static MCInstPrinter *createSBFMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + if (SyntaxVariant == 0) + return new SBFInstPrinter(MAI, MII, MRI); + return nullptr; +} + +namespace { + +class SBFMCInstrAnalysis : public MCInstrAnalysis { +public: + explicit SBFMCInstrAnalysis(const MCInstrInfo *Info) + : MCInstrAnalysis(Info) {} + + bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target) const override { + // The target is the 3rd operand of cond inst and the 1st of uncond inst. + int16_t Imm; + if (isConditionalBranch(Inst)) { + Imm = Inst.getOperand(2).getImm(); + } else if (isUnconditionalBranch(Inst)) + Imm = Inst.getOperand(0).getImm(); + else + return false; + + Target = Addr + Size + Imm * Size; + return true; + } +}; + +} // end anonymous namespace + +static MCInstrAnalysis *createSBFInstrAnalysis(const MCInstrInfo *Info) { + return new SBFMCInstrAnalysis(Info); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSBFTargetMC() { + for (Target *T : {&getTheSBFXTarget()}) { + // Register the MC asm info. + RegisterMCAsmInfo X(*T); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(*T, createSBFMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(*T, createSBFMCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(*T, + createSBFMCSubtargetInfo); + + // Register the object streamer + TargetRegistry::RegisterELFStreamer(*T, createSBFMCStreamer); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(*T, createSBFMCInstPrinter); + + // Register the MC instruction analyzer. + TargetRegistry::RegisterMCInstrAnalysis(*T, createSBFInstrAnalysis); + } + + // Register the MC code emitter + TargetRegistry::RegisterMCCodeEmitter(getTheSBFXTarget(), + createSBFMCCodeEmitter); + + // Register the ASM Backend + TargetRegistry::RegisterMCAsmBackend(getTheSBFXTarget(), + createSBFAsmBackend); +} diff --git a/llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.h b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.h new file mode 100644 index 0000000000000..aa2460ee6523f --- /dev/null +++ b/llvm/lib/Target/SBF/MCTargetDesc/SBFMCTargetDesc.h @@ -0,0 +1,64 @@ +//===-- SBFMCTargetDesc.h - SBF Target Descriptions -------------*- 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 provides SBF specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_MCTARGETDESC_SBFMCTARGETDESC_H +#define LLVM_LIB_TARGET_SBF_MCTARGETDESC_SBFMCTARGETDESC_H + +#include "llvm/Config/config.h" +#include "llvm/Support/DataTypes.h" + +#include + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCObjectTargetWriter; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCTargetOptions; +class Target; + +MCCodeEmitter *createSBFMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); +MCCodeEmitter *createSBFbeMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); + +MCAsmBackend *createSBFAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); +MCAsmBackend *createSBFbeAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); + +std::unique_ptr +createSBFELFObjectWriter(uint8_t OSABI, bool isSolana, bool useRelocAbs64); +} // namespace llvm + +// Defines symbolic names for SBF registers. This defines a mapping from +// register name to register number. +// +#define GET_REGINFO_ENUM +#include "SBFGenRegisterInfo.inc" + +// Defines symbolic names for the SBF instructions. +// +#define GET_INSTRINFO_ENUM +#include "SBFGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "SBFGenSubtargetInfo.inc" + +#endif diff --git a/llvm/lib/Target/SBF/SBF.h b/llvm/lib/Target/SBF/SBF.h new file mode 100644 index 0000000000000..e4e9cd28a72bd --- /dev/null +++ b/llvm/lib/Target/SBF/SBF.h @@ -0,0 +1,75 @@ +//===-- SBF.h - Top-level interface for SBF representation ------*- 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_LIB_TARGET_SBF_SBF_H +#define LLVM_LIB_TARGET_SBF_SBF_H + +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { +class SBFTargetMachine; + +ModulePass *createSBFAdjustOpt(); +ModulePass *createSBFCheckAndAdjustIR(); + +FunctionPass *createSBFAbstractMemberAccess(SBFTargetMachine *TM); +FunctionPass *createSBFPreserveDIType(); +FunctionPass *createSBFIRPeephole(); +FunctionPass *createSBFISelDag(SBFTargetMachine &TM); +FunctionPass *createSBFMISimplifyPatchablePass(); +FunctionPass *createSBFMIPeepholePass(); +FunctionPass *createSBFMIPeepholeTruncElimPass(); +FunctionPass *createSBFMIPreEmitPeepholePass(); +FunctionPass *createSBFMIPreEmitCheckingPass(); + +void initializeSBFAdjustOptPass(PassRegistry&); +void initializeSBFCheckAndAdjustIRPass(PassRegistry&); + +void initializeSBFAbstractMemberAccessLegacyPassPass(PassRegistry &); +void initializeSBFPreserveDITypePass(PassRegistry&); +void initializeSBFIRPeepholePass(PassRegistry&); +void initializeSBFMISimplifyPatchablePass(PassRegistry&); +void initializeSBFMIPeepholePass(PassRegistry&); +void initializeSBFMIPeepholeTruncElimPass(PassRegistry&); +void initializeSBFMIPreEmitPeepholePass(PassRegistry&); +void initializeSBFMIPreEmitCheckingPass(PassRegistry&); + +class SBFAbstractMemberAccessPass + : public PassInfoMixin { + SBFTargetMachine *TM; + +public: + SBFAbstractMemberAccessPass(SBFTargetMachine *TM) : TM(TM) {} + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + + static bool isRequired() { return true; } +}; + +class SBFPreserveDITypePass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + + static bool isRequired() { return true; } +}; + +class SBFIRPeepholePass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + + static bool isRequired() { return true; } +}; + +class SBFAdjustOptPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/SBF/SBF.td b/llvm/lib/Target/SBF/SBF.td new file mode 100644 index 0000000000000..4f0a45d0e6e2e --- /dev/null +++ b/llvm/lib/Target/SBF/SBF.td @@ -0,0 +1,72 @@ +//===-- SBF.td - Describe the SBF Target Machine -----------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +include "SBFRegisterInfo.td" +include "SBFCallingConv.td" +include "SBFInstrInfo.td" + +def SBFInstrInfo : InstrInfo; + +def DummyFeature : SubtargetFeature<"dummy", "isDummyMode", + "true", "unused feature">; + +def ALU32 : SubtargetFeature<"alu32", "HasAlu32", "true", + "Enable ALU32 instructions">; + +def DwarfRIS: SubtargetFeature<"dwarfris", "UseDwarfRIS", "true", + "Disable MCAsmInfo DwarfUsesRelocationsAcrossSections">; + +def FeatureSolana : SubtargetFeature<"solana", "IsSolana", "true", + "Enable Solana extensions">; + +def FeatureDynamicFrames : SubtargetFeature<"dynamic-frames", "HasDynamicFrames", "true", + "Enable dynamic frames">; + +def FeatureSdiv : SubtargetFeature<"sdiv", "HasSdiv", "true", + "Enable native SBF_SDIV support">; + +def FeatureRelocAbs64 : SubtargetFeature<"reloc-abs64", "UseRelocAbs64", "true", + "Fix 64bit data relocations">; + +def FeatureStaticSyscalls : SubtargetFeature<"static-syscalls", "HasStaticSyscalls", "true", + "Marker feature used for conditional compilation">; + +class Proc Features> + : Processor; + +def : Proc<"generic", []>; +def : Proc<"v1", []>; +def : Proc<"v2", []>; +def : Proc<"v3", []>; +def : Proc<"probe", []>; +def : Proc<"sbfv2", [FeatureSolana, FeatureDynamicFrames, FeatureSdiv, FeatureRelocAbs64, FeatureStaticSyscalls]>; + +def SBFInstPrinter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; + bit isMCAsmWriter = 1; +} + +def SBFAsmParser : AsmParser { + bit HasMnemonicFirst = 0; +} + +def SBFAsmParserVariant : AsmParserVariant { + int Variant = 0; + string Name = "SBF"; + string BreakCharacters = "."; + string TokenizingCharacters = "#()[]=:.<>!+*/"; +} + +def SBF : Target { + let InstructionSet = SBFInstrInfo; + let AssemblyWriters = [SBFInstPrinter]; + let AssemblyParsers = [SBFAsmParser]; + let AssemblyParserVariants = [SBFAsmParserVariant]; +} diff --git a/llvm/lib/Target/SBF/SBFAbstractMemberAccess.cpp b/llvm/lib/Target/SBF/SBFAbstractMemberAccess.cpp new file mode 100644 index 0000000000000..3293f506dcc52 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFAbstractMemberAccess.cpp @@ -0,0 +1,1125 @@ +//===------ SBFAbstractMemberAccess.cpp - Abstracting Member Accesses -----===// +// +// 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 pass abstracted struct/union member accesses in order to support +// compile-once run-everywhere (CO-RE). The CO-RE intends to compile the program +// which can run on different kernels. In particular, if bpf program tries to +// access a particular kernel data structure member, the details of the +// intermediate member access will be remembered so bpf loader can do +// necessary adjustment right before program loading. +// +// For example, +// +// struct s { +// int a; +// int b; +// }; +// struct t { +// struct s c; +// int d; +// }; +// struct t e; +// +// For the member access e.c.b, the compiler will generate code +// &e + 4 +// +// The compile-once run-everywhere instead generates the following code +// r = 4 +// &e + r +// The "4" in "r = 4" can be changed based on a particular kernel version. +// For example, on a particular kernel version, if struct s is changed to +// +// struct s { +// int new_field; +// int a; +// int b; +// } +// +// By repeating the member access on the host, the bpf loader can +// adjust "r = 4" as "r = 8". +// +// This feature relies on the following three intrinsic calls: +// addr = preserve_array_access_index(base, dimension, index) +// addr = preserve_union_access_index(base, di_index) +// !llvm.preserve.access.index +// addr = preserve_struct_access_index(base, gep_index, di_index) +// !llvm.preserve.access.index +// +// Bitfield member access needs special attention. User cannot take the +// address of a bitfield acceess. To facilitate kernel verifier +// for easy bitfield code optimization, a new clang intrinsic is introduced: +// uint32_t __builtin_preserve_field_info(member_access, info_kind) +// In IR, a chain with two (or more) intrinsic calls will be generated: +// ... +// addr = preserve_struct_access_index(base, 1, 1) !struct s +// uint32_t result = bpf_preserve_field_info(addr, info_kind) +// +// Suppose the info_kind is FIELD_SIGNEDNESS, +// The above two IR intrinsics will be replaced with +// a relocatable insn: +// signness = /* signness of member_access */ +// and signness can be changed by bpf loader based on the +// types on the host. +// +// User can also test whether a field exists or not with +// uint32_t result = bpf_preserve_field_info(member_access, FIELD_EXISTENCE) +// The field will be always available (result = 1) during initial +// compilation, but bpf loader can patch with the correct value +// on the target host where the member_access may or may not be available +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFCORE.h" +#include "SBFTargetMachine.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicsBPF.h" // TODO: jle. +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include + +#define DEBUG_TYPE "sbf-abstract-member-access" + +namespace llvm { +constexpr StringRef SBFCoreSharedInfo::AmaAttr; +uint32_t SBFCoreSharedInfo::SeqNum; + +Instruction *SBFCoreSharedInfo::insertPassThrough(Module *M, BasicBlock *BB, + Instruction *Input, + Instruction *Before) { + Function *Fn = Intrinsic::getDeclaration( + M, Intrinsic::bpf_passthrough, {Input->getType(), Input->getType()}); + Constant *SeqNumVal = ConstantInt::get(Type::getInt32Ty(BB->getContext()), + SBFCoreSharedInfo::SeqNum++); + + auto *NewInst = CallInst::Create(Fn, {SeqNumVal, Input}); + BB->getInstList().insert(Before->getIterator(), NewInst); + return NewInst; +} +} // namespace llvm + +using namespace llvm; + +namespace { +class SBFAbstractMemberAccess final { +public: + SBFAbstractMemberAccess(SBFTargetMachine *TM) : TM(TM) {} + + bool run(Function &F); + + struct CallInfo { + uint32_t Kind; + uint32_t AccessIndex; + Align RecordAlignment; + MDNode *Metadata; + Value *Base; + }; + typedef std::stack> CallInfoStack; + +private: + enum : uint32_t { + SBFPreserveArrayAI = 1, + SBFPreserveUnionAI = 2, + SBFPreserveStructAI = 3, + SBFPreserveFieldInfoAI = 4, + }; + + TargetMachine *TM; + const DataLayout *DL = nullptr; + Module *M = nullptr; + + static std::map GEPGlobals; + // A map to link preserve_*_access_index instrinsic calls. + std::map> AIChain; + // A map to hold all the base preserve_*_access_index instrinsic calls. + // The base call is not an input of any other preserve_* + // intrinsics. + std::map BaseAICalls; + + bool doTransformation(Function &F); + + void traceAICall(CallInst *Call, CallInfo &ParentInfo); + void traceBitCast(BitCastInst *BitCast, CallInst *Parent, + CallInfo &ParentInfo); + void traceGEP(GetElementPtrInst *GEP, CallInst *Parent, + CallInfo &ParentInfo); + void collectAICallChains(Function &F); + + bool IsPreserveDIAccessIndexCall(const CallInst *Call, CallInfo &Cinfo); + bool IsValidAIChain(const MDNode *ParentMeta, uint32_t ParentAI, + const MDNode *ChildMeta); + bool removePreserveAccessIndexIntrinsic(Function &F); + void replaceWithGEP(std::vector &CallList, + uint32_t NumOfZerosIndex, uint32_t DIIndex); + bool HasPreserveFieldInfoCall(CallInfoStack &CallStack); + void GetStorageBitRange(DIDerivedType *MemberTy, Align RecordAlignment, + uint32_t &StartBitOffset, uint32_t &EndBitOffset); + uint32_t GetFieldInfo(uint32_t InfoKind, DICompositeType *CTy, + uint32_t AccessIndex, uint32_t PatchImm, + Align RecordAlignment); + + Value *computeBaseAndAccessKey(CallInst *Call, CallInfo &CInfo, + std::string &AccessKey, MDNode *&BaseMeta); + MDNode *computeAccessKey(CallInst *Call, CallInfo &CInfo, + std::string &AccessKey, bool &IsInt32Ret); + uint64_t getConstant(const Value *IndexValue); + bool transformGEPChain(CallInst *Call, CallInfo &CInfo); +}; + +std::map SBFAbstractMemberAccess::GEPGlobals; + +class SBFAbstractMemberAccessLegacyPass final : public FunctionPass { + SBFTargetMachine *TM; + + bool runOnFunction(Function &F) override { + return SBFAbstractMemberAccess(TM).run(F); + } + +public: + static char ID; + + // Add optional SBFTargetMachine parameter so that SBF backend can add the + // phase with target machine to find out the endianness. The default + // constructor (without parameters) is used by the pass manager for managing + // purposes. + SBFAbstractMemberAccessLegacyPass(SBFTargetMachine *TM = nullptr) + : FunctionPass(ID), TM(TM) {} +}; + +} // End anonymous namespace + +char SBFAbstractMemberAccessLegacyPass::ID = 0; +INITIALIZE_PASS(SBFAbstractMemberAccessLegacyPass, DEBUG_TYPE, + "SBF Abstract Member Access", false, false) + +FunctionPass *llvm::createSBFAbstractMemberAccess(SBFTargetMachine *TM) { + return new SBFAbstractMemberAccessLegacyPass(TM); +} + +bool SBFAbstractMemberAccess::run(Function &F) { + LLVM_DEBUG(dbgs() << "********** Abstract Member Accesses **********\n"); + + M = F.getParent(); + if (!M) + return false; + + // Bail out if no debug info. + if (M->debug_compile_units().empty()) + return false; + + DL = &M->getDataLayout(); + return doTransformation(F); +} + +static bool SkipDIDerivedTag(unsigned Tag, bool skipTypedef) { + if (Tag != dwarf::DW_TAG_typedef && Tag != dwarf::DW_TAG_const_type && + Tag != dwarf::DW_TAG_volatile_type && + Tag != dwarf::DW_TAG_restrict_type && + Tag != dwarf::DW_TAG_member) + return false; + if (Tag == dwarf::DW_TAG_typedef && !skipTypedef) + return false; + return true; +} + +static DIType * stripQualifiers(DIType *Ty, bool skipTypedef = true) { + while (auto *DTy = dyn_cast(Ty)) { + if (!SkipDIDerivedTag(DTy->getTag(), skipTypedef)) + break; + Ty = DTy->getBaseType(); + } + return Ty; +} + +static const DIType * stripQualifiers(const DIType *Ty) { + while (auto *DTy = dyn_cast(Ty)) { + if (!SkipDIDerivedTag(DTy->getTag(), true)) + break; + Ty = DTy->getBaseType(); + } + return Ty; +} + +static uint32_t calcArraySize(const DICompositeType *CTy, uint32_t StartDim) { + DINodeArray Elements = CTy->getElements(); + uint32_t DimSize = 1; + for (uint32_t I = StartDim; I < Elements.size(); ++I) { + if (auto *Element = dyn_cast_or_null(Elements[I])) + if (Element->getTag() == dwarf::DW_TAG_subrange_type) { + const DISubrange *SR = cast(Element); + auto *CI = SR->getCount().dyn_cast(); + DimSize *= CI->getSExtValue(); + } + } + + return DimSize; +} + +static Type *getBaseElementType(const CallInst *Call) { + // Element type is stored in an elementtype() attribute on the first param. + return Call->getAttributes().getParamElementType(0); +} + +/// Check whether a call is a preserve_*_access_index intrinsic call or not. +bool SBFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call, + CallInfo &CInfo) { + if (!Call) + return false; + + const auto *GV = dyn_cast(Call->getCalledOperand()); + if (!GV) + return false; + if (GV->getName().startswith("llvm.preserve.array.access.index")) { + CInfo.Kind = SBFPreserveArrayAI; + CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index); + if (!CInfo.Metadata) + report_fatal_error("Missing metadata for llvm.preserve.array.access.index intrinsic"); + CInfo.AccessIndex = getConstant(Call->getArgOperand(2)); + CInfo.Base = Call->getArgOperand(0); + CInfo.RecordAlignment = DL->getABITypeAlign(getBaseElementType(Call)); + return true; + } + if (GV->getName().startswith("llvm.preserve.union.access.index")) { + CInfo.Kind = SBFPreserveUnionAI; + CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index); + if (!CInfo.Metadata) + report_fatal_error("Missing metadata for llvm.preserve.union.access.index intrinsic"); + CInfo.AccessIndex = getConstant(Call->getArgOperand(1)); + CInfo.Base = Call->getArgOperand(0); + CInfo.RecordAlignment = + DL->getABITypeAlign(CInfo.Base->getType()->getPointerElementType()); + return true; + } + if (GV->getName().startswith("llvm.preserve.struct.access.index")) { + CInfo.Kind = SBFPreserveStructAI; + CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index); + if (!CInfo.Metadata) + report_fatal_error("Missing metadata for llvm.preserve.struct.access.index intrinsic"); + CInfo.AccessIndex = getConstant(Call->getArgOperand(2)); + CInfo.Base = Call->getArgOperand(0); + CInfo.RecordAlignment = DL->getABITypeAlign(getBaseElementType(Call)); + return true; + } + if (GV->getName().startswith("llvm.bpf.preserve.field.info")) { + CInfo.Kind = SBFPreserveFieldInfoAI; + CInfo.Metadata = nullptr; + // Check validity of info_kind as clang did not check this. + uint64_t InfoKind = getConstant(Call->getArgOperand(1)); + if (InfoKind >= SBFCoreSharedInfo::MAX_FIELD_RELOC_KIND) + report_fatal_error("Incorrect info_kind for llvm.bpf.preserve.field.info intrinsic"); + CInfo.AccessIndex = InfoKind; + return true; + } + if (GV->getName().startswith("llvm.bpf.preserve.type.info")) { + CInfo.Kind = SBFPreserveFieldInfoAI; + CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index); + if (!CInfo.Metadata) + report_fatal_error("Missing metadata for llvm.preserve.type.info intrinsic"); + uint64_t Flag = getConstant(Call->getArgOperand(1)); + if (Flag >= SBFCoreSharedInfo::MAX_PRESERVE_TYPE_INFO_FLAG) + report_fatal_error("Incorrect flag for llvm.bpf.preserve.type.info intrinsic"); + if (Flag == SBFCoreSharedInfo::PRESERVE_TYPE_INFO_EXISTENCE) + CInfo.AccessIndex = SBFCoreSharedInfo::TYPE_EXISTENCE; + else + CInfo.AccessIndex = SBFCoreSharedInfo::TYPE_SIZE; + return true; + } + if (GV->getName().startswith("llvm.bpf.preserve.enum.value")) { + CInfo.Kind = SBFPreserveFieldInfoAI; + CInfo.Metadata = Call->getMetadata(LLVMContext::MD_preserve_access_index); + if (!CInfo.Metadata) + report_fatal_error("Missing metadata for llvm.preserve.enum.value intrinsic"); + uint64_t Flag = getConstant(Call->getArgOperand(2)); + if (Flag >= SBFCoreSharedInfo::MAX_PRESERVE_ENUM_VALUE_FLAG) + report_fatal_error("Incorrect flag for llvm.bpf.preserve.enum.value intrinsic"); + if (Flag == SBFCoreSharedInfo::PRESERVE_ENUM_VALUE_EXISTENCE) + CInfo.AccessIndex = SBFCoreSharedInfo::ENUM_VALUE_EXISTENCE; + else + CInfo.AccessIndex = SBFCoreSharedInfo::ENUM_VALUE; + return true; + } + + return false; +} + +void SBFAbstractMemberAccess::replaceWithGEP(std::vector &CallList, + uint32_t DimensionIndex, + uint32_t GEPIndex) { + for (auto Call : CallList) { + uint32_t Dimension = 1; + if (DimensionIndex > 0) + Dimension = getConstant(Call->getArgOperand(DimensionIndex)); + + Constant *Zero = + ConstantInt::get(Type::getInt32Ty(Call->getParent()->getContext()), 0); + SmallVector IdxList; + for (unsigned I = 0; I < Dimension; ++I) + IdxList.push_back(Zero); + IdxList.push_back(Call->getArgOperand(GEPIndex)); + + auto *GEP = GetElementPtrInst::CreateInBounds( + getBaseElementType(Call), Call->getArgOperand(0), IdxList, "", Call); + Call->replaceAllUsesWith(GEP); + Call->eraseFromParent(); + } +} + +bool SBFAbstractMemberAccess::removePreserveAccessIndexIntrinsic(Function &F) { + std::vector PreserveArrayIndexCalls; + std::vector PreserveUnionIndexCalls; + std::vector PreserveStructIndexCalls; + bool Found = false; + + for (auto &BB : F) + for (auto &I : BB) { + auto *Call = dyn_cast(&I); + CallInfo CInfo; + if (!IsPreserveDIAccessIndexCall(Call, CInfo)) + continue; + + Found = true; + if (CInfo.Kind == SBFPreserveArrayAI) + PreserveArrayIndexCalls.push_back(Call); + else if (CInfo.Kind == SBFPreserveUnionAI) + PreserveUnionIndexCalls.push_back(Call); + else + PreserveStructIndexCalls.push_back(Call); + } + + // do the following transformation: + // . addr = preserve_array_access_index(base, dimension, index) + // is transformed to + // addr = GEP(base, dimenion's zero's, index) + // . addr = preserve_union_access_index(base, di_index) + // is transformed to + // addr = base, i.e., all usages of "addr" are replaced by "base". + // . addr = preserve_struct_access_index(base, gep_index, di_index) + // is transformed to + // addr = GEP(base, 0, gep_index) + replaceWithGEP(PreserveArrayIndexCalls, 1, 2); + replaceWithGEP(PreserveStructIndexCalls, 0, 1); + for (auto Call : PreserveUnionIndexCalls) { + Call->replaceAllUsesWith(Call->getArgOperand(0)); + Call->eraseFromParent(); + } + + return Found; +} + +/// Check whether the access index chain is valid. We check +/// here because there may be type casts between two +/// access indexes. We want to ensure memory access still valid. +bool SBFAbstractMemberAccess::IsValidAIChain(const MDNode *ParentType, + uint32_t ParentAI, + const MDNode *ChildType) { + if (!ChildType) + return true; // preserve_field_info, no type comparison needed. + + const DIType *PType = stripQualifiers(cast(ParentType)); + const DIType *CType = stripQualifiers(cast(ChildType)); + + // Child is a derived/pointer type, which is due to type casting. + // Pointer type cannot be in the middle of chain. + if (isa(CType)) + return false; + + // Parent is a pointer type. + if (const auto *PtrTy = dyn_cast(PType)) { + if (PtrTy->getTag() != dwarf::DW_TAG_pointer_type) + return false; + return stripQualifiers(PtrTy->getBaseType()) == CType; + } + + // Otherwise, struct/union/array types + const auto *PTy = dyn_cast(PType); + const auto *CTy = dyn_cast(CType); + assert(PTy && CTy && "ParentType or ChildType is null or not composite"); + + uint32_t PTyTag = PTy->getTag(); + assert(PTyTag == dwarf::DW_TAG_array_type || + PTyTag == dwarf::DW_TAG_structure_type || + PTyTag == dwarf::DW_TAG_union_type); + + uint32_t CTyTag = CTy->getTag(); + assert(CTyTag == dwarf::DW_TAG_array_type || + CTyTag == dwarf::DW_TAG_structure_type || + CTyTag == dwarf::DW_TAG_union_type); + + // Multi dimensional arrays, base element should be the same + if (PTyTag == dwarf::DW_TAG_array_type && PTyTag == CTyTag) + return PTy->getBaseType() == CTy->getBaseType(); + + DIType *Ty; + if (PTyTag == dwarf::DW_TAG_array_type) + Ty = PTy->getBaseType(); + else + Ty = dyn_cast(PTy->getElements()[ParentAI]); + + return dyn_cast(stripQualifiers(Ty)) == CTy; +} + +void SBFAbstractMemberAccess::traceAICall(CallInst *Call, + CallInfo &ParentInfo) { + for (User *U : Call->users()) { + Instruction *Inst = dyn_cast(U); + if (!Inst) + continue; + + if (auto *BI = dyn_cast(Inst)) { + traceBitCast(BI, Call, ParentInfo); + } else if (auto *CI = dyn_cast(Inst)) { + CallInfo ChildInfo; + + if (IsPreserveDIAccessIndexCall(CI, ChildInfo) && + IsValidAIChain(ParentInfo.Metadata, ParentInfo.AccessIndex, + ChildInfo.Metadata)) { + AIChain[CI] = std::make_pair(Call, ParentInfo); + traceAICall(CI, ChildInfo); + } else { + BaseAICalls[Call] = ParentInfo; + } + } else if (auto *GI = dyn_cast(Inst)) { + if (GI->hasAllZeroIndices()) + traceGEP(GI, Call, ParentInfo); + else + BaseAICalls[Call] = ParentInfo; + } else { + BaseAICalls[Call] = ParentInfo; + } + } +} + +void SBFAbstractMemberAccess::traceBitCast(BitCastInst *BitCast, + CallInst *Parent, + CallInfo &ParentInfo) { + for (User *U : BitCast->users()) { + Instruction *Inst = dyn_cast(U); + if (!Inst) + continue; + + if (auto *BI = dyn_cast(Inst)) { + traceBitCast(BI, Parent, ParentInfo); + } else if (auto *CI = dyn_cast(Inst)) { + CallInfo ChildInfo; + if (IsPreserveDIAccessIndexCall(CI, ChildInfo) && + IsValidAIChain(ParentInfo.Metadata, ParentInfo.AccessIndex, + ChildInfo.Metadata)) { + AIChain[CI] = std::make_pair(Parent, ParentInfo); + traceAICall(CI, ChildInfo); + } else { + BaseAICalls[Parent] = ParentInfo; + } + } else if (auto *GI = dyn_cast(Inst)) { + if (GI->hasAllZeroIndices()) + traceGEP(GI, Parent, ParentInfo); + else + BaseAICalls[Parent] = ParentInfo; + } else { + BaseAICalls[Parent] = ParentInfo; + } + } +} + +void SBFAbstractMemberAccess::traceGEP(GetElementPtrInst *GEP, CallInst *Parent, + CallInfo &ParentInfo) { + for (User *U : GEP->users()) { + Instruction *Inst = dyn_cast(U); + if (!Inst) + continue; + + if (auto *BI = dyn_cast(Inst)) { + traceBitCast(BI, Parent, ParentInfo); + } else if (auto *CI = dyn_cast(Inst)) { + CallInfo ChildInfo; + if (IsPreserveDIAccessIndexCall(CI, ChildInfo) && + IsValidAIChain(ParentInfo.Metadata, ParentInfo.AccessIndex, + ChildInfo.Metadata)) { + AIChain[CI] = std::make_pair(Parent, ParentInfo); + traceAICall(CI, ChildInfo); + } else { + BaseAICalls[Parent] = ParentInfo; + } + } else if (auto *GI = dyn_cast(Inst)) { + if (GI->hasAllZeroIndices()) + traceGEP(GI, Parent, ParentInfo); + else + BaseAICalls[Parent] = ParentInfo; + } else { + BaseAICalls[Parent] = ParentInfo; + } + } +} + +void SBFAbstractMemberAccess::collectAICallChains(Function &F) { + AIChain.clear(); + BaseAICalls.clear(); + + for (auto &BB : F) + for (auto &I : BB) { + CallInfo CInfo; + auto *Call = dyn_cast(&I); + if (!IsPreserveDIAccessIndexCall(Call, CInfo) || + AIChain.find(Call) != AIChain.end()) + continue; + + traceAICall(Call, CInfo); + } +} + +uint64_t SBFAbstractMemberAccess::getConstant(const Value *IndexValue) { + const ConstantInt *CV = dyn_cast(IndexValue); + assert(CV); + return CV->getValue().getZExtValue(); +} + +/// Get the start and the end of storage offset for \p MemberTy. +void SBFAbstractMemberAccess::GetStorageBitRange(DIDerivedType *MemberTy, + Align RecordAlignment, + uint32_t &StartBitOffset, + uint32_t &EndBitOffset) { + uint32_t MemberBitSize = MemberTy->getSizeInBits(); + uint32_t MemberBitOffset = MemberTy->getOffsetInBits(); + uint32_t AlignBits = RecordAlignment.value() * 8; + if (RecordAlignment > 8 || MemberBitSize > AlignBits) + report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info, " + "requiring too big alignment"); + + StartBitOffset = MemberBitOffset & ~(AlignBits - 1); + if ((StartBitOffset + AlignBits) < (MemberBitOffset + MemberBitSize)) + report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info, " + "cross alignment boundary"); + EndBitOffset = StartBitOffset + AlignBits; +} + +uint32_t SBFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind, + DICompositeType *CTy, + uint32_t AccessIndex, + uint32_t PatchImm, + Align RecordAlignment) { + if (InfoKind == SBFCoreSharedInfo::FIELD_EXISTENCE) + return 1; + + uint32_t Tag = CTy->getTag(); + if (InfoKind == SBFCoreSharedInfo::FIELD_BYTE_OFFSET) { + if (Tag == dwarf::DW_TAG_array_type) { + auto *EltTy = stripQualifiers(CTy->getBaseType()); + PatchImm += AccessIndex * calcArraySize(CTy, 1) * + (EltTy->getSizeInBits() >> 3); + } else if (Tag == dwarf::DW_TAG_structure_type) { + auto *MemberTy = cast(CTy->getElements()[AccessIndex]); + if (!MemberTy->isBitField()) { + PatchImm += MemberTy->getOffsetInBits() >> 3; + } else { + unsigned SBitOffset, NextSBitOffset; + GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, + NextSBitOffset); + PatchImm += SBitOffset >> 3; + } + } + return PatchImm; + } + + if (InfoKind == SBFCoreSharedInfo::FIELD_BYTE_SIZE) { + if (Tag == dwarf::DW_TAG_array_type) { + auto *EltTy = stripQualifiers(CTy->getBaseType()); + return calcArraySize(CTy, 1) * (EltTy->getSizeInBits() >> 3); + } else { + auto *MemberTy = cast(CTy->getElements()[AccessIndex]); + uint32_t SizeInBits = MemberTy->getSizeInBits(); + if (!MemberTy->isBitField()) + return SizeInBits >> 3; + + unsigned SBitOffset, NextSBitOffset; + GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, NextSBitOffset); + SizeInBits = NextSBitOffset - SBitOffset; + if (SizeInBits & (SizeInBits - 1)) + report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info"); + return SizeInBits >> 3; + } + } + + if (InfoKind == SBFCoreSharedInfo::FIELD_SIGNEDNESS) { + const DIType *BaseTy; + if (Tag == dwarf::DW_TAG_array_type) { + // Signedness only checked when final array elements are accessed. + if (CTy->getElements().size() != 1) + report_fatal_error("Invalid array expression for llvm.bpf.preserve.field.info"); + BaseTy = stripQualifiers(CTy->getBaseType()); + } else { + auto *MemberTy = cast(CTy->getElements()[AccessIndex]); + BaseTy = stripQualifiers(MemberTy->getBaseType()); + } + + // Only basic types and enum types have signedness. + const auto *BTy = dyn_cast(BaseTy); + while (!BTy) { + const auto *CompTy = dyn_cast(BaseTy); + // Report an error if the field expression does not have signedness. + if (!CompTy || CompTy->getTag() != dwarf::DW_TAG_enumeration_type) + report_fatal_error("Invalid field expression for llvm.bpf.preserve.field.info"); + BaseTy = stripQualifiers(CompTy->getBaseType()); + BTy = dyn_cast(BaseTy); + } + uint32_t Encoding = BTy->getEncoding(); + return (Encoding == dwarf::DW_ATE_signed || Encoding == dwarf::DW_ATE_signed_char); + } + + if (InfoKind == SBFCoreSharedInfo::FIELD_LSHIFT_U64) { + // The value is loaded into a value with FIELD_BYTE_SIZE size, + // and then zero or sign extended to U64. + // FIELD_LSHIFT_U64 and FIELD_RSHIFT_U64 are operations + // to extract the original value. + const Triple &Triple = TM->getTargetTriple(); + DIDerivedType *MemberTy = nullptr; + bool IsBitField = false; + uint32_t SizeInBits; + + if (Tag == dwarf::DW_TAG_array_type) { + auto *EltTy = stripQualifiers(CTy->getBaseType()); + SizeInBits = calcArraySize(CTy, 1) * EltTy->getSizeInBits(); + } else { + MemberTy = cast(CTy->getElements()[AccessIndex]); + SizeInBits = MemberTy->getSizeInBits(); + IsBitField = MemberTy->isBitField(); + } + + if (!IsBitField) { + if (SizeInBits > 64) + report_fatal_error("too big field size for llvm.bpf.preserve.field.info"); + return 64 - SizeInBits; + } + + unsigned SBitOffset, NextSBitOffset; + GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, NextSBitOffset); + if (NextSBitOffset - SBitOffset > 64) + report_fatal_error("too big field size for llvm.bpf.preserve.field.info"); + + unsigned OffsetInBits = MemberTy->getOffsetInBits(); + if (Triple.getArch() == Triple::bpfel || Triple.getArch() == Triple::sbf) + return SBitOffset + 64 - OffsetInBits - SizeInBits; + else + return OffsetInBits + 64 - NextSBitOffset; + } + + if (InfoKind == SBFCoreSharedInfo::FIELD_RSHIFT_U64) { + DIDerivedType *MemberTy = nullptr; + bool IsBitField = false; + uint32_t SizeInBits; + if (Tag == dwarf::DW_TAG_array_type) { + auto *EltTy = stripQualifiers(CTy->getBaseType()); + SizeInBits = calcArraySize(CTy, 1) * EltTy->getSizeInBits(); + } else { + MemberTy = cast(CTy->getElements()[AccessIndex]); + SizeInBits = MemberTy->getSizeInBits(); + IsBitField = MemberTy->isBitField(); + } + + if (!IsBitField) { + if (SizeInBits > 64) + report_fatal_error("too big field size for llvm.bpf.preserve.field.info"); + return 64 - SizeInBits; + } + + unsigned SBitOffset, NextSBitOffset; + GetStorageBitRange(MemberTy, RecordAlignment, SBitOffset, NextSBitOffset); + if (NextSBitOffset - SBitOffset > 64) + report_fatal_error("too big field size for llvm.bpf.preserve.field.info"); + + return 64 - SizeInBits; + } + + llvm_unreachable("Unknown llvm.bpf.preserve.field.info info kind"); +} + +bool SBFAbstractMemberAccess::HasPreserveFieldInfoCall(CallInfoStack &CallStack) { + // This is called in error return path, no need to maintain CallStack. + while (CallStack.size()) { + auto StackElem = CallStack.top(); + if (StackElem.second.Kind == SBFPreserveFieldInfoAI) + return true; + CallStack.pop(); + } + return false; +} + +/// Compute the base of the whole preserve_* intrinsics chains, i.e., the base +/// pointer of the first preserve_*_access_index call, and construct the access +/// string, which will be the name of a global variable. +Value *SBFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call, + CallInfo &CInfo, + std::string &AccessKey, + MDNode *&TypeMeta) { + Value *Base = nullptr; + std::string TypeName; + CallInfoStack CallStack; + + // Put the access chain into a stack with the top as the head of the chain. + while (Call) { + CallStack.push(std::make_pair(Call, CInfo)); + CInfo = AIChain[Call].second; + Call = AIChain[Call].first; + } + + // The access offset from the base of the head of chain is also + // calculated here as all debuginfo types are available. + + // Get type name and calculate the first index. + // We only want to get type name from typedef, structure or union. + // If user wants a relocation like + // int *p; ... __builtin_preserve_access_index(&p[4]) ... + // or + // int a[10][20]; ... __builtin_preserve_access_index(&a[2][3]) ... + // we will skip them. + uint32_t FirstIndex = 0; + uint32_t PatchImm = 0; // AccessOffset or the requested field info + uint32_t InfoKind = SBFCoreSharedInfo::FIELD_BYTE_OFFSET; + while (CallStack.size()) { + auto StackElem = CallStack.top(); + Call = StackElem.first; + CInfo = StackElem.second; + + if (!Base) + Base = CInfo.Base; + + DIType *PossibleTypeDef = stripQualifiers(cast(CInfo.Metadata), + false); + DIType *Ty = stripQualifiers(PossibleTypeDef); + if (CInfo.Kind == SBFPreserveUnionAI || + CInfo.Kind == SBFPreserveStructAI) { + // struct or union type. If the typedef is in the metadata, always + // use the typedef. + TypeName = std::string(PossibleTypeDef->getName()); + TypeMeta = PossibleTypeDef; + PatchImm += FirstIndex * (Ty->getSizeInBits() >> 3); + break; + } + + assert(CInfo.Kind == SBFPreserveArrayAI); + + // Array entries will always be consumed for accumulative initial index. + CallStack.pop(); + + // SBFPreserveArrayAI + uint64_t AccessIndex = CInfo.AccessIndex; + + DIType *BaseTy = nullptr; + bool CheckElemType = false; + if (const auto *CTy = dyn_cast(Ty)) { + // array type + assert(CTy->getTag() == dwarf::DW_TAG_array_type); + + + FirstIndex += AccessIndex * calcArraySize(CTy, 1); + BaseTy = stripQualifiers(CTy->getBaseType()); + CheckElemType = CTy->getElements().size() == 1; + } else { + // pointer type + auto *DTy = cast(Ty); + assert(DTy->getTag() == dwarf::DW_TAG_pointer_type); + + BaseTy = stripQualifiers(DTy->getBaseType()); + CTy = dyn_cast(BaseTy); + if (!CTy) { + CheckElemType = true; + } else if (CTy->getTag() != dwarf::DW_TAG_array_type) { + FirstIndex += AccessIndex; + CheckElemType = true; + } else { + FirstIndex += AccessIndex * calcArraySize(CTy, 0); + } + } + + if (CheckElemType) { + auto *CTy = dyn_cast(BaseTy); + if (!CTy) { + if (HasPreserveFieldInfoCall(CallStack)) + report_fatal_error("Invalid field access for llvm.preserve.field.info intrinsic"); + return nullptr; + } + + unsigned CTag = CTy->getTag(); + if (CTag == dwarf::DW_TAG_structure_type || CTag == dwarf::DW_TAG_union_type) { + TypeName = std::string(CTy->getName()); + } else { + if (HasPreserveFieldInfoCall(CallStack)) + report_fatal_error("Invalid field access for llvm.preserve.field.info intrinsic"); + return nullptr; + } + TypeMeta = CTy; + PatchImm += FirstIndex * (CTy->getSizeInBits() >> 3); + break; + } + } + assert(TypeName.size()); + AccessKey += std::to_string(FirstIndex); + + // Traverse the rest of access chain to complete offset calculation + // and access key construction. + while (CallStack.size()) { + auto StackElem = CallStack.top(); + CInfo = StackElem.second; + CallStack.pop(); + + if (CInfo.Kind == SBFPreserveFieldInfoAI) { + InfoKind = CInfo.AccessIndex; + if (InfoKind == SBFCoreSharedInfo::FIELD_EXISTENCE) + PatchImm = 1; + break; + } + + // If the next Call (the top of the stack) is a SBFPreserveFieldInfoAI, + // the action will be extracting field info. + if (CallStack.size()) { + auto StackElem2 = CallStack.top(); + CallInfo CInfo2 = StackElem2.second; + if (CInfo2.Kind == SBFPreserveFieldInfoAI) { + InfoKind = CInfo2.AccessIndex; + assert(CallStack.size() == 1); + } + } + + // Access Index + uint64_t AccessIndex = CInfo.AccessIndex; + AccessKey += ":" + std::to_string(AccessIndex); + + MDNode *MDN = CInfo.Metadata; + // At this stage, it cannot be pointer type. + auto *CTy = cast(stripQualifiers(cast(MDN))); + PatchImm = GetFieldInfo(InfoKind, CTy, AccessIndex, PatchImm, + CInfo.RecordAlignment); + } + + // Access key is the + // "llvm." + type name + ":" + reloc type + ":" + patched imm + "$" + + // access string, + // uniquely identifying one relocation. + // The prefix "llvm." indicates this is a temporary global, which should + // not be emitted to ELF file. + AccessKey = "llvm." + TypeName + ":" + std::to_string(InfoKind) + ":" + + std::to_string(PatchImm) + "$" + AccessKey; + + return Base; +} + +MDNode *SBFAbstractMemberAccess::computeAccessKey(CallInst *Call, + CallInfo &CInfo, + std::string &AccessKey, + bool &IsInt32Ret) { + DIType *Ty = stripQualifiers(cast(CInfo.Metadata), false); + assert(!Ty->getName().empty()); + + int64_t PatchImm; + std::string AccessStr("0"); + if (CInfo.AccessIndex == SBFCoreSharedInfo::TYPE_EXISTENCE) { + PatchImm = 1; + } else if (CInfo.AccessIndex == SBFCoreSharedInfo::TYPE_SIZE) { + // typedef debuginfo type has size 0, get the eventual base type. + DIType *BaseTy = stripQualifiers(Ty, true); + PatchImm = BaseTy->getSizeInBits() / 8; + } else { + // ENUM_VALUE_EXISTENCE and ENUM_VALUE + IsInt32Ret = false; + + const auto *CE = cast(Call->getArgOperand(1)); + const GlobalVariable *GV = cast(CE->getOperand(0)); + assert(GV->hasInitializer()); + const ConstantDataArray *DA = cast(GV->getInitializer()); + assert(DA->isString()); + StringRef ValueStr = DA->getAsString(); + + // ValueStr format: : + size_t Separator = ValueStr.find_first_of(':'); + StringRef EnumeratorStr = ValueStr.substr(0, Separator); + + // Find enumerator index in the debuginfo + DIType *BaseTy = stripQualifiers(Ty, true); + const auto *CTy = cast(BaseTy); + assert(CTy->getTag() == dwarf::DW_TAG_enumeration_type); + int EnumIndex = 0; + for (const auto Element : CTy->getElements()) { + const auto *Enum = cast(Element); + if (Enum->getName() == EnumeratorStr) { + AccessStr = std::to_string(EnumIndex); + break; + } + EnumIndex++; + } + + if (CInfo.AccessIndex == SBFCoreSharedInfo::ENUM_VALUE) { + StringRef EValueStr = ValueStr.substr(Separator + 1); + PatchImm = std::stoll(std::string(EValueStr)); + } else { + PatchImm = 1; + } + } + + AccessKey = "llvm." + Ty->getName().str() + ":" + + std::to_string(CInfo.AccessIndex) + std::string(":") + + std::to_string(PatchImm) + std::string("$") + AccessStr; + + return Ty; +} + +/// Call/Kind is the base preserve_*_access_index() call. Attempts to do +/// transformation to a chain of relocable GEPs. +bool SBFAbstractMemberAccess::transformGEPChain(CallInst *Call, + CallInfo &CInfo) { + std::string AccessKey; + MDNode *TypeMeta; + Value *Base = nullptr; + bool IsInt32Ret; + + IsInt32Ret = CInfo.Kind == SBFPreserveFieldInfoAI; + if (CInfo.Kind == SBFPreserveFieldInfoAI && CInfo.Metadata) { + TypeMeta = computeAccessKey(Call, CInfo, AccessKey, IsInt32Ret); + } else { + Base = computeBaseAndAccessKey(Call, CInfo, AccessKey, TypeMeta); + if (!Base) + return false; + } + + BasicBlock *BB = Call->getParent(); + GlobalVariable *GV; + + if (GEPGlobals.find(AccessKey) == GEPGlobals.end()) { + IntegerType *VarType; + if (IsInt32Ret) + VarType = Type::getInt32Ty(BB->getContext()); // 32bit return value + else + VarType = Type::getInt64Ty(BB->getContext()); // 64bit ptr or enum value + + GV = new GlobalVariable(*M, VarType, false, GlobalVariable::ExternalLinkage, + nullptr, AccessKey); + GV->addAttribute(SBFCoreSharedInfo::AmaAttr); + GV->setMetadata(LLVMContext::MD_preserve_access_index, TypeMeta); + GEPGlobals[AccessKey] = GV; + } else { + GV = GEPGlobals[AccessKey]; + } + + if (CInfo.Kind == SBFPreserveFieldInfoAI) { + // Load the global variable which represents the returned field info. + LoadInst *LDInst; + if (IsInt32Ret) + LDInst = new LoadInst(Type::getInt32Ty(BB->getContext()), GV, "", Call); + else + LDInst = new LoadInst(Type::getInt64Ty(BB->getContext()), GV, "", Call); + + Instruction *PassThroughInst = + SBFCoreSharedInfo::insertPassThrough(M, BB, LDInst, Call); + Call->replaceAllUsesWith(PassThroughInst); + Call->eraseFromParent(); + return true; + } + + // For any original GEP Call and Base %2 like + // %4 = bitcast %struct.net_device** %dev1 to i64* + // it is transformed to: + // %6 = load llvm.sk_buff:0:50$0:0:0:2:0 + // %7 = bitcast %struct.sk_buff* %2 to i8* + // %8 = getelementptr i8, i8* %7, %6 + // %9 = bitcast i8* %8 to i64* + // using %9 instead of %4 + // The original Call inst is removed. + + // Load the global variable. + auto *LDInst = new LoadInst(Type::getInt64Ty(BB->getContext()), GV, "", Call); + + // Generate a BitCast + auto *BCInst = new BitCastInst(Base, Type::getInt8PtrTy(BB->getContext())); + BB->getInstList().insert(Call->getIterator(), BCInst); + + // Generate a GetElementPtr + auto *GEP = GetElementPtrInst::Create(Type::getInt8Ty(BB->getContext()), + BCInst, LDInst); + BB->getInstList().insert(Call->getIterator(), GEP); + + // Generate a BitCast + auto *BCInst2 = new BitCastInst(GEP, Call->getType()); + BB->getInstList().insert(Call->getIterator(), BCInst2); + + // For the following code, + // Block0: + // ... + // if (...) goto Block1 else ... + // Block1: + // %6 = load llvm.sk_buff:0:50$0:0:0:2:0 + // %7 = bitcast %struct.sk_buff* %2 to i8* + // %8 = getelementptr i8, i8* %7, %6 + // ... + // goto CommonExit + // Block2: + // ... + // if (...) goto Block3 else ... + // Block3: + // %6 = load llvm.bpf_map:0:40$0:0:0:2:0 + // %7 = bitcast %struct.sk_buff* %2 to i8* + // %8 = getelementptr i8, i8* %7, %6 + // ... + // goto CommonExit + // CommonExit + // SimplifyCFG may generate: + // Block0: + // ... + // if (...) goto Block_Common else ... + // Block2: + // ... + // if (...) goto Block_Common else ... + // Block_Common: + // PHI = [llvm.sk_buff:0:50$0:0:0:2:0, llvm.bpf_map:0:40$0:0:0:2:0] + // %6 = load PHI + // %7 = bitcast %struct.sk_buff* %2 to i8* + // %8 = getelementptr i8, i8* %7, %6 + // ... + // goto CommonExit + // For the above code, we cannot perform proper relocation since + // "load PHI" has two possible relocations. + // + // To prevent above tail merging, we use __builtin_bpf_passthrough() + // where one of its parameters is a seq_num. Since two + // __builtin_bpf_passthrough() funcs will always have different seq_num, + // tail merging cannot happen. The __builtin_bpf_passthrough() will be + // removed in the beginning of Target IR passes. + // + // This approach is also used in other places when global var + // representing a relocation is used. + Instruction *PassThroughInst = + SBFCoreSharedInfo::insertPassThrough(M, BB, BCInst2, Call); + Call->replaceAllUsesWith(PassThroughInst); + Call->eraseFromParent(); + + return true; +} + +bool SBFAbstractMemberAccess::doTransformation(Function &F) { + bool Transformed = false; + + // Collect PreserveDIAccessIndex Intrinsic call chains. + // The call chains will be used to generate the access + // patterns similar to GEP. + collectAICallChains(F); + + for (auto &C : BaseAICalls) + Transformed = transformGEPChain(C.first, C.second) || Transformed; + + return removePreserveAccessIndexIntrinsic(F) || Transformed; +} + +PreservedAnalyses +SBFAbstractMemberAccessPass::run(Function &F, FunctionAnalysisManager &AM) { + return SBFAbstractMemberAccess(TM).run(F) ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/SBF/SBFAdjustOpt.cpp b/llvm/lib/Target/SBF/SBFAdjustOpt.cpp new file mode 100644 index 0000000000000..dd13f7af8c052 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFAdjustOpt.cpp @@ -0,0 +1,387 @@ +//===---------------- SBFAdjustOpt.cpp - Adjust Optimization --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Adjust optimization to make the code more kernel verifier friendly. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFCORE.h" +#include "SBFTargetMachine.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicsBPF.h" // TODO: jle. +#include "llvm/IR/Module.h" +#include "llvm/IR/PatternMatch.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +#define DEBUG_TYPE "sbf-adjust-opt" + +using namespace llvm; +using namespace llvm::PatternMatch; + +static cl::opt + DisableSBFserializeICMP("sbf-disable-serialize-icmp", cl::Hidden, + cl::desc("SBF: Disable Serializing ICMP insns."), + cl::init(false)); + +static cl::opt DisableSBFavoidSpeculation( + "sbf-disable-avoid-speculation", cl::Hidden, + cl::desc("SBF: Disable Avoiding Speculative Code Motion."), + cl::init(false)); + +namespace { + +class SBFAdjustOpt final : public ModulePass { +public: + static char ID; + + SBFAdjustOpt() : ModulePass(ID) {} + bool runOnModule(Module &M) override; +}; + +class SBFAdjustOptImpl { + struct PassThroughInfo { + Instruction *Input; + Instruction *UsedInst; + uint32_t OpIdx; + PassThroughInfo(Instruction *I, Instruction *U, uint32_t Idx) + : Input(I), UsedInst(U), OpIdx(Idx) {} + }; + +public: + SBFAdjustOptImpl(Module *M) : M(M) {} + + bool run(); + +private: + Module *M; + SmallVector PassThroughs; + + bool adjustICmpToBuiltin(); + void adjustBasicBlock(BasicBlock &BB); + bool serializeICMPCrossBB(BasicBlock &BB); + void adjustInst(Instruction &I); + bool serializeICMPInBB(Instruction &I); + bool avoidSpeculation(Instruction &I); + bool insertPassThrough(); +}; + +} // End anonymous namespace + +char SBFAdjustOpt::ID = 0; +INITIALIZE_PASS(SBFAdjustOpt, "sbf-adjust-opt", "SBF Adjust Optimization", + false, false) + +ModulePass *llvm::createSBFAdjustOpt() { return new SBFAdjustOpt(); } + +bool SBFAdjustOpt::runOnModule(Module &M) { return SBFAdjustOptImpl(&M).run(); } + +bool SBFAdjustOptImpl::run() { + bool Changed = adjustICmpToBuiltin(); + + for (Function &F : *M) + for (auto &BB : F) { + adjustBasicBlock(BB); + for (auto &I : BB) + adjustInst(I); + } + return insertPassThrough() || Changed; +} + +// Commit acabad9ff6bf ("[InstCombine] try to canonicalize icmp with +// trunc op into mask and cmp") added a transformation to +// convert "(conv)a < power_2_const" to "a & " in certain +// cases and bpf kernel verifier has to handle the resulted code +// conservatively and this may reject otherwise legitimate program. +// Here, we change related icmp code to a builtin which will +// be restored to original icmp code later to prevent that +// InstCombine transformatin. +bool SBFAdjustOptImpl::adjustICmpToBuiltin() { + bool Changed = false; + ICmpInst *ToBeDeleted = nullptr; + for (Function &F : *M) + for (auto &BB : F) + for (auto &I : BB) { + if (ToBeDeleted) { + ToBeDeleted->eraseFromParent(); + ToBeDeleted = nullptr; + } + + auto *Icmp = dyn_cast(&I); + if (!Icmp) + continue; + + Value *Op0 = Icmp->getOperand(0); + if (!isa(Op0)) + continue; + + auto ConstOp1 = dyn_cast(Icmp->getOperand(1)); + if (!ConstOp1) + continue; + + auto ConstOp1Val = ConstOp1->getValue().getZExtValue(); + auto Op = Icmp->getPredicate(); + if (Op == ICmpInst::ICMP_ULT || Op == ICmpInst::ICMP_UGE) { + if ((ConstOp1Val - 1) & ConstOp1Val) + continue; + } else if (Op == ICmpInst::ICMP_ULE || Op == ICmpInst::ICMP_UGT) { + if (ConstOp1Val & (ConstOp1Val + 1)) + continue; + } else { + continue; + } + + Constant *Opcode = + ConstantInt::get(Type::getInt32Ty(BB.getContext()), Op); + Function *Fn = Intrinsic::getDeclaration( + M, Intrinsic::bpf_compare, {Op0->getType(), ConstOp1->getType()}); + auto *NewInst = CallInst::Create(Fn, {Opcode, Op0, ConstOp1}); + BB.getInstList().insert(I.getIterator(), NewInst); + Icmp->replaceAllUsesWith(NewInst); + Changed = true; + ToBeDeleted = Icmp; + } + + return Changed; +} + +bool SBFAdjustOptImpl::insertPassThrough() { + for (auto &Info : PassThroughs) { + auto *CI = SBFCoreSharedInfo::insertPassThrough( + M, Info.UsedInst->getParent(), Info.Input, Info.UsedInst); + Info.UsedInst->setOperand(Info.OpIdx, CI); + } + + return !PassThroughs.empty(); +} + +// To avoid combining conditionals in the same basic block by +// instrcombine optimization. +bool SBFAdjustOptImpl::serializeICMPInBB(Instruction &I) { + // For: + // comp1 = icmp ...; + // comp2 = icmp ...; + // ... or comp1 comp2 ... + // changed to: + // comp1 = icmp ...; + // comp2 = icmp ...; + // new_comp1 = __builtin_bpf_passthrough(seq_num, comp1) + // ... or new_comp1 comp2 ... + Value *Op0, *Op1; + // Use LogicalOr (accept `or i1` as well as `select i1 Op0, true, Op1`) + if (!match(&I, m_LogicalOr(m_Value(Op0), m_Value(Op1)))) + return false; + auto *Icmp1 = dyn_cast(Op0); + if (!Icmp1) + return false; + auto *Icmp2 = dyn_cast(Op1); + if (!Icmp2) + return false; + + Value *Icmp1Op0 = Icmp1->getOperand(0); + Value *Icmp2Op0 = Icmp2->getOperand(0); + if (Icmp1Op0 != Icmp2Op0) + return false; + + // Now we got two icmp instructions which feed into + // an "or" instruction. + PassThroughInfo Info(Icmp1, &I, 0); + PassThroughs.push_back(Info); + return true; +} + +// To avoid combining conditionals in the same basic block by +// instrcombine optimization. +bool SBFAdjustOptImpl::serializeICMPCrossBB(BasicBlock &BB) { + // For: + // B1: + // comp1 = icmp ...; + // if (comp1) goto B2 else B3; + // B2: + // comp2 = icmp ...; + // if (comp2) goto B4 else B5; + // B4: + // ... + // changed to: + // B1: + // comp1 = icmp ...; + // comp1 = __builtin_bpf_passthrough(seq_num, comp1); + // if (comp1) goto B2 else B3; + // B2: + // comp2 = icmp ...; + // if (comp2) goto B4 else B5; + // B4: + // ... + + // Check basic predecessors, if two of them (say B1, B2) are using + // icmp instructions to generate conditions and one is the predesessor + // of another (e.g., B1 is the predecessor of B2). Add a passthrough + // barrier after icmp inst of block B1. + BasicBlock *B2 = BB.getSinglePredecessor(); + if (!B2) + return false; + + BasicBlock *B1 = B2->getSinglePredecessor(); + if (!B1) + return false; + + Instruction *TI = B2->getTerminator(); + auto *BI = dyn_cast(TI); + if (!BI || !BI->isConditional()) + return false; + auto *Cond = dyn_cast(BI->getCondition()); + if (!Cond || B2->getFirstNonPHI() != Cond) + return false; + Value *B2Op0 = Cond->getOperand(0); + auto Cond2Op = Cond->getPredicate(); + + TI = B1->getTerminator(); + BI = dyn_cast(TI); + if (!BI || !BI->isConditional()) + return false; + Cond = dyn_cast(BI->getCondition()); + if (!Cond) + return false; + Value *B1Op0 = Cond->getOperand(0); + auto Cond1Op = Cond->getPredicate(); + + if (B1Op0 != B2Op0) + return false; + + if (Cond1Op == ICmpInst::ICMP_SGT || Cond1Op == ICmpInst::ICMP_SGE) { + if (Cond2Op != ICmpInst::ICMP_SLT && Cond1Op != ICmpInst::ICMP_SLE) + return false; + } else if (Cond1Op == ICmpInst::ICMP_SLT || Cond1Op == ICmpInst::ICMP_SLE) { + if (Cond2Op != ICmpInst::ICMP_SGT && Cond1Op != ICmpInst::ICMP_SGE) + return false; + } else { + return false; + } + + PassThroughInfo Info(Cond, BI, 0); + PassThroughs.push_back(Info); + + return true; +} + +// To avoid speculative hoisting certain computations out of +// a basic block. +bool SBFAdjustOptImpl::avoidSpeculation(Instruction &I) { + if (auto *LdInst = dyn_cast(&I)) { + if (auto *GV = dyn_cast(LdInst->getOperand(0))) { + if (GV->hasAttribute(SBFCoreSharedInfo::AmaAttr) || + GV->hasAttribute(SBFCoreSharedInfo::TypeIdAttr)) + return false; + } + } + + if (!isa(&I) && !isa(&I)) + return false; + + // For: + // B1: + // var = ... + // ... + // /* icmp may not be in the same block as var = ... */ + // comp1 = icmp var, ; + // if (comp1) goto B2 else B3; + // B2: + // ... var ... + // change to: + // B1: + // var = ... + // ... + // /* icmp may not be in the same block as var = ... */ + // comp1 = icmp var, ; + // if (comp1) goto B2 else B3; + // B2: + // var = __builtin_bpf_passthrough(seq_num, var); + // ... var ... + bool isCandidate = false; + SmallVector Candidates; + for (User *U : I.users()) { + Instruction *Inst = dyn_cast(U); + if (!Inst) + continue; + + // May cover a little bit more than the + // above pattern. + if (auto *Icmp1 = dyn_cast(Inst)) { + Value *Icmp1Op1 = Icmp1->getOperand(1); + if (!isa(Icmp1Op1)) + return false; + isCandidate = true; + continue; + } + + // Ignore the use in the same basic block as the definition. + if (Inst->getParent() == I.getParent()) + continue; + + // use in a different basic block, If there is a call or + // load/store insn before this instruction in this basic + // block. Most likely it cannot be hoisted out. Skip it. + for (auto &I2 : *Inst->getParent()) { + if (isa(&I2)) + return false; + if (isa(&I2) || isa(&I2)) + return false; + if (&I2 == Inst) + break; + } + + // It should be used in a GEP or a simple arithmetic like + // ZEXT/SEXT which is used for GEP. + if (Inst->getOpcode() == Instruction::ZExt || + Inst->getOpcode() == Instruction::SExt) { + PassThroughInfo Info(&I, Inst, 0); + Candidates.push_back(Info); + } else if (auto *GI = dyn_cast(Inst)) { + // traverse GEP inst to find Use operand index + unsigned i, e; + for (i = 1, e = GI->getNumOperands(); i != e; ++i) { + Value *V = GI->getOperand(i); + if (V == &I) + break; + } + if (i == e) + continue; + + PassThroughInfo Info(&I, GI, i); + Candidates.push_back(Info); + } + } + + if (!isCandidate || Candidates.empty()) + return false; + + llvm::append_range(PassThroughs, Candidates); + return true; +} + +void SBFAdjustOptImpl::adjustBasicBlock(BasicBlock &BB) { + if (!DisableSBFserializeICMP && serializeICMPCrossBB(BB)) + return; +} + +void SBFAdjustOptImpl::adjustInst(Instruction &I) { + if (!DisableSBFserializeICMP && serializeICMPInBB(I)) + return; + if (!DisableSBFavoidSpeculation && avoidSpeculation(I)) + return; +} + +PreservedAnalyses SBFAdjustOptPass::run(Module &M, ModuleAnalysisManager &AM) { + return SBFAdjustOptImpl(&M).run() ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/SBF/SBFAsmPrinter.cpp b/llvm/lib/Target/SBF/SBFAsmPrinter.cpp new file mode 100644 index 0000000000000..d9d58753fab27 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFAsmPrinter.cpp @@ -0,0 +1,156 @@ +//===-- SBFAsmPrinter.cpp - SBF LLVM assembly writer ----------------------===// +// +// 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 printer that converts from our internal representation +// of machine-dependent LLVM code to the SBF assembly language. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFInstrInfo.h" +#include "SBFMCInstLower.h" +#include "SBFTargetMachine.h" +#include "BTFDebug.h" +#include "MCTargetDesc/SBFInstPrinter.h" +#include "MCTargetDesc/SBFMCTargetDesc.h" +#include "TargetInfo/SBFTargetInfo.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +namespace { +class SBFAsmPrinter : public AsmPrinter { +public: + explicit SBFAsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)), BTF(nullptr) {} + + StringRef getPassName() const override { return "SBF Assembly Printer"; } + bool doInitialization(Module &M) override; + void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O); + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &O) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, + const char *ExtraCode, raw_ostream &O) override; + + void emitInstruction(const MachineInstr *MI) override; + +private: + BTFX::BTFDebug *BTF; +}; +} // namespace + +bool SBFAsmPrinter::doInitialization(Module &M) { + AsmPrinter::doInitialization(M); + + // Only emit BTF when debuginfo available. + // Unsupported for Solana: https://github.com/solana-labs/llvm-project/issues/37 + if (MAI->doesSupportDebugInformation() && !M.debug_compile_units().empty() && + !TM.getMCSubtargetInfo()->hasFeature(SBF::FeatureSolana) && TM.getTargetTriple().getArch() != Triple::sbf) { + BTF = new BTFX::BTFDebug(this); + Handlers.push_back(HandlerInfo(std::unique_ptr(BTF), "emit", + "Debug Info Emission", "BTF", + "BTF Emission")); + } + + return false; +} + +void SBFAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, + raw_ostream &O) { + const MachineOperand &MO = MI->getOperand(OpNum); + + switch (MO.getType()) { + case MachineOperand::MO_Register: + O << SBFInstPrinter::getRegisterName(MO.getReg()); + break; + + case MachineOperand::MO_Immediate: + O << MO.getImm(); + break; + + case MachineOperand::MO_MachineBasicBlock: + O << *MO.getMBB()->getSymbol(); + break; + + case MachineOperand::MO_GlobalAddress: + O << *getSymbol(MO.getGlobal()); + break; + + case MachineOperand::MO_BlockAddress: { + MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress()); + O << BA->getName(); + break; + } + + case MachineOperand::MO_ExternalSymbol: + O << *GetExternalSymbolSymbol(MO.getSymbolName()); + break; + + case MachineOperand::MO_JumpTableIndex: + case MachineOperand::MO_ConstantPoolIndex: + default: + llvm_unreachable(""); + } +} + +bool SBFAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &O) { + if (ExtraCode && ExtraCode[0]) + return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, O); + + printOperand(MI, OpNo, O); + return false; +} + +bool SBFAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNum, const char *ExtraCode, + raw_ostream &O) { + assert(OpNum + 1 < MI->getNumOperands() && "Insufficient operands"); + const MachineOperand &BaseMO = MI->getOperand(OpNum); + const MachineOperand &OffsetMO = MI->getOperand(OpNum + 1); + assert(BaseMO.isReg() && "Unexpected base pointer for inline asm memory operand."); + assert(OffsetMO.isImm() && "Unexpected offset for inline asm memory operand."); + int Offset = OffsetMO.getImm(); + + if (ExtraCode) + return true; // Unknown modifier. + + if (Offset < 0) + O << "(" << SBFInstPrinter::getRegisterName(BaseMO.getReg()) << " - " << -Offset << ")"; + else + O << "(" << SBFInstPrinter::getRegisterName(BaseMO.getReg()) << " + " << Offset << ")"; + + return false; +} + +void SBFAsmPrinter::emitInstruction(const MachineInstr *MI) { + MCInst TmpInst; + + if (!BTF || !BTF->InstLower(MI, TmpInst)) { + SBFMCInstLower MCInstLowering(OutContext, *this); + MCInstLowering.Lower(MI, TmpInst); + } + EmitToStreamer(*OutStreamer, TmpInst); +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSBFAsmPrinter() { + RegisterAsmPrinter XX(getTheSBFXTarget()); +} diff --git a/llvm/lib/Target/SBF/SBFCORE.h b/llvm/lib/Target/SBF/SBFCORE.h new file mode 100644 index 0000000000000..a73789a4bc353 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFCORE.h @@ -0,0 +1,76 @@ +//===- SBFCORE.h - Common info for Compile-Once Run-EveryWhere -*- 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_LIB_TARGET_SBF_SBFCORE_H +#define LLVM_LIB_TARGET_SBF_SBFCORE_H + +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +class BasicBlock; +class Instruction; +class Module; + +class SBFCoreSharedInfo { +public: + enum PatchableRelocKind : uint32_t { + FIELD_BYTE_OFFSET = 0, + FIELD_BYTE_SIZE, + FIELD_EXISTENCE, + FIELD_SIGNEDNESS, + FIELD_LSHIFT_U64, + FIELD_RSHIFT_U64, + BTF_TYPE_ID_LOCAL, + BTF_TYPE_ID_REMOTE, + TYPE_EXISTENCE, + TYPE_SIZE, + ENUM_VALUE_EXISTENCE, + ENUM_VALUE, + + MAX_FIELD_RELOC_KIND, + }; + + enum BTFTypeIdFlag : uint32_t { + BTF_TYPE_ID_LOCAL_RELOC = 0, + BTF_TYPE_ID_REMOTE_RELOC, + + MAX_BTF_TYPE_ID_FLAG, + }; + + enum PreserveTypeInfo : uint32_t { + PRESERVE_TYPE_INFO_EXISTENCE = 0, + PRESERVE_TYPE_INFO_SIZE, + + MAX_PRESERVE_TYPE_INFO_FLAG, + }; + + enum PreserveEnumValue : uint32_t { + PRESERVE_ENUM_VALUE_EXISTENCE = 0, + PRESERVE_ENUM_VALUE, + + MAX_PRESERVE_ENUM_VALUE_FLAG, + }; + + /// The attribute attached to globals representing a field access + static constexpr StringRef AmaAttr = "btf_ama"; + /// The attribute attached to globals representing a type id + static constexpr StringRef TypeIdAttr = "btf_type_id"; + + /// llvm.bpf.passthrough builtin seq number + static uint32_t SeqNum; + + /// Insert a bpf passthrough builtin function. + static Instruction *insertPassThrough(Module *M, BasicBlock *BB, + Instruction *Input, + Instruction *Before); +}; + +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/SBF/SBFCallingConv.td b/llvm/lib/Target/SBF/SBFCallingConv.td new file mode 100644 index 0000000000000..877c8bdb92c82 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFCallingConv.td @@ -0,0 +1,68 @@ +//===-- SBFCallingConv.td - Calling Conventions SBF --------*- tablegen -*-===// +// +// 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 describes the calling conventions for the SBF architecture. +// +//===----------------------------------------------------------------------===// + +// SBF 64-bit C return-value convention. +def RetCC_SBF64 : CallingConv<[CCIfType<[i64], CCAssignToReg<[R0]>>]>; + +// SBF 64-bit C Calling convention. +def CC_SBF64 : CallingConv<[ + // Promote i8/i16/i32 args to i64 + CCIfType<[ i8, i16, i32 ], CCPromoteToType>, + + // All arguments get passed in integer registers if there is space. + CCIfType<[i64], CCAssignToReg<[ R1, R2, R3, R4, R5 ]>>, + + // Could be assigned to the stack in 8-byte aligned units, but unsupported + CCAssignToStack<8, 8> +]>; + +def CC_SBF64_X : CallingConv<[ + CCIfType<[ i8, i16, i32 ], CCPromoteToType>, + + // Skip R5 register, reserved for passing frame pointer. + CCIfType<[i64], CCAssignToReg<[R1, R2, R3, R4]>>, + + CCAssignToStack<8, 8> +]>; + +// Return-value convention when -mattr=+alu32 enabled +def RetCC_SBF32 : CallingConv<[ + CCIfType<[i32], CCAssignToRegWithShadow<[W0], [R0]>>, + CCIfType<[i64], CCAssignToRegWithShadow<[R0], [W0]>> +]>; + +// Calling convention when -mattr=+alu32 enabled +def CC_SBF32 : CallingConv<[ + // Promote i8/i16/i32 args to i64 + CCIfType<[i32], CCAssignToRegWithShadow<[W1, W2, W3, W4, W5], + [R1, R2, R3, R4, R5]>>, + + // All arguments get passed in integer registers if there is space. + CCIfType<[i64], CCAssignToRegWithShadow<[R1, R2, R3, R4, R5], + [W1, W2, W3, W4, W5]>>, + + // Could be assigned to the stack in 8-byte aligned units, but unsupported + CCAssignToStack<8, 8> +]>; + +def CC_SBF32_X : CallingConv<[ + // Skip R5 register, reserved for passing frame pointer. + CCIfType<[i32], CCAssignToRegWithShadow<[W1, W2, W3, W4], + [R1, R2, R3, R4]>>, + + CCIfType<[i64], CCAssignToRegWithShadow<[R1, R2, R3, R4], + [W1, W2, W3, W4]>>, + + CCAssignToStack<8, 8> +]>; + +def CSR : CalleeSavedRegs<(add R6, R7, R8, R9, R10)>; diff --git a/llvm/lib/Target/SBF/SBFCheckAndAdjustIR.cpp b/llvm/lib/Target/SBF/SBFCheckAndAdjustIR.cpp new file mode 100644 index 0000000000000..50dce7a2e21b2 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFCheckAndAdjustIR.cpp @@ -0,0 +1,173 @@ +//===------------ SBFCheckAndAdjustIR.cpp - Check and Adjust IR -----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Check IR and adjust IR for verifier friendly codes. +// The following are done for IR checking: +// - no relocation globals in PHI node. +// The following are done for IR adjustment: +// - remove __builtin_bpf_passthrough builtins. Target independent IR +// optimizations are done and those builtins can be removed. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFCORE.h" +#include "SBFTargetMachine.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +#define DEBUG_TYPE "sbf-check-and-opt-ir" + +using namespace llvm; + +namespace { + +class SBFCheckAndAdjustIR final : public ModulePass { + bool runOnModule(Module &F) override; + +public: + static char ID; + SBFCheckAndAdjustIR() : ModulePass(ID) {} + +private: + void checkIR(Module &M); + bool adjustIR(Module &M); + bool removePassThroughBuiltin(Module &M); + bool removeCompareBuiltin(Module &M); +}; +} // End anonymous namespace + +char SBFCheckAndAdjustIR::ID = 0; +INITIALIZE_PASS(SBFCheckAndAdjustIR, DEBUG_TYPE, "SBF Check And Adjust IR", + false, false) + +ModulePass *llvm::createSBFCheckAndAdjustIR() { + return new SBFCheckAndAdjustIR(); +} + +void SBFCheckAndAdjustIR::checkIR(Module &M) { + // Ensure relocation global won't appear in PHI node + // This may happen if the compiler generated the following code: + // B1: + // g1 = @llvm.skb_buff:0:1... + // ... + // goto B_COMMON + // B2: + // g2 = @llvm.skb_buff:0:2... + // ... + // goto B_COMMON + // B_COMMON: + // g = PHI(g1, g2) + // x = load g + // ... + // If anything likes the above "g = PHI(g1, g2)", issue a fatal error. + for (Function &F : M) + for (auto &BB : F) + for (auto &I : BB) { + PHINode *PN = dyn_cast(&I); + if (!PN || PN->use_empty()) + continue; + for (int i = 0, e = PN->getNumIncomingValues(); i < e; ++i) { + auto *GV = dyn_cast(PN->getIncomingValue(i)); + if (!GV) + continue; + if (GV->hasAttribute(SBFCoreSharedInfo::AmaAttr) || + GV->hasAttribute(SBFCoreSharedInfo::TypeIdAttr)) + report_fatal_error("relocation global in PHI node"); + } + } +} + +bool SBFCheckAndAdjustIR::removePassThroughBuiltin(Module &M) { + // Remove __builtin_bpf_passthrough()'s which are used to prevent + // certain IR optimizations. Now major IR optimizations are done, + // remove them. + bool Changed = false; + CallInst *ToBeDeleted = nullptr; + for (Function &F : M) + for (auto &BB : F) + for (auto &I : BB) { + if (ToBeDeleted) { + ToBeDeleted->eraseFromParent(); + ToBeDeleted = nullptr; + } + + auto *Call = dyn_cast(&I); + if (!Call) + continue; + auto *GV = dyn_cast(Call->getCalledOperand()); + if (!GV) + continue; + if (!GV->getName().startswith("llvm.bpf.passthrough")) + continue; + Changed = true; + Value *Arg = Call->getArgOperand(1); + Call->replaceAllUsesWith(Arg); + ToBeDeleted = Call; + } + return Changed; +} + +bool SBFCheckAndAdjustIR::removeCompareBuiltin(Module &M) { + // Remove __builtin_bpf_compare()'s which are used to prevent + // certain IR optimizations. Now major IR optimizations are done, + // remove them. + bool Changed = false; + CallInst *ToBeDeleted = nullptr; + for (Function &F : M) + for (auto &BB : F) + for (auto &I : BB) { + if (ToBeDeleted) { + ToBeDeleted->eraseFromParent(); + ToBeDeleted = nullptr; + } + + auto *Call = dyn_cast(&I); + if (!Call) + continue; + auto *GV = dyn_cast(Call->getCalledOperand()); + if (!GV) + continue; + if (!GV->getName().startswith("llvm.bpf.compare")) + continue; + + Changed = true; + Value *Arg0 = Call->getArgOperand(0); + Value *Arg1 = Call->getArgOperand(1); + Value *Arg2 = Call->getArgOperand(2); + + auto OpVal = cast(Arg0)->getValue().getZExtValue(); + CmpInst::Predicate Opcode = (CmpInst::Predicate)OpVal; + + auto *ICmp = new ICmpInst(Opcode, Arg1, Arg2); + BB.getInstList().insert(Call->getIterator(), ICmp); + + Call->replaceAllUsesWith(ICmp); + ToBeDeleted = Call; + } + return Changed; +} + +bool SBFCheckAndAdjustIR::adjustIR(Module &M) { + bool Changed = removePassThroughBuiltin(M); + Changed = removeCompareBuiltin(M) || Changed; + return Changed; +} + +bool SBFCheckAndAdjustIR::runOnModule(Module &M) { + checkIR(M); + return adjustIR(M); +} diff --git a/llvm/lib/Target/SBF/SBFFrameLowering.cpp b/llvm/lib/Target/SBF/SBFFrameLowering.cpp new file mode 100644 index 0000000000000..f893e7f073e18 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFFrameLowering.cpp @@ -0,0 +1,70 @@ +//===-- SBFFrameLowering.cpp - SBF Frame Information ----------------------===// +// +// 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 the SBF implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "SBFFrameLowering.h" +#include "SBFInstrInfo.h" +#include "SBFSubtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" + +using namespace llvm; + +namespace { + +void adjustStackPointer(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + unsigned int Opcode) { + MachineFrameInfo &MFI = MF.getFrameInfo(); + int NumBytes = (int)MFI.getStackSize(); + if (NumBytes) { + DebugLoc Dl; + const SBFInstrInfo &TII = + *static_cast(MF.getSubtarget().getInstrInfo()); + BuildMI(MBB, MBBI, Dl, TII.get(Opcode), SBF::R11) + .addReg(SBF::R11) + .addImm(NumBytes); + } +} + +} // namespace + +bool SBFFrameLowering::hasFP(const MachineFunction &MF) const { return true; } + +void SBFFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + if (!MF.getSubtarget().getHasDynamicFrames()) { + return; + } + MachineBasicBlock::iterator MBBI = MBB.begin(); + adjustStackPointer(MF, MBB, MBBI, SBF::SUB_ri); +} + +void SBFFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + if (!MF.getSubtarget().getHasDynamicFrames()) { + return; + } + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + adjustStackPointer(MF, MBB, MBBI, SBF::ADD_ri); +} + +void SBFFrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + SavedRegs.reset(SBF::R6); + SavedRegs.reset(SBF::R7); + SavedRegs.reset(SBF::R8); + SavedRegs.reset(SBF::R9); +} diff --git a/llvm/lib/Target/SBF/SBFFrameLowering.h b/llvm/lib/Target/SBF/SBFFrameLowering.h new file mode 100644 index 0000000000000..6c4c5aff96eb8 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFFrameLowering.h @@ -0,0 +1,40 @@ +//===-- SBFFrameLowering.h - Define frame lowering for SBF -----*- 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 SBF-specific bits of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFFRAMELOWERING_H +#define LLVM_LIB_TARGET_SBF_SBFFRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class SBFSubtarget; + +class SBFFrameLowering : public TargetFrameLowering { +public: + explicit SBFFrameLowering(const SBFSubtarget &sti) + : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(8), 0) {} + + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + bool hasFP(const MachineFunction &MF) const override; + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override { + return MBB.erase(MI); + } +}; +} +#endif diff --git a/llvm/lib/Target/SBF/SBFIRPeephole.cpp b/llvm/lib/Target/SBF/SBFIRPeephole.cpp new file mode 100644 index 0000000000000..3b15c5c13e344 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFIRPeephole.cpp @@ -0,0 +1,118 @@ +//===------------ SBFIRPeephole.cpp - IR Peephole Transformation ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// IR level peephole optimization, specifically removing @llvm.stacksave() and +// @llvm.stackrestore(). +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" + +#define DEBUG_TYPE "sbf-ir-peephole" + +using namespace llvm; + +namespace { + +static bool SBFIRPeepholeImpl(Function &F) { + LLVM_DEBUG(dbgs() << "******** SBF IR Peephole ********\n"); + + bool Changed = false; + Instruction *ToErase = nullptr; + for (auto &BB : F) { + for (auto &I : BB) { + // The following code pattern is handled: + // %3 = call i8* @llvm.stacksave() + // store i8* %3, i8** %saved_stack, align 8 + // ... + // %4 = load i8*, i8** %saved_stack, align 8 + // call void @llvm.stackrestore(i8* %4) + // ... + // The goal is to remove the above four instructions, + // so we won't have instructions with r11 (stack pointer) + // if eventually there is no variable length stack allocation. + // InstrCombine also tries to remove the above instructions, + // if it is proven safe (constant alloca etc.), but depending + // on code pattern, it may still miss some. + // + // With unconditionally removing these instructions, if alloca is + // constant, we are okay then. Otherwise, SelectionDag will complain + // since SBF does not support dynamic allocation yet. + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + if (auto *Call = dyn_cast(&I)) { + if (auto *GV = dyn_cast(Call->getCalledOperand())) { + if (!GV->getName().equals("llvm.stacksave")) + continue; + if (!Call->hasOneUser()) + continue; + auto *Inst = cast(*Call->user_begin()); + LLVM_DEBUG(dbgs() << "Remove:"; I.dump()); + LLVM_DEBUG(dbgs() << "Remove:"; Inst->dump(); dbgs() << '\n'); + Changed = true; + Inst->eraseFromParent(); + ToErase = &I; + } + continue; + } + + if (auto *LD = dyn_cast(&I)) { + if (!LD->hasOneUser()) + continue; + auto *Call = dyn_cast(*LD->user_begin()); + if (!Call) + continue; + auto *GV = dyn_cast(Call->getCalledOperand()); + if (!GV) + continue; + if (!GV->getName().equals("llvm.stackrestore")) + continue; + LLVM_DEBUG(dbgs() << "Remove:"; I.dump()); + LLVM_DEBUG(dbgs() << "Remove:"; Call->dump(); dbgs() << '\n'); + Changed = true; + Call->eraseFromParent(); + ToErase = &I; + } + } + } + + return Changed; +} + +class SBFIRPeephole final : public FunctionPass { + bool runOnFunction(Function &F) override; + +public: + static char ID; + SBFIRPeephole() : FunctionPass(ID) {} +}; +} // End anonymous namespace + +char SBFIRPeephole::ID = 0; +INITIALIZE_PASS(SBFIRPeephole, DEBUG_TYPE, "SBF IR Peephole", false, false) + +FunctionPass *llvm::createSBFIRPeephole() { return new SBFIRPeephole(); } + +bool SBFIRPeephole::runOnFunction(Function &F) { return SBFIRPeepholeImpl(F); } + +PreservedAnalyses SBFIRPeepholePass::run(Function &F, + FunctionAnalysisManager &AM) { + return SBFIRPeepholeImpl(F) ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/SBF/SBFISelDAGToDAG.cpp b/llvm/lib/Target/SBF/SBFISelDAGToDAG.cpp new file mode 100644 index 0000000000000..a03e62b8aab97 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFISelDAGToDAG.cpp @@ -0,0 +1,542 @@ +//===-- SBFISelDAGToDAG.cpp - A dag to dag inst selector for SBF ----------===// +// +// 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 defines a DAG pattern matching instruction selector for SBF, +// converting from a legalized dag to a SBF dag. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFRegisterInfo.h" +#include "SBFSubtarget.h" +#include "SBFTargetMachine.h" +#include "llvm/CodeGen/FunctionLoweringInfo.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IntrinsicsBPF.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +#define DEBUG_TYPE "sbf-isel" + +// Instruction Selector Implementation +namespace { + +class SBFDAGToDAGISel : public SelectionDAGISel { + + /// Subtarget - Keep a pointer to the SBFSubtarget around so that we can + /// make the right decision when generating code for different subtargets. + const SBFSubtarget *Subtarget; + +public: + explicit SBFDAGToDAGISel(SBFTargetMachine &TM) + : SelectionDAGISel(TM), Subtarget(nullptr) {} + + StringRef getPassName() const override { + return "SBF DAG->DAG Pattern Instruction Selection"; + } + + bool runOnMachineFunction(MachineFunction &MF) override { + // Reset the subtarget each time through. + Subtarget = &MF.getSubtarget(); + return SelectionDAGISel::runOnMachineFunction(MF); + } + + void PreprocessISelDAG() override; + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintCode, + std::vector &OutOps) override; + + +private: +// Include the pieces autogenerated from the target description. +#include "SBFGenDAGISel.inc" + + void Select(SDNode *N) override; + + // Complex Pattern for address selection. + bool SelectAddr(SDValue Addr, SDValue &Base, SDValue &Offset); + bool SelectFIAddr(SDValue Addr, SDValue &Base, SDValue &Offset); + + // Node preprocessing cases + void PreprocessLoad(SDNode *Node, SelectionDAG::allnodes_iterator &I); + void PreprocessCopyToReg(SDNode *Node); + void PreprocessTrunc(SDNode *Node, SelectionDAG::allnodes_iterator &I); + + // Find constants from a constant structure + typedef std::vector val_vec_type; + bool fillGenericConstant(const DataLayout &DL, const Constant *CV, + val_vec_type &Vals, uint64_t Offset); + bool fillConstantDataArray(const DataLayout &DL, const ConstantDataArray *CDA, + val_vec_type &Vals, int Offset); + bool fillConstantArray(const DataLayout &DL, const ConstantArray *CA, + val_vec_type &Vals, int Offset); + bool fillConstantStruct(const DataLayout &DL, const ConstantStruct *CS, + val_vec_type &Vals, int Offset); + bool getConstantFieldValue(const GlobalAddressSDNode *Node, uint64_t Offset, + uint64_t Size, unsigned char *ByteSeq); + // Mapping from ConstantStruct global value to corresponding byte-list values + std::map cs_vals_; +}; +} // namespace + +// ComplexPattern used on SBF Load/Store instructions +bool SBFDAGToDAGISel::SelectAddr(SDValue Addr, SDValue &Base, SDValue &Offset) { + // if Address is FI, get the TargetFrameIndex. + SDLoc DL(Addr); + if (auto *FIN = dyn_cast(Addr)) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i64); + Offset = CurDAG->getTargetConstant(0, DL, MVT::i64); + return true; + } + + if (Addr.getOpcode() == ISD::TargetExternalSymbol || + Addr.getOpcode() == ISD::TargetGlobalAddress) + return false; + + // Addresses of the form Addr+const or Addr|const + if (CurDAG->isBaseWithConstantOffset(Addr)) { + auto *CN = cast(Addr.getOperand(1)); + if (isInt<16>(CN->getSExtValue())) { + // If the first operand is a FI, get the TargetFI Node + if (auto *FIN = dyn_cast(Addr.getOperand(0))) + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i64); + else + Base = Addr.getOperand(0); + + Offset = CurDAG->getTargetConstant(CN->getSExtValue(), DL, MVT::i64); + return true; + } + } + + Base = Addr; + Offset = CurDAG->getTargetConstant(0, DL, MVT::i64); + return true; +} + +// ComplexPattern used on SBF FI instruction +bool SBFDAGToDAGISel::SelectFIAddr(SDValue Addr, SDValue &Base, + SDValue &Offset) { + SDLoc DL(Addr); + + if (!CurDAG->isBaseWithConstantOffset(Addr)) + return false; + + // Addresses of the form Addr+const or Addr|const + auto *CN = cast(Addr.getOperand(1)); + if (isInt<16>(CN->getSExtValue())) { + // If the first operand is a FI, get the TargetFI Node + if (auto *FIN = dyn_cast(Addr.getOperand(0))) + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i64); + else + return false; + + Offset = CurDAG->getTargetConstant(CN->getSExtValue(), DL, MVT::i64); + return true; + } + + return false; +} + +bool SBFDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintCode, std::vector &OutOps) { + SDValue Op0, Op1; + switch (ConstraintCode) { + default: + return true; + case InlineAsm::Constraint_m: // memory + if (!SelectAddr(Op, Op0, Op1)) + return true; + break; + } + + SDLoc DL(Op); + SDValue AluOp = CurDAG->getTargetConstant(ISD::ADD, DL, MVT::i32);; + OutOps.push_back(Op0); + OutOps.push_back(Op1); + OutOps.push_back(AluOp); + return false; +} + +void SBFDAGToDAGISel::Select(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + + // If we have a custom node, we already have selected! + if (Node->isMachineOpcode()) { + LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << '\n'); + return; + } + + // tablegen selection should be handled here. + switch (Opcode) { + default: + break; + + case ISD::SDIV: { + if (!Subtarget->isSolana()) { + DebugLoc Empty; + const DebugLoc &DL = Node->getDebugLoc(); + if (DL != Empty) + errs() << "Error at line " << DL.getLine() << ": "; + else + errs() << "Error: "; + errs() << "Unsupport signed division for DAG: "; + Node->print(errs(), CurDAG); + errs() << "\nPlease convert to unsigned div/mod.\n"; + } + break; + } + case ISD::INTRINSIC_W_CHAIN: { + unsigned IntNo = cast(Node->getOperand(1))->getZExtValue(); + switch (IntNo) { + case Intrinsic::bpf_load_byte: + case Intrinsic::bpf_load_half: + case Intrinsic::bpf_load_word: { + SDLoc DL(Node); + SDValue Chain = Node->getOperand(0); + SDValue N1 = Node->getOperand(1); + SDValue Skb = Node->getOperand(2); + SDValue N3 = Node->getOperand(3); + + SDValue R6Reg = CurDAG->getRegister(SBF::R6, MVT::i64); + Chain = CurDAG->getCopyToReg(Chain, DL, R6Reg, Skb, SDValue()); + Node = CurDAG->UpdateNodeOperands(Node, Chain, N1, R6Reg, N3); + break; + } + } + break; + } + + case ISD::FrameIndex: { + int FI = cast(Node)->getIndex(); + EVT VT = Node->getValueType(0); + SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); + unsigned Opc = SBF::MOV_rr; + if (Node->hasOneUse()) { + CurDAG->SelectNodeTo(Node, Opc, VT, TFI); + return; + } + ReplaceNode(Node, CurDAG->getMachineNode(Opc, SDLoc(Node), VT, TFI)); + return; + } + } + + // Select the default instruction + SelectCode(Node); +} + +void SBFDAGToDAGISel::PreprocessLoad(SDNode *Node, + SelectionDAG::allnodes_iterator &I) { + union { + uint8_t c[8]; + uint16_t s; + uint32_t i; + uint64_t d; + } new_val; // hold up the constant values replacing loads. + bool to_replace = false; + SDLoc DL(Node); + const LoadSDNode *LD = cast(Node); + uint64_t size = LD->getMemOperand()->getSize(); + + if (!size || size > 8 || (size & (size - 1)) || !LD->isSimple()) + return; + + SDNode *LDAddrNode = LD->getOperand(1).getNode(); + // Match LDAddr against either global_addr or (global_addr + offset) + unsigned opcode = LDAddrNode->getOpcode(); + if (opcode == ISD::ADD) { + SDValue OP1 = LDAddrNode->getOperand(0); + SDValue OP2 = LDAddrNode->getOperand(1); + + // We want to find the pattern global_addr + offset + SDNode *OP1N = OP1.getNode(); + if (OP1N->getOpcode() <= ISD::BUILTIN_OP_END || OP1N->getNumOperands() == 0) + return; + + LLVM_DEBUG(dbgs() << "Check candidate load: "; LD->dump(); dbgs() << '\n'); + + const GlobalAddressSDNode *GADN = + dyn_cast(OP1N->getOperand(0).getNode()); + const ConstantSDNode *CDN = dyn_cast(OP2.getNode()); + if (GADN && CDN) + to_replace = + getConstantFieldValue(GADN, CDN->getZExtValue(), size, new_val.c); + } else if (LDAddrNode->getOpcode() > ISD::BUILTIN_OP_END && + LDAddrNode->getNumOperands() > 0) { + LLVM_DEBUG(dbgs() << "Check candidate load: "; LD->dump(); dbgs() << '\n'); + + SDValue OP1 = LDAddrNode->getOperand(0); + if (const GlobalAddressSDNode *GADN = + dyn_cast(OP1.getNode())) + to_replace = getConstantFieldValue(GADN, 0, size, new_val.c); + } + + if (!to_replace) + return; + + // replacing the old with a new value + uint64_t val; + if (size == 1) + val = new_val.c[0]; + else if (size == 2) + val = new_val.s; + else if (size == 4) + val = new_val.i; + else { + val = new_val.d; + } + + LLVM_DEBUG(dbgs() << "Replacing load of size " << size << " with constant " + << val << '\n'); + + /* Some load nodes have edges from TokenFactor nodes. In this case + replacing the load with a constant makes the DAG disconnected. + The following checks if any of the load operands are TokenFactor + nodes, and if another TokenFactor is a user of the load, the + operand TokenFactor is connected to the user, so that the DAG + remains connected after replacing the load node by a constant. + */ + for (unsigned I = 0, E = Node->getNumOperands(); I != E; ++I) { + const SDValue &OpV = Node->getOperand(I); + SDNode *Op = OpV.getNode(); + if (Op->getOpcode() == ISD::TokenFactor) { + for (SDNode::use_iterator UI = Node->use_begin(), UE = Node->use_end(); UI != UE; ++UI) { + SDUse &Use = UI.getUse(); + SDNode *User = Use.getUser(); + if (User->getOpcode() == ISD::TokenFactor) { + SmallVector ExtendedOps; + bool NotExtended = true; + for (unsigned UOI = 0, UOE = User->getNumOperands(); UOI != UOE; ++UOI) { + const SDValue &Operand = User->getOperand(UOI); + if (OpV == Operand) { + NotExtended = false; + break; + } + ExtendedOps.push_back(Operand); + } + if (NotExtended) { + ExtendedOps.push_back(OpV); + SDValue ExtendedTokenFactor = CurDAG->getTokenFactor(SDLoc(User), ExtendedOps); + I--; + SDValue From[] = {SDValue(User, 0)}; + SDValue To[] = {ExtendedTokenFactor}; + CurDAG->ReplaceAllUsesOfValuesWith(From, To, 1); + I++; + CurDAG->DeleteNode(User); + } + } + } + } + } + + SDValue NVal = CurDAG->getConstant(val, DL, LD->getValueType(0)); + + // After replacement, the current node is dead, we need to + // go backward one step to make iterator still work + I--; + SDValue From[] = {SDValue(Node, 0), SDValue(Node, 1)}; + SDValue To[] = {NVal, NVal}; + CurDAG->ReplaceAllUsesOfValuesWith(From, To, 2); + I++; + // It is safe to delete node now + CurDAG->DeleteNode(Node); +} + +void SBFDAGToDAGISel::PreprocessISelDAG() { + // Iterate through all nodes, interested in the following case: + // + // . loads from ConstantStruct or ConstantArray of constructs + // which can be turns into constant itself, with this we can + // avoid reading from read-only section at runtime. + // + // . Removing redundant AND for intrinsic narrow loads. + for (SelectionDAG::allnodes_iterator I = CurDAG->allnodes_begin(), + E = CurDAG->allnodes_end(); + I != E;) { + SDNode *Node = &*I++; + unsigned Opcode = Node->getOpcode(); + if (Opcode == ISD::LOAD) + PreprocessLoad(Node, I); + else if (Opcode == ISD::AND) + PreprocessTrunc(Node, I); + } +} + +bool SBFDAGToDAGISel::getConstantFieldValue(const GlobalAddressSDNode *Node, + uint64_t Offset, uint64_t Size, + unsigned char *ByteSeq) { + const GlobalVariable *V = dyn_cast(Node->getGlobal()); + + if (!V || !V->hasInitializer() || !V->isConstant()) + return false; + + const Constant *Init = V->getInitializer(); + const DataLayout &DL = CurDAG->getDataLayout(); + val_vec_type TmpVal; + + auto it = cs_vals_.find(static_cast(Init)); + if (it != cs_vals_.end()) { + TmpVal = it->second; + } else { + uint64_t total_size = 0; + if (const ConstantStruct *CS = dyn_cast(Init)) + total_size = + DL.getStructLayout(cast(CS->getType()))->getSizeInBytes(); + else if (const ConstantArray *CA = dyn_cast(Init)) + total_size = DL.getTypeAllocSize(CA->getType()->getElementType()) * + CA->getNumOperands(); + else + return false; + + val_vec_type Vals(total_size, 0); + if (fillGenericConstant(DL, Init, Vals, 0) == false) + return false; + cs_vals_[static_cast(Init)] = Vals; + TmpVal = std::move(Vals); + } + + // test whether host endianness matches target + union { + uint8_t c[2]; + uint16_t s; + } test_buf; + uint16_t test_val = 0x2345; + if (DL.isLittleEndian()) + support::endian::write16le(test_buf.c, test_val); + else + support::endian::write16be(test_buf.c, test_val); + + bool endian_match = test_buf.s == test_val; + for (uint64_t i = Offset, j = 0; i < Offset + Size; i++, j++) + ByteSeq[j] = endian_match ? TmpVal[i] : TmpVal[Offset + Size - 1 - j]; + + return true; +} + +bool SBFDAGToDAGISel::fillGenericConstant(const DataLayout &DL, + const Constant *CV, + val_vec_type &Vals, uint64_t Offset) { + uint64_t Size = DL.getTypeAllocSize(CV->getType()); + + if (isa(CV) || isa(CV)) + return true; // already done + + if (const ConstantInt *CI = dyn_cast(CV)) { + uint64_t val = CI->getZExtValue(); + LLVM_DEBUG(dbgs() << "Byte array at offset " << Offset << " with value " + << val << '\n'); + + if (Size > 8 || (Size & (Size - 1))) + return false; + + // Store based on target endian + for (uint64_t i = 0; i < Size; ++i) { + Vals[Offset + i] = DL.isLittleEndian() + ? ((val >> (i * 8)) & 0xFF) + : ((val >> ((Size - i - 1) * 8)) & 0xFF); + } + return true; + } + + if (const ConstantDataArray *CDA = dyn_cast(CV)) + return fillConstantDataArray(DL, CDA, Vals, Offset); + + if (const ConstantArray *CA = dyn_cast(CV)) + return fillConstantArray(DL, CA, Vals, Offset); + + if (const ConstantStruct *CVS = dyn_cast(CV)) + return fillConstantStruct(DL, CVS, Vals, Offset); + + return false; +} + +bool SBFDAGToDAGISel::fillConstantDataArray(const DataLayout &DL, + const ConstantDataArray *CDA, + val_vec_type &Vals, int Offset) { + for (unsigned i = 0, e = CDA->getNumElements(); i != e; ++i) { + if (fillGenericConstant(DL, CDA->getElementAsConstant(i), Vals, Offset) == + false) + return false; + Offset += DL.getTypeAllocSize(CDA->getElementAsConstant(i)->getType()); + } + + return true; +} + +bool SBFDAGToDAGISel::fillConstantArray(const DataLayout &DL, + const ConstantArray *CA, + val_vec_type &Vals, int Offset) { + for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i) { + if (fillGenericConstant(DL, CA->getOperand(i), Vals, Offset) == false) + return false; + Offset += DL.getTypeAllocSize(CA->getOperand(i)->getType()); + } + + return true; +} + +bool SBFDAGToDAGISel::fillConstantStruct(const DataLayout &DL, + const ConstantStruct *CS, + val_vec_type &Vals, int Offset) { + const StructLayout *Layout = DL.getStructLayout(CS->getType()); + for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) { + const Constant *Field = CS->getOperand(i); + uint64_t SizeSoFar = Layout->getElementOffset(i); + if (fillGenericConstant(DL, Field, Vals, Offset + SizeSoFar) == false) + return false; + } + return true; +} + +void SBFDAGToDAGISel::PreprocessTrunc(SDNode *Node, + SelectionDAG::allnodes_iterator &I) { + ConstantSDNode *MaskN = dyn_cast(Node->getOperand(1)); + if (!MaskN) + return; + + // The Reg operand should be a virtual register, which is defined + // outside the current basic block. DAG combiner has done a pretty + // good job in removing truncating inside a single basic block except + // when the Reg operand comes from bpf_load_[byte | half | word] for + // which the generic optimizer doesn't understand their results are + // zero extended. + SDValue BaseV = Node->getOperand(0); + if (BaseV.getOpcode() != ISD::INTRINSIC_W_CHAIN) + return; + + unsigned IntNo = cast(BaseV->getOperand(1))->getZExtValue(); + uint64_t MaskV = MaskN->getZExtValue(); + + if (!((IntNo == Intrinsic::bpf_load_byte && MaskV == 0xFF) || + (IntNo == Intrinsic::bpf_load_half && MaskV == 0xFFFF) || + (IntNo == Intrinsic::bpf_load_word && MaskV == 0xFFFFFFFF))) + return; + + LLVM_DEBUG(dbgs() << "Remove the redundant AND operation in: "; + Node->dump(); dbgs() << '\n'); + + I--; + CurDAG->ReplaceAllUsesWith(SDValue(Node, 0), BaseV); + I++; + CurDAG->DeleteNode(Node); +} + +FunctionPass *llvm::createSBFISelDag(SBFTargetMachine &TM) { + return new SBFDAGToDAGISel(TM); +} diff --git a/llvm/lib/Target/SBF/SBFISelLowering.cpp b/llvm/lib/Target/SBF/SBFISelLowering.cpp new file mode 100644 index 0000000000000..da6cfbb97f999 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFISelLowering.cpp @@ -0,0 +1,1197 @@ +//===-- SBFISelLowering.cpp - SBF DAG Lowering Implementation ------------===// +// +// 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 defines the interfaces that SBF uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "SBFISelLowering.h" +#include "SBF.h" +#include "SBFRegisterInfo.h" +#include "SBFSubtarget.h" +#include "SBFTargetMachine.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/IntrinsicsBPF.h" // TODO: jle. +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "sbf-lower" + +static cl::opt SBFExpandMemcpyInOrder("sbf-expand-memcpy-in-order", + cl::Hidden, cl::init(false), + cl::desc("Expand memcpy into load/store pairs in order")); + +static void fail(const SDLoc &DL, SelectionDAG &DAG, const Twine &Msg) { + MachineFunction &MF = DAG.getMachineFunction(); + DAG.getContext()->diagnose( + DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc())); +} + +static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg, + SDValue Val) { + MachineFunction &MF = DAG.getMachineFunction(); + std::string Str; + raw_string_ostream OS(Str); + OS << Msg; + Val->print(OS); + OS.flush(); + DAG.getContext()->diagnose( + DiagnosticInfoUnsupported(MF.getFunction(), Str, DL.getDebugLoc())); +} + +SBFTargetLowering::SBFTargetLowering(const TargetMachine &TM, + const SBFSubtarget &STI) + : TargetLowering(TM), Subtarget(&STI) { + + // Set up the register classes. + addRegisterClass(MVT::i64, &SBF::GPRRegClass); + if (STI.getHasAlu32()) + addRegisterClass(MVT::i32, &SBF::GPR32RegClass); + + // Compute derived properties from the register classes + computeRegisterProperties(STI.getRegisterInfo()); + + setStackPointerRegisterToSaveRestore(SBF::R11); + + setOperationAction(ISD::BR_CC, MVT::i64, Custom); + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::BRIND, MVT::Other, Expand); + setOperationAction(ISD::BRCOND, MVT::Other, Expand); + + setOperationAction(ISD::GlobalAddress, MVT::i64, Custom); + + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i64, Custom); + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); + + for (auto VT : {MVT::i8, MVT::i16, MVT::i32, MVT::i32, MVT::i64}) { + if (Subtarget->isSolana()) { + // Implement custom lowering for all atomic operations + setOperationAction(ISD::ATOMIC_SWAP, VT, Custom); + setOperationAction(ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_ADD, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_AND, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_MAX, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_MIN, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_NAND, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_OR, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_SUB, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_UMAX, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_UMIN, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_XOR, VT, Custom); + continue; + } + + if (VT == MVT::i64) { + continue; + } + + // Set unsupported atomic operations as Custom so we can emit better error + // messages than fatal error from selectiondag. + if (VT == MVT::i32) { + if (STI.getHasAlu32()) + continue; + } else { + setOperationAction(ISD::ATOMIC_LOAD_ADD, VT, Custom); + } + + setOperationAction(ISD::ATOMIC_LOAD_AND, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_OR, VT, Custom); + setOperationAction(ISD::ATOMIC_LOAD_XOR, VT, Custom); + setOperationAction(ISD::ATOMIC_SWAP, VT, Custom); + setOperationAction(ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS, VT, Custom); + } + + for (auto VT : { MVT::i32, MVT::i64 }) { + if (VT == MVT::i32 && !STI.getHasAlu32()) + continue; + + if (Subtarget->isSolana() && !STI.getHasSdiv()) { + setOperationAction(ISD::SDIV, VT, Expand); + } + + setOperationAction(ISD::SDIVREM, VT, Expand); + setOperationAction(ISD::UDIVREM, VT, Expand); + setOperationAction(ISD::SREM, VT, Expand); + setOperationAction(ISD::UREM, VT, Expand); + setOperationAction(ISD::MULHU, VT, Expand); + setOperationAction(ISD::MULHS, VT, Expand); + setOperationAction(ISD::UMUL_LOHI, VT, Expand); + setOperationAction(ISD::SMUL_LOHI, VT, Expand); + setOperationAction(ISD::ROTR, VT, Expand); + setOperationAction(ISD::ROTL, VT, Expand); + setOperationAction(ISD::SHL_PARTS, VT, Expand); + setOperationAction(ISD::SRL_PARTS, VT, Expand); + setOperationAction(ISD::SRA_PARTS, VT, Expand); + setOperationAction(ISD::CTPOP, VT, Expand); + + setOperationAction(ISD::SETCC, VT, Expand); + setOperationAction(ISD::SELECT, VT, Expand); + setOperationAction(ISD::SELECT_CC, VT, Custom); + } + + if (STI.getHasAlu32()) { + setOperationAction(ISD::BSWAP, MVT::i32, Promote); + setOperationAction(ISD::BR_CC, MVT::i32, + STI.getHasJmp32() ? Custom : Promote); + } + + if (Subtarget->isSolana()) { + setOperationAction(ISD::CTTZ, MVT::i64, Expand); + setOperationAction(ISD::CTLZ, MVT::i64, Expand); + setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::i64, Expand); + setOperationAction(ISD::CTLZ_ZERO_UNDEF, MVT::i64, Expand); + } else { + setOperationAction(ISD::CTTZ, MVT::i64, Custom); + setOperationAction(ISD::CTLZ, MVT::i64, Custom); + setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::i64, Custom); + setOperationAction(ISD::CTLZ_ZERO_UNDEF, MVT::i64, Custom); + } + + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i32, Expand); + + // Extended load operations for i1 types must be promoted + for (MVT VT : MVT::integer_valuetypes()) { + setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); + + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i8, Expand); + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i16, Expand); + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i32, Expand); + } + + setBooleanContents(ZeroOrOneBooleanContent); + + // Function alignments + setMinFunctionAlignment(Align(8)); + setPrefFunctionAlignment(Align(8)); + + if (SBFExpandMemcpyInOrder) { + // LLVM generic code will try to expand memcpy into load/store pairs at this + // stage which is before quite a few IR optimization passes, therefore the + // loads and stores could potentially be moved apart from each other which + // will cause trouble to memcpy pattern matcher inside kernel eBPF JIT + // compilers. + // + // When -sbf-expand-memcpy-in-order specified, we want to defer the expand + // of memcpy to later stage in IR optimization pipeline so those load/store + // pairs won't be touched and could be kept in order. Hence, we set + // MaxStoresPerMem* to zero to disable the generic getMemcpyLoadsAndStores + // code path, and ask LLVM to use target expander EmitTargetCodeForMemcpy. + MaxStoresPerMemset = MaxStoresPerMemsetOptSize = 0; + MaxStoresPerMemcpy = MaxStoresPerMemcpyOptSize = 0; + MaxStoresPerMemmove = MaxStoresPerMemmoveOptSize = 0; + } else { + auto SelectionDAGInfo = STI.getSelectionDAGInfo(); + SelectionDAGInfo->setSolanaFlag(STI.isSolana()); + // inline memcpy() for kernel to see explicit copy + unsigned CommonMaxStores = + SelectionDAGInfo->getCommonMaxStoresPerMemFunc(); + + MaxStoresPerMemset = MaxStoresPerMemsetOptSize = CommonMaxStores; + MaxStoresPerMemcpy = MaxStoresPerMemcpyOptSize = CommonMaxStores; + MaxStoresPerMemmove = MaxStoresPerMemmoveOptSize = CommonMaxStores; + } + + // CPU/Feature control + HasAlu32 = STI.getHasAlu32(); + HasJmp32 = STI.getHasJmp32(); + HasJmpExt = STI.getHasJmpExt(); + SBFRegisterInfo::FrameLength = STI.isSolana() ? 4096 : 512; +} + +bool SBFTargetLowering::allowsMisalignedMemoryAccesses( + EVT VT, unsigned, Align, MachineMemOperand::Flags, bool *Fast) const { + if (!VT.isSimple()) { + return false; + } + bool isSolana = Subtarget->isSolana(); + if (isSolana && Fast) { + *Fast = true; + } + return isSolana; +} + +bool SBFTargetLowering::lowerAtomicStoreAsStoreSDNode( + const StoreInst &SI) const { + return Subtarget->isSolana(); +} + +bool SBFTargetLowering::lowerAtomicLoadAsLoadSDNode(const LoadInst &LI) const { + return Subtarget->isSolana(); +} + +bool SBFTargetLowering::isOffsetFoldingLegal( + const GlobalAddressSDNode *GA) const { + return false; +} + +bool SBFTargetLowering::isTruncateFree(Type *Ty1, Type *Ty2) const { + if (!Ty1->isIntegerTy() || !Ty2->isIntegerTy()) + return false; + unsigned NumBits1 = Ty1->getPrimitiveSizeInBits(); + unsigned NumBits2 = Ty2->getPrimitiveSizeInBits(); + return NumBits1 > NumBits2; +} + +bool SBFTargetLowering::isTruncateFree(EVT VT1, EVT VT2) const { + if (!VT1.isInteger() || !VT2.isInteger()) + return false; + unsigned NumBits1 = VT1.getSizeInBits(); + unsigned NumBits2 = VT2.getSizeInBits(); + return NumBits1 > NumBits2; +} + +bool SBFTargetLowering::isZExtFree(Type *Ty1, Type *Ty2) const { + if (!getHasAlu32() || !Ty1->isIntegerTy() || !Ty2->isIntegerTy()) + return false; + unsigned NumBits1 = Ty1->getPrimitiveSizeInBits(); + unsigned NumBits2 = Ty2->getPrimitiveSizeInBits(); + return NumBits1 == 32 && NumBits2 == 64; +} + +bool SBFTargetLowering::isZExtFree(EVT VT1, EVT VT2) const { + if (!getHasAlu32() || !VT1.isInteger() || !VT2.isInteger()) + return false; + unsigned NumBits1 = VT1.getSizeInBits(); + unsigned NumBits2 = VT2.getSizeInBits(); + return NumBits1 == 32 && NumBits2 == 64; +} + +SBFTargetLowering::ConstraintType +SBFTargetLowering::getConstraintType(StringRef Constraint) const { + if (Constraint.size() == 1) { + switch (Constraint[0]) { + default: + break; + case 'w': + return C_RegisterClass; + } + } + + return TargetLowering::getConstraintType(Constraint); +} + +std::pair +SBFTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, + MVT VT) const { + if (Constraint.size() == 1) + // GCC Constraint Letters + switch (Constraint[0]) { + case 'r': // GENERAL_REGS + return std::make_pair(0U, &SBF::GPRRegClass); + case 'w': + if (HasAlu32) + return std::make_pair(0U, &SBF::GPR32RegClass); + break; + default: + break; + } + + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + +void SBFTargetLowering::ReplaceNodeResults(SDNode *N, + SmallVectorImpl &Results, + SelectionDAG &DAG) const { + const char *err_msg; + uint32_t Opcode = N->getOpcode(); + switch (Opcode) { + default: + report_fatal_error("Unhandled custom legalization"); + case ISD::ATOMIC_SWAP: + case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS: + case ISD::ATOMIC_LOAD_ADD: + case ISD::ATOMIC_LOAD_AND: + case ISD::ATOMIC_LOAD_MAX: + case ISD::ATOMIC_LOAD_MIN: + case ISD::ATOMIC_LOAD_NAND: + case ISD::ATOMIC_LOAD_OR: + case ISD::ATOMIC_LOAD_SUB: + case ISD::ATOMIC_LOAD_UMAX: + case ISD::ATOMIC_LOAD_UMIN: + case ISD::ATOMIC_LOAD_XOR: + if (Subtarget->isSolana()) { + // We do lowering during legalization, see LowerOperation() + return; + } + + if (HasAlu32 || Opcode == ISD::ATOMIC_LOAD_ADD) + err_msg = "Unsupported atomic operations, please use 32/64 bit version"; + else + err_msg = "Unsupported atomic operations, please use 64 bit version"; + break; + } + + SDLoc DL(N); + fail(DL, DAG, err_msg); +} + +SDValue SBFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + case ISD::BR_CC: + return LowerBR_CC(Op, DAG); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::SELECT_CC: + return LowerSELECT_CC(Op, DAG); + case ISD::ATOMIC_SWAP: + case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS: + case ISD::ATOMIC_LOAD_ADD: + case ISD::ATOMIC_LOAD_AND: + case ISD::ATOMIC_LOAD_MAX: + case ISD::ATOMIC_LOAD_MIN: + case ISD::ATOMIC_LOAD_NAND: + case ISD::ATOMIC_LOAD_OR: + case ISD::ATOMIC_LOAD_SUB: + case ISD::ATOMIC_LOAD_UMAX: + case ISD::ATOMIC_LOAD_UMIN: + case ISD::ATOMIC_LOAD_XOR: + return LowerATOMICRMW(Op, DAG); + case ISD::INTRINSIC_W_CHAIN: { + unsigned IntNo = cast(Op->getOperand(1))->getZExtValue(); + switch (IntNo) { + case Intrinsic::bpf_load_byte: + case Intrinsic::bpf_load_half: + case Intrinsic::bpf_load_word: + if (Subtarget->isSolana()) { + report_fatal_error( + "llvm.bpf.load.* intrinsics are not supported in SBF", false); + } + break; + default: + break; + } + + // continue the expansion as defined with tablegen + return SDValue(); + } + case ISD::DYNAMIC_STACKALLOC: + report_fatal_error("Unsupported dynamic stack allocation"); + default: + llvm_unreachable("unimplemented operation"); + } +} + +// Calling Convention Implementation +#include "SBFGenCallingConv.inc" + +SDValue SBFTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + switch (CallConv) { + default: + report_fatal_error("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + break; + } + + MachineFunction &MF = DAG.getMachineFunction(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + + // Assign locations to all of the incoming arguments. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + if (Subtarget->isSolana() && Ins.size() > MaxArgs) { + // Pass args 1-4 via registers, remaining args via stack, referenced via SBF::R5 + CCInfo.AnalyzeFormalArguments(Ins, getHasAlu32() ? CC_SBF32_X : CC_SBF64_X); + } else { + // Pass all args via registers + CCInfo.AnalyzeFormalArguments(Ins, getHasAlu32() ? CC_SBF32 : CC_SBF64); + } + + for (auto &VA : ArgLocs) { + if (VA.isRegLoc()) { + // Argument passed in registers + EVT RegVT = VA.getLocVT(); + MVT::SimpleValueType SimpleTy = RegVT.getSimpleVT().SimpleTy; + switch (SimpleTy) { + default: { + errs() << "LowerFormalArguments Unhandled argument type: " + << RegVT.getEVTString() << '\n'; + llvm_unreachable(nullptr); + } + case MVT::i32: + case MVT::i64: + Register VReg = RegInfo.createVirtualRegister( + SimpleTy == MVT::i64 ? &SBF::GPRRegClass : &SBF::GPR32RegClass); + RegInfo.addLiveIn(VA.getLocReg(), VReg); + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, VReg, RegVT); + + // If this is a value that has been promoted to a wider type, insert an + // assert[sz]ext to capture this, then truncate to the right size. + if (VA.getLocInfo() == CCValAssign::SExt) + ArgValue = DAG.getNode(ISD::AssertSext, DL, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + else if (VA.getLocInfo() == CCValAssign::ZExt) + ArgValue = DAG.getNode(ISD::AssertZext, DL, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + + if (VA.getLocInfo() != CCValAssign::Full) + ArgValue = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), ArgValue); + + InVals.push_back(ArgValue); + + break; + } + } else if (Subtarget->isSolana()) { + // Argument passed via stack + assert(VA.isMemLoc() && "Should be isMemLoc"); + + EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + EVT LocVT = VA.getLocVT(); + + // Arguments relative to SBF::R5 + unsigned reg = MF.addLiveIn(SBF::R5, &SBF::GPRRegClass); + SDValue Const = DAG.getConstant(SBFRegisterInfo::FrameLength - VA.getLocMemOffset(), DL, MVT::i64); + SDValue SDV = DAG.getCopyFromReg(Chain, DL, reg, getPointerTy(MF.getDataLayout())); + SDV = DAG.getNode(ISD::SUB, DL, PtrVT, SDV, Const); + SDV = DAG.getLoad(LocVT, DL, Chain, SDV, MachinePointerInfo(), 0); + InVals.push_back(SDV); + } else { + fail(DL, DAG, "defined with too many args"); + InVals.push_back(DAG.getConstant(0, DL, VA.getLocVT())); + } + } + + if (Subtarget->isSolana()) { + if (IsVarArg) { + fail(DL, DAG, "Functions with VarArgs are not supported"); + } + } else if (IsVarArg || MF.getFunction().hasStructRetAttr()) { + fail(DL, DAG, "functions with VarArgs or StructRet are not supported"); + } + + return Chain; +} + +const unsigned SBFTargetLowering::MaxArgs = 5; + +SDValue SBFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + auto &Outs = CLI.Outs; + auto &OutVals = CLI.OutVals; + auto &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + bool &IsTailCall = CLI.IsTailCall; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + MachineFunction &MF = DAG.getMachineFunction(); + + // SBF target does not support tail call optimization. + IsTailCall = false; + + switch (CallConv) { + default: + report_fatal_error("Unsupported calling convention"); + case CallingConv::Fast: + case CallingConv::C: + break; + } + + // Analyze operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + if (Subtarget->isSolana() && Outs.size() > MaxArgs) { + // Pass args 1-4 via registers, remaining args via stack, referenced via SBF::R5 + CCInfo.AnalyzeCallOperands(Outs, getHasAlu32() ? CC_SBF32_X : CC_SBF64_X); + } else { + // Pass all args via registers + CCInfo.AnalyzeCallOperands(Outs, getHasAlu32() ? CC_SBF32 : CC_SBF64); + } + + unsigned NumBytes = CCInfo.getNextStackOffset(); + + if (!Subtarget->isSolana()) { + if (Outs.size() > MaxArgs) + fail(CLI.DL, DAG, "too many args to ", Callee); + + for (auto &Arg : Outs) { + ISD::ArgFlagsTy Flags = Arg.Flags; + if (!Flags.isByVal()) + continue; + + fail(CLI.DL, DAG, "pass by value not supported ", Callee); + } + } + + auto PtrVT = getPointerTy(MF.getDataLayout()); + Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); + + SmallVector, MaxArgs> RegsToPass; + + // Walk arg assignments + bool HasStackArgs = false; + unsigned e, i, ae = ArgLocs.size(); + for (i = 0, e = (Subtarget->isSolana()) ? ae : std::min(ae, MaxArgs); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + SDValue Arg = OutVals[i]; + + // Promote the value if needed. + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info"); + case CCValAssign::Full: + break; + case CCValAssign::SExt: + Arg = DAG.getNode(ISD::SIGN_EXTEND, CLI.DL, VA.getLocVT(), Arg); + break; + case CCValAssign::ZExt: + Arg = DAG.getNode(ISD::ZERO_EXTEND, CLI.DL, VA.getLocVT(), Arg); + break; + case CCValAssign::AExt: + Arg = DAG.getNode(ISD::ANY_EXTEND, CLI.DL, VA.getLocVT(), Arg); + break; + } + + if (Subtarget->isSolana() && VA.isMemLoc()) { + HasStackArgs = true; + break; + } + + // Push arguments into RegsToPass vector + if (VA.isRegLoc()) + RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); + else + llvm_unreachable("call arg pass bug"); + } + + SDValue InFlag; + + if (HasStackArgs) { + SDValue FramePtr = DAG.getCopyFromReg(Chain, CLI.DL, SBF::R10, getPointerTy(MF.getDataLayout())); + + // Stack arguments have to be walked in reverse order by inserting + // chained stores, this ensures their order is not changed by the scheduler + // and that the push instruction sequence generated is correct, otherwise they + // can be freely intermixed. + for (ae = i, i = ArgLocs.size(); i != ae; --i) { + unsigned Loc = i - 1; + CCValAssign &VA = ArgLocs[Loc]; + SDValue Arg = OutVals[Loc]; + + assert(VA.isMemLoc()); + + EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + SDValue Const = DAG.getConstant(SBFRegisterInfo::FrameLength - VA.getLocMemOffset(), CLI.DL, MVT::i64); + SDValue PtrOff = DAG.getNode(ISD::SUB, CLI.DL, PtrVT, FramePtr, Const); + Chain = DAG.getStore(Chain, CLI.DL, Arg, PtrOff, MachinePointerInfo()); + } + + // Pass the current stack frame pointer via SBF::R5, gluing the + // instruction to instructions passing the first 4 arguments in + // registers below. + Chain = DAG.getCopyToReg(Chain, CLI.DL, SBF::R5, FramePtr, InFlag); + InFlag = Chain.getValue(1); + } + + // Build a sequence of copy-to-reg nodes chained together with token chain and + // flag operands which copy the outgoing args into registers. The InFlag is + // necessary since all emitted instructions must be stuck together. + for (auto &Reg : RegsToPass) { + Chain = DAG.getCopyToReg(Chain, CLI.DL, Reg.first, Reg.second, InFlag); + InFlag = Chain.getValue(1); + } + + // If the callee is a GlobalAddress node (quite common, every direct call is) + // turn it into a TargetGlobalAddress node so that legalize doesn't hack it. + // Likewise ExternalSymbol -> TargetExternalSymbol. + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), CLI.DL, PtrVT, + G->getOffset(), 0); + } else if (ExternalSymbolSDNode *E = dyn_cast(Callee)) { + Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, 0); + // This is not a warning but info, will be resolved on load + if (!Subtarget->isSolana()) { + fail(CLI.DL, DAG, Twine("A call to built-in function '" + + StringRef(E->getSymbol()) + + "' remains unresolved")); + } + } + + // Returns a chain & a flag for retval copy to use. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add argument registers to the end of the list so that they are + // known live into the call. + for (auto &Reg : RegsToPass) + Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType())); + + if (HasStackArgs) { + Ops.push_back(DAG.getRegister(SBF::R5, MVT::i64)); + } + + if (InFlag.getNode()) + Ops.push_back(InFlag); + + Chain = DAG.getNode(SBFISD::CALL, CLI.DL, NodeTys, Ops); + InFlag = Chain.getValue(1); + + // Create the CALLSEQ_END node. + Chain = DAG.getCALLSEQ_END( + Chain, DAG.getConstant(NumBytes, CLI.DL, PtrVT, true), + DAG.getConstant(0, CLI.DL, PtrVT, true), InFlag, CLI.DL); + InFlag = Chain.getValue(1); + + // Handle result values, copying them out of physregs into vregs that we + // return. + return LowerCallResult(Chain, InFlag, CallConv, IsVarArg, Ins, CLI.DL, DAG, + InVals); +} + +bool SBFTargetLowering::shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const { + return Subtarget->isSolana() && (IsSigned || Type == MVT::i32); +} + +bool SBFTargetLowering::CanLowerReturn( + CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, + const SmallVectorImpl &Outs, LLVMContext &Context) const { + if (!Subtarget->isSolana()) { + return true; + } + // At minimal return Outs.size() <= 1, or check valid types in CC. + SmallVector RVLocs; + CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); + return CCInfo.CheckReturn(Outs, getHasAlu32() ? RetCC_SBF32 : RetCC_SBF64); +} + +SDValue +SBFTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + unsigned Opc = SBFISD::RET_FLAG; + + // CCValAssign - represent the assignment of the return value to a location + SmallVector RVLocs; + MachineFunction &MF = DAG.getMachineFunction(); + + // CCState - Info about the registers and stack slot. + CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); + + if (Subtarget->isSolana()) { + if (Outs.size() > 1) { + fail(DL, DAG, "Only a single return supported"); + assert(false); + } + } else if (MF.getFunction().getReturnType()->isAggregateType()) { + fail(DL, DAG, "only integer returns supported"); + return DAG.getNode(Opc, DL, MVT::Other, Chain); + } + + // Analize return values. + CCInfo.AnalyzeReturn(Outs, getHasAlu32() ? RetCC_SBF32 : RetCC_SBF64); + + SDValue Flag; + SmallVector RetOps(1, Chain); + + // Copy the result values into the output registers. + for (unsigned i = 0; i != RVLocs.size(); ++i) { + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Can only return in registers!"); + + Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), OutVals[i], Flag); + + // Guarantee that all emitted copies are stuck together, + // avoiding something bad. + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + } + + RetOps[0] = Chain; // Update chain. + + // Add the flag if we have it. + if (Flag.getNode()) + RetOps.push_back(Flag); + + return DAG.getNode(Opc, DL, MVT::Other, RetOps); +} + +SDValue SBFTargetLowering::LowerCallResult( + SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + + MachineFunction &MF = DAG.getMachineFunction(); + // Assign locations to each value returned by this call. + SmallVector RVLocs; + CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); + + if (Subtarget->isSolana()) { + if (Ins.size() > 1) { + fail(DL, DAG, "Only a single return supported"); + assert(false); + } + } else if (Ins.size() >= 2) { + fail(DL, DAG, "only small returns supported"); + for (unsigned i = 0, e = Ins.size(); i != e; ++i) + InVals.push_back(DAG.getConstant(0, DL, Ins[i].VT)); + return DAG.getCopyFromReg(Chain, DL, 1, Ins[0].VT, InFlag).getValue(1); + } + + CCInfo.AnalyzeCallResult(Ins, getHasAlu32() ? RetCC_SBF32 : RetCC_SBF64); + + // Copy all of the result registers out of their specified physreg. + for (auto &Val : RVLocs) { + Chain = DAG.getCopyFromReg(Chain, DL, Val.getLocReg(), + Val.getValVT(), InFlag).getValue(1); + InFlag = Chain.getValue(2); + InVals.push_back(Chain.getValue(0)); + } + + return Chain; +} + +static void NegateCC(SDValue &LHS, SDValue &RHS, ISD::CondCode &CC) { + switch (CC) { + default: + break; + case ISD::SETULT: + case ISD::SETULE: + case ISD::SETLT: + case ISD::SETLE: + CC = ISD::getSetCCSwappedOperands(CC); + std::swap(LHS, RHS); + break; + } +} + +SDValue SBFTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue Chain = Op.getOperand(0); + ISD::CondCode CC = cast(Op.getOperand(1))->get(); + SDValue LHS = Op.getOperand(2); + SDValue RHS = Op.getOperand(3); + SDValue Dest = Op.getOperand(4); + SDLoc DL(Op); + + if (!getHasJmpExt()) + NegateCC(LHS, RHS, CC); + + return DAG.getNode(SBFISD::BR_CC, DL, Op.getValueType(), Chain, LHS, RHS, + DAG.getConstant(CC, DL, LHS.getValueType()), Dest); +} + +SDValue SBFTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + SDValue TrueV = Op.getOperand(2); + SDValue FalseV = Op.getOperand(3); + ISD::CondCode CC = cast(Op.getOperand(4))->get(); + SDLoc DL(Op); + + if (!getHasJmpExt()) + NegateCC(LHS, RHS, CC); + + SDValue TargetCC = DAG.getConstant(CC, DL, LHS.getValueType()); + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); + SDValue Ops[] = {LHS, RHS, TargetCC, TrueV, FalseV}; + + return DAG.getNode(SBFISD::SELECT_CC, DL, VTs, Ops); +} + +SDValue SBFTargetLowering::LowerATOMICRMW(SDValue Op, SelectionDAG &DAG) const { + SDLoc DL(Op); + AtomicSDNode *AN = cast(Op); + assert(AN && "Expected custom lowering of an atomic load node"); + + SDValue Chain = AN->getChain(); + SDValue Ptr = AN->getBasePtr(); + EVT PtrVT = AN->getMemoryVT(); + EVT RetVT = Op.getValueType(); + + // Load the current value + SDValue Load = + DAG.getExtLoad(ISD::EXTLOAD, DL, RetVT, Chain, Ptr, MachinePointerInfo(), + PtrVT, AN->getAlignment()); + Chain = Load.getValue(1); + + // Most ops return the current value, except CMP_SWAP_WITH_SUCCESS see below + SDValue Ret = Load; + SDValue RetFlag; + + // Val contains the new value we want to set. For CMP_SWAP, Cmp contains the + // expected current value. + SDValue Cmp, Val; + if (AN->isCompareAndSwap()) { + Cmp = Op.getOperand(2); + Val = Op.getOperand(3); + + // The Cmp value must match the pointer type + EVT CmpVT = Cmp->getValueType(0); + if (CmpVT != RetVT) { + Cmp = RetVT.bitsGT(CmpVT) ? DAG.getNode(ISD::SIGN_EXTEND, DL, RetVT, Cmp) + : DAG.getNode(ISD::TRUNCATE, DL, RetVT, Cmp); + } + } else { + Val = AN->getVal(); + } + + // The new value type must match the pointer type + EVT ValVT = Val->getValueType(0); + if (ValVT != RetVT) { + Val = RetVT.bitsGT(ValVT) ? DAG.getNode(ISD::SIGN_EXTEND, DL, RetVT, Val) + : DAG.getNode(ISD::TRUNCATE, DL, RetVT, Val); + ValVT = Val->getValueType(0); + } + + SDValue NewVal; + switch (Op.getOpcode()) { + case ISD::ATOMIC_SWAP: + NewVal = Val; + break; + case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS: { + EVT RetFlagVT = AN->getValueType(1); + NewVal = DAG.getSelectCC(DL, Load, Cmp, Val, Load, ISD::SETEQ); + RetFlag = DAG.getSelectCC( + DL, Load, Cmp, DAG.getBoolConstant(true, DL, RetFlagVT, RetFlagVT), + DAG.getBoolConstant(false, DL, RetFlagVT, RetFlagVT), ISD::SETEQ); + break; + } + case ISD::ATOMIC_LOAD_ADD: + NewVal = DAG.getNode(ISD::ADD, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_SUB: + NewVal = DAG.getNode(ISD::SUB, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_AND: + NewVal = DAG.getNode(ISD::AND, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_NAND: { + NewVal = + DAG.getNOT(DL, DAG.getNode(ISD::AND, DL, ValVT, Load, Val), ValVT); + break; + } + case ISD::ATOMIC_LOAD_OR: + NewVal = DAG.getNode(ISD::OR, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_XOR: + NewVal = DAG.getNode(ISD::XOR, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_MIN: + NewVal = DAG.getNode(ISD::SMIN, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_UMIN: + NewVal = DAG.getNode(ISD::UMIN, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_MAX: + NewVal = DAG.getNode(ISD::SMAX, DL, ValVT, Load, Val); + break; + case ISD::ATOMIC_LOAD_UMAX: + NewVal = DAG.getNode(ISD::UMAX, DL, ValVT, Load, Val); + break; + default: + llvm_unreachable("unknown atomicrmw op"); + } + + Chain = + DAG.getTruncStore(Chain, DL, NewVal, Ptr, MachinePointerInfo(), PtrVT); + + if (RetFlag) { + // CMP_SWAP_WITH_SUCCESS returns {value, success, chain} + Ret = DAG.getMergeValues({Ret, RetFlag, Chain}, DL); + } else { + // All the other ops return {value, chain} + Ret = DAG.getMergeValues({Ret, Chain}, DL); + } + + return Ret; +} + +const char *SBFTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch ((SBFISD::NodeType)Opcode) { + case SBFISD::FIRST_NUMBER: + break; + case SBFISD::RET_FLAG: + return "SBFISD::RET_FLAG"; + case SBFISD::CALL: + return "SBFISD::CALL"; + case SBFISD::SELECT_CC: + return "SBFISD::SELECT_CC"; + case SBFISD::BR_CC: + return "SBFISD::BR_CC"; + case SBFISD::Wrapper: + return "SBFISD::Wrapper"; + case SBFISD::MEMCPY: + return "SBFISD::MEMCPY"; + } + return nullptr; +} + +SDValue SBFTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + auto N = cast(Op); + assert(N->getOffset() == 0 && "Invalid offset for global address"); + + SDLoc DL(Op); + const GlobalValue *GV = N->getGlobal(); + SDValue GA = DAG.getTargetGlobalAddress(GV, DL, MVT::i64); + + return DAG.getNode(SBFISD::Wrapper, DL, MVT::i64, GA); +} + +unsigned +SBFTargetLowering::EmitSubregExt(MachineInstr &MI, MachineBasicBlock *BB, + unsigned Reg, bool isSigned) const { + const TargetInstrInfo &TII = *BB->getParent()->getSubtarget().getInstrInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i64); + int RShiftOp = isSigned ? SBF::SRA_ri : SBF::SRL_ri; + MachineFunction *F = BB->getParent(); + DebugLoc DL = MI.getDebugLoc(); + + MachineRegisterInfo &RegInfo = F->getRegInfo(); + + if (!isSigned) { + Register PromotedReg0 = RegInfo.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(SBF::MOV_32_64), PromotedReg0).addReg(Reg); + return PromotedReg0; + } + Register PromotedReg0 = RegInfo.createVirtualRegister(RC); + Register PromotedReg1 = RegInfo.createVirtualRegister(RC); + Register PromotedReg2 = RegInfo.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(SBF::MOV_32_64), PromotedReg0).addReg(Reg); + BuildMI(BB, DL, TII.get(SBF::SLL_ri), PromotedReg1) + .addReg(PromotedReg0).addImm(32); + BuildMI(BB, DL, TII.get(RShiftOp), PromotedReg2) + .addReg(PromotedReg1).addImm(32); + + return PromotedReg2; +} + +MachineBasicBlock * +SBFTargetLowering::EmitInstrWithCustomInserterMemcpy(MachineInstr &MI, + MachineBasicBlock *BB) + const { + MachineFunction *MF = MI.getParent()->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + MachineInstrBuilder MIB(*MF, MI); + unsigned ScratchReg; + + // This function does custom insertion during lowering SBFISD::MEMCPY which + // only has two register operands from memcpy semantics, the copy source + // address and the copy destination address. + // + // Because we will expand SBFISD::MEMCPY into load/store pairs, we will need + // a third scratch register to serve as the destination register of load and + // source register of store. + // + // The scratch register here is with the Define | Dead | EarlyClobber flags. + // The EarlyClobber flag has the semantic property that the operand it is + // attached to is clobbered before the rest of the inputs are read. Hence it + // must be unique among the operands to the instruction. The Define flag is + // needed to coerce the machine verifier that an Undef value isn't a problem + // as we anyway is loading memory into it. The Dead flag is needed as the + // value in scratch isn't supposed to be used by any other instruction. + ScratchReg = MRI.createVirtualRegister(&SBF::GPRRegClass); + MIB.addReg(ScratchReg, + RegState::Define | RegState::Dead | RegState::EarlyClobber); + + return BB; +} + +MachineBasicBlock * +SBFTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo &TII = *BB->getParent()->getSubtarget().getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Opc = MI.getOpcode(); + bool isSelectRROp = (Opc == SBF::Select || + Opc == SBF::Select_64_32 || + Opc == SBF::Select_32 || + Opc == SBF::Select_32_64); + + bool isMemcpyOp = Opc == SBF::MEMCPY; + bool isAtomicFence = Opc == SBF::ATOMIC_FENCE; + +#ifndef NDEBUG + bool isSelectRIOp = (Opc == SBF::Select_Ri || + Opc == SBF::Select_Ri_64_32 || + Opc == SBF::Select_Ri_32 || + Opc == SBF::Select_Ri_32_64); + + + assert((isSelectRROp || isSelectRIOp || isMemcpyOp || isAtomicFence) && + "Unexpected instr type to insert"); +#endif + + if (isMemcpyOp) + return EmitInstrWithCustomInserterMemcpy(MI, BB); + + if (isAtomicFence) { + // this is currently a nop + MI.eraseFromParent(); + return BB; + } + + bool is32BitCmp = (Opc == SBF::Select_32 || + Opc == SBF::Select_32_64 || + Opc == SBF::Select_Ri_32 || + Opc == SBF::Select_Ri_32_64); + + // To "insert" a SELECT instruction, we actually have to insert the diamond + // control-flow pattern. The incoming instruction knows the destination vreg + // to set, the condition code register to branch on, the true/false values to + // select between, and a branch opcode to use. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = ++BB->getIterator(); + + // ThisMBB: + // ... + // TrueVal = ... + // jmp_XX r1, r2 goto Copy1MBB + // fallthrough --> Copy0MBB + MachineBasicBlock *ThisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *Copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *Copy1MBB = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(I, Copy0MBB); + F->insert(I, Copy1MBB); + // Update machine-CFG edges by transferring all successors of the current + // block to the new block which will contain the Phi node for the select. + Copy1MBB->splice(Copy1MBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + Copy1MBB->transferSuccessorsAndUpdatePHIs(BB); + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(Copy0MBB); + BB->addSuccessor(Copy1MBB); + + // Insert Branch if Flag + int CC = MI.getOperand(3).getImm(); + int NewCC; + switch (CC) { +#define SET_NEWCC(X, Y) \ + case ISD::X: \ + if (is32BitCmp && HasJmp32) \ + NewCC = isSelectRROp ? SBF::Y##_rr_32 : SBF::Y##_ri_32; \ + else \ + NewCC = isSelectRROp ? SBF::Y##_rr : SBF::Y##_ri; \ + break + SET_NEWCC(SETGT, JSGT); + SET_NEWCC(SETUGT, JUGT); + SET_NEWCC(SETGE, JSGE); + SET_NEWCC(SETUGE, JUGE); + SET_NEWCC(SETEQ, JEQ); + SET_NEWCC(SETNE, JNE); + SET_NEWCC(SETLT, JSLT); + SET_NEWCC(SETULT, JULT); + SET_NEWCC(SETLE, JSLE); + SET_NEWCC(SETULE, JULE); + default: + report_fatal_error("unimplemented select CondCode " + Twine(CC)); + } + + Register LHS = MI.getOperand(1).getReg(); + bool isSignedCmp = (CC == ISD::SETGT || + CC == ISD::SETGE || + CC == ISD::SETLT || + CC == ISD::SETLE); + + // eBPF at the moment only has 64-bit comparison. Any 32-bit comparison need + // to be promoted, however if the 32-bit comparison operands are destination + // registers then they are implicitly zero-extended already, there is no + // need of explicit zero-extend sequence for them. + // + // We simply do extension for all situations in this method, but we will + // try to remove those unnecessary in SBFMIPeephole pass. + if (is32BitCmp && !HasJmp32) + LHS = EmitSubregExt(MI, BB, LHS, isSignedCmp); + + if (isSelectRROp) { + Register RHS = MI.getOperand(2).getReg(); + + if (is32BitCmp && !HasJmp32) + RHS = EmitSubregExt(MI, BB, RHS, isSignedCmp); + + BuildMI(BB, DL, TII.get(NewCC)).addReg(LHS).addReg(RHS).addMBB(Copy1MBB); + } else { + int64_t imm32 = MI.getOperand(2).getImm(); + // Check before we build J*_ri instruction. + assert (isInt<32>(imm32)); + BuildMI(BB, DL, TII.get(NewCC)) + .addReg(LHS).addImm(imm32).addMBB(Copy1MBB); + } + + // Copy0MBB: + // %FalseValue = ... + // # fallthrough to Copy1MBB + BB = Copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(Copy1MBB); + + // Copy1MBB: + // %Result = phi [ %FalseValue, Copy0MBB ], [ %TrueValue, ThisMBB ] + // ... + BB = Copy1MBB; + BuildMI(*BB, BB->begin(), DL, TII.get(SBF::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(5).getReg()) + .addMBB(Copy0MBB) + .addReg(MI.getOperand(4).getReg()) + .addMBB(ThisMBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +EVT SBFTargetLowering::getSetCCResultType(const DataLayout &, LLVMContext &, + EVT VT) const { + return getHasAlu32() ? MVT::i32 : MVT::i64; +} + +MVT SBFTargetLowering::getScalarShiftAmountTy(const DataLayout &DL, + EVT VT) const { + return (getHasAlu32() && VT == MVT::i32) ? MVT::i32 : MVT::i64; +} + +bool SBFTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I) const { + // No global is ever allowed as a base. + if (AM.BaseGV) + return false; + + switch (AM.Scale) { + case 0: // "r+i" or just "i", depending on HasBaseReg. + break; + case 1: + if (!AM.HasBaseReg) // allow "r+i". + break; + return false; // disallow "r+r" or "r+r+i". + default: + return false; + } + + return true; +} diff --git a/llvm/lib/Target/SBF/SBFISelLowering.h b/llvm/lib/Target/SBF/SBFISelLowering.h new file mode 100644 index 0000000000000..da0739ac6116e --- /dev/null +++ b/llvm/lib/Target/SBF/SBFISelLowering.h @@ -0,0 +1,174 @@ +//===-- SBFISelLowering.h - SBF DAG Lowering 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 defines the interfaces that SBF uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFISELLOWERING_H +#define LLVM_LIB_TARGET_SBF_SBFISELLOWERING_H + +#include "SBF.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { +class SBFSubtarget; +namespace SBFISD { +enum NodeType : unsigned { + FIRST_NUMBER = ISD::BUILTIN_OP_END, + RET_FLAG, + CALL, + SELECT_CC, + BR_CC, + Wrapper, + MEMCPY +}; +} + +class SBFTargetLowering : public TargetLowering { + const SBFSubtarget *Subtarget; +public: + explicit SBFTargetLowering(const TargetMachine &TM, const SBFSubtarget &STI); + + bool allowsMisalignedMemoryAccesses(EVT VT, unsigned, Align, + MachineMemOperand::Flags, + bool *) const override; + + // Provide custom lowering hooks for some operations. + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + // This method returns the name of a target specific DAG node. + const char *getTargetNodeName(unsigned Opcode) const override; + + // This method decides whether folding a constant offset + // with the given GlobalAddress is legal. + bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; + + SBFTargetLowering::ConstraintType + getConstraintType(StringRef Constraint) const override; + + std::pair + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const override; + + bool getHasAlu32() const { return HasAlu32; } + bool getHasJmp32() const { return HasJmp32; } + bool getHasJmpExt() const { return HasJmpExt; } + + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override; + + MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override; + + bool lowerAtomicStoreAsStoreSDNode(const StoreInst &SI) const override; + bool lowerAtomicLoadAsLoadSDNode(const LoadInst &LI) const override; + +private: + // Control Instruction Selection Features + bool HasAlu32; + bool HasJmp32; + bool HasJmpExt; + + SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerATOMICRMW(SDValue Op, SelectionDAG &DAG) const; + + // Lower the result values of a call, copying them out of physregs into vregs + SDValue LowerCallResult(SDValue Chain, SDValue InFlag, + CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; + + // Maximum number of arguments to a call + static const unsigned MaxArgs; + + // Lower a call into CALLSEQ_START - SBFISD:CALL - CALLSEQ_END chain + SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + /// Returns true if arguments should be sign-extended in lib calls. + bool shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const override; + + // Lower incoming arguments, copy physregs into vregs + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool IsVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &DL, + SelectionDAG &DAG) const override; + + void ReplaceNodeResults(SDNode *N, SmallVectorImpl &Results, + SelectionDAG &DAG) const override; + + EVT getOptimalMemOpType(const MemOp &Op, + const AttributeList &FuncAttributes) const override { + return Op.size() >= 8 ? MVT::i64 : MVT::i32; + } + + bool isIntDivCheap(EVT VT, AttributeList Attr) const override { return true; } + + bool shouldConvertConstantLoadToIntImm(const APInt &Imm, + Type *Ty) const override { + return true; + } + + // Prevent reducing load width during SelectionDag phase. + // Otherwise, we may transform the following + // ctx = ctx + reloc_offset + // ... (*(u32 *)ctx) & 0x8000... + // to + // ctx = ctx + reloc_offset + // ... (*(u8 *)(ctx + 1)) & 0x80 ... + // which will be rejected by the verifier. + bool shouldReduceLoadWidth(SDNode *Load, ISD::LoadExtType ExtTy, + EVT NewVT) const override { + return false; + } + + bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, + Type *Ty, unsigned AS, + Instruction *I = nullptr) const override; + + // isTruncateFree - Return true if it's free to truncate a value of + // type Ty1 to type Ty2. e.g. On SBF at alu32 mode, it's free to truncate + // a i64 value in register R1 to i32 by referencing its sub-register W1. + bool isTruncateFree(Type *Ty1, Type *Ty2) const override; + bool isTruncateFree(EVT VT1, EVT VT2) const override; + + // For 32bit ALU result zext to 64bit is free. + bool isZExtFree(Type *Ty1, Type *Ty2) const override; + bool isZExtFree(EVT VT1, EVT VT2) const override; + + unsigned EmitSubregExt(MachineInstr &MI, MachineBasicBlock *BB, unsigned Reg, + bool isSigned) const; + + MachineBasicBlock * EmitInstrWithCustomInserterMemcpy(MachineInstr &MI, + MachineBasicBlock *BB) + const; + +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/SBFInstrFormats.td b/llvm/lib/Target/SBF/SBFInstrFormats.td new file mode 100644 index 0000000000000..bb42a6141e6d0 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFInstrFormats.td @@ -0,0 +1,124 @@ +//===-- SBFInstrFormats.td - SBF Instruction Formats -------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +class SBFOpClass val> { + bits<3> Value = val; +} + +def SBF_LD : SBFOpClass<0x0>; +def SBF_LDX : SBFOpClass<0x1>; +def SBF_ST : SBFOpClass<0x2>; +def SBF_STX : SBFOpClass<0x3>; +def SBF_ALU : SBFOpClass<0x4>; +def SBF_JMP : SBFOpClass<0x5>; +def SBF_JMP32 : SBFOpClass<0x6>; +def SBF_ALU64 : SBFOpClass<0x7>; + +class SBFSrcType val> { + bits<1> Value = val; +} + +def SBF_K : SBFSrcType<0x0>; +def SBF_X : SBFSrcType<0x1>; + +class SBFArithOp val> { + bits<4> Value = val; +} + +def SBF_ADD : SBFArithOp<0x0>; +def SBF_SUB : SBFArithOp<0x1>; +def SBF_MUL : SBFArithOp<0x2>; +def SBF_DIV : SBFArithOp<0x3>; +def SBF_OR : SBFArithOp<0x4>; +def SBF_AND : SBFArithOp<0x5>; +def SBF_LSH : SBFArithOp<0x6>; +def SBF_RSH : SBFArithOp<0x7>; +def SBF_NEG : SBFArithOp<0x8>; +def SBF_XOR : SBFArithOp<0xa>; +def SBF_MOV : SBFArithOp<0xb>; +def SBF_ARSH : SBFArithOp<0xc>; +def SBF_END : SBFArithOp<0xd>; +def SBF_SDIV : SBFArithOp<0xe>; + +def SBF_XCHG : SBFArithOp<0xe>; +def SBF_CMPXCHG : SBFArithOp<0xf>; + +class SBFEndDir val> { + bits<1> Value = val; +} + +def SBF_TO_LE : SBFSrcType<0x0>; +def SBF_TO_BE : SBFSrcType<0x1>; + +class SBFJumpOp val> { + bits<4> Value = val; +} + +def SBF_JA : SBFJumpOp<0x0>; +def SBF_JEQ : SBFJumpOp<0x1>; +def SBF_JGT : SBFJumpOp<0x2>; +def SBF_JGE : SBFJumpOp<0x3>; +def SBF_JNE : SBFJumpOp<0x5>; +def SBF_JSGT : SBFJumpOp<0x6>; +def SBF_JSGE : SBFJumpOp<0x7>; +def SBF_CALL : SBFJumpOp<0x8>; +def SBF_EXIT : SBFJumpOp<0x9>; +def SBF_JLT : SBFJumpOp<0xa>; +def SBF_JLE : SBFJumpOp<0xb>; +def SBF_JSLT : SBFJumpOp<0xc>; +def SBF_JSLE : SBFJumpOp<0xd>; + +class SBFWidthModifer val> { + bits<2> Value = val; +} + +def SBF_W : SBFWidthModifer<0x0>; +def SBF_H : SBFWidthModifer<0x1>; +def SBF_B : SBFWidthModifer<0x2>; +def SBF_DW : SBFWidthModifer<0x3>; + +class SBFModeModifer val> { + bits<3> Value = val; +} + +def SBF_IMM : SBFModeModifer<0x0>; +def SBF_ABS : SBFModeModifer<0x1>; +def SBF_IND : SBFModeModifer<0x2>; +def SBF_MEM : SBFModeModifer<0x3>; +def SBF_ATOMIC : SBFModeModifer<0x6>; + +class SBFAtomicFlag val> { + bits<4> Value = val; +} + +def SBF_FETCH : SBFAtomicFlag<0x1>; + +class InstSBF pattern> + : Instruction { + field bits<64> Inst; + field bits<64> SoftFail = 0; + let Size = 8; + + let Namespace = "SBF"; + let DecoderNamespace = "SBF"; + + SBFOpClass SBFClass; + let Inst{58-56} = SBFClass.Value; + + dag OutOperandList = outs; + dag InOperandList = ins; + let AsmString = asmstr; + let Pattern = pattern; +} + +// Pseudo instructions +class Pseudo pattern> + : InstSBF { + let Inst{63-0} = 0; + let isPseudo = 1; +} diff --git a/llvm/lib/Target/SBF/SBFInstrInfo.cpp b/llvm/lib/Target/SBF/SBFInstrInfo.cpp new file mode 100644 index 0000000000000..886eb9e9c5fd9 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFInstrInfo.cpp @@ -0,0 +1,260 @@ +//===-- SBFInstrInfo.cpp - SBF Instruction Information ----------*- 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 the SBF implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "SBFInstrInfo.h" +#include "SBF.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include + +#define GET_INSTRINFO_CTOR_DTOR +#include "SBFGenInstrInfo.inc" + +using namespace llvm; + +SBFInstrInfo::SBFInstrInfo() + : SBFGenInstrInfo(SBF::ADJCALLSTACKDOWN, SBF::ADJCALLSTACKUP) {} + +void SBFInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + const DebugLoc &DL, MCRegister DestReg, + MCRegister SrcReg, bool KillSrc) const { + if (SBF::GPRRegClass.contains(DestReg, SrcReg)) + BuildMI(MBB, I, DL, get(SBF::MOV_rr), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + else if (SBF::GPR32RegClass.contains(DestReg, SrcReg)) + BuildMI(MBB, I, DL, get(SBF::MOV_rr_32), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + else + llvm_unreachable("Impossible reg-to-reg copy"); +} + +void SBFInstrInfo::expandMEMCPY(MachineBasicBlock::iterator MI) const { + Register DstReg = MI->getOperand(0).getReg(); + Register SrcReg = MI->getOperand(1).getReg(); + uint64_t CopyLen = MI->getOperand(2).getImm(); + uint64_t Alignment = MI->getOperand(3).getImm(); + Register ScratchReg = MI->getOperand(4).getReg(); + MachineBasicBlock *BB = MI->getParent(); + DebugLoc dl = MI->getDebugLoc(); + unsigned LdOpc, StOpc; + + switch (Alignment) { + case 1: + LdOpc = SBF::LDB; + StOpc = SBF::STB; + break; + case 2: + LdOpc = SBF::LDH; + StOpc = SBF::STH; + break; + case 4: + LdOpc = SBF::LDW; + StOpc = SBF::STW; + break; + case 8: + LdOpc = SBF::LDD; + StOpc = SBF::STD; + break; + default: + llvm_unreachable("unsupported memcpy alignment"); + } + + unsigned IterationNum = CopyLen >> Log2_64(Alignment); + for(unsigned I = 0; I < IterationNum; ++I) { + BuildMI(*BB, MI, dl, get(LdOpc)) + .addReg(ScratchReg, RegState::Define).addReg(SrcReg) + .addImm(I * Alignment); + BuildMI(*BB, MI, dl, get(StOpc)) + .addReg(ScratchReg, RegState::Kill).addReg(DstReg) + .addImm(I * Alignment); + } + + unsigned BytesLeft = CopyLen & (Alignment - 1); + unsigned Offset = IterationNum * Alignment; + bool Hanging4Byte = BytesLeft & 0x4; + bool Hanging2Byte = BytesLeft & 0x2; + bool Hanging1Byte = BytesLeft & 0x1; + if (Hanging4Byte) { + BuildMI(*BB, MI, dl, get(SBF::LDW)) + .addReg(ScratchReg, RegState::Define).addReg(SrcReg).addImm(Offset); + BuildMI(*BB, MI, dl, get(SBF::STW)) + .addReg(ScratchReg, RegState::Kill).addReg(DstReg).addImm(Offset); + Offset += 4; + } + if (Hanging2Byte) { + BuildMI(*BB, MI, dl, get(SBF::LDH)) + .addReg(ScratchReg, RegState::Define).addReg(SrcReg).addImm(Offset); + BuildMI(*BB, MI, dl, get(SBF::STH)) + .addReg(ScratchReg, RegState::Kill).addReg(DstReg).addImm(Offset); + Offset += 2; + } + if (Hanging1Byte) { + BuildMI(*BB, MI, dl, get(SBF::LDB)) + .addReg(ScratchReg, RegState::Define).addReg(SrcReg).addImm(Offset); + BuildMI(*BB, MI, dl, get(SBF::STB)) + .addReg(ScratchReg, RegState::Kill).addReg(DstReg).addImm(Offset); + } + + BB->erase(MI); +} + +bool SBFInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + if (MI.getOpcode() == SBF::MEMCPY) { + expandMEMCPY(MI); + return true; + } + + return false; +} + +void SBFInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + Register SrcReg, bool IsKill, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL; + if (I != MBB.end()) + DL = I->getDebugLoc(); + + if (RC == &SBF::GPRRegClass) + BuildMI(MBB, I, DL, get(SBF::STD)) + .addReg(SrcReg, getKillRegState(IsKill)) + .addFrameIndex(FI) + .addImm(0); + else if (RC == &SBF::GPR32RegClass) + BuildMI(MBB, I, DL, get(SBF::STW32)) + .addReg(SrcReg, getKillRegState(IsKill)) + .addFrameIndex(FI) + .addImm(0); + else + llvm_unreachable("Can't store this register to stack slot"); +} + +void SBFInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + Register DestReg, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL; + if (I != MBB.end()) + DL = I->getDebugLoc(); + + if (RC == &SBF::GPRRegClass) + BuildMI(MBB, I, DL, get(SBF::LDD), DestReg).addFrameIndex(FI).addImm(0); + else if (RC == &SBF::GPR32RegClass) + BuildMI(MBB, I, DL, get(SBF::LDW32), DestReg).addFrameIndex(FI).addImm(0); + else + llvm_unreachable("Can't load this register from stack slot"); +} + +bool SBFInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + // Start from the bottom of the block and work up, examining the + // terminator instructions. + MachineBasicBlock::iterator I = MBB.end(); + while (I != MBB.begin()) { + --I; + if (I->isDebugInstr()) + continue; + + // Working from the bottom, when we see a non-terminator + // instruction, we're done. + if (!isUnpredicatedTerminator(*I)) + break; + + // A terminator that isn't a branch can't easily be handled + // by this analysis. + if (!I->isBranch()) + return true; + + // Handle unconditional branches. + if (I->getOpcode() == SBF::JMP) { + if (!AllowModify) { + TBB = I->getOperand(0).getMBB(); + continue; + } + + // If the block has any instructions after a J, delete them. + while (std::next(I) != MBB.end()) + std::next(I)->eraseFromParent(); + Cond.clear(); + FBB = nullptr; + + // Delete the J if it's equivalent to a fall-through. + if (MBB.isLayoutSuccessor(I->getOperand(0).getMBB())) { + TBB = nullptr; + I->eraseFromParent(); + I = MBB.end(); + continue; + } + + // TBB is used to indicate the unconditinal destination. + TBB = I->getOperand(0).getMBB(); + continue; + } + // Cannot handle conditional branches + return true; + } + + return false; +} + +unsigned SBFInstrInfo::insertBranch(MachineBasicBlock &MBB, + MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded) const { + assert(!BytesAdded && "code size not handled"); + + // Shouldn't be a fall through. + assert(TBB && "insertBranch must not be told to insert a fallthrough"); + + if (Cond.empty()) { + // Unconditional branch + assert(!FBB && "Unconditional branch with multiple successors!"); + BuildMI(&MBB, DL, get(SBF::JMP)).addMBB(TBB); + return 1; + } + + llvm_unreachable("Unexpected conditional branch"); +} + +unsigned SBFInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "code size not handled"); + + MachineBasicBlock::iterator I = MBB.end(); + unsigned Count = 0; + + while (I != MBB.begin()) { + --I; + if (I->isDebugInstr()) + continue; + if (I->getOpcode() != SBF::JMP) + break; + // Remove the branch. + I->eraseFromParent(); + I = MBB.end(); + ++Count; + } + + return Count; +} diff --git a/llvm/lib/Target/SBF/SBFInstrInfo.h b/llvm/lib/Target/SBF/SBFInstrInfo.h new file mode 100644 index 0000000000000..150149bff1eed --- /dev/null +++ b/llvm/lib/Target/SBF/SBFInstrInfo.h @@ -0,0 +1,65 @@ +//===-- SBFInstrInfo.h - SBF Instruction Information ------------*- 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 the SBF implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFINSTRINFO_H +#define LLVM_LIB_TARGET_SBF_SBFINSTRINFO_H + +#include "SBFRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "SBFGenInstrInfo.inc" + +namespace llvm { + +class SBFInstrInfo : public SBFGenInstrInfo { + const SBFRegisterInfo RI; + +public: + SBFInstrInfo(); + + const SBFRegisterInfo &getRegisterInfo() const { return RI; } + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, + bool KillSrc) const override; + + bool expandPostRAPseudo(MachineInstr &MI) const override; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, Register SrcReg, + bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, Register DestReg, + int FrameIndex, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded = nullptr) const override; +private: + void expandMEMCPY(MachineBasicBlock::iterator) const; + +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/SBFInstrInfo.td b/llvm/lib/Target/SBF/SBFInstrInfo.td new file mode 100644 index 0000000000000..44a9abff44732 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFInstrInfo.td @@ -0,0 +1,1008 @@ +//===-- SBFInstrInfo.td - Target Description for SBF Target ---------------===// +// +// 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 describes the SBF instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +include "SBFInstrFormats.td" + +// Instruction Operands and Patterns + +// These are target-independent nodes, but have target-specific formats. +def SDT_SBFCallSeqStart : SDCallSeqStart<[SDTCisVT<0, iPTR>, + SDTCisVT<1, iPTR>]>; +def SDT_SBFCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>; +def SDT_SBFCall : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>; +def SDT_SBFSetFlag : SDTypeProfile<0, 3, [SDTCisSameAs<0, 1>]>; +def SDT_SBFSelectCC : SDTypeProfile<1, 5, [SDTCisSameAs<1, 2>, + SDTCisSameAs<0, 4>, + SDTCisSameAs<4, 5>]>; +def SDT_SBFBrCC : SDTypeProfile<0, 4, [SDTCisSameAs<0, 1>, + SDTCisVT<3, OtherVT>]>; +def SDT_SBFWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, + SDTCisPtrTy<0>]>; +def SDT_SBFMEMCPY : SDTypeProfile<0, 4, [SDTCisVT<0, i64>, + SDTCisVT<1, i64>, + SDTCisVT<2, i64>, + SDTCisVT<3, i64>]>; + +def SBFcall : SDNode<"SBFISD::CALL", SDT_SBFCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; +def SBFretflag : SDNode<"SBFISD::RET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def SBFcallseq_start: SDNode<"ISD::CALLSEQ_START", SDT_SBFCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; +def SBFcallseq_end : SDNode<"ISD::CALLSEQ_END", SDT_SBFCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; +def SBFbrcc : SDNode<"SBFISD::BR_CC", SDT_SBFBrCC, + [SDNPHasChain, SDNPOutGlue, SDNPInGlue]>; + +def SBFselectcc : SDNode<"SBFISD::SELECT_CC", SDT_SBFSelectCC, [SDNPInGlue]>; +def SBFWrapper : SDNode<"SBFISD::Wrapper", SDT_SBFWrapper>; +def SBFmemcpy : SDNode<"SBFISD::MEMCPY", SDT_SBFMEMCPY, + [SDNPHasChain, SDNPInGlue, SDNPOutGlue, + SDNPMayStore, SDNPMayLoad]>; +def SBFIsLittleEndian : Predicate<"CurDAG->getDataLayout().isLittleEndian()">; +def SBFIsBigEndian : Predicate<"!CurDAG->getDataLayout().isLittleEndian()">; +def SBFHasALU32 : Predicate<"Subtarget->getHasAlu32()">; +def SBFNoALU32 : Predicate<"!Subtarget->getHasAlu32()">; +def SBFSubtargetSolana : Predicate<"Subtarget->isSolana()">; + +def brtarget : Operand { + let PrintMethod = "printBrTargetOperand"; +} +def calltarget : Operand; + +def u64imm : Operand { + let PrintMethod = "printImm64Operand"; +} + +def i64immSExt32 : PatLeaf<(i64 imm), + [{return isInt<32>(N->getSExtValue()); }]>; +def i32immSExt32 : PatLeaf<(i32 imm), + [{return isInt<32>(N->getSExtValue()); }]>; + +// Addressing modes. +def ADDRri : ComplexPattern; +def FIri : ComplexPattern; + +// Address operands +def MEMri : Operand { + let PrintMethod = "printMemOperand"; + let EncoderMethod = "getMemoryOpValue"; + let DecoderMethod = "decodeMemoryOpValue"; + let MIOperandInfo = (ops GPR, i16imm); +} + +// Conditional code predicates - used for pattern matching for jump instructions +def SBF_CC_EQ : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETEQ);}]>; +def SBF_CC_NE : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETNE);}]>; +def SBF_CC_GE : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETGE);}]>; +def SBF_CC_GT : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETGT);}]>; +def SBF_CC_GTU : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETUGT);}]>; +def SBF_CC_GEU : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETUGE);}]>; +def SBF_CC_LE : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETLE);}]>; +def SBF_CC_LT : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETLT);}]>; +def SBF_CC_LTU : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETULT);}]>; +def SBF_CC_LEU : PatLeaf<(i64 imm), + [{return (N->getZExtValue() == ISD::SETULE);}]>; +def SBF_CC_EQ_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETEQ);}]>; +def SBF_CC_NE_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETNE);}]>; +def SBF_CC_GE_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETGE);}]>; +def SBF_CC_GT_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETGT);}]>; +def SBF_CC_GTU_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETUGT);}]>; +def SBF_CC_GEU_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETUGE);}]>; +def SBF_CC_LE_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETLE);}]>; +def SBF_CC_LT_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETLT);}]>; +def SBF_CC_LTU_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETULT);}]>; +def SBF_CC_LEU_32 : PatLeaf<(i32 imm), + [{return (N->getZExtValue() == ISD::SETULE);}]>; + +// For arithmetic and jump instructions the 8-bit 'code' +// field is divided into three parts: +// +// +----------------+--------+--------------------+ +// | 4 bits | 1 bit | 3 bits | +// | operation code | source | instruction class | +// +----------------+--------+--------------------+ +// (MSB) (LSB) +class TYPE_ALU_JMP op, bits<1> srctype, + dag outs, dag ins, string asmstr, list pattern> + : InstSBF { + + let Inst{63-60} = op; + let Inst{59} = srctype; +} + +//For load and store instructions the 8-bit 'code' field is divided as: +// +// +--------+--------+-------------------+ +// | 3 bits | 2 bits | 3 bits | +// | mode | size | instruction class | +// +--------+--------+-------------------+ +// (MSB) (LSB) +class TYPE_LD_ST mode, bits<2> size, + dag outs, dag ins, string asmstr, list pattern> + : InstSBF { + + let Inst{63-61} = mode; + let Inst{60-59} = size; +} + +// jump instructions +class JMP_RR + : TYPE_ALU_JMP { + bits<4> dst; + bits<4> src; + bits<16> BrDst; + + let Inst{55-52} = src; + let Inst{51-48} = dst; + let Inst{47-32} = BrDst; + let SBFClass = SBF_JMP; +} + +class JMP_RI + : TYPE_ALU_JMP { + bits<4> dst; + bits<16> BrDst; + bits<32> imm; + + let Inst{51-48} = dst; + let Inst{47-32} = BrDst; + let Inst{31-0} = imm; + let SBFClass = SBF_JMP; +} + +class JMP_RR_32 + : TYPE_ALU_JMP { + bits<4> dst; + bits<4> src; + bits<16> BrDst; + + let Inst{55-52} = src; + let Inst{51-48} = dst; + let Inst{47-32} = BrDst; + let SBFClass = SBF_JMP32; +} + +class JMP_RI_32 + : TYPE_ALU_JMP { + bits<4> dst; + bits<16> BrDst; + bits<32> imm; + + let Inst{51-48} = dst; + let Inst{47-32} = BrDst; + let Inst{31-0} = imm; + let SBFClass = SBF_JMP32; +} + +multiclass J { + def _rr : JMP_RR; + def _ri : JMP_RI; + def _rr_32 : JMP_RR_32; + def _ri_32 : JMP_RI_32; +} + +let isBranch = 1, isTerminator = 1, hasDelaySlot=0 in { +// cmp+goto instructions +defm JEQ : J; +defm JUGT : J", SBF_CC_GTU, SBF_CC_GTU_32>; +defm JUGE : J=", SBF_CC_GEU, SBF_CC_GEU_32>; +defm JNE : J", SBF_CC_GT, SBF_CC_GT_32>; +defm JSGE : J=", SBF_CC_GE, SBF_CC_GE_32>; +defm JULT : J; +defm JULE : J; +defm JSLE : J; + def _ri : ALU_RI; + def _rr_32 : ALU_RR; + def _ri_32 : ALU_RI; +} + +let Constraints = "$dst = $src2" in { + let isAsCheapAsAMove = 1 in { + defm ADD : ALU>=", srl>; + defm XOR : ALU>=", sra>; + } + + defm MUL : ALU; + + let Predicates = [SBFSubtargetSolana] in { + defm SDIV : ALU; + } +} + +class NEG_RR pattern> + : TYPE_ALU_JMP { + bits<4> dst; + + let Inst{51-48} = dst; + let SBFClass = Class; +} + +let Constraints = "$dst = $src", isAsCheapAsAMove = 1 in { + def NEG_64: NEG_RR; + def NEG_32: NEG_RR; +} + +class LD_IMM64 Pseudo, string OpcodeStr> + : TYPE_LD_ST { + + bits<4> dst; + bits<64> imm; + + let Inst{51-48} = dst; + let Inst{55-52} = Pseudo; + let Inst{47-32} = 0; + let Inst{31-0} = imm{31-0}; + let SBFClass = SBF_LD; +} + +let isReMaterializable = 1, isAsCheapAsAMove = 1 in { +def LD_imm64 : LD_IMM64<0, "=">; +def MOV_rr : ALU_RR; +def MOV_ri : ALU_RI; +def MOV_rr_32 : ALU_RR; +def MOV_ri_32 : ALU_RI; +} + +def FI_ri + : TYPE_LD_ST { + // This is a tentative instruction, and will be replaced + // with MOV_rr and ADD_ri in PEI phase + let Inst{51-48} = 0; + let Inst{55-52} = 2; + let Inst{47-32} = 0; + let Inst{31-0} = 0; + let SBFClass = SBF_LD; +} + +def LD_pseudo + : TYPE_LD_ST { + + bits<4> dst; + bits<64> imm; + bits<4> pseudo; + + let Inst{51-48} = dst; + let Inst{55-52} = pseudo; + let Inst{47-32} = 0; + let Inst{31-0} = imm{31-0}; + let SBFClass = SBF_LD; +} + +// STORE instructions +class STORE Pattern> + : TYPE_LD_ST { + bits<4> src; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = src; + let Inst{47-32} = addr{15-0}; // offset + let SBFClass = SBF_STX; +} + +class STOREi64 + : STORE; + +let Predicates = [SBFNoALU32] in { + def STW : STOREi64; + def STH : STOREi64; + def STB : STOREi64; +} +def STD : STOREi64; + +// LOAD instructions +class LOAD Pattern> + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = dst; + let Inst{55-52} = addr{19-16}; + let Inst{47-32} = addr{15-0}; + let SBFClass = SBF_LDX; +} + +class LOADi64 + : LOAD; + +let isCodeGenOnly = 1 in { + def CORE_MEM : TYPE_LD_ST; + def CORE_ALU32_MEM : TYPE_LD_ST; + let Constraints = "$dst = $src" in { + def CORE_SHIFT : ALU_RR; + } +} + +let Predicates = [SBFNoALU32] in { + def LDW : LOADi64; + def LDH : LOADi64; + def LDB : LOADi64; +} + +def LDD : LOADi64; + +class BRANCH Pattern> + : TYPE_ALU_JMP { + bits<16> BrDst; + + let Inst{47-32} = BrDst; + let SBFClass = SBF_JMP; +} + +class CALL + : TYPE_ALU_JMP { + bits<32> BrDst; + + let Inst{31-0} = BrDst; + let SBFClass = SBF_JMP; +} + +class CALLX + : TYPE_ALU_JMP { + bits<32> BrDst; + + let Inst{31-0} = BrDst; + let SBFClass = SBF_JMP; +} + +// Jump always +let isBranch = 1, isTerminator = 1, hasDelaySlot=0, isBarrier = 1 in { + def JMP : BRANCH; +} + +// Jump and link +let isCall=1, hasDelaySlot=0, Uses = [R11], + // Potentially clobbered registers + Defs = [R0, R1, R2, R3, R4, R5] in { + def JAL : CALL<"call">; + def JALX : CALLX<"callx">; +} + +class NOP_I + : TYPE_ALU_JMP { + // mov r0, r0 == nop + let Inst{55-52} = 0; + let Inst{51-48} = 0; + let SBFClass = SBF_ALU64; +} + +let hasSideEffects = 0, isCodeGenOnly = 1 in + def NOP : NOP_I<"nop">; + +class RET + : TYPE_ALU_JMP { + let Inst{31-0} = 0; + let SBFClass = SBF_JMP; +} + +let isReturn = 1, isTerminator = 1, hasDelaySlot=0, isBarrier = 1, + isNotDuplicable = 1 in { + def RET : RET<"exit">; +} + +// ADJCALLSTACKDOWN/UP pseudo insns +let Defs = [R11], Uses = [R11], isCodeGenOnly = 1 in { +def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i64imm:$amt1, i64imm:$amt2), + "#ADJCALLSTACKDOWN $amt1 $amt2", + [(SBFcallseq_start timm:$amt1, timm:$amt2)]>; +def ADJCALLSTACKUP : Pseudo<(outs), (ins i64imm:$amt1, i64imm:$amt2), + "#ADJCALLSTACKUP $amt1 $amt2", + [(SBFcallseq_end timm:$amt1, timm:$amt2)]>; +} + +let usesCustomInserter = 1, isCodeGenOnly = 1 in { + def Select : Pseudo<(outs GPR:$dst), + (ins GPR:$lhs, GPR:$rhs, i64imm:$imm, GPR:$src, GPR:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i64:$dst, + (SBFselectcc i64:$lhs, i64:$rhs, (i64 imm:$imm), i64:$src, i64:$src2))]>; + def Select_Ri : Pseudo<(outs GPR:$dst), + (ins GPR:$lhs, i64imm:$rhs, i64imm:$imm, GPR:$src, GPR:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i64:$dst, + (SBFselectcc i64:$lhs, (i64immSExt32:$rhs), (i64 imm:$imm), i64:$src, i64:$src2))]>; + def Select_64_32 : Pseudo<(outs GPR32:$dst), + (ins GPR:$lhs, GPR:$rhs, i64imm:$imm, GPR32:$src, GPR32:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i32:$dst, + (SBFselectcc i64:$lhs, i64:$rhs, (i64 imm:$imm), i32:$src, i32:$src2))]>; + def Select_Ri_64_32 : Pseudo<(outs GPR32:$dst), + (ins GPR:$lhs, i64imm:$rhs, i64imm:$imm, GPR32:$src, GPR32:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i32:$dst, + (SBFselectcc i64:$lhs, (i64immSExt32:$rhs), (i64 imm:$imm), i32:$src, i32:$src2))]>; + def Select_32 : Pseudo<(outs GPR32:$dst), + (ins GPR32:$lhs, GPR32:$rhs, i32imm:$imm, GPR32:$src, GPR32:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i32:$dst, + (SBFselectcc i32:$lhs, i32:$rhs, (i32 imm:$imm), i32:$src, i32:$src2))]>; + def Select_Ri_32 : Pseudo<(outs GPR32:$dst), + (ins GPR32:$lhs, i32imm:$rhs, i32imm:$imm, GPR32:$src, GPR32:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i32:$dst, + (SBFselectcc i32:$lhs, (i32immSExt32:$rhs), (i32 imm:$imm), i32:$src, i32:$src2))]>; + def Select_32_64 : Pseudo<(outs GPR:$dst), + (ins GPR32:$lhs, GPR32:$rhs, i32imm:$imm, GPR:$src, GPR:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i64:$dst, + (SBFselectcc i32:$lhs, i32:$rhs, (i32 imm:$imm), i64:$src, i64:$src2))]>; + def Select_Ri_32_64 : Pseudo<(outs GPR:$dst), + (ins GPR32:$lhs, i32imm:$rhs, i32imm:$imm, GPR:$src, GPR:$src2), + "# Select PSEUDO $dst = $lhs $imm $rhs ? $src : $src2", + [(set i64:$dst, + (SBFselectcc i32:$lhs, (i32immSExt32:$rhs), (i32 imm:$imm), i64:$src, i64:$src2))]>; +} + +// load 64-bit global addr into register +def : Pat<(SBFWrapper tglobaladdr:$in), (LD_imm64 tglobaladdr:$in)>; + +// 0xffffFFFF doesn't fit into simm32, optimize common case +def : Pat<(i64 (and (i64 GPR:$src), 0xffffFFFF)), + (SRL_ri (SLL_ri (i64 GPR:$src), 32), 32)>; + +// Calls +def : Pat<(SBFcall tglobaladdr:$dst), (JAL tglobaladdr:$dst)>; +def : Pat<(SBFcall texternalsym:$dst), (JAL texternalsym:$dst)>; +def : Pat<(SBFcall imm:$dst), (JAL imm:$dst)>; +def : Pat<(SBFcall GPR:$dst), (JALX GPR:$dst)>; + +// Loads +let Predicates = [SBFNoALU32] in { + def : Pat<(i64 (extloadi8 ADDRri:$src)), (i64 (LDB ADDRri:$src))>; + def : Pat<(i64 (extloadi16 ADDRri:$src)), (i64 (LDH ADDRri:$src))>; + def : Pat<(i64 (extloadi32 ADDRri:$src)), (i64 (LDW ADDRri:$src))>; +} + +// Atomic XADD for SBFNoALU32 +class XADD + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = SBF_ADD.Value; + let SBFClass = SBF_STX; +} + +let Constraints = "$dst = $val" in { + let Predicates = [SBFNoALU32] in { + def XADDW : XADD; + } +} + +// Atomic add, and, or, xor +class ATOMIC_NOFETCH + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = Opc.Value; + let SBFClass = SBF_STX; +} + +class ATOMIC32_NOFETCH + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = Opc.Value; + let SBFClass = SBF_STX; +} + +let Constraints = "$dst = $val" in { + let Predicates = [SBFHasALU32], DecoderNamespace = "SBFALU32" in { + def XADDW32 : ATOMIC32_NOFETCH; + def XANDW32 : ATOMIC32_NOFETCH; + def XORW32 : ATOMIC32_NOFETCH; + def XXORW32 : ATOMIC32_NOFETCH; + } + + def XADDD : ATOMIC_NOFETCH; + def XANDD : ATOMIC_NOFETCH; + def XORD : ATOMIC_NOFETCH; + def XXORD : ATOMIC_NOFETCH; +} + +// Atomic Fetch-and- operations +class XFALU64 + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = Opc.Value; + let Inst{3-0} = SBF_FETCH.Value; + let SBFClass = SBF_STX; +} + +class XFALU32 + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = Opc.Value; + let Inst{3-0} = SBF_FETCH.Value; + let SBFClass = SBF_STX; +} + +let Constraints = "$dst = $val" in { + let Predicates = [SBFHasALU32], DecoderNamespace = "SBFALU32" in { + def XFADDW32 : XFALU32; + def XFANDW32 : XFALU32; + def XFORW32 : XFALU32; + def XFXORW32 : XFALU32; + } + + def XFADDD : XFALU64; + def XFANDD : XFALU64; + def XFORD : XFALU64; + def XFXORD : XFALU64; +} + +// atomic_load_sub can be represented as a neg followed +// by an atomic_load_add. +def : Pat<(atomic_load_sub_32 ADDRri:$addr, GPR32:$val), + (XFADDW32 ADDRri:$addr, (NEG_32 GPR32:$val))>; +def : Pat<(atomic_load_sub_64 ADDRri:$addr, GPR:$val), + (XFADDD ADDRri:$addr, (NEG_64 GPR:$val))>; + +let Predicates = [SBFSubtargetSolana], usesCustomInserter = 1, isCodeGenOnly = 1 in { + def ATOMIC_FENCE : Pseudo< + (outs), + (ins), + "#atomic_fence", + [(atomic_fence timm, timm)]>; +} + +// Atomic Exchange +class XCHG + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = SBF_XCHG.Value; + let Inst{3-0} = SBF_FETCH.Value; + let SBFClass = SBF_STX; +} + +class XCHG32 + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = dst; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = SBF_XCHG.Value; + let Inst{3-0} = SBF_FETCH.Value; + let SBFClass = SBF_STX; +} + +let Constraints = "$dst = $val" in { + let Predicates = [SBFHasALU32], DecoderNamespace = "SBFALU32" in { + def XCHGW32 : XCHG32; + } + + def XCHGD : XCHG; +} + +// Compare-And-Exchange +class CMPXCHG + : TYPE_LD_ST { + bits<4> new; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = new; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = SBF_CMPXCHG.Value; + let Inst{3-0} = SBF_FETCH.Value; + let SBFClass = SBF_STX; +} + +class CMPXCHG32 + : TYPE_LD_ST { + bits<4> new; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = new; + let Inst{47-32} = addr{15-0}; // offset + let Inst{7-4} = SBF_CMPXCHG.Value; + let Inst{3-0} = SBF_FETCH.Value; + let SBFClass = SBF_STX; +} + +let Predicates = [SBFHasALU32], Defs = [W0], Uses = [W0], + DecoderNamespace = "SBFALU32" in { + def CMPXCHGW32 : CMPXCHG32; +} + +let Defs = [R0], Uses = [R0] in { + def CMPXCHGD : CMPXCHG; +} + +// bswap16, bswap32, bswap64 +class BSWAP SizeOp, string OpcodeStr, SBFSrcType SrcType, list Pattern> + : TYPE_ALU_JMP { + bits<4> dst; + + let Inst{51-48} = dst; + let Inst{31-0} = SizeOp; + let SBFClass = SBF_ALU; +} + + +let Constraints = "$dst = $src" in { + let Predicates = [SBFIsLittleEndian] in { + def BE16 : BSWAP<16, "be16", SBF_TO_BE, [(set GPR:$dst, (srl (bswap GPR:$src), (i64 48)))]>; + def BE32 : BSWAP<32, "be32", SBF_TO_BE, [(set GPR:$dst, (srl (bswap GPR:$src), (i64 32)))]>; + def BE64 : BSWAP<64, "be64", SBF_TO_BE, [(set GPR:$dst, (bswap GPR:$src))]>; + } + let Predicates = [SBFIsBigEndian] in { + def LE16 : BSWAP<16, "le16", SBF_TO_LE, [(set GPR:$dst, (srl (bswap GPR:$src), (i64 48)))]>; + def LE32 : BSWAP<32, "le32", SBF_TO_LE, [(set GPR:$dst, (srl (bswap GPR:$src), (i64 32)))]>; + def LE64 : BSWAP<64, "le64", SBF_TO_LE, [(set GPR:$dst, (bswap GPR:$src))]>; + } +} + +let Defs = [R0, R1, R2, R3, R4, R5], Uses = [R6], hasSideEffects = 1, + hasExtraDefRegAllocReq = 1, hasExtraSrcRegAllocReq = 1, mayLoad = 1 in { +class LOAD_ABS + : TYPE_LD_ST { + bits<32> imm; + + let Inst{31-0} = imm; + let SBFClass = SBF_LD; +} + +class LOAD_IND + : TYPE_LD_ST { + bits<4> val; + + let Inst{55-52} = val; + let SBFClass = SBF_LD; +} +} + +def LD_ABS_B : LOAD_ABS; +def LD_ABS_H : LOAD_ABS; +def LD_ABS_W : LOAD_ABS; + +def LD_IND_B : LOAD_IND; +def LD_IND_H : LOAD_IND; +def LD_IND_W : LOAD_IND; + +let isCodeGenOnly = 1 in { + def MOV_32_64 : ALU_RR; +} + +def : Pat<(i64 (sext GPR32:$src)), + (SRA_ri (SLL_ri (MOV_32_64 GPR32:$src), 32), 32)>; + +def : Pat<(i64 (zext GPR32:$src)), (MOV_32_64 GPR32:$src)>; + +// For i64 -> i32 truncation, use the 32-bit subregister directly. +def : Pat<(i32 (trunc GPR:$src)), + (i32 (EXTRACT_SUBREG GPR:$src, sub_32))>; + +// For i32 -> i64 anyext, we don't care about the high bits. +def : Pat<(i64 (anyext GPR32:$src)), + (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$src, sub_32)>; + +class STORE32 Pattern> + : TYPE_LD_ST { + bits<4> src; + bits<20> addr; + + let Inst{51-48} = addr{19-16}; // base reg + let Inst{55-52} = src; + let Inst{47-32} = addr{15-0}; // offset + let SBFClass = SBF_STX; +} + +class STOREi32 + : STORE32; + +let Predicates = [SBFHasALU32], DecoderNamespace = "SBFALU32" in { + def STW32 : STOREi32; + def STH32 : STOREi32; + def STB32 : STOREi32; +} + +class LOAD32 Pattern> + : TYPE_LD_ST { + bits<4> dst; + bits<20> addr; + + let Inst{51-48} = dst; + let Inst{55-52} = addr{19-16}; + let Inst{47-32} = addr{15-0}; + let SBFClass = SBF_LDX; +} + +class LOADi32 + : LOAD32; + +let Predicates = [SBFHasALU32], DecoderNamespace = "SBFALU32" in { + def LDW32 : LOADi32; + def LDH32 : LOADi32; + def LDB32 : LOADi32; +} + +let Predicates = [SBFHasALU32] in { + def : Pat<(truncstorei8 GPR:$src, ADDRri:$dst), + (STB32 (EXTRACT_SUBREG GPR:$src, sub_32), ADDRri:$dst)>; + def : Pat<(truncstorei16 GPR:$src, ADDRri:$dst), + (STH32 (EXTRACT_SUBREG GPR:$src, sub_32), ADDRri:$dst)>; + def : Pat<(truncstorei32 GPR:$src, ADDRri:$dst), + (STW32 (EXTRACT_SUBREG GPR:$src, sub_32), ADDRri:$dst)>; + def : Pat<(i32 (extloadi8 ADDRri:$src)), (i32 (LDB32 ADDRri:$src))>; + def : Pat<(i32 (extloadi16 ADDRri:$src)), (i32 (LDH32 ADDRri:$src))>; + def : Pat<(i64 (zextloadi8 ADDRri:$src)), + (SUBREG_TO_REG (i64 0), (LDB32 ADDRri:$src), sub_32)>; + def : Pat<(i64 (zextloadi16 ADDRri:$src)), + (SUBREG_TO_REG (i64 0), (LDH32 ADDRri:$src), sub_32)>; + def : Pat<(i64 (zextloadi32 ADDRri:$src)), + (SUBREG_TO_REG (i64 0), (LDW32 ADDRri:$src), sub_32)>; + def : Pat<(i64 (extloadi8 ADDRri:$src)), + (SUBREG_TO_REG (i64 0), (LDB32 ADDRri:$src), sub_32)>; + def : Pat<(i64 (extloadi16 ADDRri:$src)), + (SUBREG_TO_REG (i64 0), (LDH32 ADDRri:$src), sub_32)>; + def : Pat<(i64 (extloadi32 ADDRri:$src)), + (SUBREG_TO_REG (i64 0), (LDW32 ADDRri:$src), sub_32)>; +} + +let usesCustomInserter = 1, isCodeGenOnly = 1 in { + def MEMCPY : Pseudo< + (outs), + (ins GPR:$dst, GPR:$src, i64imm:$len, i64imm:$align, variable_ops), + "#memcpy dst: $dst, src: $src, len: $len, align: $align", + [(SBFmemcpy GPR:$dst, GPR:$src, imm:$len, imm:$align)]>; +} diff --git a/llvm/lib/Target/SBF/SBFMCInstLower.cpp b/llvm/lib/Target/SBF/SBFMCInstLower.cpp new file mode 100644 index 0000000000000..421592d876924 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFMCInstLower.cpp @@ -0,0 +1,81 @@ +//=-- SBFMCInstLower.cpp - Convert SBF MachineInstr to an MCInst ------------=// +// +// 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 code to lower SBF MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "SBFMCInstLower.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +MCSymbol * +SBFMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { + return Printer.getSymbol(MO.getGlobal()); +} + +MCSymbol * +SBFMCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); +} + +MCOperand SBFMCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + + if (!MO.isJTI() && MO.getOffset()) + llvm_unreachable("unknown symbol op"); + + return MCOperand::createExpr(Expr); +} + +void SBFMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { + OutMI.setOpcode(MI->getOpcode()); + + for (const MachineOperand &MO : MI->operands()) { + MCOperand MCOp; + switch (MO.getType()) { + default: + MI->print(errs()); + llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + continue; + MCOp = MCOperand::createReg(MO.getReg()); + break; + case MachineOperand::MO_Immediate: + MCOp = MCOperand::createImm(MO.getImm()); + break; + case MachineOperand::MO_MachineBasicBlock: + MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx)); + break; + case MachineOperand::MO_RegisterMask: + continue; + case MachineOperand::MO_ExternalSymbol: + MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); + break; + case MachineOperand::MO_GlobalAddress: + MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); + break; + } + + OutMI.addOperand(MCOp); + } +} diff --git a/llvm/lib/Target/SBF/SBFMCInstLower.h b/llvm/lib/Target/SBF/SBFMCInstLower.h new file mode 100644 index 0000000000000..1d03df89ee4db --- /dev/null +++ b/llvm/lib/Target/SBF/SBFMCInstLower.h @@ -0,0 +1,41 @@ +//===-- SBFMCInstLower.h - Lower MachineInstr to MCInst ---------*- 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_LIB_TARGET_SBF_SBFMCINSTLOWER_H +#define LLVM_LIB_TARGET_SBF_SBFMCINSTLOWER_H + +#include "llvm/Support/Compiler.h" + +namespace llvm { +class AsmPrinter; +class MCContext; +class MCInst; +class MCOperand; +class MCSymbol; +class MachineInstr; +class MachineOperand; + +// SBFMCInstLower - This class is used to lower an MachineInstr into an MCInst. +class LLVM_LIBRARY_VISIBILITY SBFMCInstLower { + MCContext &Ctx; + + AsmPrinter &Printer; + +public: + SBFMCInstLower(MCContext &ctx, AsmPrinter &printer) + : Ctx(ctx), Printer(printer) {} + void Lower(const MachineInstr *MI, MCInst &OutMI) const; + + MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + + MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/SBFMIChecking.cpp b/llvm/lib/Target/SBF/SBFMIChecking.cpp new file mode 100644 index 0000000000000..6fc74f7d71306 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFMIChecking.cpp @@ -0,0 +1,251 @@ +//===-------------- SBFMIChecking.cpp - MI Checking Legality -------------===// +// +// 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 pass performs checking to signal errors for certain illegal usages at +// MachineInstruction layer. Specially, the result of XADD{32,64} insn should +// not be used. The pass is done at the PreEmit pass right before the +// machine code is emitted at which point the register liveness information +// is still available. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFInstrInfo.h" +#include "SBFTargetMachine.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "sbf-mi-checking" + +namespace { + +struct SBFMIPreEmitChecking : public MachineFunctionPass { + + static char ID; + MachineFunction *MF; + const TargetRegisterInfo *TRI; + + SBFMIPreEmitChecking() : MachineFunctionPass(ID) { + initializeSBFMIPreEmitCheckingPass(*PassRegistry::getPassRegistry()); + } + +private: + // Initialize class variables. + void initialize(MachineFunction &MFParm); + + bool processAtomicInsts(); + +public: + + // Main entry point for this pass. + bool runOnMachineFunction(MachineFunction &MF) override { + if (!skipFunction(MF.getFunction())) { + initialize(MF); + return processAtomicInsts(); + } + return false; + } +}; + +// Initialize class variables. +void SBFMIPreEmitChecking::initialize(MachineFunction &MFParm) { + MF = &MFParm; + TRI = MF->getSubtarget().getRegisterInfo(); + LLVM_DEBUG(dbgs() << "*** SBF PreEmit checking pass ***\n\n"); +} + +// Make sure all Defs of XADD are dead, meaning any result of XADD insn is not +// used. +// +// NOTE: SBF backend hasn't enabled sub-register liveness track, so when the +// source and destination operands of XADD are GPR32, there is no sub-register +// dead info. If we rely on the generic MachineInstr::allDefsAreDead, then we +// will raise false alarm on GPR32 Def. +// +// To support GPR32 Def, ideally we could just enable sub-registr liveness track +// on SBF backend, then allDefsAreDead could work on GPR32 Def. This requires +// implementing TargetSubtargetInfo::enableSubRegLiveness on SBF. +// +// However, sub-register liveness tracking module inside LLVM is actually +// designed for the situation where one register could be split into more than +// one sub-registers for which case each sub-register could have their own +// liveness and kill one of them doesn't kill others. So, tracking liveness for +// each make sense. +// +// For SBF, each 64-bit register could only have one 32-bit sub-register. This +// is exactly the case which LLVM think brings no benefits for doing +// sub-register tracking, because the live range of sub-register must always +// equal to its parent register, therefore liveness tracking is disabled even +// the back-end has implemented enableSubRegLiveness. The detailed information +// is at r232695: +// +// Author: Matthias Braun +// Date: Thu Mar 19 00:21:58 2015 +0000 +// Do not track subregister liveness when it brings no benefits +// +// Hence, for SBF, we enhance MachineInstr::allDefsAreDead. Given the solo +// sub-register always has the same liveness as its parent register, LLVM is +// already attaching a implicit 64-bit register Def whenever the there is +// a sub-register Def. The liveness of the implicit 64-bit Def is available. +// For example, for "lock *(u32 *)(r0 + 4) += w9", the MachineOperand info could +// be: +// +// $w9 = XADDW32 killed $r0, 4, $w9(tied-def 0), +// implicit killed $r9, implicit-def dead $r9 +// +// Even though w9 is not marked as Dead, the parent register r9 is marked as +// Dead correctly, and it is safe to use such information or our purpose. +static bool hasLiveDefs(const MachineInstr &MI, const TargetRegisterInfo *TRI) { + const MCRegisterClass *GPR64RegClass = + &SBFMCRegisterClasses[SBF::GPRRegClassID]; + std::vector GPR32LiveDefs; + std::vector GPR64DeadDefs; + + for (const MachineOperand &MO : MI.operands()) { + bool RegIsGPR64; + + if (!MO.isReg() || MO.isUse()) + continue; + + RegIsGPR64 = GPR64RegClass->contains(MO.getReg()); + if (!MO.isDead()) { + // It is a GPR64 live Def, we are sure it is live. */ + if (RegIsGPR64) + return true; + // It is a GPR32 live Def, we are unsure whether it is really dead due to + // no sub-register liveness tracking. Push it to vector for deferred + // check. + GPR32LiveDefs.push_back(MO.getReg()); + continue; + } + + // Record any GPR64 dead Def as some unmarked GPR32 could be alias of its + // low 32-bit. + if (RegIsGPR64) + GPR64DeadDefs.push_back(MO.getReg()); + } + + // No GPR32 live Def, safe to return false. + if (GPR32LiveDefs.empty()) + return false; + + // No GPR64 dead Def, so all those GPR32 live Def can't have alias, therefore + // must be truely live, safe to return true. + if (GPR64DeadDefs.empty()) + return true; + + // Otherwise, return true if any aliased SuperReg of GPR32 is not dead. + for (auto I : GPR32LiveDefs) + for (MCSuperRegIterator SR(I, TRI); SR.isValid(); ++SR) + if (!llvm::is_contained(GPR64DeadDefs, *SR)) + return true; + + return false; +} + +bool SBFMIPreEmitChecking::processAtomicInsts() { + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + if (MI.getOpcode() != SBF::XADDW && + MI.getOpcode() != SBF::XADDD && + MI.getOpcode() != SBF::XADDW32) + continue; + + LLVM_DEBUG(MI.dump()); + if (hasLiveDefs(MI, TRI)) { + DebugLoc Empty; + const DebugLoc &DL = MI.getDebugLoc(); + if (DL != Empty) + report_fatal_error(Twine("line ") + std::to_string(DL.getLine()) + + ": Invalid usage of the XADD return value", false); + else + report_fatal_error("Invalid usage of the XADD return value", false); + } + } + } + + // Check return values of atomic_fetch_and_{add,and,or,xor}. + // If the return is not used, the atomic_fetch_and_ instruction + // is replaced with atomic_ instruction. + MachineInstr *ToErase = nullptr; + bool Changed = false; + const SBFInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + if (MI.getOpcode() != SBF::XFADDW32 && MI.getOpcode() != SBF::XFADDD && + MI.getOpcode() != SBF::XFANDW32 && MI.getOpcode() != SBF::XFANDD && + MI.getOpcode() != SBF::XFXORW32 && MI.getOpcode() != SBF::XFXORD && + MI.getOpcode() != SBF::XFORW32 && MI.getOpcode() != SBF::XFORD) + continue; + + if (hasLiveDefs(MI, TRI)) + continue; + + LLVM_DEBUG(dbgs() << "Transforming "; MI.dump()); + unsigned newOpcode; + switch (MI.getOpcode()) { + case SBF::XFADDW32: + newOpcode = SBF::XADDW32; + break; + case SBF::XFADDD: + newOpcode = SBF::XADDD; + break; + case SBF::XFANDW32: + newOpcode = SBF::XANDW32; + break; + case SBF::XFANDD: + newOpcode = SBF::XANDD; + break; + case SBF::XFXORW32: + newOpcode = SBF::XXORW32; + break; + case SBF::XFXORD: + newOpcode = SBF::XXORD; + break; + case SBF::XFORW32: + newOpcode = SBF::XORW32; + break; + case SBF::XFORD: + newOpcode = SBF::XORD; + break; + default: + llvm_unreachable("Incorrect Atomic Instruction Opcode"); + } + + BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(newOpcode)) + .add(MI.getOperand(0)) + .add(MI.getOperand(1)) + .add(MI.getOperand(2)) + .add(MI.getOperand(3)); + + ToErase = &MI; + Changed = true; + } + } + + return Changed; +} + +} // end default namespace + +INITIALIZE_PASS(SBFMIPreEmitChecking, "sbf-mi-pemit-checking", + "SBF PreEmit Checking", false, false) + +char SBFMIPreEmitChecking::ID = 0; +FunctionPass* llvm::createSBFMIPreEmitCheckingPass() +{ + return new SBFMIPreEmitChecking(); +} diff --git a/llvm/lib/Target/SBF/SBFMIPeephole.cpp b/llvm/lib/Target/SBF/SBFMIPeephole.cpp new file mode 100644 index 0000000000000..3aa7d88f2a8c7 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFMIPeephole.cpp @@ -0,0 +1,564 @@ +//===-------------- SBFMIPeephole.cpp - MI Peephole Cleanups -------------===// +// +// 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 pass performs peephole optimizations to cleanup ugly code sequences at +// MachineInstruction layer. +// +// Currently, there are two optimizations implemented: +// - One pre-RA MachineSSA pass to eliminate type promotion sequences, those +// zero extend 32-bit subregisters to 64-bit registers, if the compiler +// could prove the subregisters is defined by 32-bit operations in which +// case the upper half of the underlying 64-bit registers were zeroed +// implicitly. +// +// - One post-RA PreEmit pass to do final cleanup on some redundant +// instructions generated due to bad RA on subregister. +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFInstrInfo.h" +#include "SBFTargetMachine.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "sbf-mi-zext-elim" + +STATISTIC(ZExtElemNum, "Number of zero extension shifts eliminated"); + +namespace { + +struct SBFMIPeephole : public MachineFunctionPass { + + static char ID; + const SBFInstrInfo *TII; + MachineFunction *MF; + MachineRegisterInfo *MRI; + + SBFMIPeephole() : MachineFunctionPass(ID) { + initializeSBFMIPeepholePass(*PassRegistry::getPassRegistry()); + } + +private: + // Initialize class variables. + void initialize(MachineFunction &MFParm); + + bool isCopyFrom32Def(MachineInstr *CopyMI); + bool isInsnFrom32Def(MachineInstr *DefInsn); + bool isPhiFrom32Def(MachineInstr *MovMI); + bool isMovFrom32Def(MachineInstr *MovMI); + bool eliminateZExtSeq(); + bool eliminateZExt(); + + std::set PhiInsns; + +public: + + // Main entry point for this pass. + bool runOnMachineFunction(MachineFunction &MF) override { + if (skipFunction(MF.getFunction())) + return false; + + initialize(MF); + + // First try to eliminate (zext, lshift, rshift) and then + // try to eliminate zext. + bool ZExtSeqExist, ZExtExist; + ZExtSeqExist = eliminateZExtSeq(); + ZExtExist = eliminateZExt(); + return ZExtSeqExist || ZExtExist; + } +}; + +// Initialize class variables. +void SBFMIPeephole::initialize(MachineFunction &MFParm) { + MF = &MFParm; + MRI = &MF->getRegInfo(); + TII = MF->getSubtarget().getInstrInfo(); + LLVM_DEBUG(dbgs() << "*** SBF MachineSSA ZEXT Elim peephole pass ***\n\n"); +} + +bool SBFMIPeephole::isCopyFrom32Def(MachineInstr *CopyMI) +{ + MachineOperand &opnd = CopyMI->getOperand(1); + + if (!opnd.isReg()) + return false; + + // Return false if getting value from a 32bit physical register. + // Most likely, this physical register is aliased to + // function call return value or current function parameters. + Register Reg = opnd.getReg(); + if (!Register::isVirtualRegister(Reg)) + return false; + + if (MRI->getRegClass(Reg) == &SBF::GPRRegClass) + return false; + + MachineInstr *DefInsn = MRI->getVRegDef(Reg); + if (!isInsnFrom32Def(DefInsn)) + return false; + + return true; +} + +bool SBFMIPeephole::isPhiFrom32Def(MachineInstr *PhiMI) +{ + for (unsigned i = 1, e = PhiMI->getNumOperands(); i < e; i += 2) { + MachineOperand &opnd = PhiMI->getOperand(i); + + if (!opnd.isReg()) + return false; + + MachineInstr *PhiDef = MRI->getVRegDef(opnd.getReg()); + if (!PhiDef) + return false; + if (PhiDef->isPHI()) { + if (PhiInsns.find(PhiDef) != PhiInsns.end()) + return false; + PhiInsns.insert(PhiDef); + if (!isPhiFrom32Def(PhiDef)) + return false; + } + if (PhiDef->getOpcode() == SBF::COPY && !isCopyFrom32Def(PhiDef)) + return false; + } + + return true; +} + +// The \p DefInsn instruction defines a virtual register. +bool SBFMIPeephole::isInsnFrom32Def(MachineInstr *DefInsn) +{ + if (!DefInsn) + return false; + + if (DefInsn->isPHI()) { + if (PhiInsns.find(DefInsn) != PhiInsns.end()) + return false; + PhiInsns.insert(DefInsn); + if (!isPhiFrom32Def(DefInsn)) + return false; + } else if (DefInsn->getOpcode() == SBF::COPY) { + if (!isCopyFrom32Def(DefInsn)) + return false; + } + + return true; +} + +bool SBFMIPeephole::isMovFrom32Def(MachineInstr *MovMI) +{ + MachineInstr *DefInsn = MRI->getVRegDef(MovMI->getOperand(1).getReg()); + + LLVM_DEBUG(dbgs() << " Def of Mov Src:"); + LLVM_DEBUG(DefInsn->dump()); + + PhiInsns.clear(); + if (!isInsnFrom32Def(DefInsn)) + return false; + + LLVM_DEBUG(dbgs() << " One ZExt elim sequence identified.\n"); + + return true; +} + +bool SBFMIPeephole::eliminateZExtSeq() { + MachineInstr* ToErase = nullptr; + bool Eliminated = false; + + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + // If the previous instruction was marked for elimination, remove it now. + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + // Eliminate the 32-bit to 64-bit zero extension sequence when possible. + // + // MOV_32_64 rB, wA + // SLL_ri rB, rB, 32 + // SRL_ri rB, rB, 32 + if (MI.getOpcode() == SBF::SRL_ri && + MI.getOperand(2).getImm() == 32) { + Register DstReg = MI.getOperand(0).getReg(); + Register ShfReg = MI.getOperand(1).getReg(); + MachineInstr *SllMI = MRI->getVRegDef(ShfReg); + + LLVM_DEBUG(dbgs() << "Starting SRL found:"); + LLVM_DEBUG(MI.dump()); + + if (!SllMI || + SllMI->isPHI() || + SllMI->getOpcode() != SBF::SLL_ri || + SllMI->getOperand(2).getImm() != 32) + continue; + + LLVM_DEBUG(dbgs() << " SLL found:"); + LLVM_DEBUG(SllMI->dump()); + + MachineInstr *MovMI = MRI->getVRegDef(SllMI->getOperand(1).getReg()); + if (!MovMI || + MovMI->isPHI() || + MovMI->getOpcode() != SBF::MOV_32_64) + continue; + + LLVM_DEBUG(dbgs() << " Type cast Mov found:"); + LLVM_DEBUG(MovMI->dump()); + + Register SubReg = MovMI->getOperand(1).getReg(); + if (!isMovFrom32Def(MovMI)) { + LLVM_DEBUG(dbgs() + << " One ZExt elim sequence failed qualifying elim.\n"); + continue; + } + + BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(SBF::SUBREG_TO_REG), DstReg) + .addImm(0).addReg(SubReg).addImm(SBF::sub_32); + + SllMI->eraseFromParent(); + MovMI->eraseFromParent(); + // MI is the right shift, we can't erase it in it's own iteration. + // Mark it to ToErase, and erase in the next iteration. + ToErase = &MI; + ZExtElemNum++; + Eliminated = true; + } + } + } + + return Eliminated; +} + +bool SBFMIPeephole::eliminateZExt() { + MachineInstr* ToErase = nullptr; + bool Eliminated = false; + + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + // If the previous instruction was marked for elimination, remove it now. + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + if (MI.getOpcode() != SBF::MOV_32_64) + continue; + + // Eliminate MOV_32_64 if possible. + // MOV_32_64 rA, wB + // + // If wB has been zero extended, replace it with a SUBREG_TO_REG. + // This is to workaround SBF programs where pkt->{data, data_end} + // is encoded as u32, but actually the verifier populates them + // as 64bit pointer. The MOV_32_64 will zero out the top 32 bits. + LLVM_DEBUG(dbgs() << "Candidate MOV_32_64 instruction:"); + LLVM_DEBUG(MI.dump()); + + if (!isMovFrom32Def(&MI)) + continue; + + LLVM_DEBUG(dbgs() << "Removing the MOV_32_64 instruction\n"); + + Register dst = MI.getOperand(0).getReg(); + Register src = MI.getOperand(1).getReg(); + + // Build a SUBREG_TO_REG instruction. + BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(SBF::SUBREG_TO_REG), dst) + .addImm(0).addReg(src).addImm(SBF::sub_32); + + ToErase = &MI; + Eliminated = true; + } + } + + return Eliminated; +} + +} // end default namespace + +INITIALIZE_PASS(SBFMIPeephole, DEBUG_TYPE, + "SBF MachineSSA Peephole Optimization For ZEXT Eliminate", + false, false) + +char SBFMIPeephole::ID = 0; +FunctionPass* llvm::createSBFMIPeepholePass() { return new SBFMIPeephole(); } + +STATISTIC(RedundantMovElemNum, "Number of redundant moves eliminated"); + +namespace { + +struct SBFMIPreEmitPeephole : public MachineFunctionPass { + + static char ID; + MachineFunction *MF; + const TargetRegisterInfo *TRI; + + SBFMIPreEmitPeephole() : MachineFunctionPass(ID) { + initializeSBFMIPreEmitPeepholePass(*PassRegistry::getPassRegistry()); + } + +private: + // Initialize class variables. + void initialize(MachineFunction &MFParm); + + bool eliminateRedundantMov(); + +public: + + // Main entry point for this pass. + bool runOnMachineFunction(MachineFunction &MF) override { + if (skipFunction(MF.getFunction())) + return false; + + initialize(MF); + + return eliminateRedundantMov(); + } +}; + +// Initialize class variables. +void SBFMIPreEmitPeephole::initialize(MachineFunction &MFParm) { + MF = &MFParm; + TRI = MF->getSubtarget().getRegisterInfo(); + LLVM_DEBUG(dbgs() << "*** SBF PreEmit peephole pass ***\n\n"); +} + +bool SBFMIPreEmitPeephole::eliminateRedundantMov() { + MachineInstr* ToErase = nullptr; + bool Eliminated = false; + + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + // If the previous instruction was marked for elimination, remove it now. + if (ToErase) { + LLVM_DEBUG(dbgs() << " Redundant Mov Eliminated:"); + LLVM_DEBUG(ToErase->dump()); + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + // Eliminate identical move: + // + // MOV rA, rA + // + // Note that we cannot remove + // MOV_32_64 rA, wA + // MOV_rr_32 wA, wA + // as these two instructions having side effects, zeroing out + // top 32 bits of rA. + unsigned Opcode = MI.getOpcode(); + if (Opcode == SBF::MOV_rr) { + Register dst = MI.getOperand(0).getReg(); + Register src = MI.getOperand(1).getReg(); + + if (dst != src) + continue; + + ToErase = &MI; + RedundantMovElemNum++; + Eliminated = true; + } + } + } + + return Eliminated; +} + +} // end default namespace + +INITIALIZE_PASS(SBFMIPreEmitPeephole, "sbf-mi-pemit-peephole", + "SBF PreEmit Peephole Optimization", false, false) + +char SBFMIPreEmitPeephole::ID = 0; +FunctionPass* llvm::createSBFMIPreEmitPeepholePass() +{ + return new SBFMIPreEmitPeephole(); +} + +STATISTIC(TruncElemNum, "Number of truncation eliminated"); + +namespace { + +struct SBFMIPeepholeTruncElim : public MachineFunctionPass { + + static char ID; + const SBFInstrInfo *TII; + MachineFunction *MF; + MachineRegisterInfo *MRI; + + SBFMIPeepholeTruncElim() : MachineFunctionPass(ID) { + initializeSBFMIPeepholeTruncElimPass(*PassRegistry::getPassRegistry()); + } + +private: + // Initialize class variables. + void initialize(MachineFunction &MFParm); + + bool eliminateTruncSeq(); + +public: + + // Main entry point for this pass. + bool runOnMachineFunction(MachineFunction &MF) override { + if (skipFunction(MF.getFunction())) + return false; + + initialize(MF); + + return eliminateTruncSeq(); + } +}; + +static bool TruncSizeCompatible(int TruncSize, unsigned opcode) +{ + if (TruncSize == 1) + return opcode == SBF::LDB || opcode == SBF::LDB32; + + if (TruncSize == 2) + return opcode == SBF::LDH || opcode == SBF::LDH32; + + if (TruncSize == 4) + return opcode == SBF::LDW || opcode == SBF::LDW32; + + return false; +} + +// Initialize class variables. +void SBFMIPeepholeTruncElim::initialize(MachineFunction &MFParm) { + MF = &MFParm; + MRI = &MF->getRegInfo(); + TII = MF->getSubtarget().getInstrInfo(); + LLVM_DEBUG(dbgs() << "*** SBF MachineSSA TRUNC Elim peephole pass ***\n\n"); +} + +// Reg truncating is often the result of 8/16/32bit->64bit or +// 8/16bit->32bit conversion. If the reg value is loaded with +// masked byte width, the AND operation can be removed since +// SBF LOAD already has zero extension. +// +// This also solved a correctness issue. +// In SBF socket-related program, e.g., __sk_buff->{data, data_end} +// are 32-bit registers, but later on, kernel verifier will rewrite +// it with 64-bit value. Therefore, truncating the value after the +// load will result in incorrect code. +bool SBFMIPeepholeTruncElim::eliminateTruncSeq() { + MachineInstr* ToErase = nullptr; + bool Eliminated = false; + + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + // The second insn to remove if the eliminate candidate is a pair. + MachineInstr *MI2 = nullptr; + Register DstReg, SrcReg; + MachineInstr *DefMI; + int TruncSize = -1; + + // If the previous instruction was marked for elimination, remove it now. + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + // AND A, 0xFFFFFFFF will be turned into SLL/SRL pair due to immediate + // for SBF ANDI is i32, and this case only happens on ALU64. + if (MI.getOpcode() == SBF::SRL_ri && + MI.getOperand(2).getImm() == 32) { + SrcReg = MI.getOperand(1).getReg(); + if (!MRI->hasOneNonDBGUse(SrcReg)) + continue; + + MI2 = MRI->getVRegDef(SrcReg); + DstReg = MI.getOperand(0).getReg(); + + if (!MI2 || + MI2->getOpcode() != SBF::SLL_ri || + MI2->getOperand(2).getImm() != 32) + continue; + + // Update SrcReg. + SrcReg = MI2->getOperand(1).getReg(); + DefMI = MRI->getVRegDef(SrcReg); + if (DefMI) + TruncSize = 4; + } else if (MI.getOpcode() == SBF::AND_ri || + MI.getOpcode() == SBF::AND_ri_32) { + SrcReg = MI.getOperand(1).getReg(); + DstReg = MI.getOperand(0).getReg(); + DefMI = MRI->getVRegDef(SrcReg); + + if (!DefMI) + continue; + + int64_t imm = MI.getOperand(2).getImm(); + if (imm == 0xff) + TruncSize = 1; + else if (imm == 0xffff) + TruncSize = 2; + } + + if (TruncSize == -1) + continue; + + // The definition is PHI node, check all inputs. + if (DefMI->isPHI()) { + bool CheckFail = false; + + for (unsigned i = 1, e = DefMI->getNumOperands(); i < e; i += 2) { + MachineOperand &opnd = DefMI->getOperand(i); + if (!opnd.isReg()) { + CheckFail = true; + break; + } + + MachineInstr *PhiDef = MRI->getVRegDef(opnd.getReg()); + if (!PhiDef || PhiDef->isPHI() || + !TruncSizeCompatible(TruncSize, PhiDef->getOpcode())) { + CheckFail = true; + break; + } + } + + if (CheckFail) + continue; + } else if (!TruncSizeCompatible(TruncSize, DefMI->getOpcode())) { + continue; + } + + BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(SBF::MOV_rr), DstReg) + .addReg(SrcReg); + + if (MI2) + MI2->eraseFromParent(); + + // Mark it to ToErase, and erase in the next iteration. + ToErase = &MI; + TruncElemNum++; + Eliminated = true; + } + } + + return Eliminated; +} + +} // end default namespace + +INITIALIZE_PASS(SBFMIPeepholeTruncElim, "sbf-mi-trunc-elim", + "SBF MachineSSA Peephole Optimization For TRUNC Eliminate", + false, false) + +char SBFMIPeepholeTruncElim::ID = 0; +FunctionPass* llvm::createSBFMIPeepholeTruncElimPass() +{ + return new SBFMIPeepholeTruncElim(); +} diff --git a/llvm/lib/Target/SBF/SBFMISimplifyPatchable.cpp b/llvm/lib/Target/SBF/SBFMISimplifyPatchable.cpp new file mode 100644 index 0000000000000..50520a613b284 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFMISimplifyPatchable.cpp @@ -0,0 +1,312 @@ +//===----- SBFMISimplifyPatchable.cpp - MI Simplify Patchable Insts -------===// +// +// 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 pass targets a subset of instructions like below +// ld_imm64 r1, @global +// ldd r2, r1, 0 +// add r3, struct_base_reg, r2 +// +// Here @global should represent an AMA (abstruct member access). +// Such an access is subject to bpf load time patching. After this pass, the +// code becomes +// ld_imm64 r1, @global +// add r3, struct_base_reg, r1 +// +// Eventually, at BTF output stage, a relocation record will be generated +// for ld_imm64 which should be replaced later by bpf loader: +// r1 = +// add r3, struct_base_reg, r1 +// +// This pass also removes the intermediate load generated in IR pass for +// __builtin_btf_type_id() intrinsic. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFCORE.h" +#include "SBFInstrInfo.h" +#include "SBFTargetMachine.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "sbf-mi-simplify-patchable" + +namespace { + +struct SBFMISimplifyPatchable : public MachineFunctionPass { + + static char ID; + const SBFInstrInfo *TII; + MachineFunction *MF; + + SBFMISimplifyPatchable() : MachineFunctionPass(ID) { + initializeSBFMISimplifyPatchablePass(*PassRegistry::getPassRegistry()); + } + +private: + // Initialize class variables. + void initialize(MachineFunction &MFParm); + + bool removeLD(); + void processCandidate(MachineRegisterInfo *MRI, MachineBasicBlock &MBB, + MachineInstr &MI, Register &SrcReg, Register &DstReg, + const GlobalValue *GVal, bool IsAma); + void processDstReg(MachineRegisterInfo *MRI, Register &DstReg, + Register &SrcReg, const GlobalValue *GVal, + bool doSrcRegProp, bool IsAma); + void processInst(MachineRegisterInfo *MRI, MachineInstr *Inst, + MachineOperand *RelocOp, const GlobalValue *GVal); + void checkADDrr(MachineRegisterInfo *MRI, MachineOperand *RelocOp, + const GlobalValue *GVal); + void checkShift(MachineRegisterInfo *MRI, MachineBasicBlock &MBB, + MachineOperand *RelocOp, const GlobalValue *GVal, + unsigned Opcode); + +public: + // Main entry point for this pass. + bool runOnMachineFunction(MachineFunction &MF) override { + if (skipFunction(MF.getFunction())) + return false; + + initialize(MF); + return removeLD(); + } +}; + +// Initialize class variables. +void SBFMISimplifyPatchable::initialize(MachineFunction &MFParm) { + MF = &MFParm; + TII = MF->getSubtarget().getInstrInfo(); + LLVM_DEBUG(dbgs() << "*** SBF simplify patchable insts pass ***\n\n"); +} + +void SBFMISimplifyPatchable::checkADDrr(MachineRegisterInfo *MRI, + MachineOperand *RelocOp, const GlobalValue *GVal) { + const MachineInstr *Inst = RelocOp->getParent(); + const MachineOperand *Op1 = &Inst->getOperand(1); + const MachineOperand *Op2 = &Inst->getOperand(2); + const MachineOperand *BaseOp = (RelocOp == Op1) ? Op2 : Op1; + + // Go through all uses of %1 as in %1 = ADD_rr %2, %3 + const MachineOperand Op0 = Inst->getOperand(0); + for (MachineOperand &MO : + llvm::make_early_inc_range(MRI->use_operands(Op0.getReg()))) { + // The candidate needs to have a unique definition. + if (!MRI->getUniqueVRegDef(MO.getReg())) + continue; + + MachineInstr *DefInst = MO.getParent(); + unsigned Opcode = DefInst->getOpcode(); + unsigned COREOp; + if (Opcode == SBF::LDB || Opcode == SBF::LDH || Opcode == SBF::LDW || + Opcode == SBF::LDD || Opcode == SBF::STB || Opcode == SBF::STH || + Opcode == SBF::STW || Opcode == SBF::STD) + COREOp = SBF::CORE_MEM; + else if (Opcode == SBF::LDB32 || Opcode == SBF::LDH32 || + Opcode == SBF::LDW32 || Opcode == SBF::STB32 || + Opcode == SBF::STH32 || Opcode == SBF::STW32) + COREOp = SBF::CORE_ALU32_MEM; + else + continue; + + // It must be a form of %2 = *(type *)(%1 + 0) or *(type *)(%1 + 0) = %2. + const MachineOperand &ImmOp = DefInst->getOperand(2); + if (!ImmOp.isImm() || ImmOp.getImm() != 0) + continue; + + // Reject the form: + // %1 = ADD_rr %2, %3 + // *(type *)(%2 + 0) = %1 + if (Opcode == SBF::STB || Opcode == SBF::STH || Opcode == SBF::STW || + Opcode == SBF::STD || Opcode == SBF::STB32 || Opcode == SBF::STH32 || + Opcode == SBF::STW32) { + const MachineOperand &Opnd = DefInst->getOperand(0); + if (Opnd.isReg() && Opnd.getReg() == MO.getReg()) + continue; + } + + BuildMI(*DefInst->getParent(), *DefInst, DefInst->getDebugLoc(), TII->get(COREOp)) + .add(DefInst->getOperand(0)).addImm(Opcode).add(*BaseOp) + .addGlobalAddress(GVal); + DefInst->eraseFromParent(); + } +} + +void SBFMISimplifyPatchable::checkShift(MachineRegisterInfo *MRI, + MachineBasicBlock &MBB, MachineOperand *RelocOp, const GlobalValue *GVal, + unsigned Opcode) { + // Relocation operand should be the operand #2. + MachineInstr *Inst = RelocOp->getParent(); + if (RelocOp != &Inst->getOperand(2)) + return; + + BuildMI(MBB, *Inst, Inst->getDebugLoc(), TII->get(SBF::CORE_SHIFT)) + .add(Inst->getOperand(0)).addImm(Opcode) + .add(Inst->getOperand(1)).addGlobalAddress(GVal); + Inst->eraseFromParent(); +} + +void SBFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI, + MachineBasicBlock &MBB, MachineInstr &MI, Register &SrcReg, + Register &DstReg, const GlobalValue *GVal, bool IsAma) { + if (MRI->getRegClass(DstReg) == &SBF::GPR32RegClass) { + if (IsAma) { + // We can optimize such a pattern: + // %1:gpr = LD_imm64 @"llvm.s:0:4$0:2" + // %2:gpr32 = LDW32 %1:gpr, 0 + // %3:gpr = SUBREG_TO_REG 0, %2:gpr32, %subreg.sub_32 + // %4:gpr = ADD_rr %0:gpr, %3:gpr + // or similar patterns below for non-alu32 case. + auto Begin = MRI->use_begin(DstReg), End = MRI->use_end(); + decltype(End) NextI; + for (auto I = Begin; I != End; I = NextI) { + NextI = std::next(I); + if (!MRI->getUniqueVRegDef(I->getReg())) + continue; + + unsigned Opcode = I->getParent()->getOpcode(); + if (Opcode == SBF::SUBREG_TO_REG) { + Register TmpReg = I->getParent()->getOperand(0).getReg(); + processDstReg(MRI, TmpReg, DstReg, GVal, false, IsAma); + } + } + } + + BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(SBF::COPY), DstReg) + .addReg(SrcReg, 0, SBF::sub_32); + return; + } + + // All uses of DstReg replaced by SrcReg + processDstReg(MRI, DstReg, SrcReg, GVal, true, IsAma); +} + +void SBFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI, + Register &DstReg, Register &SrcReg, const GlobalValue *GVal, + bool doSrcRegProp, bool IsAma) { + auto Begin = MRI->use_begin(DstReg), End = MRI->use_end(); + decltype(End) NextI; + for (auto I = Begin; I != End; I = NextI) { + NextI = std::next(I); + if (doSrcRegProp) + I->setReg(SrcReg); + + // The candidate needs to have a unique definition. + if (IsAma && MRI->getUniqueVRegDef(I->getReg())) + processInst(MRI, I->getParent(), &*I, GVal); + } +} + +// Check to see whether we could do some optimization +// to attach relocation to downstream dependent instructions. +// Two kinds of patterns are recognized below: +// Pattern 1: +// %1 = LD_imm64 @"llvm.b:0:4$0:1" <== patch_imm = 4 +// %2 = LDD %1, 0 <== this insn will be removed +// %3 = ADD_rr %0, %2 +// %4 = LDW[32] %3, 0 OR STW[32] %4, %3, 0 +// The `%4 = ...` will be transformed to +// CORE_[ALU32_]MEM(%4, mem_opcode, %0, @"llvm.b:0:4$0:1") +// and later on, BTF emit phase will translate to +// %4 = LDW[32] %0, 4 STW[32] %4, %0, 4 +// and attach a relocation to it. +// Pattern 2: +// %15 = LD_imm64 @"llvm.t:5:63$0:2" <== relocation type 5 +// %16 = LDD %15, 0 <== this insn will be removed +// %17 = SRA_rr %14, %16 +// The `%17 = ...` will be transformed to +// %17 = CORE_SHIFT(SRA_ri, %14, @"llvm.t:5:63$0:2") +// and later on, BTF emit phase will translate to +// %r4 = SRA_ri %r4, 63 +void SBFMISimplifyPatchable::processInst(MachineRegisterInfo *MRI, + MachineInstr *Inst, MachineOperand *RelocOp, const GlobalValue *GVal) { + unsigned Opcode = Inst->getOpcode(); + if (Opcode == SBF::ADD_rr) + checkADDrr(MRI, RelocOp, GVal); + else if (Opcode == SBF::SLL_rr) + checkShift(MRI, *Inst->getParent(), RelocOp, GVal, SBF::SLL_ri); + else if (Opcode == SBF::SRA_rr) + checkShift(MRI, *Inst->getParent(), RelocOp, GVal, SBF::SRA_ri); + else if (Opcode == SBF::SRL_rr) + checkShift(MRI, *Inst->getParent(), RelocOp, GVal, SBF::SRL_ri); +} + +/// Remove unneeded Load instructions. +bool SBFMISimplifyPatchable::removeLD() { + MachineRegisterInfo *MRI = &MF->getRegInfo(); + MachineInstr *ToErase = nullptr; + bool Changed = false; + + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + if (ToErase) { + ToErase->eraseFromParent(); + ToErase = nullptr; + } + + // Ensure the register format is LOAD , , 0 + if (MI.getOpcode() != SBF::LDD && MI.getOpcode() != SBF::LDW && + MI.getOpcode() != SBF::LDH && MI.getOpcode() != SBF::LDB && + MI.getOpcode() != SBF::LDW32 && MI.getOpcode() != SBF::LDH32 && + MI.getOpcode() != SBF::LDB32) + continue; + + if (!MI.getOperand(0).isReg() || !MI.getOperand(1).isReg()) + continue; + + if (!MI.getOperand(2).isImm() || MI.getOperand(2).getImm()) + continue; + + Register DstReg = MI.getOperand(0).getReg(); + Register SrcReg = MI.getOperand(1).getReg(); + + MachineInstr *DefInst = MRI->getUniqueVRegDef(SrcReg); + if (!DefInst) + continue; + + if (DefInst->getOpcode() != SBF::LD_imm64) + continue; + + const MachineOperand &MO = DefInst->getOperand(1); + if (!MO.isGlobal()) + continue; + + const GlobalValue *GVal = MO.getGlobal(); + auto *GVar = dyn_cast(GVal); + if (!GVar) + continue; + + // Global variables representing structure offset or type id. + bool IsAma = false; + if (GVar->hasAttribute(SBFCoreSharedInfo::AmaAttr)) + IsAma = true; + else if (!GVar->hasAttribute(SBFCoreSharedInfo::TypeIdAttr)) + continue; + + processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal, IsAma); + + ToErase = &MI; + Changed = true; + } + } + + return Changed; +} + +} // namespace + +INITIALIZE_PASS(SBFMISimplifyPatchable, DEBUG_TYPE, + "SBF PreEmit SimplifyPatchable", false, false) + +char SBFMISimplifyPatchable::ID = 0; +FunctionPass *llvm::createSBFMISimplifyPatchablePass() { + return new SBFMISimplifyPatchable(); +} diff --git a/llvm/lib/Target/SBF/SBFPreserveDIType.cpp b/llvm/lib/Target/SBF/SBFPreserveDIType.cpp new file mode 100644 index 0000000000000..59a7ec2c51d2e --- /dev/null +++ b/llvm/lib/Target/SBF/SBFPreserveDIType.cpp @@ -0,0 +1,153 @@ +//===----------- SBFPreserveDIType.cpp - Preserve DebugInfo Types ---------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Preserve Debuginfo types encoded in __builtin_btf_type_id() metadata. +// +//===----------------------------------------------------------------------===// + +#include "SBF.h" +#include "SBFCORE.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +#define DEBUG_TYPE "sbf-preserve-di-type" + +namespace llvm { +constexpr StringRef SBFCoreSharedInfo::TypeIdAttr; +} // namespace llvm + +using namespace llvm; + +namespace { + +static bool SBFPreserveDITypeImpl(Function &F) { + LLVM_DEBUG(dbgs() << "********** preserve debuginfo type **********\n"); + + Module *M = F.getParent(); + + // Bail out if no debug info. + if (M->debug_compile_units().empty()) + return false; + + std::vector PreserveDITypeCalls; + + for (auto &BB : F) { + for (auto &I : BB) { + auto *Call = dyn_cast(&I); + if (!Call) + continue; + + const auto *GV = dyn_cast(Call->getCalledOperand()); + if (!GV) + continue; + + if (GV->getName().startswith("llvm.bpf.btf.type.id")) { + if (!Call->getMetadata(LLVMContext::MD_preserve_access_index)) + report_fatal_error( + "Missing metadata for llvm.bpf.btf.type.id intrinsic"); + PreserveDITypeCalls.push_back(Call); + } + } + } + + if (PreserveDITypeCalls.empty()) + return false; + + std::string BaseName = "llvm.btf_type_id."; + static int Count = 0; + for (auto Call : PreserveDITypeCalls) { + const ConstantInt *Flag = dyn_cast(Call->getArgOperand(1)); + assert(Flag); + uint64_t FlagValue = Flag->getValue().getZExtValue(); + + if (FlagValue >= SBFCoreSharedInfo::MAX_BTF_TYPE_ID_FLAG) + report_fatal_error("Incorrect flag for llvm.bpf.btf.type.id intrinsic"); + + MDNode *MD = Call->getMetadata(LLVMContext::MD_preserve_access_index); + + uint32_t Reloc; + if (FlagValue == SBFCoreSharedInfo::BTF_TYPE_ID_LOCAL_RELOC) { + Reloc = SBFCoreSharedInfo::BTF_TYPE_ID_LOCAL; + } else { + Reloc = SBFCoreSharedInfo::BTF_TYPE_ID_REMOTE; + DIType *Ty = cast(MD); + while (auto *DTy = dyn_cast(Ty)) { + unsigned Tag = DTy->getTag(); + if (Tag != dwarf::DW_TAG_const_type && + Tag != dwarf::DW_TAG_volatile_type) + break; + Ty = DTy->getBaseType(); + } + + if (Ty->getName().empty()) { + if (isa(Ty)) + report_fatal_error( + "SubroutineType not supported for BTF_TYPE_ID_REMOTE reloc"); + else + report_fatal_error("Empty type name for BTF_TYPE_ID_REMOTE reloc"); + } + MD = Ty; + } + + BasicBlock *BB = Call->getParent(); + IntegerType *VarType = Type::getInt64Ty(BB->getContext()); + std::string GVName = + BaseName + std::to_string(Count) + "$" + std::to_string(Reloc); + GlobalVariable *GV = new GlobalVariable( + *M, VarType, false, GlobalVariable::ExternalLinkage, nullptr, GVName); + GV->addAttribute(SBFCoreSharedInfo::TypeIdAttr); + GV->setMetadata(LLVMContext::MD_preserve_access_index, MD); + + // Load the global variable which represents the type info. + auto *LDInst = + new LoadInst(Type::getInt64Ty(BB->getContext()), GV, "", Call); + Instruction *PassThroughInst = + SBFCoreSharedInfo::insertPassThrough(M, BB, LDInst, Call); + Call->replaceAllUsesWith(PassThroughInst); + Call->eraseFromParent(); + Count++; + } + + return true; +} + +class SBFPreserveDIType final : public FunctionPass { + bool runOnFunction(Function &F) override; + +public: + static char ID; + SBFPreserveDIType() : FunctionPass(ID) {} +}; +} // End anonymous namespace + +char SBFPreserveDIType::ID = 0; +INITIALIZE_PASS(SBFPreserveDIType, DEBUG_TYPE, "SBF Preserve Debuginfo Type", + false, false) + +FunctionPass *llvm::createSBFPreserveDIType() { + return new SBFPreserveDIType(); +} + +bool SBFPreserveDIType::runOnFunction(Function &F) { + return SBFPreserveDITypeImpl(F); +} + +PreservedAnalyses SBFPreserveDITypePass::run(Function &F, + FunctionAnalysisManager &AM) { + return SBFPreserveDITypeImpl(F) ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/SBF/SBFRegisterInfo.cpp b/llvm/lib/Target/SBF/SBFRegisterInfo.cpp new file mode 100644 index 0000000000000..0cd475a829a90 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFRegisterInfo.cpp @@ -0,0 +1,150 @@ +//===-- SBFRegisterInfo.cpp - SBF Register Information ----------*- 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 the SBF implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "SBFRegisterInfo.h" +#include "SBF.h" +#include "SBFSubtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/Support/ErrorHandling.h" + +#define GET_REGINFO_TARGET_DESC +#include "SBFGenRegisterInfo.inc" +using namespace llvm; + +unsigned SBFRegisterInfo::FrameLength = 512; + +SBFRegisterInfo::SBFRegisterInfo() + : SBFGenRegisterInfo(SBF::R0) {} + +const MCPhysReg * +SBFRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + return CSR_SaveList; +} + +BitVector SBFRegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + markSuperRegs(Reserved, SBF::W10); // [W|R]10 is read only frame pointer + markSuperRegs(Reserved, SBF::W11); // [W|R]11 is pseudo stack pointer + return Reserved; +} + +static void WarnSize(int Offset, MachineFunction &MF, DebugLoc& DL) +{ + static Function *OldMF = nullptr; + if (&(MF.getFunction()) == OldMF) { + return; + } + OldMF = &(MF.getFunction()); + int MaxOffset = -1 * SBFRegisterInfo::FrameLength; + if (Offset <= MaxOffset) { + if (MF.getSubtarget().isSolana()) { + dbgs() << "Error:"; + if (DL) { + dbgs() << " "; + DL.print(dbgs()); + } + dbgs() << " Function " << MF.getFunction().getName() + << " Stack offset of " << -Offset << " exceeded max offset of " + << -MaxOffset << " by " << MaxOffset - Offset + << " bytes, please minimize large stack variables\n"; + } else { + DiagnosticInfoUnsupported DiagStackSize( + MF.getFunction(), + "SBF stack limit of 512 bytes is exceeded. " + "Please move large on stack variables into SBF per-cpu array map.\n", + DL, DiagnosticSeverity::DS_Error); + MF.getFunction().getContext().diagnose(DiagStackSize); + } + } +} + +void SBFRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + assert(SPAdj == 0 && "Unexpected"); + + unsigned i = 0; + MachineInstr &MI = *II; + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + DebugLoc DL = MI.getDebugLoc(); + + if (!DL) + /* try harder to get some debug loc */ + for (auto &I : MBB) + if (I.getDebugLoc()) { + DL = I.getDebugLoc().getFnDebugLoc(); + break; + } + + while (!MI.getOperand(i).isFI()) { + ++i; + assert(i < MI.getNumOperands() && "Instr doesn't have FrameIndex operand!"); + } + + Register FrameReg = getFrameRegister(MF); + int FrameIndex = MI.getOperand(i).getIndex(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + + if (MI.getOpcode() == SBF::MOV_rr) { + int Offset = MF.getFrameInfo().getObjectOffset(FrameIndex); + + if (!MF.getSubtarget().getHasDynamicFrames()) { + WarnSize(Offset, MF, DL); + } + MI.getOperand(i).ChangeToRegister(FrameReg, false); + Register reg = MI.getOperand(i - 1).getReg(); + BuildMI(MBB, ++II, DL, TII.get(SBF::ADD_ri), reg) + .addReg(reg) + .addImm(Offset); + return; + } + + int Offset = MF.getFrameInfo().getObjectOffset(FrameIndex) + + MI.getOperand(i + 1).getImm(); + + if (!isInt<32>(Offset)) + llvm_unreachable("bug in frame offset"); + + if (!MF.getSubtarget().getHasDynamicFrames()) { + WarnSize(Offset, MF, DL); + } + + if (MI.getOpcode() == SBF::FI_ri) { + // architecture does not really support FI_ri, replace it with + // MOV_rr , frame_reg + // ADD_ri , imm + Register reg = MI.getOperand(i - 1).getReg(); + + BuildMI(MBB, ++II, DL, TII.get(SBF::MOV_rr), reg) + .addReg(FrameReg); + BuildMI(MBB, II, DL, TII.get(SBF::ADD_ri), reg) + .addReg(reg) + .addImm(Offset); + + // Remove FI_ri instruction + MI.eraseFromParent(); + } else { + MI.getOperand(i).ChangeToRegister(FrameReg, false); + MI.getOperand(i + 1).ChangeToImmediate(Offset); + } +} + +Register SBFRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + return SBF::R10; +} diff --git a/llvm/lib/Target/SBF/SBFRegisterInfo.h b/llvm/lib/Target/SBF/SBFRegisterInfo.h new file mode 100644 index 0000000000000..bfe6741aacf68 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFRegisterInfo.h @@ -0,0 +1,40 @@ +//===-- SBFRegisterInfo.h - SBF Register Information Impl -------*- 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 the SBF implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFREGISTERINFO_H +#define LLVM_LIB_TARGET_SBF_SBFREGISTERINFO_H + +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "SBFGenRegisterInfo.inc" + +namespace llvm { + +struct SBFRegisterInfo : public SBFGenRegisterInfo { + static unsigned FrameLength; + + SBFRegisterInfo(); + + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + + BitVector getReservedRegs(const MachineFunction &MF) const override; + + void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + Register getFrameRegister(const MachineFunction &MF) const override; +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/SBFRegisterInfo.td b/llvm/lib/Target/SBF/SBFRegisterInfo.td new file mode 100644 index 0000000000000..7696b0b973159 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFRegisterInfo.td @@ -0,0 +1,51 @@ +//===-- SBFRegisterInfo.td - SBF Register defs -------------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Declarations that describe the SBF register file +//===----------------------------------------------------------------------===// + +let Namespace = "SBF" in { + def sub_32 : SubRegIndex<32>; +} + +class Wi Enc, string n> : Register { + let HWEncoding = Enc; + let Namespace = "SBF"; +} + +// Registers are identified with 4-bit ID numbers. +// Ri - 64-bit integer registers +class Ri Enc, string n, list subregs> + : RegisterWithSubRegs { + let HWEncoding = Enc; + let Namespace = "SBF"; + let SubRegIndices = [sub_32]; +} + +foreach I = 0-11 in { + // 32-bit Integer (alias to low part of 64-bit register). + def W#I : Wi, DwarfRegNum<[I]>; + // 64-bit Integer registers + def R#I : Ri("W"#I)]>, DwarfRegNum<[I]>; +} + +// Register classes. +def GPR32 : RegisterClass<"SBF", [i32], 64, (add + (sequence "W%u", 1, 9), + W0, // Return value + W11, // Stack Ptr + W10 // Frame Ptr +)>; + +def GPR : RegisterClass<"SBF", [i64], 64, (add + (sequence "R%u", 1, 9), + R0, // Return value + R11, // Stack Ptr + R10 // Frame Ptr +)>; diff --git a/llvm/lib/Target/SBF/SBFSelectionDAGInfo.cpp b/llvm/lib/Target/SBF/SBFSelectionDAGInfo.cpp new file mode 100644 index 0000000000000..0a5d3aab10462 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFSelectionDAGInfo.cpp @@ -0,0 +1,42 @@ +//===-- SBFSelectionDAGInfo.cpp - SBF SelectionDAG Info -------------------===// +// +// 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 SBFSelectionDAGInfo class. +// +//===----------------------------------------------------------------------===// + +#include "SBFTargetMachine.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/IR/DerivedTypes.h" +using namespace llvm; + +#define DEBUG_TYPE "sbf-selectiondag-info" + +SDValue SBFSelectionDAGInfo::EmitTargetCodeForMemcpy( + SelectionDAG &DAG, const SDLoc &dl, SDValue Chain, SDValue Dst, SDValue Src, + SDValue Size, Align Alignment, bool isVolatile, bool AlwaysInline, + MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const { + // Requires the copy size to be a constant. + ConstantSDNode *ConstantSize = dyn_cast(Size); + if (!ConstantSize) + return SDValue(); + + unsigned CopyLen = ConstantSize->getZExtValue(); + unsigned StoresNumEstimate = alignTo(CopyLen, Alignment) >> Log2(Alignment); + // Impose the same copy length limit as MaxStoresPerMemcpy. + if (StoresNumEstimate > getCommonMaxStoresPerMemFunc()) + return SDValue(); + + SDVTList VTs = DAG.getVTList(MVT::Other, MVT::Glue); + + Dst = DAG.getNode(SBFISD::MEMCPY, dl, VTs, Chain, Dst, Src, + DAG.getConstant(CopyLen, dl, MVT::i64), + DAG.getConstant(Alignment.value(), dl, MVT::i64)); + + return Dst.getValue(0); +} diff --git a/llvm/lib/Target/SBF/SBFSelectionDAGInfo.h b/llvm/lib/Target/SBF/SBFSelectionDAGInfo.h new file mode 100644 index 0000000000000..104246c6718b8 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFSelectionDAGInfo.h @@ -0,0 +1,44 @@ +//===-- SBFSelectionDAGInfo.h - SBF SelectionDAG Info -----------*- 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 defines the SBF subclass for SelectionDAGTargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFSELECTIONDAGINFO_H +#define LLVM_LIB_TARGET_SBF_SBFSELECTIONDAGINFO_H + +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" + +namespace llvm { + +class SBFSelectionDAGInfo : public SelectionDAGTargetInfo { +public: + SBFSelectionDAGInfo() : isSolana(false) {} + SDValue EmitTargetCodeForMemcpy(SelectionDAG &DAG, const SDLoc &dl, + SDValue Chain, SDValue Dst, SDValue Src, + SDValue Size, Align Alignment, + bool isVolatile, bool AlwaysInline, + MachinePointerInfo DstPtrInfo, + MachinePointerInfo SrcPtrInfo) const override; + + unsigned getCommonMaxStoresPerMemFunc() const { + return isSolana ? 4 : 128; + } + void setSolanaFlag(bool value) const { + isSolana = value; + } + +private: + mutable bool isSolana; + +}; + +} + +#endif diff --git a/llvm/lib/Target/SBF/SBFSubtarget.cpp b/llvm/lib/Target/SBF/SBFSubtarget.cpp new file mode 100644 index 0000000000000..e49f6a31c7ba1 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFSubtarget.cpp @@ -0,0 +1,80 @@ +//===-- SBFSubtarget.cpp - SBF Subtarget Information ----------------------===// +// +// 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 SBF specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "SBFSubtarget.h" +#include "SBF.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Host.h" + +using namespace llvm; + +#define DEBUG_TYPE "sbf-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "SBFGenSubtargetInfo.inc" + +void SBFSubtarget::anchor() {} + +SBFSubtarget &SBFSubtarget::initializeSubtargetDependencies(const Triple &TT, + StringRef CPU, + StringRef FS) { + initializeEnvironment(TT); + initSubtargetFeatures(CPU, FS); + return *this; +} + +void SBFSubtarget::initializeEnvironment(const Triple &TT) { + assert(TT.getArch() == Triple::sbf && "expected Triple::sbf"); + IsSolana = true; + HasJmpExt = false; + HasJmp32 = false; + HasAlu32 = false; + HasDynamicFrames = false; + HasSdiv = false; + UseDwarfRIS = false; +} + +void SBFSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) { + // TODO: jle: This invokes an x86 linux kernel call to probe the eBPF ISA + // revision in use. This doesn't seem to make much sense for SBF where we + // execute on a VM inside of the Solana runtime, not the VM in the linux + // kernel. + if (CPU == "probe") + CPU = sys::detail::getHostCPUNameForBPF(); + + ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS); + + if (CPU == "v2") { + HasJmpExt = true; + } + + if (CPU == "v3") { + HasJmpExt = true; + HasJmp32 = true; + HasAlu32 = true; + } + + if (CPU == "sbfv2" && !HasDynamicFrames) { + report_fatal_error("sbfv2 requires dynamic-frames\n", false); + } +} + +SBFSubtarget::SBFSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM) + : SBFGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS), InstrInfo(), + FrameLowering(initializeSubtargetDependencies(TT, CPU, FS)), + TLInfo(TM, *this) { + assert(TT.getArch() == Triple::sbf && "expected Triple::sbf"); + IsSolana = true; + TSInfo.setSolanaFlag(IsSolana); +} diff --git a/llvm/lib/Target/SBF/SBFSubtarget.h b/llvm/lib/Target/SBF/SBFSubtarget.h new file mode 100644 index 0000000000000..2334bbb1bb89b --- /dev/null +++ b/llvm/lib/Target/SBF/SBFSubtarget.h @@ -0,0 +1,110 @@ +//===-- SBFSubtarget.h - Define Subtarget for the SBF -----------*- 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 SBF specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFSUBTARGET_H +#define LLVM_LIB_TARGET_SBF_SBFSUBTARGET_H + +#include "SBFFrameLowering.h" +#include "SBFISelLowering.h" +#include "SBFInstrInfo.h" +#include "SBFSelectionDAGInfo.h" +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/Target/TargetMachine.h" + +#define GET_SUBTARGETINFO_HEADER +#include "SBFGenSubtargetInfo.inc" + +namespace llvm { +class StringRef; + +class SBFSubtarget : public SBFGenSubtargetInfo { + virtual void anchor(); + SBFInstrInfo InstrInfo; + SBFFrameLowering FrameLowering; + SBFTargetLowering TLInfo; + SBFSelectionDAGInfo TSInfo; + +private: + void initializeEnvironment(const Triple &TT); + void initSubtargetFeatures(StringRef CPU, StringRef FS); + bool probeJmpExt(); + +protected: + // unused + bool isDummyMode; + + // whether to enable Solana extensions. + bool IsSolana; + + // whether the cpu supports jmp ext + bool HasJmpExt; + + // whether the cpu supports jmp32 ext. + // NOTE: jmp32 is not enabled when alu32 enabled. + bool HasJmp32; + + // whether the cpu supports alu32 instructions. + bool HasAlu32; + + // whether we should use fixed or dynamic frames + bool HasDynamicFrames; + + // Relocate FK_Data_8 fixups as R_SBF_64_ABS64 + bool UseRelocAbs64; + + // whether the cpu supports native SBF_SDIV + bool HasSdiv; + + // Not used for anything, just set by the static-syscalls marker feature. + bool HasStaticSyscalls; + + // whether we should enable MCAsmInfo DwarfUsesRelocationsAcrossSections + bool UseDwarfRIS; + +public: + // This constructor initializes the data members to match that + // of the specified triple. + SBFSubtarget(const Triple &TT, const std::string &CPU, const std::string &FS, + const TargetMachine &TM); + + SBFSubtarget &initializeSubtargetDependencies(const Triple &TT, StringRef CPU, StringRef FS); + + // ParseSubtargetFeatures - Parses features string setting specified + // subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS); + bool isSolana() const { return IsSolana; } + bool getHasJmpExt() const { return HasJmpExt; } + bool getHasJmp32() const { return HasJmp32; } + bool getHasAlu32() const { return HasAlu32; } + bool getHasDynamicFrames() const { return HasDynamicFrames; } + bool getHasSdiv() const { return HasSdiv; } + bool getUseDwarfRIS() const { return UseDwarfRIS; } + + const SBFInstrInfo *getInstrInfo() const override { return &InstrInfo; } + const SBFFrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const SBFTargetLowering *getTargetLowering() const override { + return &TLInfo; + } + const SBFSelectionDAGInfo *getSelectionDAGInfo() const override { + return &TSInfo; + } + const TargetRegisterInfo *getRegisterInfo() const override { + return &InstrInfo.getRegisterInfo(); + } +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/SBF/SBFTargetMachine.cpp b/llvm/lib/Target/SBF/SBFTargetMachine.cpp new file mode 100644 index 0000000000000..2d28f3745d694 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFTargetMachine.cpp @@ -0,0 +1,184 @@ +//===-- SBFTargetMachine.cpp - Define TargetMachine for SBF ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implements the info about SBF target spec. +// +//===----------------------------------------------------------------------===// + +#include "SBFTargetMachine.h" +#include "SBF.h" +#include "SBFTargetTransformInfo.h" +#include "MCTargetDesc/SBFMCAsmInfo.h" +#include "TargetInfo/SBFTargetInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/PassManager.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/SimplifyCFG.h" +#include "llvm/Transforms/Utils/SimplifyCFGOptions.h" +using namespace llvm; + +static cl:: +opt DisableMIPeephole("disable-sbf-peephole", cl::Hidden, + cl::desc("Disable machine peepholes for SBF")); + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSBFTarget() { + // Register the target. + RegisterTargetMachine XX(getTheSBFXTarget()); + + PassRegistry &PR = *PassRegistry::getPassRegistry(); + initializeSBFAbstractMemberAccessLegacyPassPass(PR); + initializeSBFPreserveDITypePass(PR); + initializeSBFIRPeepholePass(PR); + initializeSBFAdjustOptPass(PR); + initializeSBFCheckAndAdjustIRPass(PR); + initializeSBFMIPeepholePass(PR); + initializeSBFMIPeepholeTruncElimPass(PR); +} + +// DataLayout: little or big endian +static std::string computeDataLayout(const Triple &TT, StringRef FS) { + // TOOD: jle; specialize this (and elsewhere) to Solana-only once the new + // back-end is integrated; e.g. we won't need IsSolana, etc. + assert(TT.getArch() == Triple::sbf && "expected Triple::sbf"); + bool IsSolana = true; + return "e-m:e-p:64:64-i64:64-n32:64-S128"; +} + +static Reloc::Model getEffectiveRelocModel(Optional RM) { + return RM.getValueOr(Reloc::PIC_); +} + +SBFTargetMachine::SBFTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : LLVMTargetMachine(T, computeDataLayout(TT, FS), TT, CPU, FS, Options, + getEffectiveRelocModel(RM), + getEffectiveCodeModel(CM, CodeModel::Small), OL), + TLOF(std::make_unique()), + Subtarget(TT, std::string(CPU), std::string(FS), *this) { + initAsmInfo(); + + SBFMCAsmInfo *MAI = + static_cast(const_cast(AsmInfo.get())); + MAI->setDwarfUsesRelocationsAcrossSections(!Subtarget.getUseDwarfRIS()); + MAI->setSupportsDebugInformation(true); +} + +namespace { +// SBF Code Generator Pass Configuration Options. +class SBFPassConfig : public TargetPassConfig { +public: + SBFPassConfig(SBFTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + SBFTargetMachine &getSBFTargetMachine() const { + return getTM(); + } + + void addIRPasses() override; + bool addInstSelector() override; + void addMachineSSAOptimization() override; + void addPreEmitPass() override; +}; +} + +TargetPassConfig *SBFTargetMachine::createPassConfig(PassManagerBase &PM) { + return new SBFPassConfig(*this, PM); +} + +void SBFTargetMachine::adjustPassManager(PassManagerBuilder &Builder) { + Builder.addExtension( + PassManagerBuilder::EP_EarlyAsPossible, + [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) { + PM.add(createSBFAbstractMemberAccess(this)); + PM.add(createSBFPreserveDIType()); + PM.add(createSBFIRPeephole()); + }); + + Builder.addExtension( + PassManagerBuilder::EP_Peephole, + [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) { + PM.add(createCFGSimplificationPass( + SimplifyCFGOptions().hoistCommonInsts(true))); + }); + Builder.addExtension( + PassManagerBuilder::EP_ModuleOptimizerEarly, + [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) { + PM.add(createSBFAdjustOpt()); + }); +} + +void SBFTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { + PB.registerPipelineStartEPCallback( + [=](ModulePassManager &MPM, OptimizationLevel) { + FunctionPassManager FPM; + FPM.addPass(SBFAbstractMemberAccessPass(this)); + FPM.addPass(SBFPreserveDITypePass()); + FPM.addPass(SBFIRPeepholePass()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + }); + PB.registerPeepholeEPCallback([=](FunctionPassManager &FPM, + OptimizationLevel Level) { + FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().hoistCommonInsts(true))); + }); + PB.registerPipelineEarlySimplificationEPCallback( + [=](ModulePassManager &MPM, OptimizationLevel) { + MPM.addPass(SBFAdjustOptPass()); + }); +} + +void SBFPassConfig::addIRPasses() { + addPass(createSBFCheckAndAdjustIR()); + TargetPassConfig::addIRPasses(); +} + +TargetTransformInfo +SBFTargetMachine::getTargetTransformInfo(const Function &F) { + return TargetTransformInfo(SBFTTIImpl(this, F)); +} + +// Install an instruction selector pass using +// the ISelDag to gen SBF code. +bool SBFPassConfig::addInstSelector() { + addPass(createSBFISelDag(getSBFTargetMachine())); + + return false; +} + +void SBFPassConfig::addMachineSSAOptimization() { + addPass(createSBFMISimplifyPatchablePass()); + + // The default implementation must be called first as we want eBPF + // Peephole ran at last. + TargetPassConfig::addMachineSSAOptimization(); + + const SBFSubtarget *Subtarget = getSBFTargetMachine().getSubtargetImpl(); + if (!DisableMIPeephole) { + if (Subtarget->getHasAlu32()) + addPass(createSBFMIPeepholePass()); + addPass(createSBFMIPeepholeTruncElimPass()); + } +} + +void SBFPassConfig::addPreEmitPass() { + addPass(createSBFMIPreEmitCheckingPass()); + if (getOptLevel() != CodeGenOpt::None) + if (!DisableMIPeephole) + addPass(createSBFMIPreEmitPeepholePass()); +} diff --git a/llvm/lib/Target/SBF/SBFTargetMachine.h b/llvm/lib/Target/SBF/SBFTargetMachine.h new file mode 100644 index 0000000000000..fef53a882d583 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFTargetMachine.h @@ -0,0 +1,48 @@ +//===-- SBFTargetMachine.h - Define TargetMachine for SBF --- 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 SBF specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFTARGETMACHINE_H +#define LLVM_LIB_TARGET_SBF_SBFTARGETMACHINE_H + +#include "SBFSubtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { +class SBFTargetMachine : public LLVMTargetMachine { + std::unique_ptr TLOF; + SBFSubtarget Subtarget; + +public: + SBFTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); + + const SBFSubtarget *getSubtargetImpl() const { return &Subtarget; } + const SBFSubtarget *getSubtargetImpl(const Function &) const override { + return &Subtarget; + } + + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + + TargetTransformInfo getTargetTransformInfo(const Function &F) override; + + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } + + void adjustPassManager(PassManagerBuilder &) override; + void registerPassBuilderCallbacks(PassBuilder &PB) override; +}; +} + +#endif diff --git a/llvm/lib/Target/SBF/SBFTargetTransformInfo.h b/llvm/lib/Target/SBF/SBFTargetTransformInfo.h new file mode 100644 index 0000000000000..649d38d083631 --- /dev/null +++ b/llvm/lib/Target/SBF/SBFTargetTransformInfo.h @@ -0,0 +1,78 @@ +//===------ SBFTargetTransformInfo.h - SBF specific TTI ---------*- 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 uses the target's specific information to +// provide more precise answers to certain TTI queries, while letting the +// target independent and default TTI implementations handle the rest. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SBF_SBFTARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_SBF_SBFTARGETTRANSFORMINFO_H + +#include "SBFTargetMachine.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" + +namespace llvm { +class SBFTTIImpl : public BasicTTIImplBase { + typedef BasicTTIImplBase BaseT; + typedef TargetTransformInfo TTI; + friend BaseT; + + const SBFSubtarget *ST; + const SBFTargetLowering *TLI; + + const SBFSubtarget *getST() const { return ST; } + const SBFTargetLowering *getTLI() const { return TLI; } + +public: + explicit SBFTTIImpl(const SBFTargetMachine *TM, const Function &F) + : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), + TLI(ST->getTargetLowering()) {} + + int getIntImmCost(const APInt &Imm, Type *Ty, TTI::TargetCostKind CostKind) { + if (Imm.getBitWidth() <= 64 && isInt<32>(Imm.getSExtValue())) + return TTI::TCC_Free; + + return TTI::TCC_Basic; + } + + InstructionCost getCmpSelInstrCost(unsigned Opcode, Type *ValTy, Type *CondTy, + CmpInst::Predicate VecPred, + TTI::TargetCostKind CostKind, + const llvm::Instruction *I = nullptr) { + if (Opcode == Instruction::Select) + return SCEVCheapExpansionBudget.getValue(); + + return BaseT::getCmpSelInstrCost(Opcode, ValTy, CondTy, VecPred, CostKind, + I); + } + + InstructionCost getArithmeticInstrCost( + unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind, + TTI::OperandValueKind Opd1Info = TTI::OK_AnyValue, + TTI::OperandValueKind Opd2Info = TTI::OK_AnyValue, + TTI::OperandValueProperties Opd1PropInfo = TTI::OP_None, + TTI::OperandValueProperties Opd2PropInfo = TTI::OP_None, + ArrayRef Args = ArrayRef(), + const Instruction *CxtI = nullptr) { + int ISD = TLI->InstructionOpcodeToISD(Opcode); + if (ISD == ISD::ADD && CostKind == TTI::TCK_RecipThroughput) + return SCEVCheapExpansionBudget.getValue() + 1; + + return BaseT::getArithmeticInstrCost(Opcode, Ty, CostKind, Opd1Info, + Opd2Info, Opd1PropInfo, + Opd2PropInfo); + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_SBF_SBFTARGETTRANSFORMINFO_H diff --git a/llvm/lib/Target/SBF/TargetInfo/CMakeLists.txt b/llvm/lib/Target/SBF/TargetInfo/CMakeLists.txt new file mode 100644 index 0000000000000..899f26fbd2f0a --- /dev/null +++ b/llvm/lib/Target/SBF/TargetInfo/CMakeLists.txt @@ -0,0 +1,10 @@ +add_llvm_component_library(LLVMSBFInfo + SBFTargetInfo.cpp + + LINK_COMPONENTS + MC + Support + + ADD_TO_COMPONENT + SBF + ) diff --git a/llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.cpp b/llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.cpp new file mode 100644 index 0000000000000..0edc2cc2ae612 --- /dev/null +++ b/llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.cpp @@ -0,0 +1,22 @@ +//===-- SBFTargetInfo.cpp - SBF Target Implementation ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "TargetInfo/SBFTargetInfo.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +Target &llvm::getTheSBFXTarget() { + static Target TheSBFTarget; + return TheSBFTarget; +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSBFTargetInfo() { + RegisterTarget XX( + getTheSBFXTarget(), "sbf", "SBF new (little endian)", "SBF"); +} diff --git a/llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.h b/llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.h new file mode 100644 index 0000000000000..5c18b063b83d1 --- /dev/null +++ b/llvm/lib/Target/SBF/TargetInfo/SBFTargetInfo.h @@ -0,0 +1,19 @@ +//===-- SBFTargetInfo.h - SBF Target Implementation -------------*- 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_LIB_TARGET_SBF_TARGETINFO_SBFTARGETINFO_H +#define LLVM_LIB_TARGET_SBF_TARGETINFO_SBFTARGETINFO_H + +namespace llvm { + +class Target; + +Target &getTheSBFXTarget(); +} // namespace llvm + +#endif // LLVM_LIB_TARGET_SBF_TARGETINFO_SBFTARGETINFO_H diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp index e9e6f130f757c..ae8be17f64c8d 100644 --- a/llvm/lib/TargetParser/Triple.cpp +++ b/llvm/lib/TargetParser/Triple.cpp @@ -1927,6 +1927,7 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::renderscript64: case Triple::riscv32: case Triple::riscv64: + case Triple::sbf: case Triple::shave: case Triple::spir64: case Triple::spir: diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-alu.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-alu.ll new file mode 100644 index 0000000000000..30eafd6cbf2aa --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-alu.ll @@ -0,0 +1,299 @@ +; RUN: llc -O2 -march=sbf -mattr=+alu32 < %s | FileCheck %s +; RUN: llc -O2 -march=sbf -mcpu=v3 < %s | FileCheck %s +; +; int mov(int a) +; { +; return a; +; } +; +; int mov_ri(void) +; { +; return 0xff; +; } +; +; int add(int a, int b) +; { +; return a + b; +; } +; +; int add_i(int a) +; { +; return a + 0x7fffffff; +; } +; +; int sub(int a, int b) +; { +; return a - b; +; } +; +; int sub_i(int a) +; { +; return a - 0xffffffff; +; } +; +; int mul(int a, int b) +; { +; return a * b; +; } +; +; int mul_i(int a) +; { +; return a * 0xf; +; } +; +; unsigned div(unsigned a, unsigned b) +; { +; return a / b; +; } +; +; unsigned div_i(unsigned a) +; { +; return a / 0xf; +; } +; +; int or(int a, int b) +; { +; return a | b; +; } +; +; int or_i(int a) +; { +; return a | 0xff; +; } +; +; int xor(int a, int b) +; { +; return a ^ b; +; } +; +; int xor_i(int a) +; { +; return a ^ 0xfff; +; } +; +; int and(int a, int b) +; { +; return a & b; +; } +; +; int and_i(int a) +; { +; return a & 0xffff; +; } +; +; int sll(int a, int b) +; { +; return a << b; +; } +; +; int sll_i(int a) +; { +; return a << 17; +; } +; +; unsigned srl(unsigned a, unsigned b) +; { +; return a >> b; +; } +; +; unsigned srl_i(unsigned a, unsigned b) +; { +; return a >> 31; +; } +; +; int sra(int a, int b) +; { +; return a >> b; +; } +; +; int sra_i(int a, int b) +; { +; return a >> 7; +; } +; +; int neg(int a) +; { +; return -a; +; } + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @mov(i32 returned %a) local_unnamed_addr #0 { +entry: + ret i32 %a +; CHECK: w{{[0-9]+}} = w{{[0-9]+}} +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @mov_ri() local_unnamed_addr #0 { +entry: + ret i32 255 +; CHECK: w{{[0-9]+}} = 255 +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @add(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %add = add nsw i32 %b, %a +; CHECK: w{{[0-9]+}} += w{{[0-9]+}} + ret i32 %add +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @add_i(i32 %a) local_unnamed_addr #0 { +entry: + %add = add nsw i32 %a, 2147483647 +; CHECK: w{{[0-9]+}} += 2147483647 + ret i32 %add +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @sub(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %sub = sub nsw i32 %a, %b +; CHECK: w{{[0-9]+}} -= w{{[0-9]+}} + ret i32 %sub +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @sub_i(i32 %a) local_unnamed_addr #0 { +entry: + %sub = add i32 %a, 1 +; CHECK: w{{[0-9]+}} += 1 + ret i32 %sub +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @mul(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %mul = mul nsw i32 %b, %a +; CHECK: w{{[0-9]+}} *= w{{[0-9]+}} + ret i32 %mul +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @mul_i(i32 %a) local_unnamed_addr #0 { +entry: + %mul = mul nsw i32 %a, 15 +; CHECK: w{{[0-9]+}} *= 15 + ret i32 %mul +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @div(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %div = udiv i32 %a, %b +; CHECK: w{{[0-9]+}} /= w{{[0-9]+}} + ret i32 %div +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @div_i(i32 %a) local_unnamed_addr #0 { +entry: + %div = udiv i32 %a, 15 +; CHECK: w{{[0-9]+}} /= 15 + ret i32 %div +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @or(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %or = or i32 %b, %a +; CHECK: w{{[0-9]+}} |= w{{[0-9]+}} + ret i32 %or +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @or_i(i32 %a) local_unnamed_addr #0 { +entry: + %or = or i32 %a, 255 +; CHECK: w{{[0-9]+}} |= 255 + ret i32 %or +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @xor(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %xor = xor i32 %b, %a +; CHECK: w{{[0-9]+}} ^= w{{[0-9]+}} + ret i32 %xor +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @xor_i(i32 %a) local_unnamed_addr #0 { +entry: + %xor = xor i32 %a, 4095 +; CHECK: w{{[0-9]+}} ^= 4095 + ret i32 %xor +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @and(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %and = and i32 %b, %a +; CHECK: w{{[0-9]+}} &= w{{[0-9]+}} + ret i32 %and +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @and_i(i32 %a) local_unnamed_addr #0 { +entry: + %and = and i32 %a, 65535 +; CHECK: w{{[0-9]+}} &= 65535 + ret i32 %and +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @sll(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %shl = shl i32 %a, %b +; CHECK: w{{[0-9]+}} <<= w{{[0-9]+}} + ret i32 %shl +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @sll_i(i32 %a) local_unnamed_addr #0 { +entry: + %shl = shl i32 %a, 17 +; CHECK: w{{[0-9]+}} <<= 17 + ret i32 %shl +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @srl(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %shr = lshr i32 %a, %b +; CHECK: w{{[0-9]+}} >>= w{{[0-9]+}} + ret i32 %shr +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @srl_i(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %shr = lshr i32 %a, 31 +; CHECK: w{{[0-9]+}} >>= 31 + ret i32 %shr +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @sra(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %shr = ashr i32 %a, %b +; CHECK: w{{[0-9]+}} s>>= w{{[0-9]+}} + ret i32 %shr +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @sra_i(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %shr = ashr i32 %a, 7 +; CHECK: w{{[0-9]+}} s>>= 7 + ret i32 %shr +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @neg(i32 %a) local_unnamed_addr #0 { +entry: + %sub = sub nsw i32 0, %a +; CHECK: w{{[0-9]+}} = -w{{[0-9]+}} + ret i32 %sub +} diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-cond-select.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-cond-select.ll new file mode 100644 index 0000000000000..9076cda149a34 --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-cond-select.ll @@ -0,0 +1,117 @@ +; RUN: llc -O2 -march=sbf -mattr=+alu32 < %s | FileCheck %s +; +; unsigned int select_cc_32 (unsigned a, unsigned b, int c, int d) +; { +; if (a > b) +; return c; +; else +; return d; +; } +; +; long long select_cc_32_64 (unsigned a, unsigned b, long long c, long long d) +; { +; if (a > b) +; return c; +; else +; return d; +; } +; +; int select_cc_64_32 (long long a, long long b, int c, int d) +; { +; if (a > b) +; return c; +; else +; return d; +; } +; +; int selecti_cc_32 (unsigned a, int c, int d) +; { +; if (a > 10) +; return c; +; else +; return d; +; } +; +; long long selecti_cc_32_64 (unsigned a, long long c, long long d) +; { +; if (a > 11) +; return c; +; else +; return d; +; } +; +; int selecti_cc_64_32 (long long a, int c, int d) +; { +; if (a > 12) +; return c; +; else +; return d; +; } + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @select_cc_32(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr #0 { +entry: + %cmp = icmp ugt i32 %a, %b + %c.d = select i1 %cmp, i32 %c, i32 %d + ret i32 %c.d +} +; CHECK-LABEL: select_cc_32 +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + +; Function Attrs: norecurse nounwind readnone +define dso_local i64 @select_cc_32_64(i32 %a, i32 %b, i64 %c, i64 %d) local_unnamed_addr #0 { +entry: + %cmp = icmp ugt i32 %a, %b + %c.d = select i1 %cmp, i64 %c, i64 %d + ret i64 %c.d +} +; CHECK-LABEL: select_cc_32_64 +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @select_cc_64_32(i64 %a, i64 %b, i32 %c, i32 %d) local_unnamed_addr #0 { +entry: + %cmp = icmp sgt i64 %a, %b + %c.d = select i1 %cmp, i32 %c, i32 %d + ret i32 %c.d +} +; CHECK-LABEL: select_cc_64_32 +; CHECK-NOT: r{{[0-9]+}} <<= 32 + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @selecti_cc_32(i32 %a, i32 %c, i32 %d) local_unnamed_addr #0 { +entry: + %cmp = icmp ugt i32 %a, 10 + %c.d = select i1 %cmp, i32 %c, i32 %d + ret i32 %c.d +} +; CHECK-LABEL: selecti_cc_32 +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + +; Function Attrs: norecurse nounwind readnone +define dso_local i64 @selecti_cc_32_64(i32 %a, i64 %c, i64 %d) local_unnamed_addr #0 { +entry: + %cmp = icmp ugt i32 %a, 11 + %c.d = select i1 %cmp, i64 %c, i64 %d + ret i64 %c.d +} +; CHECK-LABEL: selecti_cc_32_64 +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @selecti_cc_64_32(i64 %a, i32 %c, i32 %d) local_unnamed_addr #0 { +entry: + %cmp = icmp sgt i64 %a, 12 + %c.d = select i1 %cmp, i32 %c, i32 %d + ret i32 %c.d +} +; CHECK-LABEL: selecti_cc_64_32 +; CHECK-NOT: r{{[0-9]+}} <<= 32 diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-load-store.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-load-store.ll new file mode 100644 index 0000000000000..63645383dd06f --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-load-store.ll @@ -0,0 +1,107 @@ +; RUN: llc -O2 -march=sbf -mattr=+alu32 < %s | FileCheck %s +; +; unsigned char loadu8(unsigned char *p) +; { +; return *p; +; } +; +; unsigned short loadu16(unsigned short *p) +; { +; return *p; +; } +; +; unsigned loadu32(unsigned *p) +; { +; return *p; +; } +; +; unsigned long long loadu64(unsigned long long *p) +; { +; return *p; +; } +; +; void storeu8(unsigned char *p, unsigned long long v) +; { +; *p = (unsigned char)v; +; } +; +; void storeu16(unsigned short *p, unsigned long long v) +; { +; *p = (unsigned short)v; +; } +; +; void storeu32(unsigned *p, unsigned long long v) +; { +; *p = (unsigned)v; +; } +; +; void storeu64(unsigned long long *p, unsigned long long v) +; { +; *p = v; +; } +; Function Attrs: norecurse nounwind readonly +define dso_local zeroext i8 @loadu8(i8* nocapture readonly %p) local_unnamed_addr #0 { +entry: + %0 = load i8, i8* %p, align 1 +; CHECK: w{{[0-9]+}} = *(u8 *)(r{{[0-9]+}} + 0) + ret i8 %0 +} + +; Function Attrs: norecurse nounwind readonly +define dso_local zeroext i16 @loadu16(i16* nocapture readonly %p) local_unnamed_addr #0 { +entry: + %0 = load i16, i16* %p, align 2 +; CHECK: w{{[0-9]+}} = *(u16 *)(r{{[0-9]+}} + 0) + ret i16 %0 +} + +; Function Attrs: norecurse nounwind readonly +define dso_local i32 @loadu32(i32* nocapture readonly %p) local_unnamed_addr #0 { +entry: + %0 = load i32, i32* %p, align 4 +; CHECK: w{{[0-9]+}} = *(u32 *)(r{{[0-9]+}} + 0) + ret i32 %0 +} + +; Function Attrs: norecurse nounwind readonly +define dso_local i64 @loadu64(i64* nocapture readonly %p) local_unnamed_addr #0 { +entry: + %0 = load i64, i64* %p, align 8 +; CHECK: r{{[0-9]+}} = *(u64 *)(r{{[0-9]+}} + 0) + ret i64 %0 +} + +; Function Attrs: norecurse nounwind +define dso_local void @storeu8(i8* nocapture %p, i64 %v) local_unnamed_addr #1 { +entry: + %conv = trunc i64 %v to i8 + store i8 %conv, i8* %p, align 1 +; CHECK: *(u8 *)(r{{[0-9]+}} + 0) = w{{[0-9]+}} + ret void +} + +; Function Attrs: norecurse nounwind +define dso_local void @storeu16(i16* nocapture %p, i64 %v) local_unnamed_addr #1 { +entry: + %conv = trunc i64 %v to i16 + store i16 %conv, i16* %p, align 2 +; CHECK: *(u16 *)(r{{[0-9]+}} + 0) = w{{[0-9]+}} + ret void +} + +; Function Attrs: norecurse nounwind +define dso_local void @storeu32(i32* nocapture %p, i64 %v) local_unnamed_addr #1 { +entry: + %conv = trunc i64 %v to i32 + store i32 %conv, i32* %p, align 4 +; CHECK: *(u32 *)(r{{[0-9]+}} + 0) = w{{[0-9]+}} + ret void +} + +; Function Attrs: norecurse nounwind +define dso_local void @storeu64(i64* nocapture %p, i64 %v) local_unnamed_addr #1 { +entry: + store i64 %v, i64* %p, align 8 +; CHECK: *(u64 *)(r{{[0-9]+}} + 0) = r{{[0-9]+}} + ret void +} diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-1.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-1.ll new file mode 100644 index 0000000000000..26012d19f0db2 --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-1.ll @@ -0,0 +1,34 @@ +; RUN: llc -O2 -march=sbf -mcpu=v2 -mattr=+alu32 < %s | FileCheck %s +; +; For the below test case, 'b' in 'ret == b' needs SLL/SLR. +; 'ret' in 'ret == b' does not need SLL/SLR as all 'ret' values +; are assigned through 'w = ' alu32 operations. +; +; extern int helper(int); +; int test(int a, int b, int c, int d) { +; int ret; +; if (a < b) +; ret = (c < d) ? -1 : 0; +; else +; ret = (c < a) ? 1 : 2; +; return helper(ret == b); +; } + +define dso_local i32 @test(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr { +entry: + %cmp = icmp slt i32 %a, %b + %cmp1 = icmp slt i32 %c, %d + %cond = sext i1 %cmp1 to i32 + %cmp2 = icmp slt i32 %c, %a + %cond3 = select i1 %cmp2, i32 1, i32 2 + %ret.0 = select i1 %cmp, i32 %cond, i32 %cond3 + %cmp4 = icmp eq i32 %ret.0, %b + %conv = zext i1 %cmp4 to i32 + %call = tail call i32 @helper(i32 %conv) + ret i32 %call +} +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} >>= 32 +; CHECK: if r{{[0-9]+}} == r{{[0-9]+}} goto + +declare dso_local i32 @helper(i32) local_unnamed_addr diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-2.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-2.ll new file mode 100644 index 0000000000000..d8f301e70f247 --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-2.ll @@ -0,0 +1,34 @@ +; RUN: llc -O2 -march=sbf -mcpu=v2 -mattr=+alu32 < %s | FileCheck %s +; +; For the below test case, both 'ret' and 'b' at 'ret == b' +; need SLL/SLR. For 'ret', 'ret = a' may receive the value +; from argument with high 32-bit invalid data. +; +; extern int helper(int); +; int test(int a, int b, int c, int d) { +; int ret; +; if (a < b) +; ret = (c < d) ? a : 0; +; else +; ret = (c < a) ? 1 : 2; +; return helper(ret == b); +; } + +define dso_local i32 @test(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr { +entry: + %cmp = icmp slt i32 %a, %b + %cmp1 = icmp slt i32 %c, %d + %cond = select i1 %cmp1, i32 %a, i32 0 + %cmp2 = icmp slt i32 %c, %a + %cond3 = select i1 %cmp2, i32 1, i32 2 + %ret.0 = select i1 %cmp, i32 %cond, i32 %cond3 + %cmp4 = icmp eq i32 %ret.0, %b + %conv = zext i1 %cmp4 to i32 + %call = tail call i32 @helper(i32 %conv) + ret i32 %call +} +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK: if r{{[0-9]+}} == r{{[0-9]+}} goto + +declare dso_local i32 @helper(i32) local_unnamed_addr diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-3.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-3.ll new file mode 100644 index 0000000000000..3c70c3940dcbc --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole-phi-3.ll @@ -0,0 +1,53 @@ +; RUN: llc -O2 -march=sbf -mcpu=v2 -mattr=+alu32 < %s | FileCheck %s +; +; For the below example, two phi node in the loop may depend on +; each other. So implementation must handle recursion properly. +; +; int test(unsigned long a, unsigned long b, unsigned long c) { +; int val = 0; +; +; #pragma clang loop unroll(disable) +; for (long i = 0; i < 100; i++) { +; if (a > b) +; val = 1; +; a += b; +; if (b > c) +; val = 1; +; b += c; +; } +; +; return val == 0 ? 1 : 0; +; } + + +define dso_local i32 @test(i64 %a, i64 %b, i64 %c) local_unnamed_addr { +entry: + br label %for.body + +for.cond.cleanup: ; preds = %for.body + %cmp6 = icmp eq i32 %val.2, 0 + %cond = zext i1 %cmp6 to i32 + ret i32 %cond + +for.body: ; preds = %for.body, %entry + %i.018 = phi i64 [ 0, %entry ], [ %inc, %for.body ] + %val.017 = phi i32 [ 0, %entry ], [ %val.2, %for.body ] + %a.addr.016 = phi i64 [ %a, %entry ], [ %add, %for.body ] + %b.addr.015 = phi i64 [ %b, %entry ], [ %add5, %for.body ] + %cmp1 = icmp ugt i64 %a.addr.016, %b.addr.015 + %add = add i64 %a.addr.016, %b.addr.015 + %cmp2 = icmp ugt i64 %b.addr.015, %c + %0 = or i1 %cmp2, %cmp1 + %val.2 = select i1 %0, i32 1, i32 %val.017 + %add5 = add i64 %b.addr.015, %c + %inc = add nuw nsw i64 %i.018, 1 + %exitcond = icmp eq i64 %inc, 100 + br i1 %exitcond, label %for.cond.cleanup, label %for.body, !llvm.loop !2 +} +; CHECK: [[VAL:r[0-9]+]] = w{{[0-9]+}} +; CHECK-NOT: [[VAL:r[0-9]+]] <<= 32 +; CHECK-NOT: [[VAL]] >>= 32 +; CHECK: if [[VAL]] == 0 goto + +!2 = distinct !{!2, !3} +!3 = !{!"llvm.loop.unroll.disable"} diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-peephole.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole.ll new file mode 100644 index 0000000000000..b5c7764f9150a --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-peephole.ll @@ -0,0 +1,126 @@ +; RUN: llc -O2 -march=sbf -mcpu=v2 -mattr=+alu32 < %s | FileCheck %s +; +; long long select_u(unsigned a, unsigned b, long long c, long long d) +; { +; if (a > b) +; return c; +; else +; return d; +; } +; +; long long select_u_2(unsigned a, unsigned long long b, long long c, long long d) +; { +; if (a > b) +; return c; +; else +; return d; +; } +; +; long long select_s(signed a, signed b, long long c, long long d) +; { +; if (a > b) +; return c; +; else +; return d; +; } +; +; long long bar (); +; +; int foo (int b, int c) +; { +; unsigned int i32_val = (unsigned int) bar(); +; +; if (i32_val < 10) +; return b; +; else +; return c; +; } +; +; int *inc_p (int *p, unsigned a) +; { +; return p + a; +; } + +; Function Attrs: norecurse nounwind readnone +define dso_local i64 @select_u(i32 %a, i32 %b, i64 %c, i64 %d) local_unnamed_addr #0 { +; CHECK-LABEL: select_u: +entry: + %cmp = icmp ugt i32 %a, %b + %c.d = select i1 %cmp, i64 %c, i64 %d +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 +; CHECK: if r{{[0-9]+}} {{<|>}} r{{[0-9]+}} goto + ret i64 %c.d +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i64 @select_u_2(i32 %a, i64 %b, i64 %c, i64 %d) local_unnamed_addr #0 { +; CHECK-LABEL: select_u_2: +entry: + %conv = zext i32 %a to i64 +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + %cmp = icmp ugt i64 %conv, %b + %c.d = select i1 %cmp, i64 %c, i64 %d + ret i64 %c.d +} + +; Function Attrs: norecurse nounwind readnone +define dso_local i64 @select_s(i32 %a, i32 %b, i64 %c, i64 %d) local_unnamed_addr #0 { +; CHECK-LABEL: select_s: +entry: + %cmp = icmp sgt i32 %a, %b + %c.d = select i1 %cmp, i64 %c, i64 %d +; CHECK: r{{[0-9]+}} <<= 32 +; CHECK-NEXT: r{{[0-9]+}} s>>= 32 +; CHECK: if r{{[0-9]+}} s{{<|>}} r{{[0-9]+}} goto + ret i64 %c.d +} + +; Function Attrs: nounwind +define dso_local i32 @foo(i32 %b, i32 %c) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +entry: + %call = tail call i64 bitcast (i64 (...)* @bar to i64 ()*)() #2 + %conv = trunc i64 %call to i32 + %cmp = icmp ult i32 %conv, 10 +; %call comes from function call returning i64 so the high bits will need +; to be cleared. +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + %b.c = select i1 %cmp, i32 %b, i32 %c +; CHECK: if r{{[0-9]+}} {{<|>}} {{[0-9]+}} goto + ret i32 %b.c +} + +declare dso_local i64 @bar(...) local_unnamed_addr #1 + +; Function Attrs: norecurse nounwind readnone +define dso_local i32* @inc_p(i32* readnone %p, i32 %a) local_unnamed_addr #0 { +; CHECK-LABEL: inc_p: +entry: + %idx.ext = zext i32 %a to i64 +; CHECK: r{{[0-9]+}} = w{{[0-9]+}} +; CHECK-NOT: r{{[0-9]+}} <<= 32 +; CHECK-NOT: r{{[0-9]+}} >>= 32 + %add.ptr = getelementptr inbounds i32, i32* %p, i64 %idx.ext + ret i32* %add.ptr +} + +define dso_local i32 @test() local_unnamed_addr { +; CHECK-LABEL: test: +entry: + %call = tail call i32 bitcast (i32 (...)* @helper to i32 ()*)() + %cmp = icmp sgt i32 %call, 6 +; The shifts can't be optimized out because %call comes from function call +; return i32 so the high bits might be invalid. +; CHECK: r{{[0-9]+}} <<= 32 +; CHECK-NEXT: r{{[0-9]+}} s>>= 32 + %cond = zext i1 %cmp to i32 +; CHECK: if r{{[0-9]+}} s{{<|>}} {{[0-9]+}} goto + ret i32 %cond +} +declare dso_local i32 @helper(...) local_unnamed_addr diff --git a/llvm/test/CodeGen/SBF/32-bit-subreg-zext.ll b/llvm/test/CodeGen/SBF/32-bit-subreg-zext.ll new file mode 100644 index 0000000000000..d6f2f9896aba0 --- /dev/null +++ b/llvm/test/CodeGen/SBF/32-bit-subreg-zext.ll @@ -0,0 +1,21 @@ +; RUN: llc -O2 -march=sbf -mattr=+alu32 < %s | FileCheck %s +; RUN: llc -O2 -march=sbf -mcpu=v3 < %s | FileCheck %s +; RUN: llc -O2 -march=sbf -mattr=+alu32 < %s | FileCheck %s +; RUN: llc -O2 -march=sbf -mcpu=v3 < %s | FileCheck %s +; +; long zext(unsigned int a) +; { +; long b = a; +; return b; +; } + +; Function Attrs: norecurse nounwind +define dso_local i64 @zext(i32 %a) local_unnamed_addr #0 { +entry: + %conv = zext i32 %a to i64 + ; CHECK-NOT: r[[#]] <<= 32 + ; CHECK-NOT: r[[#]] >>= 32 + ret i64 %conv +} + +attributes #0 = { norecurse nounwind } diff --git a/llvm/test/CodeGen/SBF/TODO-NOTES b/llvm/test/CodeGen/SBF/TODO-NOTES new file mode 100644 index 0000000000000..89536f32f2d72 --- /dev/null +++ b/llvm/test/CodeGen/SBF/TODO-NOTES @@ -0,0 +1,12 @@ +Note that BTF has been disabled for SBF as of commit +559ff25f978a675230cc2dbcc18851424ace7fb9. + +Moreover, we are tentatively going to completely remove BTF and related +support from SBF. + +We have therefore removed the BTF/ and CORE/ test subdirectories which only +apply when BTF is available. + +Once we determine for certain that BTF will definitely be removed, delete +this TODO-NOTE. If for some reason BTF is deemed necessary, re-instate the +test directories mentioned above. diff --git a/llvm/test/CodeGen/SBF/adjust-opt-icmp1.ll b/llvm/test/CodeGen/SBF/adjust-opt-icmp1.ll new file mode 100644 index 0000000000000..de28abf192b97 --- /dev/null +++ b/llvm/test/CodeGen/SBF/adjust-opt-icmp1.ll @@ -0,0 +1,95 @@ +; RUN: opt -O2 -mtriple=sbf %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK %s +; RUN: opt -passes='default' -mtriple=sbf %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK %s +; RUN: opt -O2 -mtriple=sbf -sbf-disable-serialize-icmp %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-DISABLE %s +; RUN: opt -passes='default' -mtriple=sbf -sbf-disable-serialize-icmp %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-DISABLE %s +; +; Source: +; int foo(); +; int bar(int); +; int test() { +; int ret = foo(); +; if (ret <= 0 || ret > 7) +; return 0; +; return bar(ret); +; } +; Compilation flag: +; clang -target sbf -O2 -S -emit-llvm -Xclang -disable-llvm-passes test.c + +; Function Attrs: nounwind +define dso_local i32 @test() #0 { +entry: + %retval = alloca i32, align 4 + %ret = alloca i32, align 4 + %cleanup.dest.slot = alloca i32, align 4 + %0 = bitcast i32* %ret to i8* + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3 + %call = call i32 bitcast (i32 (...)* @foo to i32 ()*)() + store i32 %call, i32* %ret, align 4, !tbaa !2 + %1 = load i32, i32* %ret, align 4, !tbaa !2 + %cmp = icmp sle i32 %1, 0 + br i1 %cmp, label %if.then, label %lor.lhs.false + +; CHECK: [[REG1:r[0-9]+]] <<= 32 +; CHECK: [[REG1]] s>>= 32 +; CHECK: [[REG2:r[0-9]+]] = 1 +; CHECK: if [[REG2]] s> [[REG1]] goto +; CHECK: if [[REG1]] s> 7 goto + +; CHECK-DISABLE: [[REG1:r[0-9]+]] += -8 +; CHECK-DISABLE: [[REG1]] <<= 32 +; CHECK-DISABLE: [[REG1]] >>= 32 +; CHECK-DISABLE: [[REG2:r[0-9]+]] = 4294967289 +; CHECK-DISABLE: if [[REG2]] > [[REG1]] goto + +lor.lhs.false: ; preds = %entry + %2 = load i32, i32* %ret, align 4, !tbaa !2 + %cmp1 = icmp sgt i32 %2, 7 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %lor.lhs.false, %entry + store i32 0, i32* %retval, align 4 + store i32 1, i32* %cleanup.dest.slot, align 4 + br label %cleanup + +if.end: ; preds = %lor.lhs.false + %3 = load i32, i32* %ret, align 4, !tbaa !2 + %call2 = call i32 @bar(i32 %3) + store i32 %call2, i32* %retval, align 4 + store i32 1, i32* %cleanup.dest.slot, align 4 + br label %cleanup + +cleanup: ; preds = %if.end, %if.then + %4 = bitcast i32* %ret to i8* + call void @llvm.lifetime.end.p0i8(i64 4, i8* %4) #3 + %5 = load i32, i32* %retval, align 4 + ret i32 %5 +} + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local i32 @foo(...) #2 + +declare dso_local i32 @bar(i32) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git ca9c5433a6c31e372092fcd8bfd0e4fddd7e8784)"} +!2 = !{!3, !3, i64 0} +!3 = !{!"int", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} diff --git a/llvm/test/CodeGen/SBF/adjust-opt-icmp2.ll b/llvm/test/CodeGen/SBF/adjust-opt-icmp2.ll new file mode 100644 index 0000000000000..dd3b7eee10c91 --- /dev/null +++ b/llvm/test/CodeGen/SBF/adjust-opt-icmp2.ll @@ -0,0 +1,98 @@ +; RUN: opt -O2 -mtriple=sbf %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK %s +; RUN: opt -O2 -mtriple=sbf -sbf-disable-serialize-icmp %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-DISABLE %s +; +; Source: +; int foo(); +; int bar(int); +; int test() { +; int ret = foo(); +; if (ret <= 0) +; return 0; +; if (ret > 7) +; return 0; +; return bar(ret); +; } +; Compilation flag: +; clang -target sbf -O2 -S -emit-llvm -Xclang -disable-llvm-passes test.c + +; Function Attrs: nounwind +define dso_local i32 @test() #0 { +entry: + %retval = alloca i32, align 4 + %ret = alloca i32, align 4 + %cleanup.dest.slot = alloca i32, align 4 + %0 = bitcast i32* %ret to i8* + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3 + %call = call i32 bitcast (i32 (...)* @foo to i32 ()*)() + store i32 %call, i32* %ret, align 4, !tbaa !2 + %1 = load i32, i32* %ret, align 4, !tbaa !2 + %cmp = icmp sle i32 %1, 0 + br i1 %cmp, label %if.then, label %if.end + +; CHECK: [[REG1:r[0-9]+]] <<= 32 +; CHECK: [[REG1]] s>>= 32 +; CHECK: [[REG2:r[0-9]+]] = 1 +; CHECK: if [[REG2]] s> [[REG1]] goto +; CHECK: if [[REG1]] s> 7 goto + +; CHECK-DISABLE: [[REG1:r[0-9]+]] += -8 +; CHECK-DISABLE: [[REG1]] <<= 32 +; CHECK-DISABLE: [[REG1]] >>= 32 +; CHECK-DISABLE: [[REG2:r[0-9]+]] = 4294967289 +; CHECK-DISABLE: if [[REG2]] > [[REG1]] goto + +if.then: ; preds = %entry + store i32 0, i32* %retval, align 4 + store i32 1, i32* %cleanup.dest.slot, align 4 + br label %cleanup + +if.end: ; preds = %entry + %2 = load i32, i32* %ret, align 4, !tbaa !2 + %cmp1 = icmp sgt i32 %2, 7 + br i1 %cmp1, label %if.then2, label %if.end3 + +if.then2: ; preds = %if.end + store i32 0, i32* %retval, align 4 + store i32 1, i32* %cleanup.dest.slot, align 4 + br label %cleanup + +if.end3: ; preds = %if.end + %3 = load i32, i32* %ret, align 4, !tbaa !2 + %call4 = call i32 @bar(i32 %3) + store i32 %call4, i32* %retval, align 4 + store i32 1, i32* %cleanup.dest.slot, align 4 + br label %cleanup + +cleanup: ; preds = %if.end3, %if.then2, %if.then + %4 = bitcast i32* %ret to i8* + call void @llvm.lifetime.end.p0i8(i64 4, i8* %4) #3 + %5 = load i32, i32* %retval, align 4 + ret i32 %5 +} + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local i32 @foo(...) #2 + +declare dso_local i32 @bar(i32) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git ca9c5433a6c31e372092fcd8bfd0e4fddd7e8784)"} +!2 = !{!3, !3, i64 0} +!3 = !{!"int", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} diff --git a/llvm/test/CodeGen/SBF/adjust-opt-icmp3.ll b/llvm/test/CodeGen/SBF/adjust-opt-icmp3.ll new file mode 100644 index 0000000000000..f7eed6fea77d9 --- /dev/null +++ b/llvm/test/CodeGen/SBF/adjust-opt-icmp3.ll @@ -0,0 +1,85 @@ +; RUN: opt -O2 -S -mtriple=sbf %s -o %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK,CHECK-V1 %s +; RUN: opt -O2 -S -mtriple=sbf %s -o %t1 +; RUN: llc %t1 -mcpu=v3 -o - | FileCheck -check-prefixes=CHECK,CHECK-V3 %s +; +; Source: +; int test1(unsigned long a) { +; if ((unsigned)a <= 3) return 2; +; return 3; +; } +; int test2(unsigned long a) { +; if ((unsigned)a < 4) return 2; +; return 3; +; } +; Compilation flag: +; clang -target sbf -O2 -S -emit-llvm -Xclang -disable-llvm-passes test.c + +; Function Attrs: nounwind +define dso_local i32 @test1(i64 %a) #0 { +entry: + %retval = alloca i32, align 4 + %a.addr = alloca i64, align 8 + store i64 %a, i64* %a.addr, align 8, !tbaa !3 + %0 = load i64, i64* %a.addr, align 8, !tbaa !3 + %conv = trunc i64 %0 to i32 + %cmp = icmp ule i32 %conv, 3 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 2, i32* %retval, align 4 + br label %return + +if.end: ; preds = %entry + store i32 3, i32* %retval, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %1 = load i32, i32* %retval, align 4 + ret i32 %1 +} + +; CHECK-LABEL: test1 +; CHECK-V1: if r[[#]] > r[[#]] goto +; CHECK-V3: if w[[#]] < 4 goto + +; Function Attrs: nounwind +define dso_local i32 @test2(i64 %a) #0 { +entry: + %retval = alloca i32, align 4 + %a.addr = alloca i64, align 8 + store i64 %a, i64* %a.addr, align 8, !tbaa !3 + %0 = load i64, i64* %a.addr, align 8, !tbaa !3 + %conv = trunc i64 %0 to i32 + %cmp = icmp ult i32 %conv, 4 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 2, i32* %retval, align 4 + br label %return + +if.end: ; preds = %entry + store i32 3, i32* %retval, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %1 = load i32, i32* %retval, align 4 + ret i32 %1 +} + +; CHECK-LABEL: test2 +; CHECK-V1: if r[[#]] > r[[#]] goto +; CHECK-V3: if w[[#]] < 4 goto + +attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git b7892f95881c891032742e0cd81861b845512653)"} +!3 = !{!4, !4, i64 0} +!4 = !{!"long", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C/C++ TBAA"} diff --git a/llvm/test/CodeGen/SBF/adjust-opt-icmp4.ll b/llvm/test/CodeGen/SBF/adjust-opt-icmp4.ll new file mode 100644 index 0000000000000..ac079f5dbe533 --- /dev/null +++ b/llvm/test/CodeGen/SBF/adjust-opt-icmp4.ll @@ -0,0 +1,85 @@ +; RUN: opt -O2 -S -mtriple=sbf %s -o %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK,CHECK-V1 %s +; RUN: opt -O2 -S -mtriple=sbf %s -o %t1 +; RUN: llc %t1 -mcpu=v3 -o - | FileCheck -check-prefixes=CHECK,CHECK-V3 %s +; +; Source: +; int test1(unsigned long a) { +; if ((unsigned)a > 3) return 2; +; return 3; +; } +; int test2(unsigned long a) { +; if ((unsigned)a >= 4) return 2; +; return 3; +; } +; Compilation flag: +; clang -target sbf -O2 -S -emit-llvm -Xclang -disable-llvm-passes test.c + +; Function Attrs: nounwind +define dso_local i32 @test1(i64 %a) #0 { +entry: + %retval = alloca i32, align 4 + %a.addr = alloca i64, align 8 + store i64 %a, i64* %a.addr, align 8, !tbaa !3 + %0 = load i64, i64* %a.addr, align 8, !tbaa !3 + %conv = trunc i64 %0 to i32 + %cmp = icmp ugt i32 %conv, 3 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 2, i32* %retval, align 4 + br label %return + +if.end: ; preds = %entry + store i32 3, i32* %retval, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %1 = load i32, i32* %retval, align 4 + ret i32 %1 +} + +; CHECK-LABEL: test1 +; CHECK-V1: if r[[#]] > 3 goto +; CHECK-V3: if w[[#]] > 3 goto + +; Function Attrs: nounwind +define dso_local i32 @test2(i64 %a) #0 { +entry: + %retval = alloca i32, align 4 + %a.addr = alloca i64, align 8 + store i64 %a, i64* %a.addr, align 8, !tbaa !3 + %0 = load i64, i64* %a.addr, align 8, !tbaa !3 + %conv = trunc i64 %0 to i32 + %cmp = icmp uge i32 %conv, 4 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 2, i32* %retval, align 4 + br label %return + +if.end: ; preds = %entry + store i32 3, i32* %retval, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %1 = load i32, i32* %retval, align 4 + ret i32 %1 +} + +; CHECK-LABEL: test2 +; CHECK-V1: if r[[#]] > 3 goto +; CHECK-V3: if w[[#]] > 3 goto + +attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 930ccf0191b4a33332d924522e5676fff583f083)"} +!3 = !{!4, !4, i64 0} +!4 = !{!"long", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C/C++ TBAA"} diff --git a/llvm/test/CodeGen/SBF/adjust-opt-speculative1.ll b/llvm/test/CodeGen/SBF/adjust-opt-speculative1.ll new file mode 100644 index 0000000000000..46a39411bedc2 --- /dev/null +++ b/llvm/test/CodeGen/SBF/adjust-opt-speculative1.ll @@ -0,0 +1,84 @@ +; RUN: opt -O2 -mtriple=sbf %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-COMMON,CHECK %s +; RUN: opt -O2 -mtriple=sbf -sbf-disable-avoid-speculation %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-COMMON,CHECK-DISABLE %s +; +; Source: +; unsigned long foo(); +; void *test(void *p) { +; unsigned long ret = foo(); +; if (ret <= 7) +; p += ret; +; return p; +; } +; Compilation flag: +; clang -target sbf -O2 -S -emit-llvm -Xclang -disable-llvm-passes test.c + +; Function Attrs: nounwind +define dso_local i8* @test(i8* %p) #0 { +entry: + %p.addr = alloca i8*, align 8 + %ret = alloca i64, align 8 + store i8* %p, i8** %p.addr, align 8, !tbaa !2 + %0 = bitcast i64* %ret to i8* + call void @llvm.lifetime.start.p0i8(i64 8, i8* %0) #3 + %call = call i64 bitcast (i64 (...)* @foo to i64 ()*)() + store i64 %call, i64* %ret, align 8, !tbaa !6 + %1 = load i64, i64* %ret, align 8, !tbaa !6 + %cmp = icmp ule i64 %1, 7 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %2 = load i64, i64* %ret, align 8, !tbaa !6 + %3 = load i8*, i8** %p.addr, align 8, !tbaa !2 + %add.ptr = getelementptr i8, i8* %3, i64 %2 + store i8* %add.ptr, i8** %p.addr, align 8, !tbaa !2 + br label %if.end + +if.end: ; preds = %if.then, %entry + %4 = load i8*, i8** %p.addr, align 8, !tbaa !2 + %5 = bitcast i64* %ret to i8* + call void @llvm.lifetime.end.p0i8(i64 8, i8* %5) #3 + ret i8* %4 +} +; CHECK-COMMON: [[REG6:r[0-9]+]] = r1 +; CHECK-COMMON: call foo + +; CHECK: if r0 > 7 goto [[LABEL:.*]] +; CHECK: [[REG6]] += r0 +; CHECK: [[LABEL]]: +; CHECK: r0 = [[REG6]] + +; CHECK-DISABLE: [[REG1:r[0-9]+]] = 8 +; CHECK-DISABLE: if [[REG1]] > r0 goto [[LABEL:.*]] +; CHECK-DISABLE: r0 = 0 +; CHECK-DISABLE: [[LABEL]]: +; CHECK-DISABLE: [[REG6]] += r0 +; CHECK-DISABLE: r0 = [[REG6]] + +; CHECK-COMMON: exit + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local i64 @foo(...) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git ca9c5433a6c31e372092fcd8bfd0e4fddd7e8784)"} +!2 = !{!3, !3, i64 0} +!3 = !{!"any pointer", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} +!6 = !{!7, !7, i64 0} +!7 = !{!"long", !4, i64 0} diff --git a/llvm/test/CodeGen/SBF/adjust-opt-speculative2.ll b/llvm/test/CodeGen/SBF/adjust-opt-speculative2.ll new file mode 100644 index 0000000000000..8f663160dd7f9 --- /dev/null +++ b/llvm/test/CodeGen/SBF/adjust-opt-speculative2.ll @@ -0,0 +1,93 @@ +; RUN: opt -O2 -mtriple=sbf %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-COMMON,CHECK %s +; RUN: opt -O2 -mtriple=sbf -sbf-disable-avoid-speculation %s | llvm-dis > %t1 +; RUN: llc %t1 -o - | FileCheck -check-prefixes=CHECK-COMMON,CHECK-DISABLE %s +; +; Source: +; unsigned foo(); +; void *test(void *p) { +; unsigned ret = foo(); +; if (ret <= 7) +; p += ret; +; return p; +; } +; Compilation flag: +; clang -target sbf -O2 -S -emit-llvm -Xclang -disable-llvm-passes test.c + +; Function Attrs: nounwind +define dso_local i8* @test(i8* %p) #0 { +entry: + %p.addr = alloca i8*, align 8 + %ret = alloca i32, align 4 + store i8* %p, i8** %p.addr, align 8, !tbaa !2 + %0 = bitcast i32* %ret to i8* + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3 + %call = call i32 bitcast (i32 (...)* @foo to i32 ()*)() + store i32 %call, i32* %ret, align 4, !tbaa !6 + %1 = load i32, i32* %ret, align 4, !tbaa !6 + %cmp = icmp ule i32 %1, 7 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %2 = load i32, i32* %ret, align 4, !tbaa !6 + %3 = load i8*, i8** %p.addr, align 8, !tbaa !2 + %idx.ext = zext i32 %2 to i64 + %add.ptr = getelementptr i8, i8* %3, i64 %idx.ext + store i8* %add.ptr, i8** %p.addr, align 8, !tbaa !2 + br label %if.end + +if.end: ; preds = %if.then, %entry + %4 = load i8*, i8** %p.addr, align 8, !tbaa !2 + %5 = bitcast i32* %ret to i8* + call void @llvm.lifetime.end.p0i8(i64 4, i8* %5) #3 + ret i8* %4 +} + +; CHECK-COMMON: [[REG6:r[0-9]+]] = r1 +; CHECK-COMMON: call foo + +; CHECK: r0 <<= 32 +; CHECK: r0 >>= 32 +; CHECK: if r0 > 7 goto [[LABEL:.*]] +; CHECK: [[REG6]] += r0 +; CHECK: [[LABEL]]: +; CHECK: r0 = [[REG6]] + +; CHECK-DISABLE: [[REG1:r[0-9]+]] = r0 +; CHECK-DISABLE: [[REG1]] <<= 32 +; CHECK-DISABLE: [[REG1]] >>= 32 +; CHECK-DISABLE: [[REG2:r[0-9]+]] = 8 +; CHECK-DISABLE: if [[REG2]] > [[REG1]] goto [[LABEL:.*]] +; CHECK-DISABLE: r0 = 0 +; CHECK-DISABLE: [[LABEL]]: +; CHECK-DISABLE: r0 <<= 32 +; CHECK-DISABLE: r0 >>= 32 +; CHECK-DISABLE: [[REG6]] += r0 +; CHECK-DISABLE: r0 = [[REG6]] + +; CHECK-COMMON: exit + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local i32 @foo(...) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git ca9c5433a6c31e372092fcd8bfd0e4fddd7e8784)"} +!2 = !{!3, !3, i64 0} +!3 = !{!"any pointer", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} +!6 = !{!7, !7, i64 0} +!7 = !{!"int", !4, i64 0} diff --git a/llvm/test/CodeGen/SBF/alu8.ll b/llvm/test/CodeGen/SBF/alu8.ll new file mode 100644 index 0000000000000..35e5f7e63dcb3 --- /dev/null +++ b/llvm/test/CodeGen/SBF/alu8.ll @@ -0,0 +1,45 @@ +; RUN: llc -march=sbf -show-mc-encoding < %s | FileCheck %s + +define i8 @mov(i8 %a, i8 %b) nounwind { +; CHECK-LABEL: mov: +; CHECK: r0 = r2 # encoding: [0xbf,0x20,0x00,0x00,0x00,0x00,0x00,0x00] +; CHECK: exit # encoding: [0x95,0x00,0x00,0x00,0x00,0x00,0x00,0x00] + ret i8 %b +} + +define i8 @add(i8 %a, i8 %b) nounwind { +; CHECK-LABEL: add: +; CHECK: r0 = r1 # encoding: [0xbf,0x10,0x00,0x00,0x00,0x00,0x00,0x00] +; CHECK: r0 += r2 # encoding: [0x0f,0x20,0x00,0x00,0x00,0x00,0x00,0x00] + %1 = add i8 %a, %b + ret i8 %1 +} + +define i8 @and(i8 %a, i8 %b) nounwind { +; CHECK-LABEL: and: +; CHECK: r0 &= r2 # encoding: [0x5f,0x20,0x00,0x00,0x00,0x00,0x00,0x00] + %1 = and i8 %a, %b + ret i8 %1 +} + +define i8 @bis(i8 %a, i8 %b) nounwind { +; CHECK-LABEL: bis: +; CHECK: r0 |= r2 # encoding: [0x4f,0x20,0x00,0x00,0x00,0x00,0x00,0x00] + %1 = or i8 %a, %b + ret i8 %1 +} + +define i8 @xorand(i8 %a, i8 %b) nounwind { +; CHECK-LABEL: xorand: +; CHECK: r2 ^= -1 # encoding: [0xa7,0x02,0x00,0x00,0xff,0xff,0xff,0xff] + %1 = xor i8 %b, -1 + %2 = and i8 %a, %1 + ret i8 %2 +} + +define i8 @xor(i8 %a, i8 %b) nounwind { +; CHECK-LABEL: xor: +; CHECK: r0 ^= r2 # encoding: [0xaf,0x20,0x00,0x00,0x00,0x00,0x00,0x00] + %1 = xor i8 %a, %b + ret i8 %1 +} diff --git a/llvm/test/CodeGen/SBF/atomics_sbf.ll b/llvm/test/CodeGen/SBF/atomics_sbf.ll new file mode 100644 index 0000000000000..8e448e4aba13a --- /dev/null +++ b/llvm/test/CodeGen/SBF/atomics_sbf.ll @@ -0,0 +1,276 @@ +; RUN: llc < %s -march=sbf -mcpu=v3 -verify-machineinstrs | tee -i /tmp/log | FileCheck %s +; +; CHECK-LABEL: test_load_add_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: w3 += w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_load_add_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw add i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_load_add_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: r3 += r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i32 @test_load_add_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw add i64* %p, i64 %v seq_cst + %conv = trunc i64 %0 to i32 + ret i32 %conv +} + +; CHECK-LABEL: test_load_sub_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: w3 -= w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_load_sub_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw sub i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_load_sub_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: r3 -= r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i32 @test_load_sub_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw sub i64* %p, i64 %v seq_cst + %conv = trunc i64 %0 to i32 + ret i32 %conv +} + +; CHECK-LABEL: test_xchg_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: *(u32 *)(r1 + 0) = w2 +define dso_local i32 @test_xchg_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw xchg i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_xchg_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: *(u64 *)(r1 + 0) = r2 +define dso_local i32 @test_xchg_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw xchg i64* %p, i64 %v seq_cst + %conv = trunc i64 %0 to i32 + ret i32 %conv +} + +; CHECK-LABEL: test_cas_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: if w0 == w2 goto +; CHECK: w3 = w0 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_cas_32(i32* nocapture %p, i32 %old, i32 %new) local_unnamed_addr { +entry: + %0 = cmpxchg i32* %p, i32 %old, i32 %new seq_cst seq_cst + %1 = extractvalue { i32, i1 } %0, 0 + ret i32 %1 +} + +; CHECK-LABEL: test_cas_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: if r0 == r2 goto +; CHECK: r3 = r0 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_cas_64(i64* nocapture %p, i64 %old, i64 %new) local_unnamed_addr { +entry: + %0 = cmpxchg i64* %p, i64 %old, i64 %new seq_cst seq_cst + %1 = extractvalue { i64, i1 } %0, 0 + ret i64 %1 +} + +; CHECK-LABEL: test_load_and_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: w3 &= w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_load_and_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw and i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_load_and_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: r3 &= r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_load_and_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw and i64* %p, i64 %v seq_cst + ret i64 %0 +} + +; CHECK-LABEL: test_load_nand_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: w3 &= w2 +; CHECK: w3 ^= -1 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_load_nand_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw nand i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_load_nand_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: r3 &= r2 +; CHECK: r3 ^= -1 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_load_nand_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw nand i64* %p, i64 %v seq_cst + ret i64 %0 +} + +; CHECK-LABEL: test_load_or_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: w3 |= w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_load_or_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw or i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_load_or_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: r3 |= r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_load_or_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw or i64* %p, i64 %v seq_cst + ret i64 %0 +} + +; CHECK-LABEL: test_load_xor_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: w3 ^= w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_load_xor_32(i32* nocapture %p, i32 %v) local_unnamed_addr { +entry: + %0 = atomicrmw xor i32* %p, i32 %v seq_cst + ret i32 %0 +} + +; CHECK-LABEL: test_load_xor_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: r3 ^= r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_load_xor_64(i64* nocapture %p, i64 %v) local_unnamed_addr { +entry: + %0 = atomicrmw xor i64* %p, i64 %v seq_cst + ret i64 %0 +} + +; CHECK-LABEL: test_min_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: if w0 s< w2 goto +; CHECK: w3 = w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_min_32(i32* nocapture %ptr, i32 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw min i32* %ptr, i32 %v release, align 1 + ret i32 %0 +} + +; CHECK-LABEL: test_min_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: if r0 s< r2 goto +; CHECK: r3 = r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_min_64(i64* nocapture %ptr, i64 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw min i64* %ptr, i64 %v release, align 1 + ret i64 %0 +} + +; CHECK-LABEL: test_max_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: if w0 s> w2 goto +; CHECK: w3 = w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_max_32(i32* nocapture %ptr, i32 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw max i32* %ptr, i32 %v release, align 1 + ret i32 %0 +} + +; CHECK-LABEL: test_max_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: if r0 s> r2 goto +; CHECK: r3 = r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_max_64(i64* nocapture %ptr, i64 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw max i64* %ptr, i64 %v release, align 1 + ret i64 %0 +} + +; CHECK-LABEL: test_umin_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: if w0 < w2 goto +; CHECK: w3 = w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_umin_32(i32* nocapture %ptr, i32 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw umin i32* %ptr, i32 %v release, align 1 + ret i32 %0 +} + +; CHECK-LABEL: test_umin_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: if r0 < r2 goto +; CHECK: r3 = r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_umin_64(i64* nocapture %ptr, i64 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw umin i64* %ptr, i64 %v release, align 1 + ret i64 %0 +} + +; CHECK-LABEL: test_umax_32 +; CHECK: w0 = *(u32 *)(r1 + 0) +; CHECK: w3 = w0 +; CHECK: if w0 > w2 goto +; CHECK: w3 = w2 +; CHECK: *(u32 *)(r1 + 0) = w3 +define dso_local i32 @test_umax_32(i32* nocapture %ptr, i32 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw umax i32* %ptr, i32 %v release, align 1 + ret i32 %0 +} + +; CHECK-LABEL: test_umax_64 +; CHECK: r0 = *(u64 *)(r1 + 0) +; CHECK: r3 = r0 +; CHECK: if r0 > r2 goto +; CHECK: r3 = r2 +; CHECK: *(u64 *)(r1 + 0) = r3 +define dso_local i64 @test_umax_64(i64* nocapture %ptr, i64 %v) local_unnamed_addr #0 { +entry: + %0 = atomicrmw umax i64* %ptr, i64 %v release, align 1 + ret i64 %0 +} diff --git a/llvm/test/CodeGen/SBF/basictest.ll b/llvm/test/CodeGen/SBF/basictest.ll new file mode 100644 index 0000000000000..5cc98bfaf65b8 --- /dev/null +++ b/llvm/test/CodeGen/SBF/basictest.ll @@ -0,0 +1,28 @@ +; RUN: llc < %s -march=sbf | FileCheck %s + +define i32 @test0(i32 %X) { + %tmp.1 = add i32 %X, 1 + ret i32 %tmp.1 +; CHECK-LABEL: test0: +; CHECK: r0 += 1 +} + +; CHECK-LABEL: store_imm: +; CHECK: *(u32 *)(r1 + 0) = r{{[03]}} +; CHECK: *(u32 *)(r2 + 4) = r{{[03]}} +define i32 @store_imm(i32* %a, i32* %b) { +entry: + store i32 0, i32* %a, align 4 + %0 = getelementptr inbounds i32, i32* %b, i32 1 + store i32 0, i32* %0, align 4 + ret i32 0 +} + +@G = external global i8 +define zeroext i8 @loadG() { + %tmp = load i8, i8* @G + ret i8 %tmp +; CHECK-LABEL: loadG: +; CHECK: r1 = +; CHECK: r0 = *(u8 *)(r1 + 0) +} diff --git a/llvm/test/CodeGen/SBF/byval.ll b/llvm/test/CodeGen/SBF/byval.ll new file mode 100644 index 0000000000000..ba24c7a99aa3a --- /dev/null +++ b/llvm/test/CodeGen/SBF/byval.ll @@ -0,0 +1,31 @@ +; RUN: llc -O2 -march=sbf < %s | FileCheck %s + +%struct.S = type { [10 x i32] } + +; Function Attrs: nounwind uwtable +define void @bar(i32 %a) #0 { +; CHECK-LABEL: bar: +; CHECK: r2 = 8589934593 ll +; CHECK: *(u64 *)(r10 - 40) = r2 +; CHECK: r2 = r10 +; CHECK: r2 += -40 +; CHECK: call foo +entry: + %.compoundliteral = alloca %struct.S, align 8 + %arrayinit.begin = getelementptr inbounds %struct.S, %struct.S* %.compoundliteral, i64 0, i32 0, i64 0 + store i32 1, i32* %arrayinit.begin, align 8 + %arrayinit.element = getelementptr inbounds %struct.S, %struct.S* %.compoundliteral, i64 0, i32 0, i64 1 + store i32 2, i32* %arrayinit.element, align 4 + %arrayinit.element2 = getelementptr inbounds %struct.S, %struct.S* %.compoundliteral, i64 0, i32 0, i64 2 + store i32 3, i32* %arrayinit.element2, align 8 + %arrayinit.start = getelementptr inbounds %struct.S, %struct.S* %.compoundliteral, i64 0, i32 0, i64 3 + %scevgep4 = bitcast i32* %arrayinit.start to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %scevgep4, i8 0, i64 28, i1 false) + call void @foo(i32 %a, %struct.S* byval(%struct.S) align 8 %.compoundliteral) #3 + ret void +} + +declare void @foo(i32, %struct.S* byval(%struct.S) align 8) #1 + +; Function Attrs: nounwind +declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i1) #3 diff --git a/llvm/test/CodeGen/SBF/callx.ll b/llvm/test/CodeGen/SBF/callx.ll new file mode 100644 index 0000000000000..04ab27a09821a --- /dev/null +++ b/llvm/test/CodeGen/SBF/callx.ll @@ -0,0 +1,20 @@ +; RUN: llc < %s -march=sbf | FileCheck %s +; source: +; int test(int (*f)(void)) { return f(); } + +; Function Attrs: nounwind +define dso_local i32 @test(i32 ()* nocapture %f) local_unnamed_addr #0 { +entry: + %call = tail call i32 %f() #1 +; CHECK: callx r{{[0-9]+}} + ret i32 %call +} + +attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 7015a5c54b53d8d2297a3aa38bc32aab167bdcfc)"} diff --git a/llvm/test/CodeGen/SBF/cc_args.ll b/llvm/test/CodeGen/SBF/cc_args.ll new file mode 100644 index 0000000000000..5d4a26945b66c --- /dev/null +++ b/llvm/test/CodeGen/SBF/cc_args.ll @@ -0,0 +1,93 @@ +; RUN: llc < %s -march=sbf -show-mc-encoding | FileCheck %s + +define void @test() #0 { +entry: +; CHECK: test: + +; CHECK: r1 = 123 # encoding: [0xb7,0x01,0x00,0x00,0x7b,0x00,0x00,0x00] +; CHECK: call f_i16 + call void @f_i16(i16 123) + +; CHECK: r1 = 12345678 # encoding: [0xb7,0x01,0x00,0x00,0x4e,0x61,0xbc,0x00] +; CHECK: call f_i32 + call void @f_i32(i32 12345678) + +; CHECK: r1 = 72623859790382856 ll # encoding: [0x18,0x01,0x00,0x00,0x08,0x07,0x06,0x05,0x00,0x00,0x00,0x00,0x04,0x03,0x02,0x01] +; CHECK: call f_i64 + call void @f_i64(i64 72623859790382856) + +; CHECK: r1 = 1234 +; CHECK: r2 = 5678 +; CHECK: call f_i32_i32 + call void @f_i32_i32(i32 1234, i32 5678) + +; CHECK: r1 = 2 +; CHECK: r2 = 3 +; CHECK: r3 = 4 +; CHECK: call f_i16_i32_i16 + call void @f_i16_i32_i16(i16 2, i32 3, i16 4) + +; CHECK: r1 = 5 +; CHECK: r2 = 7262385979038285 ll +; CHECK: r3 = 6 +; CHECK: call f_i16_i64_i16 + call void @f_i16_i64_i16(i16 5, i64 7262385979038285, i16 6) + + ret void +} + +@g_i16 = common global i16 0, align 2 +@g_i32 = common global i32 0, align 2 +@g_i64 = common global i64 0, align 4 + +define void @f_i16(i16 %a) #0 { +; CHECK: f_i16: +; CHECK: *(u16 *)(r2 + 0) = r1 # encoding: [0x6b,0x12,0x00,0x00,0x00,0x00,0x00,0x00] + store volatile i16 %a, i16* @g_i16, align 2 + ret void +} + +define void @f_i32(i32 %a) #0 { +; CHECK: f_i32: +; CHECK: *(u32 *)(r2 + 0) = r1 # encoding: [0x63,0x12,0x00,0x00,0x00,0x00,0x00,0x00] + store volatile i32 %a, i32* @g_i32, align 2 + ret void +} + +define void @f_i64(i64 %a) #0 { +; CHECK: f_i64: +; CHECK: *(u64 *)(r2 + 0) = r1 # encoding: [0x7b,0x12,0x00,0x00,0x00,0x00,0x00,0x00] + store volatile i64 %a, i64* @g_i64, align 2 + ret void +} + +define void @f_i32_i32(i32 %a, i32 %b) #0 { +; CHECK: f_i32_i32: +; CHECK: *(u32 *)(r3 + 0) = r1 + store volatile i32 %a, i32* @g_i32, align 4 +; CHECK: *(u32 *)(r3 + 0) = r2 + store volatile i32 %b, i32* @g_i32, align 4 + ret void +} + +define void @f_i16_i32_i16(i16 %a, i32 %b, i16 %c) #0 { +; CHECK: f_i16_i32_i16: +; CHECK: *(u16 *)(r4 + 0) = r1 + store volatile i16 %a, i16* @g_i16, align 2 +; CHECK: *(u32 *)(r1 + 0) = r2 + store volatile i32 %b, i32* @g_i32, align 4 +; CHECK: *(u16 *)(r4 + 0) = r3 + store volatile i16 %c, i16* @g_i16, align 2 + ret void +} + +define void @f_i16_i64_i16(i16 %a, i64 %b, i16 %c) #0 { +; CHECK: f_i16_i64_i16: +; CHECK: *(u16 *)(r4 + 0) = r1 + store volatile i16 %a, i16* @g_i16, align 2 +; CHECK: *(u64 *)(r1 + 0) = r2 # encoding: [0x7b,0x21,0x00,0x00,0x00,0x00,0x00,0x00] + store volatile i64 %b, i64* @g_i64, align 8 +; CHECK: *(u16 *)(r4 + 0) = r3 + store volatile i16 %c, i16* @g_i16, align 2 + ret void +} diff --git a/llvm/test/CodeGen/SBF/cc_ret.ll b/llvm/test/CodeGen/SBF/cc_ret.ll new file mode 100644 index 0000000000000..8625e548b8ff8 --- /dev/null +++ b/llvm/test/CodeGen/SBF/cc_ret.ll @@ -0,0 +1,48 @@ +; RUN: llc < %s -march=sbf | FileCheck %s + +define void @test() #0 { +entry: +; CHECK: test: + +; CHECK: call f_i16 +; CHECK: *(u16 *)(r1 + 0) = r0 + %0 = call i16 @f_i16() + store volatile i16 %0, i16* @g_i16 + +; CHECK: call f_i32 +; CHECK: *(u32 *)(r1 + 0) = r0 + %1 = call i32 @f_i32() + store volatile i32 %1, i32* @g_i32 + +; CHECK: call f_i64 +; CHECK: *(u64 *)(r1 + 0) = r0 + %2 = call i64 @f_i64() + store volatile i64 %2, i64* @g_i64 + + ret void +} + +@g_i16 = common global i16 0, align 2 +@g_i32 = common global i32 0, align 2 +@g_i64 = common global i64 0, align 2 + +define i16 @f_i16() #0 { +; CHECK: f_i16: +; CHECK: r0 = 1 +; CHECK: exit + ret i16 1 +} + +define i32 @f_i32() #0 { +; CHECK: f_i32: +; CHECK: r0 = 16909060 +; CHECK: exit + ret i32 16909060 +} + +define i64 @f_i64() #0 { +; CHECK: f_i64: +; CHECK: r0 = 72623859790382856 ll +; CHECK: exit + ret i64 72623859790382856 +} diff --git a/llvm/test/CodeGen/SBF/cmp.ll b/llvm/test/CodeGen/SBF/cmp.ll new file mode 100644 index 0000000000000..2f90b50bb2b11 --- /dev/null +++ b/llvm/test/CodeGen/SBF/cmp.ll @@ -0,0 +1,119 @@ +; RUN: llc < %s -march=sbf | FileCheck %s + +; Function Attrs: nounwind readnone uwtable +define signext i8 @foo_cmp1(i8 signext %a, i8 signext %b) #0 { + %1 = icmp sgt i8 %a, %b + br i1 %1, label %2, label %4 + +;