Skip to content

Commit e40bd3d

Browse files
David SehrRichard Diamond
David Sehr
authored and
Richard Diamond
committed
Insert denominator zero checks for NaCl
This IR pass for ARM inserts a comparison and a branch to trap if the denominator of a DIV or REM instruction is zero. This makes ARM fault identically to x86 in this case. BUG= https://code.google.com/p/nativeclient/issues/detail?id=2833 [email protected] Review URL: https://codereview.chromium.org/14607004 (cherry picked from commit 8c9803a)
1 parent 39f22bb commit e40bd3d

File tree

8 files changed

+320
-0
lines changed

8 files changed

+320
-0
lines changed

include/llvm/InitializePasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ void initializeExpandTlsPass(PassRegistry&);
278278
void initializeExpandVarArgsPass(PassRegistry&);
279279
void initializeFlattenGlobalsPass(PassRegistry&);
280280
void initializeGlobalCleanupPass(PassRegistry&);
281+
void initializeInsertDivideCheckPass(PassRegistry&);
281282
void initializeNaClCcRewritePass(PassRegistry&);
282283
void initializePNaClABIVerifyModulePass(PassRegistry&);
283284
void initializePNaClABIVerifyFunctionsPass(PassRegistry&);

include/llvm/Transforms/NaCl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ModulePass *createFlattenGlobalsPass();
3030
ModulePass *createGlobalCleanupPass();
3131
ModulePass *createResolveAliasesPass();
3232
ModulePass *createStripMetadataPass();
33+
FunctionPass *createInsertDivideCheckPass();
3334

3435
Instruction *PhiSafeInsertPt(Use *U);
3536
void PhiSafeReplaceUses(Use *U, Value *NewVal);

lib/Target/ARM/ARMTargetMachine.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include "llvm/Support/FormattedStream.h"
2121
#include "llvm/Support/TargetRegistry.h"
2222
#include "llvm/Target/TargetOptions.h"
23+
// @LOCALMOD-START
24+
#include "llvm/Transforms/NaCl.h"
25+
// @LOCALMOD-END
2326
#include "llvm/Transforms/Scalar.h"
2427
using namespace llvm;
2528

@@ -176,6 +179,9 @@ class ARMPassConfig : public TargetPassConfig {
176179
virtual bool addPreRegAlloc();
177180
virtual bool addPreSched2();
178181
virtual bool addPreEmitPass();
182+
// @LOCALMOD-START
183+
virtual void addIRPasses();
184+
// @LOCALMOD-END
179185
};
180186
} // namespace
181187

@@ -259,6 +265,15 @@ bool ARMPassConfig::addPreEmitPass() {
259265
return true;
260266
}
261267

