private int createStackPointerVariables(final Function func, TaskMonitor monitor)
			throws CancelledException {
		// check if this is a jump thunk through a function pointer
//		if (checkThunk(func, monitor)) {
//			return func.getStackPurgeSize();
//		}

		stackReg = program.getCompilerSpec().getStackPointer();

		final List<Variable> sortedVariables = new ArrayList<>();

		// Add stack variables with undefined types to map to simplify merging
		addFunctionStackVariablesToSortedList(func, sortedVariables);

		SymbolicPropogator symEval = new SymbolicPropogator(program);
		symEval.setParamRefCheck(false);
		symEval.setReturnRefCheck(false);
		symEval.setStoredRefCheck(false);

		// follow all flows building up context
		// use context to fill out addresses on certain instructions 
		ContextEvaluator eval = new ContextEvaluatorAdapter() {

			@Override
			public boolean evaluateContext(VarnodeContext context, Instruction instr) {
				if (instr.getFlowType().isTerminal()) {
					RegisterValue value = context.getRegisterValue(stackReg);
					if (value != null) {
						BigInteger signedValue = value.getSignedValue();
						if (signedValue != null) {
							purge = signedValue.intValue();
						}
					}
				}
				if (isX86 && instr.getMnemonicString().equals("LEA")) {
					Register destReg = instr.getRegister(0);
					if (destReg != null) {
						Varnode value = context.getRegisterVarnodeValue(destReg);
						if (value != null) {
							checkForStackOffset(context, instr, value.getAddress(), 0);
						}
					}
				}
				return false;
			}

			@Override
			public boolean evaluateSymbolicReference(VarnodeContext context, Instruction instr,
					Address address) {
				if (instr.getFlowType().isTerminal()) {
					return false;
				}
				checkForStackOffset(context, instr, address, -1);
				return false;
			}

			private void checkForStackOffset(VarnodeContext context, Instruction instr,
					Address address, int opIndex) {
				AddressSpace space = address.getAddressSpace();
				String spaceName = space.getName();
				if (spaceName.startsWith("track_") || context.isStackSpaceName(spaceName)) {
					if (opIndex == -1) {
						opIndex = getStackOpIndex(context, instr, (int) address.getOffset());
						// if didn't get the right opIndex, and has a delayslot, check for good stack ref
						if (opIndex == -1 && instr.getPrototype().hasDelaySlots()) {
							instr = instr.getNext();
							if (instr == null) {
								return;
							}
							opIndex = getStackOpIndex(context, instr, (int) address.getOffset());
						}
					}
					if (opIndex == -1) {
						return;
					}
					// if restoring the same value into the register, don't record the reference
					// TODO: Dirty Dirty nasty Hack for POP EBP problem, only very few cases of this!
					if (isX86 && instr.getMnemonicString().equals("POP")) {
						Register reg = instr.getRegister(opIndex);
						if (reg != null && reg.getName().contains("BP")) {
							return;
						}
					}
					long extendedOffset = extendOffset(address.getOffset(), stackReg.getBitLength());
					
					defineFuncVariable(symEval, func, instr, opIndex, (int) extendedOffset, sortedVariables);
				}
			}

			private long extendOffset(long offset, int bitLength) {
				// Properly extend the stack offset for -/+
				return (offset << (64 - bitLength)) >> (64 - bitLength);
			}
		};

		// set the stack pointer to be tracked
		Address entryPoint = func.getEntryPoint();
		symEval.setRegister(entryPoint, stackReg);

		symEval.flowConstants(entryPoint, func.getBody(), eval, true, monitor);

		if (sortedVariables.size() != 0) {

			List<Variable> protectedFuncVars = new ArrayList<>();
			for (Variable v : func.getAllVariables()) {
				if (isProtectedVariable(v)) {
					protectedFuncVars.add(v);
				}
			}
			VariableStorageConflicts conflicts =
				new VariableStorageConflicts(sortedVariables, protectedFuncVars, false, monitor);

			SourceType signatureSource = func.getSignatureSource();
			if (signatureSource != SourceType.IMPORTED &&
				signatureSource != SourceType.USER_DEFINED) {
				addStackParameters(func, sortedVariables, conflicts, monitor);
			}

			addStackLocalVariables(func, sortedVariables, conflicts, monitor);

		}
		return purge;
	}
Create locals and parameters based on references involving purely the stack pointer.
Pushes, Pops, and arithmetic manipulation of the stack pointer must be tracked.

@param func - function to analyze stack pointer references
Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/NewFunctionStackAnalysisCmd.java