Пример #1
0
SizeOffsetType ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
  SizeOffsetType TrueSide  = compute(I.getTrueValue());
  SizeOffsetType FalseSide = compute(I.getFalseValue());
  if (bothKnown(TrueSide) && bothKnown(FalseSide)) {
    if (TrueSide == FalseSide) {
        return TrueSide;
    }

    APInt TrueResult = getSizeWithOverflow(TrueSide);
    APInt FalseResult = getSizeWithOverflow(FalseSide);

    if (TrueResult == FalseResult) {
      return TrueSide;
    }
    if (Options.EvalMode == ObjectSizeOpts::Mode::Min) {
      if (TrueResult.slt(FalseResult))
        return TrueSide;
      return FalseSide;
    }
    if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
      if (TrueResult.sgt(FalseResult))
        return TrueSide;
      return FalseSide;
    }
  }
  return unknown();
}
Пример #2
0
/*
 * function join
 *
 * Here we perform the join operation in the interval lattice,
 * using Cousot's widening operator. We join the current abstract state
 * with the new_abstract_state and update the current abstract state of
 * the node.
 *
 * This function returns true if the current abstract state has changed.
 */
bool llvm::RangeAnalysis::join(GraphNode* Node, Range new_abstract_state){

	//Do not widen the range of a value that has an initial value
	if (!getInitialState(Node).isMaxRange()) {
		return false;
	}


	Range oldInterval = out_state[Node];
	Range newInterval = new_abstract_state;

	APInt oldLower = oldInterval.getLower();
	APInt oldUpper = oldInterval.getUpper();
	APInt newLower = newInterval.getLower();
	APInt newUpper = newInterval.getUpper();



	if (oldInterval.isUnknown())
		out_state[Node] = newInterval;
	else {

		if (widening_count[Node] < MaxIterationCount) {
			out_state[Node] = oldInterval.unionWith(newInterval);
			widening_count[Node]++;
		} else {
			//Widening
			APInt oldLower = oldInterval.getLower();
			APInt oldUpper = oldInterval.getUpper();
			APInt newLower = newInterval.getLower();
			APInt newUpper = newInterval.getUpper();
			if (newLower.slt(oldLower))
				if (newUpper.sgt(oldUpper))
					out_state[Node] = Range(Min, Max);
				else
					out_state[Node] = Range(Min, oldUpper);
			else if (newUpper.sgt(oldUpper))
				out_state[Node] = Range(oldLower, Max);
		}
	}

	bool hasChanged = oldInterval != out_state[Node];

	return hasChanged;
}
Пример #3
0
/// FoldSPFofSPF - We have an SPF (e.g. a min or max) of an SPF of the form:
///   SPF2(SPF1(A, B), C)
Instruction *InstCombiner::FoldSPFofSPF(Instruction *Inner,
                                        SelectPatternFlavor SPF1,
                                        Value *A, Value *B,
                                        Instruction &Outer,
                                        SelectPatternFlavor SPF2, Value *C) {
  if (C == A || C == B) {
    // MAX(MAX(A, B), B) -> MAX(A, B)
    // MIN(MIN(a, b), a) -> MIN(a, b)
    if (SPF1 == SPF2)
      return ReplaceInstUsesWith(Outer, Inner);

    // MAX(MIN(a, b), a) -> a
    // MIN(MAX(a, b), a) -> a
    if ((SPF1 == SPF_SMIN && SPF2 == SPF_SMAX) ||
        (SPF1 == SPF_SMAX && SPF2 == SPF_SMIN) ||
        (SPF1 == SPF_UMIN && SPF2 == SPF_UMAX) ||
        (SPF1 == SPF_UMAX && SPF2 == SPF_UMIN))
      return ReplaceInstUsesWith(Outer, C);
  }

  if (SPF1 == SPF2) {
    if (ConstantInt *CB = dyn_cast<ConstantInt>(B)) {
      if (ConstantInt *CC = dyn_cast<ConstantInt>(C)) {
        APInt ACB = CB->getValue();
        APInt ACC = CC->getValue();

        // MIN(MIN(A, 23), 97) -> MIN(A, 23)
        // MAX(MAX(A, 97), 23) -> MAX(A, 97)
        if ((SPF1 == SPF_UMIN && ACB.ule(ACC)) ||
            (SPF1 == SPF_SMIN && ACB.sle(ACC)) ||
            (SPF1 == SPF_UMAX && ACB.uge(ACC)) ||
            (SPF1 == SPF_SMAX && ACB.sge(ACC)))
          return ReplaceInstUsesWith(Outer, Inner);

        // MIN(MIN(A, 97), 23) -> MIN(A, 23)
        // MAX(MAX(A, 23), 97) -> MAX(A, 97)
        if ((SPF1 == SPF_UMIN && ACB.ugt(ACC)) ||
            (SPF1 == SPF_SMIN && ACB.sgt(ACC)) ||
            (SPF1 == SPF_UMAX && ACB.ult(ACC)) ||
            (SPF1 == SPF_SMAX && ACB.slt(ACC))) {
          Outer.replaceUsesOfWith(Inner, A);
          return &Outer;
        }
      }
    }
  }
  return nullptr;
}
Пример #4
0
APInt swift::constantFoldComparison(APInt lhs, APInt rhs, BuiltinValueKind ID) {
  bool result;
  switch (ID) {
    default: llvm_unreachable("Invalid integer compare kind");
    case BuiltinValueKind::ICMP_EQ:  result = lhs == rhs; break;
    case BuiltinValueKind::ICMP_NE:  result = lhs != rhs; break;
    case BuiltinValueKind::ICMP_SLT: result = lhs.slt(rhs); break;
    case BuiltinValueKind::ICMP_SGT: result = lhs.sgt(rhs); break;
    case BuiltinValueKind::ICMP_SLE: result = lhs.sle(rhs); break;
    case BuiltinValueKind::ICMP_SGE: result = lhs.sge(rhs); break;
    case BuiltinValueKind::ICMP_ULT: result = lhs.ult(rhs); break;
    case BuiltinValueKind::ICMP_UGT: result = lhs.ugt(rhs); break;
    case BuiltinValueKind::ICMP_ULE: result = lhs.ule(rhs); break;
    case BuiltinValueKind::ICMP_UGE: result = lhs.uge(rhs); break;
  }
  return APInt(1, result);
}
/// MultiplyOverflows - True if the multiply can not be expressed in an int
/// this size.
static bool MultiplyOverflows(ConstantInt *C1, ConstantInt *C2, bool sign) {
  uint32_t W = C1->getBitWidth();
  APInt LHSExt = C1->getValue(), RHSExt = C2->getValue();
  if (sign) {
    LHSExt = LHSExt.sext(W * 2);
    RHSExt = RHSExt.sext(W * 2);
  } else {
    LHSExt = LHSExt.zext(W * 2);
    RHSExt = RHSExt.zext(W * 2);
  }
  
  APInt MulExt = LHSExt * RHSExt;
  
  if (!sign)
    return MulExt.ugt(APInt::getLowBitsSet(W * 2, W));
  
  APInt Min = APInt::getSignedMinValue(W).sext(W * 2);
  APInt Max = APInt::getSignedMaxValue(W).sext(W * 2);
  return MulExt.slt(Min) || MulExt.sgt(Max);
}
Пример #6
0
/// FoldSPFofSPF - We have an SPF (e.g. a min or max) of an SPF of the form:
///   SPF2(SPF1(A, B), C)
Instruction *InstCombiner::FoldSPFofSPF(Instruction *Inner,
                                        SelectPatternFlavor SPF1,
                                        Value *A, Value *B,
                                        Instruction &Outer,
                                        SelectPatternFlavor SPF2, Value *C) {
  if (C == A || C == B) {
    // MAX(MAX(A, B), B) -> MAX(A, B)
    // MIN(MIN(a, b), a) -> MIN(a, b)
    if (SPF1 == SPF2)
      return ReplaceInstUsesWith(Outer, Inner);

    // MAX(MIN(a, b), a) -> a
    // MIN(MAX(a, b), a) -> a
    if ((SPF1 == SPF_SMIN && SPF2 == SPF_SMAX) ||
        (SPF1 == SPF_SMAX && SPF2 == SPF_SMIN) ||
        (SPF1 == SPF_UMIN && SPF2 == SPF_UMAX) ||
        (SPF1 == SPF_UMAX && SPF2 == SPF_UMIN))
      return ReplaceInstUsesWith(Outer, C);
  }

  if (SPF1 == SPF2) {
    if (ConstantInt *CB = dyn_cast<ConstantInt>(B)) {
      if (ConstantInt *CC = dyn_cast<ConstantInt>(C)) {
        APInt ACB = CB->getValue();
        APInt ACC = CC->getValue();

        // MIN(MIN(A, 23), 97) -> MIN(A, 23)
        // MAX(MAX(A, 97), 23) -> MAX(A, 97)
        if ((SPF1 == SPF_UMIN && ACB.ule(ACC)) ||
            (SPF1 == SPF_SMIN && ACB.sle(ACC)) ||
            (SPF1 == SPF_UMAX && ACB.uge(ACC)) ||
            (SPF1 == SPF_SMAX && ACB.sge(ACC)))
          return ReplaceInstUsesWith(Outer, Inner);

        // MIN(MIN(A, 97), 23) -> MIN(A, 23)
        // MAX(MAX(A, 23), 97) -> MAX(A, 97)
        if ((SPF1 == SPF_UMIN && ACB.ugt(ACC)) ||
            (SPF1 == SPF_SMIN && ACB.sgt(ACC)) ||
            (SPF1 == SPF_UMAX && ACB.ult(ACC)) ||
            (SPF1 == SPF_SMAX && ACB.slt(ACC))) {
          Outer.replaceUsesOfWith(Inner, A);
          return &Outer;
        }
      }
    }
  }

  // ABS(ABS(X)) -> ABS(X)
  // NABS(NABS(X)) -> NABS(X)
  if (SPF1 == SPF2 && (SPF1 == SPF_ABS || SPF1 == SPF_NABS)) {
    return ReplaceInstUsesWith(Outer, Inner);
  }

  // ABS(NABS(X)) -> ABS(X)
  // NABS(ABS(X)) -> NABS(X)
  if ((SPF1 == SPF_ABS && SPF2 == SPF_NABS) ||
      (SPF1 == SPF_NABS && SPF2 == SPF_ABS)) {
    SelectInst *SI = cast<SelectInst>(Inner);
    Value *NewSI = Builder->CreateSelect(
        SI->getCondition(), SI->getFalseValue(), SI->getTrueValue());
    return ReplaceInstUsesWith(Outer, NewSI);
  }
  return nullptr;
}
Пример #7
0
// caller must free returned value
const EmuVal* eval_rexpr(const Expr* e){
	errs() << "\nDEBUG: about to eval rexpr:\n";
	e->dump();

	if(isa<IntegerLiteral>(e)){
		const IntegerLiteral *obj = (const IntegerLiteral*)e;
		APInt i = obj->getValue();
		if(i.slt(EMU_MIN_INT) || i.sgt(EMU_MAX_INT)){
			e->dump();
			cant_handle();
		}
		return new EmuNum<NUM_TYPE_INT>(i);
	} else if(isa<CharacterLiteral>(e)){
		const CharacterLiteral *obj = (const CharacterLiteral*)e;
		unsigned int i = obj->getValue();
		if(i > 127){
			e->dump();
			cant_handle();
		}
		return new EmuNum<NUM_TYPE_CHAR>(new APInt(8, i, true));
	} else if(isa<UnaryOperator>(e)){
		const UnaryOperator *obj = (const UnaryOperator*)e;
		const Expr* sub = obj->getSubExpr();
		const auto op = obj->getOpcode();
		switch(op){
		case UO_AddrOf:
		{
			lvalue arg = eval_lexpr(sub);
			return new EmuPtr(arg.ptr, e->getType());
		}
		case UO_LNot:
		case UO_Minus:
		{
			const EmuVal* arg = eval_rexpr(sub);
			if(!arg->obj_type->isIntegerType()){
				cant_cast();
			}
			if(op == UO_LNot){
				return ((const EmuNumGeneric*)arg)->lnot();
			} else if (op == UO_Minus){
				return ((const EmuNumGeneric*)arg)->neg();
			}
		}
		case UO_Deref:
		case UO_Extension:
		case UO_Imag:
		case UO_Real:
		case UO_Not:
		case UO_PostInc:
		case UO_PostDec:
		case UO_PreInc:
		case UO_PreDec:
		case UO_Plus:
		default:
			llvm::errs() << "Got opcode " << obj->getOpcode() << "\n";
			cant_handle();
		}
	} else if(isa<BinaryOperator>(e)){
		const BinaryOperator* ex = (const BinaryOperator*)e;
		BinaryOperatorKind op = ex->getOpcode();

		// right always an rexpr
		const EmuVal *right = eval_rexpr(ex->getRHS());

		switch(op){
		case BO_Assign:
		{
			lvalue left = eval_lexpr(ex->getLHS());
			const EmuVal* ans = right->cast_to(left.type);
			delete right;
			left.ptr.block->write(ans, left.ptr.offset);
			return ans;
		}
		case BO_LT:
		case BO_GT:
		case BO_LE:
		case BO_GE:
		case BO_EQ:
		case BO_NE:
		{
			const EmuVal *left = eval_rexpr(ex->getLHS());
			QualType tl = left->obj_type.getCanonicalType();
			QualType tr = right->obj_type.getCanonicalType();
			if(tl != IntType || tr != IntType){
				left->obj_type.dump();
				right->obj_type.dump();
				cant_handle();
			}
			const llvm::APInt* lval = &((const EmuNum<NUM_TYPE_INT>*)left)->val;
			llvm::APInt rval = ((const EmuNum<NUM_TYPE_INT>*)right)->val;
			int ans;
			if(lval->isNegative()){
				if(op == BO_LT)    ans = (lval->slt(rval))?1:0;
				else if(op==BO_GT) ans = (lval->sgt(rval))?1:0;
				else if(op==BO_LE) ans = (lval->sle(rval))?1:0;
				else if(op==BO_GE) ans = (lval->sge(rval))?1:0;
				else if(op==BO_EQ) ans = (lval->eq( rval))?1:0;
				else if(op==BO_NE) ans = (lval->ne( rval))?1:0;
			} else if(rval.isNegative()){
				if(op == BO_LT)    ans = 0;
				else if(op==BO_GT) ans = 1;
				else if(op==BO_LE) ans = 0;
				else if(op==BO_GE) ans = 1;
				else if(op==BO_EQ) ans = 0;
				else if(op==BO_NE) ans = 1;
			} else {
				if(op == BO_LT)    ans = (lval->ult(rval))?1:0;
				else if(op==BO_GT) ans = (lval->ugt(rval))?1:0;
				else if(op==BO_LE) ans = (lval->ule(rval))?1:0;
				else if(op==BO_GE) ans = (lval->uge(rval))?1:0;
				else if(op==BO_EQ) ans = (lval->eq( rval))?1:0;
				else if(op==BO_NE) ans = (lval->ne( rval))?1:0;
			}
			delete left;
			delete right;
			return new EmuNum<NUM_TYPE_INT>(APInt(32, apint_signed_repr(ans), true));
		}
		case BO_AddAssign:
		case BO_SubAssign:
		{
			lvalue left = eval_lexpr(ex->getLHS());
			QualType tl = left.type.getCanonicalType();
			QualType tr = right->obj_type.getCanonicalType();
			if(tl != IntType || tr != IntType){
				left.type.dump();
				right->obj_type.dump();
				cant_handle();
			}
			void* ptr = &((char*)left.ptr.block->data)[left.ptr.offset];
			size_t space = left.ptr.block->size;
			if(space < 4 || space-4 < left.ptr.offset){
				bad_memread();
			}
			const EmuNum<NUM_TYPE_INT> value(ptr);
			const EmuNum<NUM_TYPE_INT>* result;
			if(op == BO_AddAssign) result = value.add((const EmuNum<NUM_TYPE_INT>*)right);
			else                   result = value.sub((const EmuNum<NUM_TYPE_INT>*)right);
			left.ptr.block->write(result, left.ptr.offset);
			delete right;
			return result;
		}
		case BO_Add:
		case BO_Sub:
		case BO_Mul:
		case BO_Div:
		case BO_And:
		case BO_Or:
		{
			const EmuVal* left = eval_rexpr(ex->getLHS());
			if(!right->obj_type->isIntegerType()){
				right->obj_type.dump();
				cant_cast();
			}
			const EmuNumGeneric* trueright = (const EmuNumGeneric*)right;
			const EmuVal* retval;

			QualType tl = left->obj_type;
			// special case: add integer to pointer
			if(tl->isPointerType()){
				int n;
				if(op == BO_Add) n = trueright->val.getSExtValue();
				else if(op == BO_Sub) n = -trueright->val.getSExtValue();
				else err_exit("Undefined op on pointer");
				
				QualType sub = tl->getAs<PointerType>()->getPointeeType();
				int s = getSizeOf(sub);
				const EmuPtr* lp = (const EmuPtr*)left;
				retval = new EmuPtr(mem_ptr(lp->u.block,lp->offset+n*s), tl);
			} else if(tl->isIntegerType()){
				const EmuNumGeneric* trueleft = (const EmuNumGeneric*)left;
				if(op == BO_Add)      retval = trueleft->add(trueright);
				else if(op == BO_Sub) retval = trueleft->sub(trueright);
				else if(op == BO_Mul) retval = trueleft->mul(trueright);
				else if(op == BO_Div) retval = trueleft->div(trueright);
				else if(op == BO_Or) retval = trueleft->_or(trueright);
				else if(op == BO_And)retval = trueleft->_and(trueright);
				else cant_cast();
			} else {
				tl.dump();
				cant_cast();
			}

			delete left;
			delete right;

			return retval;
		}
		case BO_PtrMemD:
		case BO_PtrMemI:
		case BO_Rem:
		case BO_Shl:
		case BO_Shr:
		case BO_LAnd:
		case BO_Xor:
		case BO_LOr:
		case BO_MulAssign:
		case BO_DivAssign:
		case BO_RemAssign:
		case BO_ShlAssign:
		case BO_ShrAssign:
		case BO_AndAssign:
		case BO_XorAssign:
		case BO_OrAssign:
		case BO_Comma:
		default:
			e->dump();
			cant_handle();
		}
	} else if(isa<CastExpr>(e)){
		const CastExpr* expr = (const CastExpr*)e;
		const Expr* sub = expr->getSubExpr();
		switch(expr->getCastKind()){
		case CK_LValueToRValue:
			return from_lvalue(eval_lexpr(sub));
		case CK_NoOp:
			return eval_rexpr(sub);
		case CK_BitCast:
		{
			if(isa<ExplicitCastExpr>(e)){
				const ExplicitCastExpr* expr = (const ExplicitCastExpr*)e;
				return eval_rexpr(sub)->cast_to(expr->getTypeAsWritten());
			}
			// else ImplicitCastExpr
			return eval_rexpr(sub)->cast_to(e->getType());
		}
		case CK_IntegralCast:
		{
			return eval_rexpr(sub)->cast_to(expr->getType());
		}
		case CK_FunctionToPointerDecay:
		{
			lvalue l = eval_lexpr(sub);
			if(!l.type->isFunctionType()){
				e->dump();
				cant_cast();
			}
			return new EmuPtr(l.ptr, sources[curr_source]->getPointerType(l.type));
		}
		case CK_ArrayToPointerDecay:
		{
			lvalue l = eval_lexpr(sub);
			const EmuVal* ans = new EmuPtr(l.ptr, expr->getType());
			return ans;
		}
		case CK_BuiltinFnToFnPtr:
		{
			if(!isa<DeclRefExpr>(sub)){
				err_exit("Don't know how to convert builtin function");
			}
			std::string name = ((const DeclRefExpr*)sub)->getDecl()->getNameAsString();
			const EmuFunc* f = get_external_func(name, sub->getType());
			mem_block* ptr = new mem_block(MEM_TYPE_STATIC, f);
			delete f;
			return new EmuPtr(mem_ptr(ptr,0), expr->getType());
		}
		case CK_NullToPointer:
		{
			return new EmuPtr(mem_ptr(nullptr,0), expr->getType());
		}
		case CK_PointerToIntegral:
		{
			const EmuVal* ptr = eval_rexpr(sub);
			if(!ptr->obj_type->isPointerType()){
				err_exit("Expected pointer");
			}
			const EmuPtr* p = (const EmuPtr*)ptr;
			if(p->status != STATUS_DEFINED) cant_handle();
			uint64_t segment;
			uint64_t offset = p->offset;
			if(p->u.block == nullptr){
				segment = 0;
			} else {
				segment = p->u.block->id;
			}
			delete ptr;
			if((expr->getType()->getAs<BuiltinType>())->isSignedInteger()){
				return new EmuNum<NUM_TYPE_LONGLONG>(APInt(64, (segment << 32) + offset, true));
			} else {
				return new EmuNum<NUM_TYPE_ULONGLONG>(APInt(64, (segment << 32) + offset, false));				
			}
		}
		case CK_VectorSplat:
		case CK_IntegralToBoolean:
		case CK_IntegralToFloating:
		case CK_FloatingToIntegral:
		case CK_FloatingToBoolean:
		case CK_FloatingCast:
		case CK_CPointerToObjCPointerCast:
		case CK_BlockPointerToObjCPointerCast:
		case CK_AnyPointerToBlockPointerCast:
		case CK_ObjCObjectLValueCast:
		case CK_FloatingRealToComplex:
		case CK_FloatingComplexToReal:
		case CK_FloatingComplexToBoolean:
		case CK_FloatingComplexCast:
		case CK_FloatingComplexToIntegralComplex:
		case CK_IntegralRealToComplex:
		case CK_IntegralComplexToReal:
		case CK_IntegralComplexToBoolean:
		case CK_IntegralComplexCast:
		case CK_IntegralComplexToFloatingComplex:
		case CK_ARCProduceObject:
		case CK_ARCConsumeObject:
		case CK_ARCReclaimReturnedObject:
		case CK_ARCExtendBlockObject:
		case CK_AtomicToNonAtomic:
		case CK_NonAtomicToAtomic:
		case CK_CopyAndAutoreleaseBlockObject:
		case CK_ZeroToOCLEvent:
		case CK_AddressSpaceConversion:
		case CK_ReinterpretMemberPointer:
		case CK_UserDefinedConversion:
		case CK_ConstructorConversion:
		case CK_IntegralToPointer:
		case CK_PointerToBoolean:
		case CK_ToVoid:
		default:
			llvm::errs() << "\n\n";
			e->dump();
			cant_cast();
		}
	} else if(isa<CallExpr>(e)){
		const CallExpr* expr = (const CallExpr*)e;
		const Expr* const* args = expr->getArgs();
		const Expr* callee = expr->getCallee();

		llvm::errs() << "DOUG DEBUG: executing the following call:\n";
		callee->dump();

		const EmuVal* f = eval_rexpr(callee);
		if(f->status != STATUS_DEFINED || !f->obj_type->isFunctionPointerType()){
			f->obj_type.dump();
			err_exit("Calling an invalid function");
		}

		const EmuPtr* p = (const EmuPtr*)f;
		if(p->u.block->memtype == MEM_TYPE_EXTERN){
			err_exit("Tried to call an unimplemented function");
		}

		const EmuFunc* func = (const EmuFunc*)from_lvalue(lvalue(p->u.block, ((const PointerType*)p->obj_type.getTypePtr())->getPointeeType(), p->offset));
		uint32_t fid = func->func_id;
		
		const EmuVal* retval;

		add_stack_frame();
		if(fid < NUM_EXTERNAL_FUNCTIONS){
			if(is_lvalue_based_macro(fid)){
				// special handling for va_args stuff
				for(unsigned int i=0; i < expr->getNumArgs(); i++){
					const Expr* arg = args[i];
					while(isa<ImplicitCastExpr>(arg)){
						arg = ((const ImplicitCastExpr*)arg)->getSubExpr();
					}
					if(!isa<DeclRefExpr>(arg)){
						err_exit("Passed non-variable as lvalue to builtin macro");
					}
					std::string name = ((const DeclRefExpr*)arg)->getDecl()->getNameAsString();
					std::unordered_map<std::string,std::deque<std::pair<int,int> > >::const_iterator list = stack_var_map.find(name);
					if(list == stack_var_map.end()){
						err_exit("Can't find appropriate lvalue for macro");
					}
					const auto test = list->second;
					const auto item = test.back();
					const EmuVal* val = new EmuStackPos(item.first, item.second);
					mem_block* storage = new mem_block(MEM_TYPE_STACK, val);
					add_stack_var("", lvalue(storage,val->obj_type,0));
					delete val;
				}
			} else {
				// we are dealing with an external function
				for(unsigned int i=0; i < expr->getNumArgs(); i++){
					const EmuVal* val = eval_rexpr(args[i]);
					mem_block* storage = new mem_block(MEM_TYPE_STACK, val);
					add_stack_var("", lvalue(storage,val->obj_type,0));
					delete val;
				}
			}
			retval = call_external(fid);
		} else {
			const auto it = global_functions.find(fid);
			const FunctionDecl* defn = (const FunctionDecl*)it->second.second;

			for(unsigned int i=0; i < expr->getNumArgs(); i++){
				const EmuVal* val = eval_rexpr(args[i]);
				mem_block* storage = new mem_block(MEM_TYPE_STACK, val);
				std::string name;
				if(i >= defn->getNumParams()){
					name = ""; // relevant for later args of e.g. printf(char*, ...)
				} else {
					name = defn->getParamDecl(i)->getNameAsString();
				}
				llvm::errs() << "DOUG DEBUG: adding stack variable "<<name<<" for arg "<<i<<" of internal function call (numparams="<< defn->getNumParams() <<")\n";
				defn->dump();
				
				add_stack_var(name, lvalue(storage,val->obj_type,0));
				delete val;
			}

			int save = curr_source;
			curr_source = it->second.first;
			llvm::errs() << "DOUG DEBUG: actually executing:\n";
			defn->getBody()->dump();
			retval = exec_stmt(defn->getBody());
			llvm::errs() << "DOUG DEBUG: call returned with retval at "<<((const void*)retval)<<"\n";
			curr_source = save;
		}
		llvm::errs() << "DOUG DEBUG: popping frame leaving call\n";
		pop_stack_frame();
		return retval;
	} else if(isa<UnaryExprOrTypeTraitExpr>(e)){
		const UnaryExprOrTypeTraitExpr* expr = (const UnaryExprOrTypeTraitExpr*)e;
		switch(expr->getKind()){
		case UETT_SizeOf:
		{
			QualType qt = expr->getArgumentType();
			const EmuVal* fake = from_lvalue(lvalue(nullptr, qt, 0));
			uint64_t thesize = (uint64_t)fake->size();
			delete fake;
			return new EmuNum<NUM_TYPE_ULONG>(APInt(32, thesize, false));
		}
		case UETT_AlignOf:
		case UETT_VecStep:
		default:
			e->dump();
			cant_handle();
		}
	} else if(isa<InitListExpr>(e)){
		const InitListExpr* expr = (const InitListExpr*)e;
		unsigned int n = expr->getNumInits();
		QualType qt = expr->getType();
		if(qt->isArrayType()){
			const EmuPtr* array = (const EmuPtr*)from_lvalue(lvalue(nullptr, qt, 0));
			if(array->status != STATUS_DEFINED) cant_handle();
			size_t loc = 0;
			for(unsigned int i = 0; i < n; i++){
				const EmuVal* curr = eval_rexpr(expr->getInit(i));
				array->u.block->write(curr, loc);
				loc += curr->size();
				delete curr;
			}
			return array;
		} else if(qt->isStructureType()){
			unsigned int n = expr->getNumInits();
			const EmuVal** arr = new const EmuVal*[n];
			for(unsigned int i = 0; i < n; i++){
				arr[i] = eval_rexpr(expr->getInit(i));
			}
			return new EmuStruct(STATUS_DEFINED, qt, n, arr);
		}
		cant_handle();
	} else if(isa<ImplicitValueInitExpr>(e)){
		return zero_init(e->getType());
	} else if(isa<ParenExpr>(e)){
		return eval_rexpr(((const ParenExpr*)e)->getSubExpr());
	}
	e->dump();
	cant_handle();
}