// WebAssembly 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_WEBASSEMBLY_LIBRARY_HEADER_INCLUDED
#define ECS_WEBASSEMBLY_LIBRARY_HEADER_INCLUDED

#include "wasm"

#define DEFINE_CODE(name) \
	CODE (_##name##_code) GROUP (_s10_codes) CODE_ (I32 (size (@_##name##_code) - 5), VEC (U32 (0)))

#define DEFINE_FUNCTION(name, type) \
	HEADER (_##name##_function) GROUP (_s02_functions) IF ((type) !== ()) REQUIRE (_##name##_code) TYPEIDX (I32 (index (@_##type##_type))) ENDIF

#define DEFINE_GLOBAL(name, type) \
	HEADER (_##name##_global) GROUP (_s05_globals) GLOBAL (GLOBALTYPE (INSTR (valtype type), MUT_VAR))

#define DEFINE_EXPORT(name, exportdesc) \
	HEADER (_##name##_export) REQUIRED GROUP (_s06_exports) EXPORT (NAME (VEC (U32 (offset (name_) - 1)) BYTE (#name) LABEL (name_)), exportdesc)

#define DEFINE_IMPORT(module, name, importdesc) \
	HEADER (_##module.name##_import) GROUP (_s01_imports) IMPORT (NAME (VEC (U32 (offset (module_) - 1)) BYTE (#module) LABEL (module_)), NAME (VEC (U32 (offset (name_) - 1)) BYTE (#name) LABEL (name_)), importdesc)

#define DEFINE_TYPE(name, result, ...) \
	DEFINE (type) IF ((_0) !== ()) INSTR (valtype _0) ENDIF ENDDEF \
	DEFINE (types) INSTR (type _0) INSTR (type _1) INSTR (type _2) INSTR (type _3) INSTR (type _4) INSTR (type _5) INSTR (type _6) INSTR (type _7) INSTR (type _8) INSTR (type _9) ENDDEF \
	HEADER (_##name##_type) GROUP (_s00_types) FUNCTYPE (RESULTTYPE (VEC (U32 (offset (parameter_types) - 1)) INSTR (types __VA_ARGS__) LABEL (parameter_types)), RESULTTYPE (VEC (U32 (offset (result_types) - 1)) INSTR (types result) LABEL (result_types)))

#define EXPORT_FUNCTION(name, result, ...) asm ( \
	DEFINE (push) IF ((_0) !== ()) INSTR (global.get) I32 (index (@_$sp)) IF ((_0) === (i32) || (_0) === (f32)) INSTR (i32.const 4) ELSE INSTR (i32.const 8) ENDIF INSTR (i32.sub) INSTR (global.set) I32 (index (@_$sp)) INSTR (global.get) I32 (index (@_$sp)) INSTR (local.get _1) INSTR (_0.store 0 0) ENDIF ENDDEF \
	DEFINE (parameters) INSTR (push _0, 0) INSTR (push _1, 1) INSTR (push _2, 2) INSTR (push _3, 3) INSTR (push _4, 4) INSTR (push _5, 5) INSTR (push _6, 6) INSTR (push _7, 7) INSTR (push _8, 8) INSTR (push _9, 9) ENDDEF \
	DEFINE_CODE (name##_export) INSTR (parameters __VA_ARGS__) INSTR (call) I32 (index (@_##name##_function)) IF ((result) !== ()) INSTR (global.get) I32 (index (@_$res_##result)) ENDIF INSTR (end) \
	DEFINE_FUNCTION (name##_export, name) \
	DEFINE_EXPORT (name, EXPORTDESC_FUNC (FUNCIDX (I32 (index (@_##name##_export_function))))) \
	DEFINE_TYPE (name, result, __VA_ARGS__) \
	);

#define EXPORT_GLOBAL(name) \
	asm (DEFINE_EXPORT (name, EXPORTDESC_GLOBAL (GLOBALIDX (I32 (index (name))))));

#define IMPORT_FUNCTION(module, name, result, ...) asm ( \
	DEFINE (push) IF ((_0) !== ()) INSTR (global.get) I32 (index (@_$0_i32)) INSTR (_0.load 0 0) INSTR (global.get) I32 (index (@_$0_i32)) IF ((_0) === (i32) || (_0) === (f32)) INSTR (i32.const 4) ELSE INSTR (i32.const 8) ENDIF INSTR (i32.add) INSTR (global.set) I32 (index (@_$0_i32)) ENDIF ENDDEF \
	DEFINE (parameters) INSTR (push _0) INSTR (push _1) INSTR (push _2) INSTR (push _3) INSTR (push _4) INSTR (push _5) INSTR (push _6) INSTR (push _7) INSTR (push _8) INSTR (push _9) ENDDEF \
	DEFINE_CODE (module.name) REQUIRE (_##module.name##_import) INSTR (global.get) I32 (index (@_$sp)) INSTR (global.set) I32 (index (@_$0_i32)) INSTR (parameters __VA_ARGS__) INSTR (call) I32 (index (@_##module.name##_import_function)) IF ((result) !== ()) INSTR (global.set) I32 (index (@_$res_##result)) ENDIF INSTR (end) \
	DEFINE_FUNCTION (module.name, main) \
	DEFINE_FUNCTION (module.name##_import, ) \
	DEFINE_IMPORT (module, name, IMPORTDESC_FUNC (I32 (index (@_##module.name##_type)))) \
	DEFINE_TYPE (module.name, result, __VA_ARGS__) \
	);

#endif // ECS_WEBASSEMBLY_LIBRARY_HEADER_INCLUDED
