diff options
-rw-r--r-- | include/klee/Expr/Expr.h | 8 | ||||
-rw-r--r-- | lib/Core/Executor.cpp | 8 | ||||
-rw-r--r-- | lib/Expr/Expr.cpp | 31 | ||||
-rw-r--r-- | test/VectorInstructions/external_call.c | 79 |
4 files changed, 115 insertions, 11 deletions
diff --git a/include/klee/Expr/Expr.h b/include/klee/Expr/Expr.h index b509294c..ae2760a2 100644 --- a/include/klee/Expr/Expr.h +++ b/include/klee/Expr/Expr.h @@ -95,7 +95,9 @@ public: /// The type of an expression is simply its width, in bits. typedef unsigned Width; - + + // NOTE: The prefix "Int" in no way implies the integer type of expression. + // For example, Int64 can indicate i64, double or <2 * i32> in different cases. static const Width InvalidWidth = 0; static const Width Bool = 1; static const Width Int8 = 8; @@ -103,6 +105,10 @@ public: static const Width Int32 = 32; static const Width Int64 = 64; static const Width Fl80 = 80; + static const Width Int128 = 128; + static const Width Int256 = 256; + static const Width Int512 = 512; + static const Width MaxWidth = Int512; enum Kind { diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 42405982..6fad8830 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -3810,11 +3810,13 @@ void Executor::callExternalFunction(ExecutionState &state, } // normal external function handling path - // allocate 128 bits for each argument (+return value) to support fp80's; + // allocate 512 bits for each argument (+return value) to support + // fp80's and SIMD vectors as parameters for external calls; // we could iterate through all the arguments first and determine the exact // size we need, but this is faster, and the memory usage isn't significant. - uint64_t *args = (uint64_t*) alloca(2*sizeof(*args) * (arguments.size() + 1)); - memset(args, 0, 2 * sizeof(*args) * (arguments.size() + 1)); + size_t allocatedBytes = Expr::MaxWidth / 8 * (arguments.size() + 1); + uint64_t *args = (uint64_t*) alloca(allocatedBytes); + memset(args, 0, allocatedBytes); unsigned wordIndex = 2; for (std::vector<ref<Expr> >::iterator ai = arguments.begin(), ae = arguments.end(); ai!=ae; ++ai) { diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp index 50020fb1..d82cbee3 100644 --- a/lib/Expr/Expr.cpp +++ b/lib/Expr/Expr.cpp @@ -16,6 +16,7 @@ // Core. If we need to do arithmetic, we probably want to use APInt. #include "klee/Support/IntEvaluation.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Hashing.h" #if LLVM_VERSION_CODE >= LLVM_VERSION(13, 0) #include "llvm/ADT/StringExtras.h" @@ -311,6 +312,9 @@ void Expr::printWidth(llvm::raw_ostream &os, Width width) { case Expr::Int32: os << "Expr::Int32"; break; case Expr::Int64: os << "Expr::Int64"; break; case Expr::Fl80: os << "Expr::Fl80"; break; + case Expr::Int128: os << "Expr::Int128"; break; + case Expr::Int256: os << "Expr::Int256"; break; + case Expr::Int512: os << "Expr::Int512"; break; default: os << "<invalid type: " << (unsigned) width << ">"; } } @@ -336,23 +340,32 @@ void Expr::dump() const { ref<Expr> ConstantExpr::fromMemory(void *address, Width width) { switch (width) { + default: assert(0 && "invalid width"); case Expr::Bool: return ConstantExpr::create(*(( uint8_t*) address), width); case Expr::Int8: return ConstantExpr::create(*(( uint8_t*) address), width); case Expr::Int16: return ConstantExpr::create(*((uint16_t*) address), width); case Expr::Int32: return ConstantExpr::create(*((uint32_t*) address), width); case Expr::Int64: return ConstantExpr::create(*((uint64_t*) address), width); // FIXME: what about machines without x87 support? - default: - return ConstantExpr::alloc( - llvm::APInt(width, - (width + llvm::APFloatBase::integerPartWidth - 1) / - llvm::APFloatBase::integerPartWidth, - (const uint64_t *)address)); + case Expr::Fl80: { + size_t numWords = (width + llvm::APFloatBase::integerPartWidth - 1) / + llvm::APFloatBase::integerPartWidth; + return ConstantExpr::alloc(llvm::APInt( + width, llvm::ArrayRef<uint64_t>((const uint64_t *)address, numWords))); + } + case Expr::Int128: + case Expr::Int256: + case Expr::Int512: { + size_t numWords = width / APInt::APINT_BITS_PER_WORD; + return ConstantExpr::alloc(llvm::APInt( + width, llvm::ArrayRef<uint64_t>((const uint64_t *)address, numWords))); + } } } void ConstantExpr::toMemory(void *address) { - switch (getWidth()) { + auto width = getWidth(); + switch (width) { default: assert(0 && "invalid type"); case Expr::Bool: *(( uint8_t*) address) = getZExtValue(1); break; case Expr::Int8: *(( uint8_t*) address) = getZExtValue(8); break; @@ -363,6 +376,10 @@ void ConstantExpr::toMemory(void *address) { case Expr::Fl80: *((long double*) address) = *(const long double*) value.getRawData(); break; + case Expr::Int128: + case Expr::Int256: + case Expr::Int512: + memcpy(address, value.getRawData(), width / 8); } } diff --git a/test/VectorInstructions/external_call.c b/test/VectorInstructions/external_call.c new file mode 100644 index 00000000..e3b46e0e --- /dev/null +++ b/test/VectorInstructions/external_call.c @@ -0,0 +1,79 @@ +// RUN: %clang -DDYNAMIC_LIBRARY=1 %s -shared -o %t1.so +// RUN: %clang %s -emit-llvm %O0opt -g -c -o %t1.bc +// RUN: rm -rf %t.klee-out +// NOTE: Have to pass `--optimize=false` to avoid vector operations being +// constant folded away. +// RUN: export LD_PRELOAD=%t1.so +// RUN: export DYLD_INSERT_LIBRARIES=%t1.so +// RUN: %klee --output-dir=%t.klee-out --optimize=false --exit-on-error --external-calls=all %t1.bc + +#include <stdint.h> + +typedef uint32_t v4ui __attribute__((vector_size(16))); // 128-bit vector +typedef uint32_t v8ui __attribute__((vector_size(32))); // 256-bit vector +typedef uint32_t v16ui __attribute__((vector_size(64))); // 512-bit vector + +v4ui call4(v4ui v); + +v8ui call8(v8ui v); + +v16ui call16(v16ui v); + +int call_mixed(v16ui v16, v8ui v8, v4ui v4); + +#ifdef DYNAMIC_LIBRARY + +v4ui call4(v4ui v) { + return v; +} + +v8ui call8(v8ui v) { + return v; +} + +v16ui call16(v16ui v) { + return v; +} + +int call_mixed(v16ui v16, v8ui v8, v4ui v4) { + return v16[15] + v8[7] + v4[3]; +} + +#else + +#include "assert.h" +#include "klee/klee.h" + +int main() { + v4ui v4 = {1, 2, 3, 4}; + { + v4ui r = call4(v4); + for (int i = 0; i < 4; ++i) { + klee_assert(r[i] == v4[i]); + } + } + + v8ui v8 = {1, 2, 3, 4, 5, 6, 7, 8}; + { + v8ui r = call8(v8); + for (int i = 0; i < 8; ++i) { + klee_assert(r[i] == v8[i]); + } + } + + v16ui v16 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + { + v16ui r = call16(v16); + for (int i = 0; i < 16; ++i) { + klee_assert(r[i] == v16[i]); + } + } + + { + int r = call_mixed(v16, v8, v4); + klee_assert(r == 28); + } + return 0; +} + +#endif |