private void handleFunctionSideEffects(Instruction instruction, Address target,
			TaskMonitor monitor) {

		Function targetFunc = null;
		if (target != null) {
			targetFunc = getFunctionAt(target);
		}
		Address fallThruAddr = instruction.getFallThrough();
		// if the call is right below this routine, ignore the call
		//   otherwise clear out the return and killed by call variables
		if (fallThruAddr == null || target == null ||
			target.getOffset() != fallThruAddr.getOffset()) {

			// need to pass noAddress if don't know the target, in case
			// evaluator wants to look something up about the function
			if (checkForParamRefs && evaluator != null &&
				evaluator.evaluateReference(context, instruction, PcodeOp.UNIMPLEMENTED,
					(target == null ? Address.NO_ADDRESS : target), 0,
					null, RefType.UNCONDITIONAL_CALL)) {
				// put references on any register parameters with values in
				// them.
				addParamReferences(targetFunc, target, instruction, context, monitor);
			}

			// clear out settings for any return variables
			Varnode returnVarnodes[] = context.getReturnVarnode(targetFunc);
			if (returnVarnodes != null) {
				for (Varnode varnode : returnVarnodes) {
					context.putValue(varnode, context.createBadVarnode(), false);
				}
			}
			
			// clear out any killed by call variables
			Varnode killedVarnodes[] = context.getKilledVarnodes(targetFunc);
			if (killedVarnodes != null) {
				for (Varnode varnode : killedVarnodes) {
					context.putValue(varnode, context.createBadVarnode(), false);
				}
			}
		}

		// inlined function, don't worry about it's function effects
		if (targetFunc != null && targetFunc.isInline()) {
			return;
		}
		
		if (targetFunc != null && targetFunc.hasNoReturn()) {
			context.propogateResults(false);
		}

		// Update the stack offset if necessary
		Varnode outStack = context.getStackVarnode();

		if (outStack != null && (targetFunc == null || !targetFunc.isInline())) {
			int purge = getFunctionPurge(program, targetFunc);
			purge = addStackOverride(program, instruction.getMinAddress(), purge);

			if (purge == Function.UNKNOWN_STACK_DEPTH_CHANGE ||
				purge == Function.INVALID_STACK_DEPTH_CHANGE) {
				Varnode curVal = null;
				if (fallThruAddr != null) {

					// find any flowing addresses to this location in hopes
					// of finding a good stack value right after this function from some other flow
					Address[] knownFlowToAddresses = context.getKnownFlowToAddresses(fallThruAddr);

					for (Address knownFlowToAddresse : knownFlowToAddresses) {
						curVal = context.getRegisterVarnodeValue(context.getStackRegister(),
							knownFlowToAddresse, fallThruAddr, false);
						if (curVal != null) {
							break;
						}
					}

				}
				if (curVal != null) {
					context.putValue(outStack, curVal, false);
				}
				else if (instruction.getLength() != 1) {
					context.putValue(outStack, null, false);
				}
			}
			else if (purge != 0) {
					Varnode purgeVar = context.createConstantVarnode(purge, outStack.getSize());
					Varnode val1 = context.getValue(outStack, true, evaluator);
					Varnode val2 = null;
					if (val1 != null) {
						val2 = context.add(val1, purgeVar, evaluator);
					}
					context.putValue(outStack, val2, false);
			}
		}
	}
Apply pcode from an instruction to current varnode context.
Following a flow will push a new context state based on the current context state onto the contextStack

@param contextStack context state stack
@param vContext varnode context
@param instruction instruction to apply pcode from
@param continueAfterHittingFlow true if should continue after hitting an already processed flow
@param monitor to cancel
@return true to to continue this instruction path, false otherwise
Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java