#include "lib.h"
#include "symbol.h"
#include "target.h"
#include "machine.h"
#include <string.h>
#include <stdio.h>

#define RISCV_32BIT	(1 << 0)
#define RISCV_64BIT	(1 << 1)
#define RISCV_MUL	(1 << 2)
#define RISCV_DIV	(1 << 3)
#define RISCV_ATOMIC	(1 << 4)
#define RISCV_FLOAT	(1 << 5)
#define RISCV_DOUBLE	(1 << 6)
#define RISCV_FDIV	(1 << 7)
#define RISCV_COMP	(1 << 8)
#define RISCV_EMBD	(1 << 9)
#define RISCV_FPU	(RISCV_FLOAT|RISCV_DOUBLE|RISCV_FDIV)
#define RISCV_GENERIC	(RISCV_MUL|RISCV_DIV|RISCV_ATOMIC|RISCV_FPU|RISCV_ZICSR|RISCV_ZIFENCEI)
#define RISCV_ZICSR	(1 << 10)
#define RISCV_ZIFENCEI	(1 << 11)
#define RISCV_ZICBOM	(1 << 12)
#define RISCV_ZIHINTPAUSE	(1 << 13)
#define RISCV_VECTOR	(1 << 14)

static unsigned int riscv_flags;

static void parse_march_riscv(const char *arg)
{
	static struct {
		const char *pattern;
		unsigned int flags;
	} basic_sets[] = {
		{ "rv32i",	RISCV_32BIT },
		{ "rv32e",	RISCV_32BIT|RISCV_EMBD },
		{ "rv32g",	RISCV_32BIT|RISCV_GENERIC },
		{ "rv64i",	RISCV_64BIT },
		{ "rv64g",	RISCV_64BIT|RISCV_GENERIC },
	}, extensions[] = {
		{ "m",		RISCV_MUL|RISCV_DIV },
		{ "a",		RISCV_ATOMIC },
		{ "f",		RISCV_FLOAT|RISCV_FDIV|RISCV_ZICSR },
		{ "d",		RISCV_DOUBLE|RISCV_FDIV|RISCV_ZICSR },
		{ "c",		RISCV_COMP },
		{ "v",		RISCV_VECTOR|RISCV_FPU|RISCV_ZICSR },
		{ "_zicsr",	RISCV_ZICSR },
		{ "_zifencei",	RISCV_ZIFENCEI },
		{ "_zicbom",	RISCV_ZICBOM },
		{ "_zihintpause",	RISCV_ZIHINTPAUSE },
	};
	int i;

	// Each -march=.. options entirely overrides previous ones
	riscv_flags = 0;

	for (i = 0; i < ARRAY_SIZE(basic_sets); i++) {
		const char *pat = basic_sets[i].pattern;
		size_t len = strlen(pat);

		if (!strncmp(arg, pat, len)) {
			riscv_flags |= basic_sets[i].flags;
			arg += len;
			goto ext;
		}
	}

unknown:
	fprintf(stderr, "WARNING: invalid argument to '-march': '%s'\n", arg);
	return;

ext:
	for (i = 0; i < ARRAY_SIZE(extensions); i++) {
		const char *pat = extensions[i].pattern;
		size_t len = strlen(pat);

		if (!strncmp(arg, pat, len)) {
			riscv_flags |= extensions[i].flags;
			arg += len;
		}
	}
	if (arg[0])
		goto unknown;
}

static void init_riscv(const struct target *self)
{
	if (arch_cmodel == CMODEL_UNKNOWN)
		arch_cmodel = CMODEL_MEDLOW;
	if (fpic)
		arch_cmodel = CMODEL_PIC;

	if (riscv_flags == 0)
		riscv_flags = self->flags;
}

static void init_riscv32(const struct target *self)
{
	fast16_ctype = &int_ctype;
	ufast16_ctype = &uint_ctype;
	fast32_ctype = &int_ctype;
	ufast32_ctype = &uint_ctype;

	init_riscv(self);
}

static void predefine_riscv(const struct target *self)
{
	static const char *cmodels[CMODEL_LAST] = {
		[CMODEL_MEDANY] = "medany",
		[CMODEL_MEDLOW] = "medlow",
		[CMODEL_PIC]    = "pic",
	};
	const char *cmodel = cmodels[arch_cmodel];

	predefine("__riscv", 1, "1");
	predefine("__riscv_xlen", 1, "%d", ptr_ctype.bit_size);

	if (riscv_flags & RISCV_ATOMIC)
		predefine("__riscv_atomic", 1, "1");
	if (riscv_flags & RISCV_COMP)
		predefine("__riscv_compressed", 1, "1");
	if (riscv_flags & RISCV_DIV)
		predefine("__riscv_div", 1, "1");
	if (riscv_flags & RISCV_EMBD)
		predefine("__riscv_32e", 1, "1");
	if (riscv_flags & RISCV_FPU)
		predefine("__riscv_flen", 1, "%d", (riscv_flags & RISCV_DOUBLE) ? 64 : 32);
	if (riscv_flags & RISCV_FDIV)
		predefine("__riscv_fdiv", 1, "1");
	if (riscv_flags & RISCV_FDIV)
		predefine("__riscv_fsqrt", 1, "1");
	if (riscv_flags & RISCV_MUL)
		predefine("__riscv_mul", 1, "1");
	if ((riscv_flags & RISCV_MUL) && (riscv_flags & RISCV_DIV))
		predefine("__riscv_muldiv", 1, "1");
	if (riscv_flags & RISCV_ZICSR)
		predefine("__riscv_zicsr", 1, "1");
	if (riscv_flags & RISCV_ZIFENCEI)
		predefine("__riscv_zifencei", 1, "1");
	if (riscv_flags & RISCV_ZICBOM)
		predefine("__riscv_zicbom", 1, "1");
	if (riscv_flags & RISCV_ZIHINTPAUSE)
		predefine("__riscv_zihintpause", 1, "1");
	if (riscv_flags & RISCV_VECTOR) {
		predefine("__riscv_vector", 1, "1");
		predefine("__riscv_v_min_vlen", 1, "128");
		predefine("__riscv_v_elen", 1, "64");
		predefine("__riscv_v_elen_fp", 1, "64");
	}

	if (cmodel)
		predefine_strong("__riscv_cmodel_%s", cmodel);
}

const struct target target_riscv32 = {
	.mach = MACH_RISCV32,
	.bitness = ARCH_LP32,
	.big_endian = 0,
	.unsigned_char = 1,
	.flags = RISCV_32BIT|RISCV_GENERIC|RISCV_COMP,

	.target_64bit = &target_riscv64,

	.init = init_riscv32,
	.predefine = predefine_riscv,
	.parse_march = parse_march_riscv,
};

const struct target target_riscv64 = {
	.mach = MACH_RISCV64,
	.bitness = ARCH_LP64,
	.big_endian = 0,
	.unsigned_char = 1,
	.has_int128 = 1,
	.flags = RISCV_64BIT|RISCV_GENERIC|RISCV_COMP,

	.target_32bit = &target_riscv32,

	.init = init_riscv,
	.predefine = predefine_riscv,
	.parse_march = parse_march_riscv,
};
