// Linux library definitions
// Copyright (C) Florian Negele

// This file is part of the Eigen Compiler Suite.

// The ECS is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The ECS is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of the GNU General Public License version 3,
// the copyright holder grants you additional permissions to use
// this file as described in the ECS Runtime Support Exception.

// You should have received a copy of the GNU General Public License
// and a copy of the ECS Runtime Support Exception along with
// the ECS.  If not, see <https://www.gnu.org/licenses/>.

#ifndef ECS_DEF_LINUXLIB_HEADER_INCLUDED
#define ECS_DEF_LINUXLIB_HEADER_INCLUDED

#if defined __amd32__

	#define BASE 0x08048000
	#define EICLASS ELFCLASS32
	#define R_TYPE 1

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (jmp dword [@_##name])

#elif defined __amd64__

	#define BASE 0x08048000
	#define EICLASS ELFCLASS64
	#define R_TYPE 1

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (pop rbx) \
		IF (parameters > 0) INSTR (mov rdi, [rsp]) ENDIF \
		IF (parameters > 1) INSTR (mov rsi, [rsp + 8]) ENDIF \
		IF (parameters > 2) INSTR (mov rdx, [rsp + 16]) ENDIF \
		IF (parameters > 3) INSTR (mov rcx, [rsp + 24]) ENDIF \
		IF (parameters > 4) INSTR (mov r8, [rsp + 32]) ENDIF \
		IF (parameters > 5) INSTR (mov r9, [rsp + 40]) ENDIF \
		INSTR (mov r12, rsp) INSTR (and rsp, ~1111b) INSTR (call qword [rip + @_##name]) INSTR (mov rsp, r12) INSTR (jmp rbx)

#elif defined __arma32__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS32
	#define R_TYPE 2

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (mov r10, lr) \
		IF (parameters > 0) INSTR (ldr r0, [sp, 0]) ENDIF \
		IF (parameters > 1) INSTR (ldr r1, [sp, 4]) ENDIF \
		IF (parameters > 2) INSTR (ldr r2, [sp, 8]) ENDIF \
		IF (parameters > 3) INSTR (ldr r3, [sp, 12]) ENDIF \
		IF (parameters > 4) INSTR (add sp, sp, parameters * 4) ENDIF \
		INSTR (ldr r4, [pc, offset (address)]) INSTR (ldr r4, [r4, 0]) INSTR (blx r4) \
		IF (parameters > 4) INSTR (sub sp, sp, parameters * 4) ENDIF \
		INSTR (bx r10) LABEL (address) QBYTE (@_##name)

#elif defined __arma64__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS64
	#define R_TYPE 257

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (mov x27, x30) \
		IF (parameters > 0) INSTR (ldr x0, [sp, 0]) ENDIF \
		IF (parameters > 1) INSTR (ldr x1, [sp, 16]) ENDIF \
		IF (parameters > 2) INSTR (ldr x2, [sp, 32]) ENDIF \
		IF (parameters > 3) INSTR (ldr x3, [sp, 48]) ENDIF \
		IF (parameters > 4) INSTR (ldr x4, [sp, 64]) ENDIF \
		IF (parameters > 5) INSTR (ldr x5, [sp, 80]) ENDIF \
		INSTR (ldr x8, @_##name) INSTR (br x8) INSTR (ret x27)

#elif defined __armt32__ || defined __armt32fpe__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS32
	#define R_TYPE 2

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED ALIGNMENT (4) INSTR (mov r10, lr) \
		IF (parameters > 0) INSTR (ldr r0, [sp, 0]) ENDIF \
		IF (parameters > 1) INSTR (ldr r1, [sp, 4]) ENDIF \
		IF (parameters > 2) INSTR (ldr r2, [sp, 8]) ENDIF \
		IF (parameters > 3) INSTR (ldr r3, [sp, 12]) ENDIF \
		IF (parameters > 4) INSTR (ldr r4, [sp, 16]) ENDIF \
		IF (parameters > 5) INSTR (ldr r5, [sp, 20]) ENDIF \
		IF (parameters > 4) INSTR (add sp, sp, parameters * 4) ENDIF \
		INSTR (ldr r4, [pc, offset (address)]) INSTR (ldr r4, [r4, 0]) INSTR (blx r4) \
		IF (parameters > 4) INSTR (sub sp, sp, parameters * 4) ENDIF \
		INSTR (bx r10) LABEL (address) QBYTE (@_##name)

#elif defined __m68k__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS32
	#define R_TYPE 2

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (movea.l (@_##name).l, a1) INSTR (jmp (a1))

#elif defined __mips32__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS32
	#define R_TYPE 2

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (jr r31) INSTR (nop)

#elif defined __mips64__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS64
	#define R_TYPE 2

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (jr r31) INSTR (nop)

#elif defined __or1k__

	#define BASE 0x00008000
	#define EICLASS ELFCLASS32
	#define R_TYPE 2

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED

#elif defined __xtensa__

	#define BASE 0x08048000
	#define EICLASS ELFCLASS32
	#define R_TYPE 3

	#define FUNCTION_CALL(name, parameters) \
		CODE (name) SHARED INSTR (ret)

#else

	#error platform not supported

#endif

#include "elf"

#define LIBRARY(name, file) asm ( \
	CONST (_dynamic_section_##name) SHARED GROUP (_dynamic_section) REQUIRE (_dynamic_section_sentinel) ALIGNMENT (ADDRSIZE) DYNAMIC_SECTION_ENTRY (1, position (@_##name##_string) + 1) \
	CONST (_##name##_string) SHARED GROUP (_string_table) BYTE (file, 0) \
	);

#define FUNCTION(library, name, parameters) asm ( \
	CONST (_##name##_symbol) SHARED GROUP (_symbol_table) REQUIRE (_dynamic_section_##library) ALIGNMENT (ADDRSIZE) SYMBOL_TABLE_ENTRY (position (@_##name##_string) + 1, 0, 0, STB_GLOBAL << 4 | STT_FUNC, STV_DEFAULT, 0) \
	CONST (_##name##_string) SHARED GROUP (_string_table) BYTE (#name, 0) \
	CONST (_##name##_relocation) SHARED GROUP (_relocation_table) ALIGNMENT (ADDRSIZE) RELOCATION_TABLE_ENTRY (@_##name, R_TYPE, index (@_##name##_symbol) + 1, 0) \
	CONST (_##name) SHARED REQUIRE (_##name##_relocation) ALIGNMENT (ADDRSIZE) RESERVE (ADDRSIZE) \
	FUNCTION_CALL (name, parameters) \
	);

#endif // ECS_DEF_LINUXLIB_HEADER_INCLUDED
