FunctionInvocation *
FunctionInvocation::make_random_unary(CGContext &cg_context, const Type* type)
{
	DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationRandomUnary, NULL);
	assert(type);
	eUnaryOps op = (eUnaryOps)(rnd_upto(MAX_UNARY_OP, UNARY_OPS_PROB_FILTER));
	ERROR_GUARD(NULL);
	SafeOpFlags *flags = NULL;
	if (op == eMinus && type->eType != eVector) {
		flags = SafeOpFlags::make_random(sOpUnary);
		ERROR_GUARD(NULL);
		type = flags->get_lhs_type();
		assert(type);
	}
	// Type restirctions for vectors.
	if (type->eType == eVector) {
		// Not returns signed type.
		if (!type->is_signed() && op == eNot) {
			op = ePlus;
		}
		// Unlikely, but ++ and -- might overflow.
		if (type->is_signed() && (op == ePlus || op == eMinus)) {
			op = eNot;
		}
	}

	FunctionInvocation *fi = FunctionInvocationUnary::CreateFunctionInvocationUnary(cg_context, op, flags);

	Expression *operand = Expression::make_random(cg_context, type);
	ERROR_GUARD_AND_DEL1(NULL, fi);

	fi->param_value.push_back(operand);
	return fi;
}
FunctionInvocation *
FunctionInvocation::make_random_unary(CGContext &cg_context, const Type* type)
{
	DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationRandomUnary, NULL);
	assert(type);
	eUnaryOps op;
	do {
		op = (eUnaryOps)(rnd_upto(MAX_UNARY_OP, UNARY_OPS_PROB_FILTER));
	} while (type->is_float() && !UnaryOpWorksForFloat(op));
	ERROR_GUARD(NULL);
	SafeOpFlags *flags = NULL;
	flags = SafeOpFlags::make_random_unary(type, NULL, op);
	ERROR_GUARD(NULL);
	type = flags->get_lhs_type();
	assert(type);

	FunctionInvocation *fi = FunctionInvocationUnary::CreateFunctionInvocationUnary(cg_context, op, flags);

	Expression *operand = Expression::make_random(cg_context, type);
	ERROR_GUARD_AND_DEL1(NULL, fi);

	fi->param_value.push_back(operand);
	return fi;
}
FunctionInvocation *
FunctionInvocation::make_random_binary(CGContext &cg_context, const Type* type)
{
	DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationRandomBinary, NULL);
	if (rnd_flipcoin(10) && Type::has_pointer_type() && type->eType != eVector) {
		ERROR_GUARD(NULL);
		return make_random_binary_ptr_comparison(cg_context);
	}

	eBinaryOps op = (eBinaryOps)(rnd_upto(MAX_BINARY_OP, BINARY_OPS_PROB_FILTER));
	ERROR_GUARD(NULL);
	assert(type);
	SafeOpFlags *flags = SafeOpFlags::make_random(sOpBinary, op);
	assert(flags);
	ERROR_GUARD(NULL);
	// Special stuff for vectors.
	if (type->eType == eVector) {
		// Only signed type can use logicals. Also no div or mod.
		// TODO: Instead of changing the op, flip the sign of the type.
		if (!type->is_signed() && (
			op==eCmpGt || op==eCmpLt || op==eCmpGe || op==eCmpLe ||
			op==eCmpEq || op==eCmpNe || op==eAnd   || op==eOr    ||
			op==eDiv   || op==eMod)) {
			op = eAdd;
		}
		// Only unsigned can use unsafe ops.
		if (type->is_signed() && (
			op==eAdd || op==eSub    || op==eMul || op==eDiv ||
			op==eMod || op==eRShift || op==eLShift)) {
			op = eAnd;
		}
		delete flags;
		flags = SafeOpFlags::make_dummy_flags();
	}
	FunctionInvocationBinary *fi = FunctionInvocationBinary::CreateFunctionInvocationBinary(cg_context, op, flags);

	Effect lhs_eff_accum;
	CGContext lhs_cg_context(cg_context, cg_context.get_effect_context(), &lhs_eff_accum);

	// Generate an expression with the correct type required by safe math operands 
	const Type* lhs_type = flags->get_lhs_type();
	const Type* rhs_type = flags->get_rhs_type();
	// More special stuff for vectors.
	if (type->eType == eVector) {
		lhs_type = type;
		rhs_type = type;
	}
	assert(lhs_type && rhs_type);
	Expression *lhs = Expression::make_random(lhs_cg_context, lhs_type); 
	ERROR_GUARD_AND_DEL1(NULL, fi);
	Expression *rhs = 0;

	cg_context.merge_param_context(lhs_cg_context, true);
	FactMgr* fm = get_fact_mgr(&cg_context);
	vector<const Fact*> facts_copy = fm->global_facts;

