aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDaniel Dunbar <daniel@zuster.org>2009-06-27 00:55:57 +0000
committerDaniel Dunbar <daniel@zuster.org>2009-06-27 00:55:57 +0000
commit7ea8afa20439c579c4a1ccb251a78770ef873787 (patch)
tree59d4455f6e3f078aa093d3b95c394050b96d43dd
parent648aa4261c5d2ac2c69dd08bf7a727bf6929c185 (diff)
downloadklee-7ea8afa20439c579c4a1ccb251a78770ef873787.tar.gz
Start move to using APFloat (support long double).
- Incomplete, still have to move some conversion operations. - Also, there isn't support yet for copying long double values to native memory. - Still, should be a monotonic improvement and we are no longer faking long double support. git-svn-id: https://llvm.org/svn/llvm-project/klee/trunk@74363 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/klee/Expr.h9
-rw-r--r--lib/Core/Executor.cpp263
-rw-r--r--lib/Expr/Expr.cpp16
-rw-r--r--test/Feature/LongDoubleSupport.c33
4 files changed, 141 insertions, 180 deletions
diff --git a/include/klee/Expr.h b/include/klee/Expr.h
index e3a57123..6e55cdd3 100644
--- a/include/klee/Expr.h
+++ b/include/klee/Expr.h
@@ -310,6 +310,12 @@ public:
unsigned getNumKids() const { return 0; }
ref<Expr> getKid(unsigned i) const { return 0; }
+ /// getAPValue - Return the arbitrary precision value directly.
+ ///
+ /// Clients should generally not use the APInt value directly and instead use
+ /// native ConstantExpr APIs.
+ const llvm::APInt &getAPValue() const { return value; }
+
/// getZExtValue - Return the constant value for a limited number of bits.
///
/// This routine should be used in situations where the width of the constant
@@ -410,6 +416,9 @@ public:
ref<ConstantExpr> Shl(const ref<ConstantExpr> &RHS);
ref<ConstantExpr> LShr(const ref<ConstantExpr> &RHS);
ref<ConstantExpr> AShr(const ref<ConstantExpr> &RHS);
+
+ // Comparisons return a constant expression of width 1.
+
ref<ConstantExpr> Eq(const ref<ConstantExpr> &RHS);
ref<ConstantExpr> Ne(const ref<ConstantExpr> &RHS);
ref<ConstantExpr> Ult(const ref<ConstantExpr> &RHS);
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 8f5b3daa..79b2040b 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -933,41 +933,7 @@ ref<klee::ConstantExpr> Executor::evalConstant(Constant *c) {
if (const ConstantInt *ci = dyn_cast<ConstantInt>(c)) {
return ConstantExpr::alloc(ci->getValue());
} else if (const ConstantFP *cf = dyn_cast<ConstantFP>(c)) {
- switch(cf->getType()->getTypeID()) {
- case Type::FloatTyID: {
- float f = cf->getValueAPF().convertToFloat();
- return ConstantExpr::create(floats::FloatAsUInt64(f), Expr::Int32);
- }
- case Type::DoubleTyID: {
- double d = cf->getValueAPF().convertToDouble();
- return ConstantExpr::create(floats::DoubleAsUInt64(d), Expr::Int64);
- }
- case Type::X86_FP80TyID: {
- // FIXME: This is really broken, but for now we just convert
- // to a double. This isn't going to work at all in general,
- // but we need support for wide constants.
- APFloat apf = cf->getValueAPF();
- bool ignored;
- APFloat::opStatus r = apf.convert(APFloat::IEEEdouble,
- APFloat::rmNearestTiesToAway,
- &ignored);
- (void) r;
- //assert(!(r & APFloat::opOverflow) && !(r & APFloat::opUnderflow) &&
- // "Overflow/underflow while converting from FP80 (x87) to 64-bit double");
- double d = apf.convertToDouble();
- return ConstantExpr::create(floats::DoubleAsUInt64(d), Expr::Int64);
- }
- default:
- llvm::cerr << "Constant of type " << cf->getType()->getDescription()
- << " not supported\n";
- llvm::cerr << "Constant used at ";
- KConstant *kc = kmodule->getKConstant((Constant*) cf);
- if (kc && kc->ki && kc->ki->info)
- llvm::cerr << kc->ki->info->file << ":" << kc->ki->info->line << "\n";
- else llvm::cerr << "<unknown>\n";
-
- assert(0 && "Arbitrary bit width floating point constants unsupported");
- }
+ return ConstantExpr::alloc(cf->getValueAPF().bitcastToAPInt());
} else if (const GlobalValue *gv = dyn_cast<GlobalValue>(c)) {
return globalAddresses.find(gv)->second;
} else if (isa<ConstantPointerNull>(c)) {
@@ -1897,66 +1863,62 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
break;
}
- // Floating point specific instructions
-
-#define FP_CONSTANT_BINOP(op, type, l, r, target, state) \
- if (type > 64) \
- return terminateStateOnExecError(state, \
- "Unsupported FP operation"); \
- bindLocal(target, state, \
- ConstantExpr::alloc(op(toConstant(state, l, \
- "floating point")->getZExtValue(), \
- toConstant(state, r, \
- "floating point")->getZExtValue(), \
- type), type))
+ // Floating point instructions
case Instruction::FAdd: {
- BinaryOperator *bi = cast<BinaryOperator>(i);
- ref<Expr> left = eval(ki, 0, state).value;
- ref<Expr> right = eval(ki, 1, state).value;
- Expr::Width type = Expr::getWidthForLLVMType(bi->getType());
- FP_CONSTANT_BINOP(floats::add, type, left, right, ki, state);
+ ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
+ "floating point");
+ ref<ConstantExpr> right = toConstant(state, eval(ki, 1, state).value,
+ "floating point");
+ llvm::APFloat Res(left->getAPValue());
+ Res.add(APFloat(right->getAPValue()), APFloat::rmNearestTiesToEven);
+ bindLocal(ki, state, ConstantExpr::alloc(Res.bitcastToAPInt()));
break;
}
case Instruction::FSub: {
- BinaryOperator *bi = cast<BinaryOperator>(i);
- ref<Expr> left = eval(ki, 0, state).value;
- ref<Expr> right = eval(ki, 1, state).value;
- Expr::Width type = Expr::getWidthForLLVMType(bi->getType());
- FP_CONSTANT_BINOP(floats::sub, type, left, right, ki, state);
+ ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
+ "floating point");
+ ref<ConstantExpr> right = toConstant(state, eval(ki, 1, state).value,
+ "floating point");
+ llvm::APFloat Res(left->getAPValue());
+ Res.subtract(APFloat(right->getAPValue()), APFloat::rmNearestTiesToEven);
+ bindLocal(ki, state, ConstantExpr::alloc(Res.bitcastToAPInt()));
break;
}
case Instruction::FMul: {
- BinaryOperator *bi = cast<BinaryOperator>(i);
- ref<Expr> left = eval(ki, 0, state).value;
- ref<Expr> right = eval(ki, 1, state).value;
- Expr::Width type = Expr::getWidthForLLVMType(bi->getType());
- FP_CONSTANT_BINOP(floats::mul, type, left, right, ki, state);
+ ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
+ "floating point");
+ ref<ConstantExpr> right = toConstant(state, eval(ki, 1, state).value,
+ "floating point");
+ llvm::APFloat Res(left->getAPValue());
+ Res.multiply(APFloat(right->getAPValue()), APFloat::rmNearestTiesToEven);
+ bindLocal(ki, state, ConstantExpr::alloc(Res.bitcastToAPInt()));
break;
}
case Instruction::FDiv: {
- BinaryOperator *bi = cast<BinaryOperator>(i);
-
- ref<Expr> dividend = eval(ki, 0, state).value;
- ref<Expr> divisor = eval(ki, 1, state).value;
- Expr::Width type = Expr::getWidthForLLVMType(bi->getType());
- FP_CONSTANT_BINOP(floats::div, type, dividend, divisor, ki, state);
+ ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
+ "floating point");
+ ref<ConstantExpr> right = toConstant(state, eval(ki, 1, state).value,
+ "floating point");
+ llvm::APFloat Res(left->getAPValue());
+ Res.divide(APFloat(right->getAPValue()), APFloat::rmNearestTiesToEven);
+ bindLocal(ki, state, ConstantExpr::alloc(Res.bitcastToAPInt()));
break;
}
case Instruction::FRem: {
- BinaryOperator *bi = cast<BinaryOperator>(i);
-
- ref<Expr> dividend = eval(ki, 0, state).value;
- ref<Expr> divisor = eval(ki, 1, state).value;
- Expr::Width type = Expr::getWidthForLLVMType(bi->getType());
- FP_CONSTANT_BINOP(floats::mod, type, dividend, divisor, ki, state);
+ ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
+ "floating point");
+ ref<ConstantExpr> right = toConstant(state, eval(ki, 1, state).value,
+ "floating point");
+ llvm::APFloat Res(left->getAPValue());
+ Res.mod(APFloat(right->getAPValue()), APFloat::rmNearestTiesToEven);
+ bindLocal(ki, state, ConstantExpr::alloc(Res.bitcastToAPInt()));
break;
}
-#undef FP_CONSTANT_BINOP
case Instruction::FPTrunc: {
FPTruncInst *fi = cast<FPTruncInst>(i);
@@ -1964,7 +1926,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
ref<ConstantExpr> arg = toConstant(state, eval(ki, 0, state).value,
"floating point");
if (arg->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
+ return terminateStateOnExecError(state, "Unsupported FPTrunc operation");
uint64_t value = floats::trunc(arg->getZExtValue(),
resultType,
arg->getWidth());
@@ -1978,7 +1940,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
ref<ConstantExpr> arg = toConstant(state, eval(ki, 0, state).value,
"floating point");
if (arg->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
+ return terminateStateOnExecError(state, "Unsupported FPExt operation");
uint64_t value = floats::ext(arg->getZExtValue(),
resultType,
arg->getWidth());
@@ -1992,7 +1954,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
ref<ConstantExpr> arg = toConstant(state, eval(ki, 0, state).value,
"floating point");
if (arg->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
+ return terminateStateOnExecError(state, "Unsupported FPToUI operation");
uint64_t value = floats::toUnsignedInt(arg->getZExtValue(),
resultType,
arg->getWidth());
@@ -2006,7 +1968,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
ref<ConstantExpr> arg = toConstant(state, eval(ki, 0, state).value,
"floating point");
if (arg->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
+ return terminateStateOnExecError(state, "Unsupported FPToSI operation");
uint64_t value = floats::toSignedInt(arg->getZExtValue(),
resultType,
arg->getWidth());
@@ -2020,7 +1982,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
ref<ConstantExpr> arg = toConstant(state, eval(ki, 0, state).value,
"floating point");
if (arg->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
+ return terminateStateOnExecError(state, "Unsupported UIToFP operation");
uint64_t value = floats::UnsignedIntToFP(arg->getZExtValue(),
resultType);
bindLocal(ki, state, ConstantExpr::alloc(value, resultType));
@@ -2033,7 +1995,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
ref<ConstantExpr> arg = toConstant(state, eval(ki, 0, state).value,
"floating point");
if (arg->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
+ return terminateStateOnExecError(state, "Unsupported SIToFP operation");
uint64_t value = floats::SignedIntToFP(arg->getZExtValue(),
resultType,
arg->getWidth());
@@ -2043,111 +2005,90 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
case Instruction::FCmp: {
FCmpInst *fi = cast<FCmpInst>(i);
- Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType());
- ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
- "floating point");
+ ref<ConstantExpr> left = toConstant(state, eval(ki, 0, state).value,
+ "floating point");
ref<ConstantExpr> right = toConstant(state, eval(ki, 1, state).value,
"floating point");
- if (left->getWidth() > 64)
- return terminateStateOnExecError(state, "Unsupported FP operation");
- uint64_t leftVal = left->getZExtValue();
- uint64_t rightVal = right->getZExtValue();
-
- //determine whether the operands are NANs
- unsigned inWidth = left->getWidth();
- bool leftIsNaN = floats::isNaN( leftVal, inWidth );
- bool rightIsNaN = floats::isNaN( rightVal, inWidth );
-
- //handle NAN based on whether the predicate is "ordered" or "unordered"
- uint64_t ret = (uint64_t)-1;
- bool done = false;
+ APFloat LHS(left->getAPValue());
+ APFloat RHS(right->getAPValue());
+ APFloat::cmpResult CmpRes = LHS.compare(RHS);
+
+ bool Result = false;
switch( fi->getPredicate() ) {
- //predicates which only care about whether or not the operands are NaNs
+ // Predicates which only care about whether or not the operands are NaNs.
case FCmpInst::FCMP_ORD:
- done = true;
- ret = !leftIsNaN && !rightIsNaN;
+ Result = CmpRes != APFloat::cmpUnordered;
break;
case FCmpInst::FCMP_UNO:
- done = true;
- ret = leftIsNaN || rightIsNaN;
+ Result = CmpRes == APFloat::cmpUnordered;
break;
- //ordered comparisons return false if either operand is NaN
- case FCmpInst::FCMP_OEQ:
- case FCmpInst::FCMP_OGT:
- case FCmpInst::FCMP_OGE:
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_OLE:
- case FCmpInst::FCMP_ONE:
- if( !leftIsNaN && !rightIsNaN) //only fall through and return false if there are NaN(s)
+ // Ordered comparisons return false if either operand is NaN. Unordered
+ // comparisons return true if either operand is NaN.
+ case FCmpInst::FCMP_UEQ:
+ if (CmpRes == APFloat::cmpUnordered) {
+ Result = true;
break;
-
- case FCmpInst::FCMP_FALSE: { //always return false for this predicate
- done = true;
- ret = false;
+ }
+ case FCmpInst::FCMP_OEQ:
+ Result = CmpRes == APFloat::cmpEqual;
break;
- }
- //unordered comparisons return true if either operand is NaN
- case FCmpInst::FCMP_UEQ:
case FCmpInst::FCMP_UGT:
- case FCmpInst::FCMP_UGE:
- case FCmpInst::FCMP_ULT:
- case FCmpInst::FCMP_ULE:
- case FCmpInst::FCMP_UNE:
- if( !leftIsNaN && !rightIsNaN) //only fall through and return true if there are NaN(s)
+ if (CmpRes == APFloat::cmpUnordered) {
+ Result = true;
break;
-
- case FCmpInst::FCMP_TRUE: //always return true for this predicate
- done = true;
- ret = true;
-
- default:
- case FCmpInst::BAD_FCMP_PREDICATE: /* will fall through and trigger fatal in the next switch */
+ }
+ case FCmpInst::FCMP_OGT:
+ Result = CmpRes == APFloat::cmpGreaterThan;
break;
- }
-
- //if not done, then we need to actually do a comparison to get the result
- if( !done ) {
- switch( fi->getPredicate() ) {
- //ordered comparisons return false if either operand is NaN
- case FCmpInst::FCMP_OEQ:
- case FCmpInst::FCMP_UEQ:
- ret = floats::eq( leftVal, rightVal, inWidth );
- break;
- case FCmpInst::FCMP_OGT:
- case FCmpInst::FCMP_UGT:
- ret = floats::gt( leftVal, rightVal, inWidth );
+ case FCmpInst::FCMP_UGE:
+ if (CmpRes == APFloat::cmpUnordered) {
+ Result = true;
break;
+ }
+ case FCmpInst::FCMP_OGE:
+ Result = CmpRes == APFloat::cmpGreaterThan || CmpRes == APFloat::cmpEqual;
+ break;
- case FCmpInst::FCMP_OGE:
- case FCmpInst::FCMP_UGE:
- ret = floats::ge( leftVal, rightVal, inWidth );
+ case FCmpInst::FCMP_ULT:
+ if (CmpRes == APFloat::cmpUnordered) {
+ Result = true;
break;
+ }
+ case FCmpInst::FCMP_OLT:
+ Result = CmpRes == APFloat::cmpLessThan;
+ break;
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_ULT:
- ret = floats::lt( leftVal, rightVal, inWidth );
+ case FCmpInst::FCMP_ULE:
+ if (CmpRes == APFloat::cmpUnordered) {
+ Result = true;
break;
+ }
+ case FCmpInst::FCMP_OLE:
+ Result = CmpRes == APFloat::cmpLessThan || CmpRes == APFloat::cmpEqual;
+ break;
- case FCmpInst::FCMP_OLE:
- case FCmpInst::FCMP_ULE:
- ret = floats::le( leftVal, rightVal, inWidth );
- break;
+ case FCmpInst::FCMP_UNE:
+ Result = CmpRes == APFloat::cmpUnordered || CmpRes != APFloat::cmpEqual;
+ break;
+ case FCmpInst::FCMP_ONE:
+ Result = CmpRes != APFloat::cmpUnordered && CmpRes != APFloat::cmpEqual;
+ break;
- case FCmpInst::FCMP_ONE:
- case FCmpInst::FCMP_UNE:
- ret = floats::ne( leftVal, rightVal, inWidth );
- break;
-
- default:
- terminateStateOnExecError(state, "invalid FCmp predicate");
- }
+ default:
+ assert(0 && "Invalid FCMP predicate!");
+ case FCmpInst::FCMP_FALSE:
+ Result = false;
+ break;
+ case FCmpInst::FCMP_TRUE:
+ Result = true;
+ break;
}
- bindLocal(ki, state, ConstantExpr::alloc(ret, resultType));
+ bindLocal(ki, state, ConstantExpr::alloc(Result, Expr::Bool));
break;
}
diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp
index d4042e3f..fc450d76 100644
--- a/lib/Expr/Expr.cpp
+++ b/lib/Expr/Expr.cpp
@@ -277,20 +277,10 @@ void Expr::printWidth(std::ostream &os, Width width) {
Expr::Width Expr::getWidthForLLVMType(const llvm::Type *t) {
switch (t->getTypeID()) {
- case llvm::Type::IntegerTyID: {
- Width w = cast<IntegerType>(t)->getBitWidth();
-
- // should remove this limitation soon
- if (w == 1 || w == 8 || w == 16 || w == 32 || w == 64)
- return w;
- else {
- assert(0 && "XXX arbitrary bit widths unsupported");
- abort();
- }
- }
+ case llvm::Type::IntegerTyID: return cast<IntegerType>(t)->getBitWidth();
case llvm::Type::FloatTyID: return Expr::Int32;
case llvm::Type::DoubleTyID: return Expr::Int64;
- case llvm::Type::X86_FP80TyID: return Expr::Int64; // XXX: needs to be fixed
+ case llvm::Type::X86_FP80TyID: return 80;
case llvm::Type::PointerTyID: return kMachinePointerType;
default:
cerr << "non-primitive type argument to Expr::getTypeForLLVMType()\n";
@@ -332,6 +322,7 @@ ref<Expr> ConstantExpr::fromMemory(void *address, Width 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: Should support long double, at least.
}
}
@@ -343,6 +334,7 @@ void ConstantExpr::toMemory(void *address) {
case Expr::Int16: *((uint16_t*) address) = getZExtValue(16); break;
case Expr::Int32: *((uint32_t*) address) = getZExtValue(32); break;
case Expr::Int64: *((uint64_t*) address) = getZExtValue(64); break;
+ // FIXME: Should support long double, at least.
}
}
diff --git a/test/Feature/LongDoubleSupport.c b/test/Feature/LongDoubleSupport.c
index b4631832..185a5485 100644
--- a/test/Feature/LongDoubleSupport.c
+++ b/test/Feature/LongDoubleSupport.c
@@ -1,8 +1,9 @@
// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc
-// RUN: %klee --exit-on-error %t1.bc > %t2.out
+// RUN: %klee --optimize=0 --exit-on-error %t1.bc > %t2.out
#include <stdio.h>
#include <float.h>
+#include <assert.h>
// FIXME: This doesn't really work at all, it just doesn't
// crash. Until we have wide constant support, that is all we care
@@ -10,11 +11,29 @@
// constants, we don't actually end up seeing much code which uses long
// double.
int main() {
- long double a = LDBL_MAX;
- long double b = -1;
- long double c = a + b;
- printf("a = %Lg\n", a);
- printf("b = %Lg\n", b);
- printf("c = %Lg\n", c);
+ unsigned N0 = 0, N1 = 0, N2 = 0;
+
+ float V0 = .1;
+ while (V0 != 0) {
+ V0 *= V0;
+ N0++;
+ }
+ double V1 = .1;
+ while (V1 != 0) {
+ V1 *= V1;
+ N1++;
+ }
+ long double V2 = .1;
+ while (V2 != 0) {
+ V2 *= V2;
+ N2++;
+ }
+
+ printf("counts: %d, %d, %d\n", N0, N1, N2);
+
+ assert(N0 == 6);
+ assert(N1 == 9);
+ assert(N2 == 13);
+
return 0;
}