static void extract_arg(RzAnalysis *analysis, RzAnalysisFunction *fcn, RzAnalysisOp *op, const char *reg, const char *sign, char type) {
	st64 ptr = 0;
	char *addr, *esil_buf = NULL;

	rz_return_if_fail(analysis && fcn && op && reg);

	size_t i;
	for (i = 0; i < RZ_ARRAY_SIZE(op->src); i++) {
		if (op->src[i] && op->src[i]->reg && op->src[i]->reg->name) {
			if (!strcmp(reg, op->src[i]->reg->name)) {
				st64 delta = op->src[i]->delta;
				if ((delta > 0 && *sign == '+') || (delta < 0 && *sign == '-')) {
					ptr = RZ_ABS(op->src[i]->delta);
					break;
				}
			}
		}
	}

	if (!ptr) {
		const char *op_esil = rz_strbuf_get(&op->esil);
		if (!op_esil) {
			return;
		}
		esil_buf = strdup(op_esil);
		if (!esil_buf) {
			return;
		}
		char *ptr_end = strstr(esil_buf, sdb_fmt(",%s,%s,", reg, sign));
		if (!ptr_end) {
			free(esil_buf);
			return;
		}
		*ptr_end = 0;
		addr = ptr_end;
		while ((addr[0] != '0' || addr[1] != 'x') && addr >= esil_buf + 1 && *addr != ',') {
			addr--;
		}
		if (strncmp(addr, "0x", 2)) {
			//XXX: This is a workaround for inconsistent esil
			if (!op->stackop && op->dst) {
				const char *sp = rz_reg_get_name(analysis->reg, RZ_REG_NAME_SP);
				const char *bp = rz_reg_get_name(analysis->reg, RZ_REG_NAME_BP);
				const char *rn = op->dst->reg ? op->dst->reg->name : NULL;
				if (rn && ((bp && !strcmp(bp, rn)) || (sp && !strcmp(sp, rn)))) {
					if (analysis->verbose) {
						eprintf("Warning: Analysis didn't fill op->stackop for instruction that alters stack at 0x%" PFMT64x ".\n", op->addr);
					}
					goto beach;
				}
			}
			if (*addr == ',') {
				addr++;
			}
			if (!op->stackop && op->type != RZ_ANALYSIS_OP_TYPE_PUSH && op->type != RZ_ANALYSIS_OP_TYPE_POP && op->type != RZ_ANALYSIS_OP_TYPE_RET && rz_str_isnumber(addr)) {
				ptr = (st64)rz_num_get(NULL, addr);
				if (ptr && op->src[0] && ptr == op->src[0]->imm) {
					goto beach;
				}
			} else if ((op->stackop == RZ_ANALYSIS_STACK_SET) || (op->stackop == RZ_ANALYSIS_STACK_GET)) {
				if (op->ptr % 4) {
					goto beach;
				}
				ptr = RZ_ABS(op->ptr);
			} else {
				goto beach;
			}
		} else {
			ptr = (st64)rz_num_get(NULL, addr);
		}
	}

	if (analysis->verbose && (!op->src[0] || !op->dst)) {
		eprintf("Warning: Analysis didn't fill op->src/dst at 0x%" PFMT64x ".\n", op->addr);
	}

	int rw = (op->direction == RZ_ANALYSIS_OP_DIR_WRITE) ? RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE : RZ_ANALYSIS_VAR_ACCESS_TYPE_READ;
	if (*sign == '+') {
		const bool isarg = type == RZ_ANALYSIS_VAR_KIND_SPV ? ptr >= fcn->stack : ptr >= fcn->bp_off;
		const char *pfx = isarg ? ARGPREFIX : VARPREFIX;
		st64 frame_off;
		if (type == RZ_ANALYSIS_VAR_KIND_SPV) {
			frame_off = ptr - fcn->stack;
		} else {
			frame_off = ptr - fcn->bp_off;
		}
		RzAnalysisVar *var = get_stack_var(fcn, frame_off);
		if (var) {
			rz_analysis_var_set_access(var, reg, op->addr, rw, ptr);
			goto beach;
		}
		char *varname = NULL;
		RzType *vartype = NULL;
		if (isarg) {
			const char *place = fcn->cc ? rz_analysis_cc_arg(analysis, fcn->cc, ST32_MAX) : NULL;
			bool stack_rev = place ? !strcmp(place, "stack_rev") : false;
			char *fname = rz_analysis_function_name_guess(analysis->typedb, fcn->name);
			if (fname) {
				ut64 sum_sz = 0;
				size_t from, to, i;
				if (stack_rev) {
					const size_t cnt = rz_type_func_args_count(analysis->typedb, fname);
					from = cnt ? cnt - 1 : cnt;
					to = fcn->cc ? rz_analysis_cc_max_arg(analysis, fcn->cc) : 0;
				} else {
					from = fcn->cc ? rz_analysis_cc_max_arg(analysis, fcn->cc) : 0;
					to = rz_type_func_args_count(analysis->typedb, fname);
				}
				const int bytes = (fcn->bits ? fcn->bits : analysis->bits) / 8;
				for (i = from; stack_rev ? i >= to : i < to; stack_rev ? i-- : i++) {
					RzType *tp = rz_type_func_args_type(analysis->typedb, fname, i);
					if (!tp) {
						break;
					}
					if (sum_sz == frame_off) {
						vartype = tp;
						varname = strdup(rz_type_func_args_name(analysis->typedb, fname, i));
						break;
					}
					ut64 bit_sz = rz_type_db_get_bitsize(analysis->typedb, tp);
					sum_sz += bit_sz ? bit_sz / 8 : bytes;
					sum_sz = RZ_ROUND(sum_sz, bytes);
				}
				free(fname);
			}
		}
		if (!varname) {
			if (analysis->opt.varname_stack) {
				varname = rz_str_newf("%s_%" PFMT64x "h", pfx, RZ_ABS(frame_off));
			} else {
				varname = rz_analysis_function_autoname_var(fcn, type, pfx, ptr);
			}
		}
		if (varname) {
			RzAnalysisVar *var = rz_analysis_function_set_var(fcn, frame_off, type, vartype, analysis->bits / 8, isarg, varname);
			if (var) {
				rz_analysis_var_set_access(var, reg, op->addr, rw, ptr);
			}
			free(varname);
		}
	} else {
		st64 frame_off = -(ptr + fcn->bp_off);
		RzAnalysisVar *var = get_stack_var(fcn, frame_off);
		if (var) {
			rz_analysis_var_set_access(var, reg, op->addr, rw, -ptr);
			goto beach;
		}
		char *varname = analysis->opt.varname_stack
			? rz_str_newf("%s_%" PFMT64x "h", VARPREFIX, RZ_ABS(frame_off))
			: rz_analysis_function_autoname_var(fcn, type, VARPREFIX, -ptr);
		if (varname) {
			RzAnalysisVar *var = rz_analysis_function_set_var(fcn, frame_off, type, NULL, analysis->bits / 8, false, varname);
			if (var) {
				rz_analysis_var_set_access(var, reg, op->addr, rw, -ptr);
			}
			free(varname);
		}
	}
beach:
	free(esil_buf);
}