268+
// @LOCALMOD-START
269+
void ARMPassConfig::addIRPasses() {
270+
if (getARMSubtarget().isTargetNaCl()) {
271+
addPass(createInsertDivideCheckPass());
272+
}
273+
TargetPassConfig::addIRPasses();
274+
}
275+
// @LOCALMOD-END
276+
262277
bool ARMBaseTargetMachine::addCodeEmitter(PassManagerBase &PM,
263278
JITCodeEmitter &JCE) {
264279
// Machine code emitter pass for ARM.

lib/Transforms/NaCl/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_llvm_library(LLVMNaClTransforms
77
ExpandTlsConstantExpr.cpp
88
ExpandUtils.cpp
99
ExpandVarArgs.cpp
10+
InsertDivideCheck.cpp
1011
FlattenGlobals.cpp
1112
GlobalCleanup.cpp
1213
StripMetadata.cpp
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//===- InsertDivideCheck.cpp - Add divide by zero checks ------------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This pass adds a check for divide by zero before every integer DIV or REM.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#define DEBUG_TYPE "add-divide-check"
15+
#include "llvm/ADT/SmallPtrSet.h"
16+
#include "llvm/ADT/STLExtras.h"
17+
#include "llvm/IR/BasicBlock.h"
18+
#include "llvm/IR/Constants.h"
19+
#include "llvm/IR/Function.h"
20+
#include "llvm/IR/Instructions.h"
21+
#include "llvm/IR/Intrinsics.h"
22+
#include "llvm/IR/LLVMContext.h"
23+
#include "llvm/Pass.h"
24+
#include "llvm/Support/CFG.h"
25+
#include "llvm/Transforms/NaCl.h"
26+
27+
using namespace llvm;
28+
29+
namespace {
30+
class InsertDivideCheck : public FunctionPass {
31+
public:
32+
static char ID;
33+
InsertDivideCheck() : FunctionPass(ID) {
34+
initializeInsertDivideCheckPass(*PassRegistry::getPassRegistry());
35+
}
36+
37+
bool runOnFunction(Function &F);
38+
};
39+
}
40+
41+
static BasicBlock *CreateTrapBlock(Function &F, DebugLoc dl) {
42+
BasicBlock *TrapBlock = BasicBlock::Create(F.getContext(), "divrem.by.zero",
43+
&F);
44+
Value *TrapFn = Intrinsic::getDeclaration(F.getParent(), Intrinsic::trap);
45+
CallInst::Create(TrapFn, "", TrapBlock)->setDebugLoc(dl);
46+
(new UnreachableInst(F.getContext(), TrapBlock))->setDebugLoc(dl);
47+
return TrapBlock;
48+
}
49+
50+
bool InsertDivideCheck::runOnFunction(Function &F) {
51+
SmallPtrSet<Instruction*, 8> GuardedDivs;
52+
// If the pass finds a DIV/REM that needs to be checked for zero denominator,
53+
// it will insert a new "trap" block, and split the block that contains the
54+
// DIV/REM into two blocks. The new BasicBlocks are added after the current
55+
// BasicBlock, so that if there is more than one DIV/REM in the same block,
56+
// all are visited.
57+
for (Function::iterator I = F.begin(); I != F.end(); I++) {
58+
BasicBlock *BB = I;
59+
60+
for (BasicBlock::iterator BI = BB->begin(), BE = BB->end();
61+
BI != BE; BI++) {
62+
BinaryOperator *DivInst = dyn_cast<BinaryOperator>(BI);
63+
if (!DivInst || (GuardedDivs.count(DivInst) != 0))
64+
continue;
65+
unsigned Opcode = DivInst->getOpcode();
66+
if (Opcode != Instruction::SDiv && Opcode != Instruction::UDiv &&
67+
Opcode != Instruction::SRem && Opcode != Instruction::URem)
68+
continue;
69+
Value *Denominator = DivInst->getOperand(1);
70+
if (!Denominator->getType()->isIntegerTy())
71+
continue;
72+
DebugLoc dl = DivInst->getDebugLoc();
73+
if (ConstantInt *DenomConst = dyn_cast<ConstantInt>(Denominator)) {
74+
// Divides by constants do not need a denominator test.
75+
if (DenomConst->isZero()) {
76+
// For explicit divides by zero, insert a trap before DIV/REM
77+
Value *TrapFn = Intrinsic::getDeclaration(F.getParent(),
78+
Intrinsic::trap);
79+
CallInst::Create(TrapFn, "", DivInst)->setDebugLoc(dl);
80+
}
81+
continue;
82+
}
83+
// Create a trap block.
84+
BasicBlock *TrapBlock = CreateTrapBlock(F, dl);
85+
// Move instructions in BB from DivInst to BB's end to a new block.
86+
BasicBlock *Successor = BB->splitBasicBlock(BI, "guarded.divrem");
87+
// Remove the unconditional branch inserted by splitBasicBlock.
88+
BB->getTerminator()->eraseFromParent();
89+
// Remember that DivInst was already processed, so that when we process
90+
// inserted blocks later, we do not attempt to again guard it.
91+
GuardedDivs.insert(DivInst);
92+
// Compare the denominator with zero.
93+
Value *Zero = ConstantInt::get(Denominator->getType(), 0);
94+
Value *DenomIsZero = new ICmpInst(*BB, ICmpInst::ICMP_EQ, Denominator,
95+
Zero, "");
96+
// Put in a condbranch to the trap block.
97+
BranchInst::Create(TrapBlock, Successor, DenomIsZero, BB);
98+
// BI is invalidated when we split. Stop the BasicBlock iterator.
99+
break;
100+
}
101+
}
102+
103+
return false;
104+
}
105+
106+
char InsertDivideCheck::ID = 0;
107+
INITIALIZE_PASS(InsertDivideCheck, "insert-divide-check",
108+
"Insert divide by zero checks", false, false)
109+
110+
FunctionPass *llvm::createInsertDivideCheckPass() {
111+
return new InsertDivideCheck();
112+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
; RUN: opt < %s -insert-divide-check -S | FileCheck -check-prefix=OPT %s
2+
3+
declare void @foo()
4+
5+
; Check for multiple divs that occur one block.
6+
define i32 @twodivs_one_block(i32 %x, i32 %y) #0 {
7+
entry:
8+
call void @foo()
9+
br label %divblock
10+
divblock:
11+
%div1 = sdiv i32 %x, %y
12+
%div2 = sdiv i32 %x, %y
13+
; OPT: %0 = icmp eq i32 %y, 0
14+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
15+
; OPT: guarded.divrem:
16+
; OPT-NEXT: sdiv
17+
; OPT: %1 = icmp eq i32 %y, 0
18+
; OPT-NEXT: br i1 %1, label %divrem.by.zero1, label %guarded.divrem2
19+
; OPT: guarded.divrem2:
20+
; OPT-NEXT: sdiv
21+
; OPT-NEXT: add
22+
; OPT: divrem.by.zero:
23+
; OPT-NEXT: call void @llvm.trap()
24+
; OPT-NEXT: unreachable
25+
; OPT: divrem.by.zero1:
26+
; OPT-NEXT: call void @llvm.trap()
27+
; OPT-NEXT: unreachable
28+
%sum = add i32 %div1, %div2
29+
ret i32 %sum
30+
}
31+
32+
define i32 @twodivs_three_blocks(i32 %x, i32 %y) #0 {
33+
entry:
34+
call void @foo()
35+
br label %divblock
36+
divblock:
37+
%div1 = sdiv i32 %x, %y
38+
; OPT: %0 = icmp eq i32 %y, 0
39+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
40+
; OPT: guarded.divrem:
41+
; OPT-NEXT: sdiv
42+
; OPT-NEXT: br label %exitblock
43+
br label %exitblock
44+
exitblock:
45+
call void @foo()
46+
%div2 = sdiv i32 %x, %y
47+
; OPT: %1 = icmp eq i32 %y, 0
48+
; OPT-NEXT: br i1 %1, label %divrem.by.zero1, label %guarded.divrem2
49+
; OPT: guarded.divrem2:
50+
; OPT-NEXT: sdiv
51+
; OPT-NEXT: add
52+
; OPT: divrem.by.zero:
53+
; OPT-NEXT: call void @llvm.trap()
54+
; OPT-NEXT: unreachable
55+
; OPT: divrem.by.zero1:
56+
; OPT-NEXT: call void @llvm.trap()
57+
; OPT-NEXT: unreachable
58+
%sum = add i32 %div1, %div2
59+
ret i32 %sum
60+
}
61+
62+
; Check for divs that occur in blocks with multiple predecessors.
63+
define i32 @onediv_two_predecessors(i32 %x, i32 %y) #0 {
64+
entry:
65+
call void @foo()
66+
br label %divblock
67+
divblock:
68+
%x1 = phi i32 [%x, %entry], [%x2, %divblock]
69+
%div1 = sdiv i32 %x, %y
70+
; OPT: %0 = icmp eq i32 %y, 0
71+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
72+
; OPT: guarded.divrem:
73+
; OPT-NEXT: sdiv
74+
; OPT-NEXT: sub
75+
; OPT: divrem.by.zero:
76+
; OPT-NEXT: call void @llvm.trap()
77+
; OPT-NEXT: unreachable
78+
%x2 = sub i32 %x1, 1
79+
%p = icmp ne i32 %x2, 0
80+
br i1 %p, label %divblock, label %exitblock
81+
exitblock:
82+
call void @foo()
83+
ret i32 %div1
84+
}
85+

