Index: llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h =================================================================== --- llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h +++ llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.h @@ -17,6 +17,10 @@ // Log2 of the NaCl MIPS sandbox's instruction bundle size. static const unsigned MIPS_NACL_BUNDLE_ALIGN = 4u; +bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx, + bool *IsStore = NULL); +bool baseRegNeedsLoadStoreMask(unsigned Reg); + // This function creates an MCELFStreamer for Mips NaCl. MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, raw_ostream &OS, Index: llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp +++ llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp @@ -9,8 +9,10 @@ // // This file implements MCELFStreamer for Mips NaCl. It emits .o object files // as required by NaCl's SFI sandbox. It inserts address-masking instructions -// before dangerous control-flow instructions. It aligns on bundle size all -// functions and all targets of indirect branches. +// before dangerous control-flow and memory access instructions. It inserts +// address-masking instructions after instructions that change the stack +// pointer. It ensures that the mask and the dangerous instruction are always +// emitted in the same bundle. // //===----------------------------------------------------------------------===// @@ -25,6 +27,7 @@ namespace { const unsigned IndirectBranchMaskReg = Mips::T6; +const unsigned LoadStoreStackMaskReg = Mips::T7; /// Extend the generic MCELFStreamer class so that it can mask dangerous /// instructions. @@ -42,6 +45,11 @@ return MI.getOpcode() == Mips::JR || MI.getOpcode() == Mips::RET; } + bool isStackPointerFirstOperand(const MCInst &MI) { + return (MI.getNumOperands() > 0 && MI.getOperand(0).isReg() + && MI.getOperand(0).getReg() == Mips::SP); + } + void emitMask(unsigned AddrReg, unsigned MaskReg, const MCSubtargetInfo &STI) { MCInst MaskInst; @@ -63,14 +71,57 @@ EmitBundleUnlock(); } + // Sandbox memory access or SP change. Insert mask operation before and/or + // after the instruction. + void sandboxLoadStoreStackChange(const MCInst &MI, unsigned AddrIdx, + const MCSubtargetInfo &STI, bool MaskBefore, + bool MaskAfter) { + EmitBundleLock(false); + if (MaskBefore) { + // Sandbox memory access. + unsigned BaseReg = MI.getOperand(AddrIdx).getReg(); + emitMask(BaseReg, LoadStoreStackMaskReg, STI); + } + MCELFStreamer::EmitInstruction(MI, STI); + if (MaskAfter) { + // Sandbox SP change. + unsigned SPReg = MI.getOperand(0).getReg(); + assert((Mips::SP == SPReg) && "Unexpected stack-pointer register."); + emitMask(SPReg, LoadStoreStackMaskReg, STI); + } + EmitBundleUnlock(); + } + public: /// This function is the one used to emit instruction data into the ELF /// streamer. We override it to mask dangerous instructions. virtual void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) { - if (isIndirectJump(Inst)) + // Sandbox indirect jumps. + if (isIndirectJump(Inst)) { sandboxIndirectJump(Inst, STI); - else - MCELFStreamer::EmitInstruction(Inst, STI); + return; + } + + // Sandbox loads, stores and SP changes. + unsigned AddrIdx; + bool IsStore; + bool IsMemAccess = isBasePlusOffsetMemoryAccess(Inst.getOpcode(), &AddrIdx, + &IsStore); + bool IsSPFirstOperand = isStackPointerFirstOperand(Inst); + if (IsMemAccess || IsSPFirstOperand) { + bool MaskBefore = (IsMemAccess + && baseRegNeedsLoadStoreMask(Inst.getOperand(AddrIdx) + .getReg())); + bool MaskAfter = IsSPFirstOperand && !IsStore; + if (MaskBefore || MaskAfter) + sandboxLoadStoreStackChange(Inst, AddrIdx, STI, MaskBefore, MaskAfter); + else + MCELFStreamer::EmitInstruction(Inst, STI); + return; + } + + // None of the sandboxing applies, just emit the instruction. + MCELFStreamer::EmitInstruction(Inst, STI); } }; @@ -78,6 +129,56 @@ namespace llvm { +bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx, + bool *IsStore) { + if (IsStore) + *IsStore = false; + + switch (Opcode) { + default: + return false; + + // Load instructions with base address register in position 1. + case Mips::LB: + case Mips::LBu: + case Mips::LH: + case Mips::LHu: + case Mips::LW: + case Mips::LWC1: + case Mips::LDC1: + case Mips::LL: + case Mips::LWL: + case Mips::LWR: + *AddrIdx = 1; + return true; + + // Store instructions with base address register in position 1. + case Mips::SB: + case Mips::SH: + case Mips::SW: + case Mips::SWC1: + case Mips::SDC1: + case Mips::SWL: + case Mips::SWR: + *AddrIdx = 1; + if (IsStore) + *IsStore = true; + return true; + + // Store instructions with base address register in position 2. + case Mips::SC: + *AddrIdx = 2; + if (IsStore) + *IsStore = true; + return true; + } +} + +bool baseRegNeedsLoadStoreMask(unsigned Reg) { + // The contents of SP and thread pointer register do not require masking. + return Reg != Mips::SP && Reg != Mips::T8; +} + MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, raw_ostream &OS, MCCodeEmitter *Emitter, bool RelaxAll, Index: llvm/trunk/lib/Target/Mips/MipsDelaySlotFiller.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MipsDelaySlotFiller.cpp +++ llvm/trunk/lib/Target/Mips/MipsDelaySlotFiller.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "delay-slot-filler" +#include "MCTargetDesc/MipsMCNaCl.h" #include "Mips.h" #include "MipsInstrInfo.h" #include "MipsTargetMachine.h" @@ -531,6 +532,18 @@ if (delayHasHazard(*I, RegDU, IM)) continue; + if (TM.getSubtarget().isTargetNaCl()) { + // In NaCl, instructions that must be masked are forbidden in delay slots. + // We only check for loads, stores and SP changes. Calls, returns and + // branches are not checked because non-NaCl targets never put them in + // delay slots. + unsigned AddrIdx; + if ((isBasePlusOffsetMemoryAccess(I->getOpcode(), &AddrIdx) + && baseRegNeedsLoadStoreMask(I->getOperand(AddrIdx).getReg())) + || I->modifiesRegister(Mips::SP, TM.getRegisterInfo())) + continue; + } + Filler = I; return true; } Index: llvm/trunk/test/CodeGen/Mips/nacl-branch-delay.ll =================================================================== --- llvm/trunk/test/CodeGen/Mips/nacl-branch-delay.ll +++ llvm/trunk/test/CodeGen/Mips/nacl-branch-delay.ll @@ -0,0 +1,71 @@ +; RUN: llc -filetype=asm -mtriple=mipsel-none-linux -relocation-model=static \ +; RUN: -O3 < %s | FileCheck %s + +; RUN: llc -filetype=asm -mtriple=mipsel-none-nacl -relocation-model=static \ +; RUN: -O3 < %s | FileCheck %s -check-prefix=CHECK-NACL + +@x = global i32 0, align 4 +declare void @f1(i32) +declare void @f2() + + +define void @test1() { + %1 = load i32* @x, align 4 + call void @f1(i32 %1) + ret void + + +; CHECK-LABEL: test1 + +; We first make sure that for non-NaCl targets branch-delay slot contains +; dangerous instructions. + +; Check that branch-delay slot is used to load argument from x before function +; call. + +; CHECK: jal +; CHECK-NEXT: lw $4, %lo(x)(${{[0-9]+}}) + +; Check that branch-delay slot is used for adjusting sp before return. + +; CHECK: jr $ra +; CHECK-NEXT: addiu $sp, $sp, {{[0-9]+}} + + +; For NaCl, check that branch-delay slot doesn't contain dangerous instructions. + +; CHECK-NACL: jal +; CHECK-NACL-NEXT: nop + +; CHECK-NACL: jr $ra +; CHECK-NACL-NEXT: nop +} + + +define void @test2() { + store i32 1, i32* @x, align 4 + tail call void @f2() + ret void + + +; CHECK-LABEL: test2 + +; Check that branch-delay slot is used for storing to x before function call. + +; CHECK: jal +; CHECK-NEXT: sw ${{[0-9]+}}, %lo(x)(${{[0-9]+}}) + +; Check that branch-delay slot is used for adjusting sp before return. + +; CHECK: jr $ra +; CHECK-NEXT: addiu $sp, $sp, {{[0-9]+}} + + +; For NaCl, check that branch-delay slot doesn't contain dangerous instructions. + +; CHECK-NACL: jal +; CHECK-NACL-NEXT: nop + +; CHECK-NACL: jr $ra +; CHECK-NACL-NEXT: nop +} Index: llvm/trunk/test/MC/Mips/nacl-mask.s =================================================================== --- llvm/trunk/test/MC/Mips/nacl-mask.s +++ llvm/trunk/test/MC/Mips/nacl-mask.s @@ -5,6 +5,10 @@ # This test tests that address-masking sandboxing is added when given assembly # input. + +# Test that address-masking sandboxing is added before indirect branches and +# returns. + test1: .set noreorder @@ -26,3 +30,190 @@ # CHECK: and $ra, $ra, $14 # CHECK-NEXT: jr $ra + + + +# Test that address-masking sandboxing is added before load instructions. + +test2: + .set noreorder + + lb $4, 0($1) + nop + lbu $4, 0($2) + lh $4, 0($3) + lhu $1, 0($4) + lw $4, 0($5) + lwc1 $f0, 0($6) + ldc1 $f2, 0($7) + ll $4, 0($8) + lwl $4, 0($9) + lwr $4, 0($10) + + lw $4, 0($sp) + lw $4, 0($t8) + +# CHECK-LABEL: test2: + +# CHECK: and $1, $1, $15 +# CHECK-NEXT: lb $4, 0($1) + +# Check that additional nop is inserted, to align mask and load to the next +# bundle. + +# CHECK: nop +# CHECK: nop + +# CHECK: and $2, $2, $15 +# CHECK-NEXT: lbu $4, 0($2) + +# CHECK: and $3, $3, $15 +# CHECK-NEXT: lh $4, 0($3) + +# CHECK: and $4, $4, $15 +# CHECK-NEXT: lhu $1, 0($4) + +# CHECK: and $5, $5, $15 +# CHECK-NEXT: lw $4, 0($5) + +# CHECK: and $6, $6, $15 +# CHECK-NEXT: lwc1 $f0, 0($6) + +# CHECK: and $7, $7, $15 +# CHECK-NEXT: ldc1 $f2, 0($7) + +# CHECK: and $8, $8, $15 +# CHECK-NEXT: ll $4, 0($8) + +# CHECK: and $9, $9, $15 +# CHECK-NEXT: lwl $4, 0($9) + +# CHECK: and $10, $10, $15 +# CHECK-NEXT: lwr $4, 0($10) + + +# Check that loads where base register is $sp or $t8 (thread pointer register) +# are not masked. + +# CHECK-NOT: and +# CHECK: lw $4, 0($sp) +# CHECK-NOT: and +# CHECK: lw $4, 0($24) + + + +# Test that address-masking sandboxing is added before store instructions. + +test3: + .set noreorder + + sb $4, 0($1) + nop + sh $4, 0($2) + sw $4, 0($3) + swc1 $f0, 0($4) + sdc1 $f2, 0($5) + swl $4, 0($6) + swr $4, 0($7) + sc $4, 0($8) + + sw $4, 0($sp) + sw $4, 0($t8) + +# CHECK-LABEL: test3: + +# CHECK: and $1, $1, $15 +# CHECK-NEXT: sb $4, 0($1) + +# Check that additional nop is inserted, to align mask and store to the next +# bundle. + +# CHECK: nop +# CHECK: nop + +# CHECK: and $2, $2, $15 +# CHECK-NEXT: sh $4, 0($2) + +# CHECK: and $3, $3, $15 +# CHECK-NEXT: sw $4, 0($3) + +# CHECK: and $4, $4, $15 +# CHECK-NEXT: swc1 $f0, 0($4) + +# CHECK: and $5, $5, $15 +# CHECK-NEXT: sdc1 $f2, 0($5) + +# CHECK: and $6, $6, $15 +# CHECK-NEXT: swl $4, 0($6) + +# CHECK: and $7, $7, $15 +# CHECK-NEXT: swr $4, 0($7) + +# CHECK: and $8, $8, $15 +# CHECK-NEXT: sc $4, 0($8) + + +# Check that stores where base register is $sp or $t8 (thread pointer register) +# are not masked. + +# CHECK-NOT: and +# CHECK: sw $4, 0($sp) +# CHECK-NOT: and +# CHECK: sw $4, 0($24) + + + +# Test that address-masking sandboxing is added after instructions that change +# stack pointer. + +test4: + .set noreorder + + addiu $sp, $sp, 24 + nop + addu $sp, $sp, $1 + lw $sp, 0($2) + lw $sp, 123($sp) + sw $sp, 123($sp) + +# CHECK-LABEL: test4: + +# CHECK: addiu $sp, $sp, 24 +# CHECK-NEXT: and $sp, $sp, $15 + +# Check that additional nop is inserted, to align instruction and mask to the +# next bundle. + +# CHECK: nop +# CHECK: nop + +# CHECK: addu $sp, $sp, $1 +# CHECK-NEXT: and $sp, $sp, $15 + +# Since we next check sandboxing sequence which consists of 3 instructions, +# check that 2 additional nops are inserted, to align it to the next bundle. + +# CHECK: nop +# CHECK: nop + + +# Check that for instructions that change stack-pointer and load from memory +# masks are added before and after the instruction. + +# CHECK: and $2, $2, $15 +# CHECK-NEXT: lw $sp, 0($2) +# CHECK-NEXT: and $sp, $sp, $15 + +# For loads where $sp is destination and base, check that mask is added after +# but not before. + +# CHECK-NOT: and +# CHECK: lw $sp, 123($sp) +# CHECK-NEXT: and $sp, $sp, $15 + +# For stores where $sp is destination and base, check that mask is added neither +# before nor after. + +# CHECK-NOT: and +# CHECK: sw $sp, 123($sp) +# CHECK-NOT: and