From d95825bef428b631099440d01a3955e7f6276b16 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 19 Dec 2015 11:21:15 +0000 Subject: Implement support for lowering the ``llvm.objectsize`` intrinsic introduced in LLVM 2.7. Previously KLEE would emit the following error message when ``IntrinsicLowering::LowerIntrinsicCall()`` was called on the intrinsic ``` LLVM ERROR: Code generator does not support intrinsic function 'llvm.objectsize.i64.p0i8'! ``` The ``IntrinsicCleaner`` pass now lowers this intrinsic to a constant integer depending on the second argument to the intrinsic. This corresponds to the case where the size of the object pointed to by the first argument is unknown. An alternative design would be to handle this intrinsic in the Executor where is actually possible to know the size of objects during execution. However that would be much more complicated because if the pointer is symbolic we would have to fork for every object that could be pointed to. The implementation is similar to #260 but we handle the second argument to the intrinsic correctly and also have a simple test case. Unfortunately we have to have a different version of the test case for LLVM 2.9 because the expected suffix for the intrinsic is different in LLVM 2.9. --- lib/Module/IntrinsicCleaner.cpp | 25 ++++++++++++++++++++++++- test/Intrinsics/objectsize.ll | 36 ++++++++++++++++++++++++++++++++++++ test/Intrinsics/objectsize.llvm29.ll | 34 ++++++++++++++++++++++++++++++++++ test/lit.cfg | 2 ++ 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 test/Intrinsics/objectsize.ll create mode 100644 test/Intrinsics/objectsize.llvm29.ll diff --git a/lib/Module/IntrinsicCleaner.cpp b/lib/Module/IntrinsicCleaner.cpp index da97621a..54ad7db7 100644 --- a/lib/Module/IntrinsicCleaner.cpp +++ b/lib/Module/IntrinsicCleaner.cpp @@ -232,7 +232,30 @@ bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b, Module &M) { dirty = true; break; } - + case Intrinsic::objectsize: { + // We don't know the size of an object in general so we replace + // with 0 or -1 depending on the second argument to the intrinsic. + assert(ii->getNumArgOperands() == 2 && "wrong number of arguments"); + Value *minArg = ii->getArgOperand(1); + assert(minArg && "Failed to get second argument"); + ConstantInt *minArgAsInt = dyn_cast(minArg); + assert(minArgAsInt && "Second arg is not a ConstantInt"); + assert(minArgAsInt->getBitWidth() == 1 && "Second argument is not an i1"); + Value *replacement = NULL; + LLVM_TYPE_Q IntegerType *intType = dyn_cast(ii->getType()); + assert(intType && "intrinsic does not have integer return type"); + if (minArgAsInt->isZero()) { + // min=false + replacement = ConstantInt::get(intType, -1, /*isSigned=*/true); + } else { + // min=true + replacement = ConstantInt::get(intType, 0, /*isSigned=*/false); + } + ii->replaceAllUsesWith(replacement); + ii->eraseFromParent(); + dirty = true; + break; + } default: if (LowerIntrinsics) IL->LowerIntrinsicCall(ii); diff --git a/test/Intrinsics/objectsize.ll b/test/Intrinsics/objectsize.ll new file mode 100644 index 00000000..8b75ce8f --- /dev/null +++ b/test/Intrinsics/objectsize.ll @@ -0,0 +1,36 @@ +; Unfortunately LLVM 2.9 has a different suffix for the ``llvm.objectsize`` instrinsic +; so this LLVM IR fails to verify for that version. +; +; REQUIRES: not-llvm-2.9 +; RUN: %llvmas %s -o=%t.bc +; RUN: rm -rf %t.klee-out +; RUN: %klee -exit-on-error --output-dir=%t.klee-out -disable-opt %t.bc +; ModuleID = 'objectsize.c' +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @main() nounwind uwtable { +entry: + %a = alloca i8*, align 8 + %0 = load i8** %a, align 8 + %1 = call i64 @llvm.objectsize.i64.p0i8(i8* %0, i1 true) + %cmp = icmp ne i64 %1, 0 + br i1 %cmp, label %abort.block, label %continue.block + +continue.block: + %2 = load i8** %a, align 8 + %3 = call i64 @llvm.objectsize.i64.p0i8(i8* %2, i1 false) + %cmp1 = icmp ne i64 %3, -1 + br i1 %cmp1, label %abort.block, label %exit.block + +exit.block: + ret i32 0 + +abort.block: + call void @abort() + unreachable +} + +declare i64 @llvm.objectsize.i64.p0i8(i8*, i1) nounwind readnone + +declare void @abort() noreturn nounwind diff --git a/test/Intrinsics/objectsize.llvm29.ll b/test/Intrinsics/objectsize.llvm29.ll new file mode 100644 index 00000000..386eaddb --- /dev/null +++ b/test/Intrinsics/objectsize.llvm29.ll @@ -0,0 +1,34 @@ +; FIXME: Remove this test case when we drop LLVM 2.9 support +; REQUIRES: llvm-2.9 +; RUN: %llvmas %s -o=%t.bc +; RUN: rm -rf %t.klee-out +; RUN: %klee -exit-on-error --output-dir=%t.klee-out -disable-opt %t.bc +; ModuleID = 'objectsize.c' +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @main() nounwind { +entry: + %a = alloca i8*, align 8 + %0 = load i8** %a, align 8 + %1 = call i64 @llvm.objectsize.i64(i8* %0, i1 true) + %cmp = icmp ne i64 %1, 0 + br i1 %cmp, label %abort.block, label %continue.block + +continue.block: + %2 = load i8** %a, align 8 + %3 = call i64 @llvm.objectsize.i64(i8* %2, i1 false) + %cmp1 = icmp ne i64 %3, -1 + br i1 %cmp1, label %abort.block, label %exit.block + +exit.block: + ret i32 0 + +abort.block: + call void @abort() + unreachable +} + +declare i64 @llvm.objectsize.i64(i8*, i1) nounwind readnone + +declare void @abort() noreturn nounwind diff --git a/test/lit.cfg b/test/lit.cfg index 06ad17aa..f1253ea6 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -85,6 +85,8 @@ for name in subs: # Add a substitution for lli. config.substitutions.append( ('%lli', os.path.join(llvm_tools_dir, 'lli')) ) +# Add a substitution for llvm-as +config.substitutions.append( ('%llvmas', os.path.join(llvm_tools_dir, 'llvm-as')) ) # Get KLEE and Kleaver specific parameters passed on llvm-lit cmd line # e.g. llvm-lit --param klee_opts=--help -- cgit 1.4.1