test/NaCl/ARM/divrem-guards.ll

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
; RUN: opt < %s -insert-divide-check -S | FileCheck -check-prefix=OPT %s
2+
; RUN: llc -mtriple=armv7-unknown-nacl -sfi-branch -filetype=obj %s -o - \
3+
; RUN: | llvm-objdump -disassemble -triple armv7 -mattr=+nacl-trap - \
4+
; RUN: | FileCheck -check-prefix=ARM %s
5+
6+
7+
; Check for all four operators that need guards.
8+
define i32 @mysdiv(i32 %x, i32 %y) #0 {
9+
entry:
10+
%div1 = sdiv i32 %x, %y
11+
; OPT: %0 = icmp eq i32 %y, 0
12+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
13+
; OPT: guarded.divrem:
14+
; OPT-NEXT: sdiv
15+
; OPT-NEXT: ret
16+
; OPT: divrem.by.zero:
17+
; OPT-NEXT: call void @llvm.trap()
18+
; OPT-NEXT: unreachable
19+
; ARM: cmp r1, #0
20+
; ARM-NEXT: beq
21+
ret i32 %div1
22+
; ARM: f0 de fe e7
23+
}
24+
25+
define i32 @myudiv(i32 %x, i32 %y) #0 {
26+
entry:
27+
%div1 = udiv i32 %x, %y
28+
; OPT: %0 = icmp eq i32 %y, 0
29+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
30+
; OPT: guarded.divrem:
31+
; OPT-NEXT: udiv
32+
; OPT-NEXT: ret
33+
; OPT: divrem.by.zero:
34+
; OPT-NEXT: call void @llvm.trap()
35+
; OPT-NEXT: unreachable
36+
; ARM: cmp r1, #0
37+
; ARM-NEXT: beq
38+
ret i32 %div1
39+
; ARM: f0 de fe e7
40+
}
41+
42+
define i32 @mysrem(i32 %x, i32 %y) #0 {
43+
entry:
44+
%rem1 = srem i32 %x, %y
45+
; OPT: %0 = icmp eq i32 %y, 0
46+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
47+
; OPT: guarded.divrem:
48+
; OPT-NEXT: srem
49+
; OPT-NEXT: ret
50+
; OPT: divrem.by.zero:
51+
; OPT-NEXT: call void @llvm.trap()
52+
; OPT-NEXT: unreachable
53+
; ARM: cmp r1, #0
54+
; ARM-NEXT: beq
55+
ret i32 %rem1
56+
; ARM: f0 de fe e7
57+
}
58+
59+
define i32 @myurem(i32 %x, i32 %y) #0 {
60+
entry:
61+
%rem1 = urem i32 %x, %y
62+
; OPT: %0 = icmp eq i32 %y, 0
63+
; OPT-NEXT: br i1 %0, label %divrem.by.zero, label %guarded.divrem
64+
; OPT: guarded.divrem:
65+
; OPT-NEXT: urem
66+
; OPT-NEXT: ret
67+
; OPT: divrem.by.zero:
68+
; OPT-NEXT: call void @llvm.trap()
69+
; OPT-NEXT: unreachable
70+
; ARM: cmp r1, #0
71+
; ARM-NEXT: beq
72+
ret i32 %rem1
73+
; ARM: f0 de fe e7
74+
}
75+
76+
; Divides by non-zero constants should not be guarded.
77+
define i32 @mysdiv_const(i32 %x) #0 {
78+
entry:
79+
%div1 = sdiv i32 %x, 10
80+
; OPT-NOT: icmp
81+
; OPT-NOT: br
82+
; OPT-NOT: guarded.divrem:
83+
; OPT-NOT: divrem.by.zero:
84+
; OPT-NOT: call void @llvm.trap()
85+
; OPT-NOT: unreachable
86+
; ARM-NOT: cmp r1, #0
87+
; ARM-NOT: f0 de fe e7
88+
ret i32 %div1
89+
}
90+
91+
; Divides by explicit zero should prefixed by a trap.
92+
define i32 @mysdiv_zero(i32 %x) #0 {
93+
entry:
94+
%div1 = sdiv i32 %x, 0
95+
; OPT-NOT: guarded.divrem:
96+
; OPT-NOT: divrem.by.zero:
97+
; OPT: call void @llvm.trap()
98+
; OPT-NEXT: sdiv
99+
; ARM-NOT: cmp r1, #0
100+
; ARM: f0 de fe e7
101+
ret i32 %div1
102+
}
103+
104+
attributes #0 = { nounwind }

tools/opt/opt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ int main(int argc, char **argv) {
431431
initializeExpandVarArgsPass(Registry);
432432
initializeFlattenGlobalsPass(Registry);
433433
initializeGlobalCleanupPass(Registry);
434+
initializeInsertDivideCheckPass(Registry);
434435
initializePNaClABIVerifyFunctionsPass(Registry);
435436
initializePNaClABIVerifyModulePass(Registry);
436437
initializeResolveAliasesPass(Registry);

0 commit comments

Comments
 (0)