about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--include/klee/Expr/Expr.h8
-rw-r--r--lib/Core/Executor.cpp8
-rw-r--r--lib/Expr/Expr.cpp31
-rw-r--r--test/VectorInstructions/external_call.c79
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