#if 0
	if (lhs->term_type == eVariable) {
		lhs_eff_accum.read_deref_volatile((ExpressionVariable*)lhs);
	}
#endif

	// If we are guaranteed that the LHS will be evaluated before the RHS,
	// or if the LHS is pure (not merely side-effect-free),
	// then we can generate the RHS under the original effect context.
	if (IsOrderedStandardFunc(op)) { // || lhs_eff_accum.is_pure()) { TODO: need more thoughts on the purity issue. 
		rhs = Expression::make_random(cg_context, rhs_type);
	}
	else {
		// Otherwise, the RHS must be generated under the combined effect
		// of the original effect and the LHS effect.
		Effect rhs_eff_context(cg_context.get_effect_context());
		rhs_eff_context.add_effect(lhs_eff_accum, true);
		Effect rhs_eff_accum;

		CGContext rhs_cg_context(cg_context, rhs_eff_context, &rhs_eff_accum);
		if (op == eLShift || op == eRShift) {
			eTermType tt = MAX_TERM_TYPES;
			bool not_constant = rnd_flipcoin(ShiftByNonConstantProb);
			// avoid shifting negative or too much
			if (!not_constant) {
				rhs = lhs_type->eType == eVector ?
					dynamic_cast<Expression*>(CLSmith::ExpressionVector::make_constant(rhs_type, lhs_type->SizeInBytes() * 8)) :
					dynamic_cast<Expression*>(Constant::make_random_upto(lhs_type->SizeInBytes() * 8));
			} else {
				rhs = Expression::make_random(rhs_cg_context, rhs_type, NULL, false, true, tt);
			}
		}
		else {
			rhs = Expression::make_random(rhs_cg_context, rhs_type);
			// avoid divide by zero or possible zero (reached by pointer comparison) 
			if ((op == eMod || op == eDiv) && (rhs->equals(0) || rhs->is_0_or_1())) {
				VectorFilter f;
				f.add(eMod).add(eDiv).add(eLShift).add(eRShift);
				op = (eBinaryOps)(rnd_upto(MAX_BINARY_OP, &f));
				fi->set_operation(op);
			}
		}
		cg_context.merge_param_context(rhs_cg_context, true);
	}

	ERROR_GUARD_AND_DEL2(NULL, fi, lhs);

	if (CompatibleChecker::compatible_check(lhs, rhs)) {
		Error::set_error(COMPATIBLE_CHECK_ERROR);
		delete lhs;
		delete rhs;
		delete fi;
		return NULL;
	}

	// ordered operators such as "||" or "&&" may skip the 2nd parameter
	if (IsOrderedStandardFunc(op)) {
		fm->makeup_new_var_facts(facts_copy, fm->global_facts);
		merge_facts(fm->global_facts, facts_copy);
	}
	// TODO: fix `rhs' for eLShift and eRShift and ...
	// Currently, the "fix" is handled in `FunctionInvocationBinary::Output'.
	fi->param_value.push_back(lhs);
	fi->param_value.push_back(rhs);
	return fi;
}