Index: include/llvm/CodeGen/CommandFlags.h =================================================================== --- include/llvm/CodeGen/CommandFlags.h +++ include/llvm/CodeGen/CommandFlags.h @@ -203,6 +203,12 @@ cl::value_desc("pass-name"), cl::init("")); +cl::opt +NOPInsertion("nop-insertion", + cl::desc("Randomly add NOPs."), + cl::init(false)); + + // Common utility function tightly tied to the options listed here. Initializes // a TargetOptions object with CodeGen flags and returns it. static inline TargetOptions InitTargetOptionsFromCodeGenFlags() { @@ -225,6 +231,7 @@ Options.TrapFuncName = TrapFuncName; Options.PositionIndependentExecutable = EnablePIE; Options.UseInitArray = UseInitArray; + Options.NOPInsertion = NOPInsertion; return Options; } Index: include/llvm/Target/TargetOptions.h =================================================================== --- include/llvm/Target/TargetOptions.h +++ include/llvm/Target/TargetOptions.h @@ -49,7 +49,7 @@ JITEmitDebugInfoToDisk(false), GuaranteedTailCallOpt(false), DisableTailCalls(false), StackAlignmentOverride(0), EnableFastISel(false), PositionIndependentExecutable(false), - UseInitArray(false), + NOPInsertion(false), UseInitArray(false), DisableIntegratedAS(false), CompressDebugSections(false), TrapFuncName(""), FloatABIType(FloatABI::Default), AllowFPOpFusion(FPOpFusion::Standard) {} @@ -152,6 +152,9 @@ /// if the relocation model is anything other than PIC. unsigned PositionIndependentExecutable : 1; + /// Attempt to insert NOPs + unsigned NOPInsertion : 1; + /// UseInitArray - Use .init_array instead of .ctors for static /// constructors. unsigned UseInitArray : 1; Index: lib/LTO/LTOCodeGenerator.cpp =================================================================== --- lib/LTO/LTOCodeGenerator.cpp +++ lib/LTO/LTOCodeGenerator.cpp @@ -141,6 +141,7 @@ Options.TrapFuncName = options.TrapFuncName; Options.PositionIndependentExecutable = options.PositionIndependentExecutable; Options.UseInitArray = options.UseInitArray; + Options.NOPInsertion = options.NOPInsertion; } void LTOCodeGenerator::setDebugInfo(lto_debug_model debug) { Index: lib/Target/X86/CMakeLists.txt =================================================================== --- lib/Target/X86/CMakeLists.txt +++ lib/Target/X86/CMakeLists.txt @@ -13,6 +13,7 @@ add_public_tablegen_target(X86CommonTableGen) set(sources + NOPInsertion.cpp X86AsmPrinter.cpp X86COFFMachineModuleInfo.cpp X86CodeEmitter.cpp Index: lib/Target/X86/NOPInsertion.cpp =================================================================== --- /dev/null +++ lib/Target/X86/NOPInsertion.cpp @@ -0,0 +1,146 @@ +//===- NOPInsertion.cpp - Insert NOPs between instructions ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the NOPInsertion pass. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "nop-insertion" +#include "X86InstrBuilder.h" +#include "X86InstrInfo.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Target/TargetInstrInfo.h" + +using namespace llvm; + +static cl::opt +NOPInsertionPercentage( + "nop-insertion-percentage", + cl::desc("Percentage of instructions that have NOPs prepended"), + cl::init(50)); + +static cl::opt +MaxNOPsPerInstruction( + "max-nops-per-instruction", + llvm::cl::desc("Maximum number of NOPs per instruction"), + llvm::cl::init(1)); + + +STATISTIC(InsertedNOPs, + "Total number of noop type instructions inserted for diversity"); + +namespace { +class NOPInsertionPass : public MachineFunctionPass { + + static char ID; + + bool is64Bit; + +public: + NOPInsertionPass(bool is64Bit_) : + MachineFunctionPass(ID), is64Bit(is64Bit_) { + } + + virtual bool runOnMachineFunction(MachineFunction &MF); + + virtual const char *getPassName() const { + return "NOP insertion pass"; + } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } +}; +} + +char NOPInsertionPass::ID = 0; + +enum { NOP, + MOV_EBP, MOV_ESP, + LEA_ESI, LEA_EDI, + MAX_NOPS }; + +static const unsigned nopRegs[MAX_NOPS][2] = { + { 0, 0 }, + { X86::EBP, X86::RBP }, + { X86::ESP, X86::RSP }, + { X86::ESI, X86::RSI }, + { X86::EDI, X86::RDI }, +}; + +bool NOPInsertionPass::runOnMachineFunction(MachineFunction &Fn) { + const TargetInstrInfo *TII = Fn.getTarget().getInstrInfo(); + for (MachineFunction::iterator BB = Fn.begin(), E = Fn.end(); BB != E; ++BB) { + MachineBasicBlock::iterator FirstTerm = BB->getFirstTerminator(); + // Insert NOPs before instruction. + for (MachineBasicBlock::iterator I = BB->begin(); I != BB->end(); ) { + MachineBasicBlock::iterator NextI = std::next(I); + if (I->isPseudo()) { + I = NextI; + continue; + } + // Insert random number of NOP-like instructions. + for (unsigned i = 0; i < MaxNOPsPerInstruction; i++) { + unsigned Roll = RandomNumberGenerator::Get()->Random(100); + if (Roll >= NOPInsertionPercentage) + continue; + + unsigned NOPCode = RandomNumberGenerator::Get()->Random(MAX_NOPS); + + MachineInstr *NewMI = NULL; + unsigned reg = nopRegs[NOPCode][is64Bit]; + switch (NOPCode) { + case NOP: + NewMI = BuildMI(*BB, I, I->getDebugLoc(), TII->get(X86::NOOP)); + break; + case MOV_EBP: + case MOV_ESP: { + unsigned opc = is64Bit ? X86::MOV64rr : X86::MOV32rr; + NewMI = BuildMI(*BB, I, I->getDebugLoc(), TII->get(opc), reg) + .addReg(reg); + break; + } + + case LEA_ESI: + case LEA_EDI: { + unsigned opc = is64Bit ? X86::LEA64r : X86::LEA32r; + NewMI = addRegOffset(BuildMI(*BB, I, I->getDebugLoc(), + TII->get(opc), reg), + reg, false, 0); + break; + } + } + + if (NewMI != NULL) + ++InsertedNOPs; + } + // Do not insert NOPs between terminators. + if (I == FirstTerm) + break; + + I = NextI; + } + } + return true; +} + +namespace llvm { + FunctionPass *createNOPInsertionPass(bool is64Bit) { + return new NOPInsertionPass(is64Bit); + } +} Index: lib/Target/X86/X86.h =================================================================== --- lib/Target/X86/X86.h +++ lib/Target/X86/X86.h @@ -50,6 +50,10 @@ /// AVX and SSE. FunctionPass *createX86IssueVZeroUpperPass(); +/// createNOPInsertionPass - This pass adds NOPs at random between +/// instructions. +FunctionPass *createNOPInsertionPass(bool is64Bit); + /// createX86CodeEmitterPass - Return a pass that emits the collected X86 code /// to the specified MCE object. FunctionPass *createX86JITCodeEmitterPass(X86TargetMachine &TM, Index: lib/Target/X86/X86TargetMachine.cpp =================================================================== --- lib/Target/X86/X86TargetMachine.cpp +++ lib/Target/X86/X86TargetMachine.cpp @@ -224,6 +224,11 @@ ShouldPrint = true; } + if (TM->Options.NOPInsertion) { + addPass(createNOPInsertionPass(getX86Subtarget().is64Bit())); + ShouldPrint = true; + } + return ShouldPrint; } Index: test/CodeGen/X86/nop-insert-percentage.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/nop-insert-percentage.ll @@ -0,0 +1,47 @@ +; RUN: llc < %s -rng-seed=5 -nop-insertion -nop-insertion-percentage=10 \ +; RUN: | FileCheck %s --check-prefix=PERCENT10 +; RUN: llc < %s -rng-seed=5 -nop-insertion -nop-insertion-percentage=50 \ +; RUN: | FileCheck %s --check-prefix=PERCENT50 +; RUN: llc < %s -rng-seed=5 -nop-insertion -nop-insertion-percentage=100 \ +; RUN: | FileCheck %s --check-prefix=PERCENT100 + +; This test case tests NOP insertion at varying percentage levels. + +define i32 @test(i32 %x, i32 %y, i32 %z) { +entry: + %t1 = add i32 %x, %y + %t2 = mul i32 %t1, %z + %t3 = add i32 %t2, %x + %t4 = mul i32 %t3, %z + %t5 = add i32 %t4, %x + %t6 = mul i32 %t5, %z + %t7 = add i32 %t6, %x + %t8 = mul i32 %t7, %z + %t9 = add i32 %t8, %x + %t10 = mul i32 %t9, %z + %t11 = add i32 %t10, %x + ret i32 %t11 +} + +; PERCENT10: leaq (%rsi), %rsi + +; PERCENT50: movq %rbp, %rbp +; PERCENT50: leaq (%rsi), %rsi +; PERCENT50: movq %rbp, %rbp +; PERCENT50: leaq (%rdi), %rdi +; PERCENT50: movq %rsp, %rsp +; PERCENT50: leaq (%rsi), %rsi +; PERCENT50: leaq (%rdi), %rdi + +; PERCENT100: leaq (%rdi), %rdi +; PERCENT100: movq %rbp, %rbp +; PERCENT100: leaq (%rsi), %rsi +; PERCENT100: movq %rbp, %rbp +; PERCENT100: leaq (%rdi), %rdi +; PERCENT100: movq %rsp, %rsp +; PERCENT100: leaq (%rsi), %rsi +; PERCENT100: leaq (%rsi), %rsi +; PERCENT100: nop +; PERCENT100: leaq (%rsi), %rsi +; PERCENT100: movq %rsp, %rsp +; PERCENT100: movq %rbp, %rbp Index: test/CodeGen/X86/nop-insert.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/nop-insert.ll @@ -0,0 +1,44 @@ +; RUN: llc < %s -nop-insertion | FileCheck %s +; RUN: llc < %s -nop-insertion -entropy-data="test" -rng-seed=1 | FileCheck %s --check-prefix=SEED1 +; RUN: llc < %s -nop-insertion -entropy-data="test" -rng-seed=25 | FileCheck %s --check-prefix=SEED2 +; RUN: llc < %s -nop-insertion -entropy-data="test" -rng-seed=1534 | FileCheck %s --check-prefix=SEED3 +; RUN: llc < %s -nop-insertion -entropy-data="different entropy" -rng-seed=1 | FileCheck %s --check-prefix=ENTROPY + +; This test case checks that NOPs are inserted, and that the RNG seed +; affects both the placement (position of imull) and choice of these NOPs. + +; CHECK: movq %rsp, %rsp +; CHECK: imull +; CHECK: movq %rbp, %rbp +; CHECK-NOT: leaq +; CHECK-NOT: nop + +; SEED1: imull +; SEED1: leaq (%rsi), %rsi +; SEED1-NOT: movq +; SEED1-NOT: nop + +; SEED2: movq %rbp, %rbp +; SEED2: imull +; SEED2: movq %rbp, %rbp +; SEED2-NOT: leaq +; SEED2-NOT: nop + +; SEED3: movq %rsp, %rsp +; SEED3: imull +; SEED3: movq %rsp, %rsp +; SEED3-NOT: leaq +; SEED3-NOT: nop + +; ENTROPY: imull +; ENTROPY: nop +; ENTROPY: nop +; ENTROPY-NOT: leaq +; ENTROPY-NOT: movq + +define i32 @test1(i32 %x, i32 %y, i32 %z) { +entry: + %tmp = mul i32 %x, %y + %tmp2 = add i32 %tmp, %z + ret i32 %tmp2 +}