From 9249622410a5ac083a321869d7ed9d38ec812df6 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Fri, 13 Jul 2018 15:19:33 +0000 Subject: [PATCH] [cfi-verify] Support AArch64. This patch adds support for AArch64 to cfi-verify. This required three changes to cfi-verify. First, it generalizes checking if an instruction is a trap by adding a new isTrap flag to TableGen (and defining it for x86 and AArch64). Second, the code that ensures that the operand register is not clobbered between the CFI check and the indirect call needs to allow a single dereference (in x86 this happens as part of the jump instruction). Third, we needed to ensure that return instructions are not counted as indirect branches. Technically, returns are indirect branches and can be covered by CFI, but LLVM's forward-edge CFI does not protect them, and x86 does not consider them, so we keep that behavior. In addition, we had to improve AArch64's code to evaluate the branch target of a MCInst to handle calls where the destination is not the first operand (which it often is not). Differential Revision: https://reviews.llvm.org/D48836 llvm-svn: 337007 --- include/llvm/MC/MCInstrDesc.h | 6 +- include/llvm/Target/Target.td | 1 + lib/Target/AArch64/AArch64InstrInfo.td | 2 + .../MCTargetDesc/AArch64MCTargetDesc.cpp | 20 +- lib/Target/X86/X86InstrSystem.td | 2 +- .../AArch64/protected-lineinfo.s | 183 +++++++++++ .../AArch64/unprotected-lineinfo.s | 169 ++++++++++ tools/llvm-cfi-verify/lib/FileAnalysis.cpp | 42 ++- tools/llvm-cfi-verify/lib/FileAnalysis.h | 11 +- .../tools/llvm-cfi-verify/FileAnalysis.cpp | 303 ++++++++++++++++-- utils/TableGen/CodeGenInstruction.cpp | 1 + utils/TableGen/CodeGenInstruction.h | 1 + utils/TableGen/InstrDocsEmitter.cpp | 1 + utils/TableGen/InstrInfoEmitter.cpp | 1 + 14 files changed, 692 insertions(+), 51 deletions(-) create mode 100644 test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s create mode 100644 test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s diff --git a/include/llvm/MC/MCInstrDesc.h b/include/llvm/MC/MCInstrDesc.h index 3f8749571ef..3e000a2210e 100644 --- a/include/llvm/MC/MCInstrDesc.h +++ b/include/llvm/MC/MCInstrDesc.h @@ -149,7 +149,8 @@ enum Flag { ExtractSubreg, InsertSubreg, Convergent, - Add + Add, + Trap }; } @@ -245,6 +246,9 @@ public: /// Return true if the instruction is an add instruction. bool isAdd() const { return Flags & (1ULL << MCID::Add); } + /// Return true if this instruction is a trap. + bool isTrap() const { return Flags & (1ULL << MCID::Trap); } + /// Return true if the instruction is a register to register move. bool isMoveReg() const { return Flags & (1ULL << MCID::MoveReg); } diff --git a/include/llvm/Target/Target.td b/include/llvm/Target/Target.td index dc1728de1f6..b746505d2a4 100644 --- a/include/llvm/Target/Target.td +++ b/include/llvm/Target/Target.td @@ -448,6 +448,7 @@ class Instruction { bit isBarrier = 0; // Can control flow fall through this instruction? bit isCall = 0; // Is this instruction a call instruction? bit isAdd = 0; // Is this instruction an add instruction? + bit isTrap = 0; // Is this instruction a trap instruction? bit canFoldAsLoad = 0; // Can this be folded as a simple memory operand? bit mayLoad = ?; // Is it possible for this inst to read memory? bit mayStore = ?; // Is it possible for this inst to write memory? diff --git a/lib/Target/AArch64/AArch64InstrInfo.td b/lib/Target/AArch64/AArch64InstrInfo.td index 0d008beacd6..e5ab8044598 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.td +++ b/lib/Target/AArch64/AArch64InstrInfo.td @@ -1442,7 +1442,9 @@ def : Pat<(AArch64call texternalsym:$func), (BL texternalsym:$func)>; //===----------------------------------------------------------------------===// // Exception generation instructions. //===----------------------------------------------------------------------===// +let isTrap = 1 in { def BRK : ExceptionGeneration<0b001, 0b00, "brk">; +} def DCPS1 : ExceptionGeneration<0b101, 0b01, "dcps1">; def DCPS2 : ExceptionGeneration<0b101, 0b10, "dcps2">; def DCPS3 : ExceptionGeneration<0b101, 0b11, "dcps3">; diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp index 85402d7d411..4ceda7e122f 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp @@ -140,14 +140,18 @@ public: bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, uint64_t &Target) const override { - if (Inst.getNumOperands() == 0 || - Info->get(Inst.getOpcode()).OpInfo[0].OperandType != - MCOI::OPERAND_PCREL) - return false; - - int64_t Imm = Inst.getOperand(0).getImm() * 4; - Target = Addr + Imm; - return true; + // Search for a PC-relative argument. + // This will handle instructions like bcc (where the first argument is the + // condition code) and cbz (where it is a register). + const auto &Desc = Info->get(Inst.getOpcode()); + for (unsigned i = 0, e = Inst.getNumOperands(); i != e; i++) { + if (Desc.OpInfo[i].OperandType == MCOI::OPERAND_PCREL) { + int64_t Imm = Inst.getOperand(i).getImm() * 4; + Target = Addr + Imm; + return true; + } + } + return false; } }; diff --git a/lib/Target/X86/X86InstrSystem.td b/lib/Target/X86/X86InstrSystem.td index 2dfad13dbf7..35ee00b9e01 100644 --- a/lib/Target/X86/X86InstrSystem.td +++ b/lib/Target/X86/X86InstrSystem.td @@ -22,7 +22,7 @@ let Defs = [RAX, RCX, RDX] in // CPU flow control instructions -let mayLoad = 1, mayStore = 0, hasSideEffects = 1 in { +let mayLoad = 1, mayStore = 0, hasSideEffects = 1, isTrap = 1 in { def TRAP : I<0x0B, RawFrm, (outs), (ins), "ud2", [(trap)]>, TB; def UD2B : I<0xB9, RawFrm, (outs), (ins), "ud2b", []>, TB; } diff --git a/test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s b/test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s new file mode 100644 index 00000000000..bdee5ffd847 --- /dev/null +++ b/test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s @@ -0,0 +1,183 @@ +# RUN: llvm-mc %s -filetype obj -triple aarch64-- -o %t.o +# RUN: llvm-cfi-verify %t.o | FileCheck %s + +# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}} +# CHECK-NEXT: tiny.cc:9 + +# CHECK: Expected Protected: 1 (100.00%) +# CHECK: Unexpected Protected: 0 (0.00%) +# CHECK: Expected Unprotected: 0 (0.00%) +# CHECK: Unexpected Unprotected (BAD): 0 (0.00%) + +# Source (tiny.cc): +# int a() { return 42; } +# int b() { return 137; } +# int main(int argc, char** argv) { +# int(*ptr)(); +# if (argc == 1) +# ptr = &a; +# else +# ptr = &b; +# return ptr(); +# } + .text + .file "ld-temp.o" + .p2align 2 + .type _Z1av.cfi,@function +_Z1av.cfi: +.Lfunc_begin0: + .file 1 "/tmp/tiny.cc" + .loc 1 1 0 + .cfi_startproc + .loc 1 1 11 prologue_end + mov w0, #42 + ret +.Ltmp0: +.Lfunc_end0: + .size _Z1av.cfi, .Lfunc_end0-_Z1av.cfi + .cfi_endproc + + .p2align 2 + .type _Z1bv.cfi,@function +_Z1bv.cfi: +.Lfunc_begin1: + .loc 1 2 0 + .cfi_startproc + .loc 1 2 11 prologue_end + mov w0, #137 + ret +.Ltmp1: +.Lfunc_end1: + .size _Z1bv.cfi, .Lfunc_end1-_Z1bv.cfi + .cfi_endproc + + .p2align 2 + .type main,@function +main: +.Lfunc_begin2: + .loc 1 3 0 + .cfi_startproc + sub sp, sp, #48 + stp x29, x30, [sp, #32] + add x29, sp, #32 + .cfi_def_cfa w29, 16 + .cfi_offset w30, -8 + .cfi_offset w29, -16 + stur wzr, [x29, #-4] + stur w0, [x29, #-8] + str x1, [sp, #16] +.Ltmp2: + .loc 1 5 7 prologue_end + ldur w8, [x29, #-8] + cmp w8, #1 + b.ne .LBB2_2 + .loc 1 0 7 is_stmt 0 + adrp x8, _Z1av + add x8, x8, :lo12:_Z1av + .loc 1 6 9 is_stmt 1 + str x8, [sp, #8] + .loc 1 6 5 is_stmt 0 + b .LBB2_3 +.LBB2_2: + .loc 1 0 5 + adrp x8, _Z1bv + add x8, x8, :lo12:_Z1bv + .loc 1 8 9 is_stmt 1 + str x8, [sp, #8] +.LBB2_3: + .loc 1 0 9 is_stmt 0 + adrp x8, .L.cfi.jumptable + add x9, x8, :lo12:.L.cfi.jumptable + .loc 1 9 10 is_stmt 1 + ldr x8, [sp, #8] + sub x9, x8, x9 + lsr x10, x9, #2 + orr x9, x10, x9, lsl #62 + cmp x9, #1 + b.ls .LBB2_5 + brk #0x1 +.LBB2_5: + blr x8 + .loc 1 9 3 is_stmt 0 + ldp x29, x30, [sp, #32] + add sp, sp, #48 + ret +.Ltmp3: +.Lfunc_end2: + .size main, .Lfunc_end2-main + .cfi_endproc + + .p2align 2 + .type .L.cfi.jumptable,@function +.L.cfi.jumptable: +.Lfunc_begin3: + .cfi_startproc + //APP + b _Z1av.cfi + b _Z1bv.cfi + + //NO_APP +.Lfunc_end3: + .size .L.cfi.jumptable, .Lfunc_end3-.L.cfi.jumptable + .cfi_endproc + + .type .L__unnamed_1,@object + .section .rodata,"a",@progbits + .p2align 2 +.L__unnamed_1: + .size .L__unnamed_1, 0 + + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 7.0.0 (trunk 335774) (llvm/trunk 335775)" +.Linfo_string1: + .asciz "tiny.cc" +.Linfo_string2: + .asciz "" + .section .debug_abbrev,"",@progbits + .byte 1 + .byte 17 + .byte 0 + .byte 37 + .byte 14 + .byte 19 + .byte 5 + .byte 3 + .byte 14 + .byte 16 + .byte 23 + .byte 27 + .byte 14 + .byte 17 + .byte 1 + .byte 18 + .byte 6 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",@progbits +.Lcu_begin0: + .word 38 + .hword 4 + .word .debug_abbrev + .byte 8 + .byte 1 + .word .Linfo_string0 + .hword 4 + .word .Linfo_string1 + .word .Lline_table_start0 + .word .Linfo_string2 + .xword .Lfunc_begin0 + .word .Lfunc_end2-.Lfunc_begin0 + .section .debug_ranges,"",@progbits + .section .debug_macinfo,"",@progbits + .byte 0 + + .type _Z1av,@function +.set _Z1av, .L.cfi.jumptable + .type _Z1bv,@function +.set _Z1bv, .L.cfi.jumptable+4 + .ident "clang version 7.0.0 (trunk 335774) (llvm/trunk 335775)" + .section ".note.GNU-stack","",@progbits + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s b/test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s new file mode 100644 index 00000000000..3bec1d3dbff --- /dev/null +++ b/test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s @@ -0,0 +1,169 @@ +# RUN: llvm-mc %s -filetype obj -triple aarch64-- -o %t.o +# RUN: llvm-cfi-verify %t.o | FileCheck %s + +# CHECK-LABEL: {{^Instruction: .* \(FAIL_BAD_CONDITIONAL_BRANCH\)}} +# CHECK-NEXT: tiny.cc:9 + +# CHECK: Expected Protected: 0 (0.00%) +# CHECK: Unexpected Protected: 0 (0.00%) +# CHECK: Expected Unprotected: 0 (0.00%) +# CHECK: Unexpected Unprotected (BAD): 1 (100.00%) + +# Source (tiny.cc): +# int a() { return 42; } +# int b() { return 137; } +# int main(int argc, char** argv) { +# int(*ptr)(); +# if (argc == 1) +# ptr = &a; +# else +# ptr = &b; +# return ptr(); +# } +# Compile with: +# clang++ -target aarch64-- -gmlt tiny.cc -S -o tiny.s + .text + .file "tiny.cc" + .globl _Z1av + .p2align 2 + .type _Z1av,@function +_Z1av: // @_Z1av +.Lfunc_begin0: + .file 1 "tiny.cc" + .loc 1 1 0 // tiny.cc:1:0 + .cfi_startproc +// BB#0: + mov w0, #42 +.Ltmp0: + .loc 1 1 11 prologue_end // tiny.cc:1:11 + ret +.Ltmp1: +.Lfunc_end0: + .size _Z1av, .Lfunc_end0-_Z1av + .cfi_endproc + + .globl _Z1bv + .p2align 2 + .type _Z1bv,@function +_Z1bv: // @_Z1bv +.Lfunc_begin1: + .loc 1 2 0 // tiny.cc:2:0 + .cfi_startproc +// BB#0: + mov w0, #137 +.Ltmp2: + .loc 1 2 11 prologue_end // tiny.cc:2:11 + ret +.Ltmp3: +.Lfunc_end1: + .size _Z1bv, .Lfunc_end1-_Z1bv + .cfi_endproc + + .globl main + .p2align 2 + .type main,@function +main: // @main +.Lfunc_begin2: + .loc 1 3 0 // tiny.cc:3:0 + .cfi_startproc +// BB#0: + sub sp, sp, #48 // =48 + stp x29, x30, [sp, #32] // 8-byte Folded Spill + add x29, sp, #32 // =32 +.Lcfi0: + .cfi_def_cfa w29, 16 +.Lcfi1: + .cfi_offset w30, -8 +.Lcfi2: + .cfi_offset w29, -16 + stur wzr, [x29, #-4] + stur w0, [x29, #-8] + str x1, [sp, #16] +.Ltmp4: + .loc 1 5 7 prologue_end // tiny.cc:5:7 + ldur w0, [x29, #-8] + cmp w0, #1 // =1 + b.ne .LBB2_2 +// BB#1: + .loc 1 0 7 is_stmt 0 // tiny.cc:0:7 + adrp x8, _Z1av + add x8, x8, :lo12:_Z1av + .loc 1 6 9 is_stmt 1 // tiny.cc:6:9 + str x8, [sp, #8] + .loc 1 6 5 is_stmt 0 // tiny.cc:6:5 + b .LBB2_3 +.LBB2_2: + .loc 1 0 5 // tiny.cc:0:5 + adrp x8, _Z1bv + add x8, x8, :lo12:_Z1bv + .loc 1 8 9 is_stmt 1 // tiny.cc:8:9 + str x8, [sp, #8] +.LBB2_3: + .loc 1 9 10 // tiny.cc:9:10 + ldr x8, [sp, #8] + blr x8 + .loc 1 9 3 is_stmt 0 // tiny.cc:9:3 + ldp x29, x30, [sp, #32] // 8-byte Folded Reload + add sp, sp, #48 // =48 + ret +.Ltmp5: +.Lfunc_end2: + .size main, .Lfunc_end2-main + .cfi_endproc + + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 4.0.1-10 (tags/RELEASE_401/final)" // string offset=0 +.Linfo_string1: + .asciz "tiny.cc" // string offset=48 +.Linfo_string2: + .asciz "/tmp" // string offset=56 + .section .debug_loc,"",@progbits + .section .debug_abbrev,"",@progbits +.Lsection_abbrev: + .byte 1 // Abbreviation Code + .byte 17 // DW_TAG_compile_unit + .byte 0 // DW_CHILDREN_no + .byte 37 // DW_AT_producer + .byte 14 // DW_FORM_strp + .byte 19 // DW_AT_language + .byte 5 // DW_FORM_data2 + .byte 3 // DW_AT_name + .byte 14 // DW_FORM_strp + .byte 16 // DW_AT_stmt_list + .byte 23 // DW_FORM_sec_offset + .byte 27 // DW_AT_comp_dir + .byte 14 // DW_FORM_strp + .byte 17 // DW_AT_low_pc + .byte 1 // DW_FORM_addr + .byte 18 // DW_AT_high_pc + .byte 6 // DW_FORM_data4 + .byte 0 // EOM(1) + .byte 0 // EOM(2) + .byte 0 // EOM(3) + .section .debug_info,"",@progbits +.Lsection_info: +.Lcu_begin0: + .word 38 // Length of Unit + .hword 4 // DWARF version number + .word .Lsection_abbrev // Offset Into Abbrev. Section + .byte 8 // Address Size (in bytes) + .byte 1 // Abbrev [1] 0xb:0x1f DW_TAG_compile_unit + .word .Linfo_string0 // DW_AT_producer + .hword 4 // DW_AT_language + .word .Linfo_string1 // DW_AT_name + .word .Lline_table_start0 // DW_AT_stmt_list + .word .Linfo_string2 // DW_AT_comp_dir + .xword .Lfunc_begin0 // DW_AT_low_pc + .word .Lfunc_end2-.Lfunc_begin0 // DW_AT_high_pc + .section .debug_ranges,"",@progbits +.Ldebug_range: + .section .debug_macinfo,"",@progbits +.Ldebug_macinfo: +.Lcu_macro_begin0: + .byte 0 // End Of Macro List Mark + + .ident "clang version 4.0.1-10 (tags/RELEASE_401/final)" + .section ".note.GNU-stack","",@progbits + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp index 75482544718..15bf20f091d 100644 --- a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -154,7 +154,8 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const { } bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const { - return MII->getName(InstrMeta.Instruction.getOpcode()) == "TRAP"; + const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); + return InstrDesc.isTrap(); } bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const { @@ -296,20 +297,38 @@ uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const else Node = Branch.Fallthrough; - while (Node != Graph.BaseAddress) { + // Some architectures (e.g., AArch64) cannot load in an indirect branch, so + // we allow them one load. + bool canLoad = !MII->get(IndirectCF.Instruction.getOpcode()).mayLoad(); + + // We walk backwards from the indirect CF. It is the last node returned by + // Graph.flattenAddress, so we skip it since we already handled it. + DenseSet CurRegisterNumbers = RegisterNumbers; + std::vector Nodes = Graph.flattenAddress(Node); + for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) { + Node = *I; const Instr &NodeInstr = getInstructionOrDie(Node); const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode()); - for (unsigned RegNum : RegisterNumbers) { + for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end(); + RI != RE; ++RI) { + unsigned RegNum = *RI; if (InstrDesc.hasDefOfPhysReg(NodeInstr.Instruction, RegNum, - *RegisterInfo)) - return Node; + *RegisterInfo)) { + if (!canLoad || !InstrDesc.mayLoad()) + return Node; + canLoad = false; + CurRegisterNumbers.erase(RI); + // Add the registers this load reads to those we check for clobbers. + for (unsigned i = InstrDesc.getNumDefs(), + e = InstrDesc.getNumOperands(); i != e; i++) { + const auto Operand = NodeInstr.Instruction.getOperand(i); + if (Operand.isReg()) + CurRegisterNumbers.insert(Operand.getReg()); + } + break; + } } - - const auto &KV = Graph.IntermediateNodes.find(Node); - assert((KV != Graph.IntermediateNodes.end()) && - "Could not get next node."); - Node = KV->second; } } @@ -456,6 +475,9 @@ void FileAnalysis::parseSectionContents(ArrayRef SectionBytes, if (!usesRegisterOperand(InstrMeta)) continue; + if (InstrDesc.isReturn()) + continue; + // Check if this instruction exists in the range of the DWARF metadata. if (!IgnoreDWARFFlag) { auto LineInfo = diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.h b/tools/llvm-cfi-verify/lib/FileAnalysis.h index ce81f8bfbe3..3f0a7048788 100644 --- a/tools/llvm-cfi-verify/lib/FileAnalysis.h +++ b/tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -149,10 +149,13 @@ public: CFIProtectionStatus validateCFIProtection(const GraphResult &Graph) const; // Returns the first place the operand register is clobbered between the CFI- - // check and the indirect CF instruction execution. If the register is not - // modified, returns the address of the indirect CF instruction. The result is - // undefined if the provided graph does not fall under either the - // FAIL_REGISTER_CLOBBERED or PROTECTED status (see CFIProtectionStatus). + // check and the indirect CF instruction execution. We do this by walking + // backwards from the indirect CF and ensuring there is at most one load + // involving the operand register (which is the indirect CF itself on x86). + // If the register is not modified, returns the address of the indirect CF + // instruction. The result is undefined if the provided graph does not fall + // under either the FAIL_REGISTER_CLOBBERED or PROTECTED status (see + // CFIProtectionStatus). uint64_t indirectCFOperandClobber(const GraphResult& Graph) const; // Prints an instruction to the provided stream using this object's pretty- diff --git a/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp b/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp index 27275d6e231..05880fcec80 100644 --- a/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp +++ b/unittests/tools/llvm-cfi-verify/FileAnalysis.cpp @@ -45,10 +45,10 @@ using ::testing::Field; namespace llvm { namespace cfi_verify { namespace { -class ELFx86TestFileAnalysis : public FileAnalysis { +class ELFTestFileAnalysis : public FileAnalysis { public: - ELFx86TestFileAnalysis() - : FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {} + ELFTestFileAnalysis(StringRef Trip) + : FileAnalysis(Triple(Trip), SubtargetFeatures()) {} // Expose this method publicly for testing. void parseSectionContents(ArrayRef SectionBytes, @@ -62,6 +62,8 @@ public: }; class BasicFileAnalysisTest : public ::testing::Test { +public: + BasicFileAnalysisTest(StringRef Trip) : Analysis(Trip) {} protected: virtual void SetUp() { IgnoreDWARFFlag = true; @@ -70,17 +72,27 @@ protected: handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) { SuccessfullyInitialised = false; outs() - << "Note: CFIVerifyTests are disabled due to lack of x86 support " + << "Note: CFIVerifyTests are disabled due to lack of support " "on this build.\n"; }); } } bool SuccessfullyInitialised; - ELFx86TestFileAnalysis Analysis; + ELFTestFileAnalysis Analysis; }; -TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) { +class BasicX86FileAnalysisTest : public BasicFileAnalysisTest { +public: + BasicX86FileAnalysisTest() : BasicFileAnalysisTest("x86_64--") {} +}; + +class BasicAArch64FileAnalysisTest : public BasicFileAnalysisTest { +public: + BasicAArch64FileAnalysisTest() : BasicFileAnalysisTest("aarch64--") {} +}; + +TEST_F(BasicX86FileAnalysisTest, BasicDisassemblyTraversalTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -190,7 +202,7 @@ TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) { EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta)); } -TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) { +TEST_F(BasicX86FileAnalysisTest, PrevAndNextFromBadInst) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -213,7 +225,7 @@ TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) { EXPECT_EQ(1u, GoodInstrMeta->InstructionSize); } -TEST_F(BasicFileAnalysisTest, CFITrapTest) { +TEST_F(BasicX86FileAnalysisTest, CFITrapTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -248,7 +260,7 @@ TEST_F(BasicFileAnalysisTest, CFITrapTest) { Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28))); } -TEST_F(BasicFileAnalysisTest, FallThroughTest) { +TEST_F(BasicX86FileAnalysisTest, FallThroughTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -288,7 +300,7 @@ TEST_F(BasicFileAnalysisTest, FallThroughTest) { Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19))); } -TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) { +TEST_F(BasicX86FileAnalysisTest, DefiniteNextInstructionTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -378,7 +390,7 @@ TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) { EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress); } -TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) { +TEST_F(BasicX86FileAnalysisTest, ControlFlowXRefsTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -483,7 +495,7 @@ TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) { EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty()); } -TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionInvalidTargets) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -507,7 +519,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicFallthroughToUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -522,7 +534,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicJumpToUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -537,7 +549,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -555,7 +567,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathSingleUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -572,7 +584,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitUpwards) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -597,7 +609,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) { SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch; } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitDownwards) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -621,7 +633,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) { SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionGoodAndBadPaths) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -637,7 +649,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -653,7 +665,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionComplexExample) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionComplexExample) { if (!SuccessfullyInitialised) return; // See unittests/GraphBuilder.cpp::BuildFlowGraphComplexExample for this @@ -683,7 +695,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionComplexExample) { SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) { +TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTest) { Analysis.parseSectionContents( { 0x77, 0x0d, // 0x688118: ja 0x688127 [+12] @@ -702,7 +714,7 @@ TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) { SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) { +TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTestFarAway) { Analysis.parseSectionContents( { 0x74, 0x73, // 0x7759eb: je 0x775a60 @@ -741,7 +753,7 @@ TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) { SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -757,7 +769,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -773,7 +785,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathImplicit) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -789,7 +801,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) { Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberDualPathImplicit) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -807,6 +819,243 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) { Analysis.validateCFIProtection(Result)); } +TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicUnprotected) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x00, 0x01, 0x3f, 0xd6, // 0: blr x8 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF); + EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicProtected) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x00, 0x01, 0x3f, 0xd6, // 8: blr x8 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 8); + EXPECT_EQ(CFIProtectionStatus::PROTECTED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberBasic) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x08, 0x05, 0x00, 0x91, // 8: add x8, x8, #1 + 0x00, 0x01, 0x3f, 0xd6, // 12: blr x8 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12); + EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberOneLoad) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16] + 0x20, 0x00, 0x1f, 0xd6, // 12: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12); + EXPECT_EQ(CFIProtectionStatus::PROTECTED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddGood) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x21, 0x04, 0x00, 0x91, // 8: add x1, x1, #1 + 0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16] + 0x20, 0x00, 0x1f, 0xd6, // 16: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16); + EXPECT_EQ(CFIProtectionStatus::PROTECTED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16] + 0x21, 0x04, 0x00, 0x91, // 12: add x1, x1, #1 + 0x20, 0x00, 0x1f, 0xd6, // 16: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16); + EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad2) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x29, 0x04, 0x00, 0x91, // 16: add x9, x1, #1 + 0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16] + 0x20, 0x00, 0x1f, 0xd6, // 16: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16); + EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberTwoLoads) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16] + 0x21, 0x08, 0x40, 0xf9, // 12: ldr x1, [x1,#16] + 0x20, 0x00, 0x1f, 0xd6, // 16: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16); + EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedSecondLoad) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16] + 0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16] + 0x20, 0x00, 0x1f, 0xd6, // 16: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16); + EXPECT_EQ(CFIProtectionStatus::PROTECTED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedLoads) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x49, 0x00, 0x00, 0x54, // 0: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1 + 0x22, 0x09, 0x40, 0xf9, // 8: ldr x2, [x9,#16] + 0x22, 0x08, 0x40, 0xf9, // 12: ldr x2, [x1,#16] + 0x20, 0x00, 0x1f, 0xd6, // 16: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16); + EXPECT_EQ(CFIProtectionStatus::PROTECTED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64GoodAndBadPaths) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x03, 0x00, 0x00, 0x14, // 0: b 12 + 0x49, 0x00, 0x00, 0x54, // 4: b.ls 8 + 0x20, 0x00, 0x20, 0xd4, // 8: brk #0x1 + 0x20, 0x00, 0x1f, 0xd6, // 12: br x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12); + EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPaths) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0xc9, 0x00, 0x00, 0x54, // 0: b.ls 24 + 0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16] + 0x03, 0x00, 0x00, 0x14, // 8: b 12 + 0x69, 0x00, 0x00, 0x54, // 12: b.ls 12 + 0x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16] + 0x20, 0x00, 0x1f, 0xd6, // 20: br x1 + 0x20, 0x00, 0x20, 0xd4, // 24: brk #0x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 20); + EXPECT_EQ(CFIProtectionStatus::PROTECTED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad1) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0xe9, 0x00, 0x00, 0x54, // 0: b.ls 28 + 0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16] + 0x21, 0x08, 0x40, 0xf9, // 8: ldr x1, [x1,#16] + 0x03, 0x00, 0x00, 0x14, // 12: b 12 + 0x69, 0x00, 0x00, 0x54, // 16: b.ls 12 + 0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16] + 0x20, 0x00, 0x1f, 0xd6, // 24: br x1 + 0x20, 0x00, 0x20, 0xd4, // 28: brk #0x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 24); + EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED, + Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad2) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0xe9, 0x00, 0x00, 0x54, // 0: b.ls 28 + 0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16] + 0x03, 0x00, 0x00, 0x14, // 8: b 12 + 0x89, 0x00, 0x00, 0x54, // 12: b.ls 16 + 0x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16] + 0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16] + 0x20, 0x00, 0x1f, 0xd6, // 24: br x1 + 0x20, 0x00, 0x20, 0xd4, // 28: brk #0x1 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 24); + EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED, + Analysis.validateCFIProtection(Result)); +} + } // anonymous namespace } // end namespace cfi_verify } // end namespace llvm diff --git a/utils/TableGen/CodeGenInstruction.cpp b/utils/TableGen/CodeGenInstruction.cpp index 45519c02423..eb35020d3d3 100644 --- a/utils/TableGen/CodeGenInstruction.cpp +++ b/utils/TableGen/CodeGenInstruction.cpp @@ -312,6 +312,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R) isBarrier = R->getValueAsBit("isBarrier"); isCall = R->getValueAsBit("isCall"); isAdd = R->getValueAsBit("isAdd"); + isTrap = R->getValueAsBit("isTrap"); canFoldAsLoad = R->getValueAsBit("canFoldAsLoad"); isPredicable = Operands.isPredicable || R->getValueAsBit("isPredicable"); isConvertibleToThreeAddress = R->getValueAsBit("isConvertibleToThreeAddress"); diff --git a/utils/TableGen/CodeGenInstruction.h b/utils/TableGen/CodeGenInstruction.h index 8d7810a49ca..a50c3e60e6e 100644 --- a/utils/TableGen/CodeGenInstruction.h +++ b/utils/TableGen/CodeGenInstruction.h @@ -232,6 +232,7 @@ template class ArrayRef; bool isBarrier : 1; bool isCall : 1; bool isAdd : 1; + bool isTrap : 1; bool canFoldAsLoad : 1; bool mayLoad : 1; bool mayLoad_Unset : 1; diff --git a/utils/TableGen/InstrDocsEmitter.cpp b/utils/TableGen/InstrDocsEmitter.cpp index fa9ee956942..65cb28cd17a 100644 --- a/utils/TableGen/InstrDocsEmitter.cpp +++ b/utils/TableGen/InstrDocsEmitter.cpp @@ -109,6 +109,7 @@ void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS) { FLAG(isBarrier) FLAG(isCall) FLAG(isAdd) + FLAG(isTrap) FLAG(canFoldAsLoad) FLAG(mayLoad) //FLAG(mayLoad_Unset) // Deliberately omitted. diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp index 0aff1aa6f94..a492daac0d0 100644 --- a/utils/TableGen/InstrInfoEmitter.cpp +++ b/utils/TableGen/InstrInfoEmitter.cpp @@ -576,6 +576,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, if (Inst.isMoveReg) OS << "|(1ULL<