RZ_API void rz_analysis_extract_rarg(RzAnalysis *analysis, RzAnalysisOp *op, RzAnalysisFunction *fcn, int *reg_set, int *count) {
	int i, argc = 0;
	rz_return_if_fail(analysis && op && fcn);
	const char *opsreg = op->src[0] ? get_regname(analysis, op->src[0]) : NULL;
	const char *opdreg = op->dst ? get_regname(analysis, op->dst) : NULL;
	const int size = (fcn->bits ? fcn->bits : analysis->bits) / 8;
	if (!fcn->cc) {
		RZ_LOG_DEBUG("No calling convention for function '%s' to extract register arguments\n", fcn->name);
		return;
	}
	char *fname = rz_analysis_function_name_guess(analysis->typedb, fcn->name);
	int max_count = rz_analysis_cc_max_arg(analysis, fcn->cc);
	if (!max_count || (*count >= max_count)) {
		free(fname);
		return;
	}
	if (fname) {
		argc = rz_type_func_args_count(analysis->typedb, fname);
	}

	bool is_call = (op->type & 0xf) == RZ_ANALYSIS_OP_TYPE_CALL || (op->type & 0xf) == RZ_ANALYSIS_OP_TYPE_UCALL;
	if (is_call && *count < max_count) {
		RzList *callee_rargs_l = NULL;
		int callee_rargs = 0;
		char *callee = NULL;
		ut64 offset = op->jump == UT64_MAX ? op->ptr : op->jump;
		RzAnalysisFunction *f = rz_analysis_get_function_at(analysis, offset);
		if (!f) {
			RzCore *core = (RzCore *)analysis->coreb.core;
			RzFlagItem *flag = rz_flag_get_by_spaces(core->flags, offset, RZ_FLAGS_FS_IMPORTS, NULL);
			if (flag) {
				callee = rz_analysis_function_name_guess(analysis->typedb, flag->name);
				if (callee) {
					const char *cc = rz_analysis_cc_func(analysis, callee);
					if (cc && !strcmp(fcn->cc, cc)) {
						callee_rargs = RZ_MIN(max_count, rz_type_func_args_count(analysis->typedb, callee));
					}
				}
			}
		} else if (!f->is_variadic && !strcmp(fcn->cc, f->cc)) {
			callee = rz_analysis_function_name_guess(analysis->typedb, f->name);
			if (callee) {
				callee_rargs = RZ_MIN(max_count, rz_type_func_args_count(analysis->typedb, callee));
			}
			callee_rargs = callee_rargs
				? callee_rargs
				: rz_analysis_var_count(analysis, f, RZ_ANALYSIS_VAR_KIND_REG, 1);
			callee_rargs_l = rz_analysis_var_list(analysis, f, RZ_ANALYSIS_VAR_KIND_REG);
		}
		size_t i;
		for (i = 0; i < callee_rargs; i++) {
			if (reg_set[i]) {
				continue;
			}
			const char *vname = NULL;
			RzType *type = NULL;
			char *name = NULL;
			int delta = 0;
			const char *regname = rz_analysis_cc_arg(analysis, fcn->cc, i);
			RzRegItem *ri = rz_reg_get(analysis->reg, regname, -1);
			if (ri) {
				delta = ri->index;
			}
			if (fname) {
				type = rz_type_func_args_type(analysis->typedb, fname, i);
				vname = rz_type_func_args_name(analysis->typedb, fname, i);
			}
			if (!vname && callee) {
				type = rz_type_func_args_type(analysis->typedb, callee, i);
				vname = rz_type_func_args_name(analysis->typedb, callee, i);
			}
			if (vname) {
				reg_set[i] = 1;
			} else {
				RzListIter *it;
				RzAnalysisVar *arg, *found_arg = NULL;
				rz_list_foreach (callee_rargs_l, it, arg) {
					if (rz_analysis_var_get_argnum(arg) == i) {
						found_arg = arg;
						break;
					}
				}
				if (found_arg) {
					type = found_arg->type;
					vname = name = strdup(found_arg->name);
				}
			}
			if (!vname) {
				name = rz_str_newf("arg%zu", i + 1);
				vname = name;
			}
			rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, type, size, true, vname);
			(*count)++;
			free(name);
		}
		free(callee);
		rz_list_free(callee_rargs_l);
		free(fname);
		return;
	}

	for (i = 0; i < max_count; i++) {
		const char *regname = rz_analysis_cc_arg(analysis, fcn->cc, i);
		if (regname) {
			int delta = 0;
			RzRegItem *ri = NULL;
			RzAnalysisVar *var = NULL;
			bool is_used_like_an_arg = is_used_like_arg(regname, opsreg, opdreg, op, analysis);
			if (reg_set[i] != 2 && is_used_like_an_arg) {
				ri = rz_reg_get(analysis->reg, regname, -1);
				if (ri) {
					delta = ri->index;
				}
			}
			if (reg_set[i] == 1 && is_used_like_an_arg) {
				var = rz_analysis_function_get_var(fcn, RZ_ANALYSIS_VAR_KIND_REG, delta);
			} else if (reg_set[i] != 2 && is_used_like_an_arg) {
				const char *vname = NULL;
				RzType *type = NULL;
				char *name = NULL;
				if ((i < argc) && fname) {
					type = rz_type_func_args_type(analysis->typedb, fname, i);
					vname = rz_type_func_args_name(analysis->typedb, fname, i);
				}
				if (!vname) {
					name = rz_str_newf("arg%d", i + 1);
					vname = name;
				}
				var = rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, type, size, true, vname);
				free(name);
				(*count)++;
			} else {
				if (is_reg_in_src(regname, analysis, op) || STR_EQUAL(opdreg, regname)) {
					reg_set[i] = 2;
				}
				continue;
			}
			if (is_reg_in_src(regname, analysis, op) || STR_EQUAL(regname, opdreg)) {
				reg_set[i] = 1;
			}
			if (var) {
				rz_analysis_var_set_access(var, var->regname, op->addr, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, 0);
				rz_meta_set_string(analysis, RZ_META_TYPE_VARTYPE, op->addr, var->name);
			}
		}
	}

	const char *selfreg = rz_analysis_cc_self(analysis, fcn->cc);
	if (selfreg) {
		bool is_used_like_an_arg = is_used_like_arg(selfreg, opsreg, opdreg, op, analysis);
		if (reg_set[i] != 2 && is_used_like_an_arg) {
			int delta = 0;
			char *vname = strdup("self");
			RzRegItem *ri = rz_reg_get(analysis->reg, selfreg, -1);
			if (ri) {
				delta = ri->index;
			}
			RzAnalysisVar *newvar = rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, NULL, size, true, vname);
			if (newvar) {
				rz_analysis_var_set_access(newvar, newvar->regname, op->addr, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, 0);
			}
			rz_meta_set_string(analysis, RZ_META_TYPE_VARTYPE, op->addr, vname);
			free(vname);
			(*count)++;
		} else {
			if (is_reg_in_src(selfreg, analysis, op) || STR_EQUAL(opdreg, selfreg)) {
				reg_set[i] = 2;
			}
		}
		i++;
	}

	const char *errorreg = rz_analysis_cc_error(analysis, fcn->cc);
	if (errorreg) {
		if (reg_set[i] == 0 && STR_EQUAL(opdreg, errorreg)) {
			int delta = 0;
			char *vname = strdup("error");
			RzRegItem *ri = rz_reg_get(analysis->reg, errorreg, -1);
			if (ri) {
				delta = ri->index;
			}
			RzAnalysisVar *newvar = rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, NULL, size, true, vname);
			if (newvar) {
				rz_analysis_var_set_access(newvar, newvar->regname, op->addr, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, 0);
			}
			rz_meta_set_string(analysis, RZ_META_TYPE_VARTYPE, op->addr, vname);
			free(vname);
			(*count)++;
			reg_set[i] = 2;
		}
	}
	free(fname);
}
RZ_API void rz_analysis_extract_rarg(RzAnalysis *analysis, RzAnalysisOp *op, RzAnalysisFunction *fcn, int *reg_set, int *count) {
	int i, argc = 0;
	rz_return_if_fail(analysis && op && fcn);
	const char *opsreg = op->src[0] ? get_regname(analysis, op->src[0]) : NULL;
	const char *opdreg = op->dst ? get_regname(analysis, op->dst) : NULL;
	const int size = (fcn->bits ? fcn->bits : analysis->bits) / 8;
	if (!fcn->cc) {
		RZ_LOG_DEBUG("No calling convention for function '%s' to extract register arguments\n", fcn->name);
		return;
	}
	char *fname = rz_analysis_function_name_guess(analysis->typedb, fcn->name);
	int max_count = rz_analysis_cc_max_arg(analysis, fcn->cc);
	if (!max_count || (*count >= max_count)) {
		free(fname);
		return;
	}
	if (fname) {
		argc = rz_type_func_args_count(analysis->typedb, fname);
	}

	bool is_call = (op->type & 0xf) == RZ_ANALYSIS_OP_TYPE_CALL || (op->type & 0xf) == RZ_ANALYSIS_OP_TYPE_UCALL;
	if (is_call && *count < max_count) {
		RzList *callee_rargs_l = NULL;
		int callee_rargs = 0;
		char *callee = NULL;
		ut64 offset = op->jump == UT64_MAX ? op->ptr : op->jump;
		RzAnalysisFunction *f = rz_analysis_get_function_at(analysis, offset);
		if (!f) {
			RzCore *core = (RzCore *)analysis->coreb.core;
			RzFlagItem *flag = rz_flag_get_by_spaces(core->flags, offset, RZ_FLAGS_FS_IMPORTS, NULL);
			if (flag) {
				callee = rz_analysis_function_name_guess(analysis->typedb, flag->name);
				if (callee) {
					const char *cc = rz_analysis_cc_func(analysis, callee);
					if (cc && !strcmp(fcn->cc, cc)) {
						callee_rargs = RZ_MIN(max_count, rz_type_func_args_count(analysis->typedb, callee));
					}
				}
			}
		} else if (!f->is_variadic && !strcmp(fcn->cc, f->cc)) {
			callee = rz_analysis_function_name_guess(analysis->typedb, f->name);
			if (callee) {
				callee_rargs = RZ_MIN(max_count, rz_type_func_args_count(analysis->typedb, callee));
			}
			callee_rargs = callee_rargs
				? callee_rargs
				: rz_analysis_var_count(analysis, f, RZ_ANALYSIS_VAR_KIND_REG, 1);
			callee_rargs_l = rz_analysis_var_list(analysis, f, RZ_ANALYSIS_VAR_KIND_REG);
		}
		size_t i;
		for (i = 0; i < callee_rargs; i++) {
			if (reg_set[i]) {
				continue;
			}
			const char *vname = NULL;
			RzType *type = NULL;
			char *name = NULL;
			int delta = 0;
			const char *regname = rz_analysis_cc_arg(analysis, fcn->cc, i);
			RzRegItem *ri = rz_reg_get(analysis->reg, regname, -1);
			if (ri) {
				delta = ri->index;
			}
			if (fname) {
				type = rz_type_func_args_type(analysis->typedb, fname, i);
				vname = rz_type_func_args_name(analysis->typedb, fname, i);
			}
			if (!vname && callee) {
				type = rz_type_func_args_type(analysis->typedb, callee, i);
				vname = rz_type_func_args_name(analysis->typedb, callee, i);
			}
			if (vname) {
				reg_set[i] = 1;
			} else {
				RzListIter *it;
				RzAnalysisVar *arg, *found_arg = NULL;
				rz_list_foreach (callee_rargs_l, it, arg) {
					if (rz_analysis_var_get_argnum(arg) == i) {
						found_arg = arg;
						break;
					}
				}
				if (found_arg) {
					type = found_arg->type;
					vname = name = strdup(found_arg->name);
				}
			}
			if (!vname) {
				name = rz_str_newf("arg%zu", i + 1);
				vname = name;
			}
			rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, type, size, true, vname);
			(*count)++;
			free(name);
		}
		free(callee);
		rz_list_free(callee_rargs_l);
		free(fname);
		return;
	}

	for (i = 0; i < max_count; i++) {
		const char *regname = rz_analysis_cc_arg(analysis, fcn->cc, i);
		if (regname) {
			int delta = 0;
			RzRegItem *ri = NULL;
			RzAnalysisVar *var = NULL;
			bool is_used_like_an_arg = is_used_like_arg(regname, opsreg, opdreg, op, analysis);
			if (reg_set[i] != 2 && is_used_like_an_arg) {
				ri = rz_reg_get(analysis->reg, regname, -1);
				if (ri) {
					delta = ri->index;
				}
			}
			if (reg_set[i] == 1 && is_used_like_an_arg) {
				var = rz_analysis_function_get_var(fcn, RZ_ANALYSIS_VAR_KIND_REG, delta);
			} else if (reg_set[i] != 2 && is_used_like_an_arg) {
				const char *vname = NULL;
				RzType *type = NULL;
				char *name = NULL;
				if ((i < argc) && fname) {
					type = rz_type_func_args_type(analysis->typedb, fname, i);
					vname = rz_type_func_args_name(analysis->typedb, fname, i);
				}
				if (!vname) {
					name = rz_str_newf("arg%d", i + 1);
					vname = name;
				}
				var = rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, type, size, true, vname);
				free(name);
				(*count)++;
			} else {
				if (is_reg_in_src(regname, analysis, op) || STR_EQUAL(opdreg, regname)) {
					reg_set[i] = 2;
				}
				continue;
			}
			if (is_reg_in_src(regname, analysis, op) || STR_EQUAL(regname, opdreg)) {
				reg_set[i] = 1;
			}
			if (var) {
				rz_analysis_var_set_access(var, var->regname, op->addr, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, 0);
				rz_meta_set_string(analysis, RZ_META_TYPE_VARTYPE, op->addr, var->name);
			}
		}
	}

	const char *selfreg = rz_analysis_cc_self(analysis, fcn->cc);
	if (selfreg) {
		bool is_used_like_an_arg = is_used_like_arg(selfreg, opsreg, opdreg, op, analysis);
		if (reg_set[i] != 2 && is_used_like_an_arg) {
			int delta = 0;
			char *vname = strdup("self");
			RzRegItem *ri = rz_reg_get(analysis->reg, selfreg, -1);
			if (ri) {
				delta = ri->index;
			}
			RzAnalysisVar *newvar = rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, NULL, size, true, vname);
			if (newvar) {
				rz_analysis_var_set_access(newvar, newvar->regname, op->addr, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, 0);
			}
			rz_meta_set_string(analysis, RZ_META_TYPE_VARTYPE, op->addr, vname);
			free(vname);
			(*count)++;
		} else {
			if (is_reg_in_src(selfreg, analysis, op) || STR_EQUAL(opdreg, selfreg)) {
				reg_set[i] = 2;
			}
		}
		i++;
	}

	const char *errorreg = rz_analysis_cc_error(analysis, fcn->cc);
	if (errorreg) {
		if (reg_set[i] == 0 && STR_EQUAL(opdreg, errorreg)) {
			int delta = 0;
			char *vname = strdup("error");
			RzRegItem *ri = rz_reg_get(analysis->reg, errorreg, -1);
			if (ri) {
				delta = ri->index;
			}
			RzAnalysisVar *newvar = rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, NULL, size, true, vname);
			if (newvar) {
				rz_analysis_var_set_access(newvar, newvar->regname, op->addr, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, 0);
			}
			rz_meta_set_string(analysis, RZ_META_TYPE_VARTYPE, op->addr, vname);
			free(vname);
			(*count)++;
			reg_set[i] = 2;
		}
	}
	free(fname);
}